PHP compatibility and project lifecycle
Source is the PHP-8.4 branch of php-src. Requires a PHP build with the embed SAPI enabled (./configure --enable-embed), a C compiler and php-config. Platform, threading and PHP 8.5 behavior are unknown from the source. Not a browser WASM runtime.
Official sources checked 2026-10-22. Browser lessons on this site run PHP 8.5.
It is based only on the README of the embed SAPI directory in the PHP-8.4 branch of php-src.
What this project is
A server application programming interface (SAPI) is the entry point into the Zend Engine. The embed SAPI is a lightweight SAPI for calling into the Zend Engine from C, or from other languages that have C bindings.
The embed SAPI is therefore part of PHP core's source tree. It is not a web server, a framework, a CMS or a separate interpreter. It does not replace PHP. It is one way of linking the real PHP engine into your own native program. Drupal, for example, is a CMS product built on PHP and is unrelated to this SAPI. Framework components and CMS products also sit above PHP rather than beside it. Swoole and Open Swoole are separate projects as well, and this source says nothing about them.
It is also separate from browser WebAssembly builds of PHP. The embed SAPI described here targets a native host process that is compiled and linked on your machine.
Architecture
The host is an ordinary C program that includes sapi/embed/php_embed.h and links against the PHP shared library. The README states that libphp.so is built when the embed SAPI is enabled. The host owns main(). PHP is a library it calls into.
The README describes two macros that wrap your code:
PHP_EMBED_START_BLOCK(argc, argv)runs the initialization phases: SAPI startup (SINIT), module startup (MINIT) and request startup (RINIT). It also opens azend_tryblock that catches azend_bailout().PHP_EMBED_END_BLOCK()closes that block and runs the shutdown phases: request shutdown (RSHUTDOWN), module shutdown (MSHUTDOWN) and SAPI shutdown (SSHUTDOWN).
Between the two macros your code can read engine state, call PHP functions with zend_call_function, evaluate strings with zend_eval_stringl, or run a file with php_execute_script. The README demonstrates each of these.
Execution and content model
There is no HTTP layer and no built-in routing. Input and output are whatever the host decides. php_printf writes output, and scripts are files or strings that you hand to the engine.
The README examples follow the classic lifetime. Each program starts the engine, runs one request-shaped unit of work, and shuts everything down. That is the same startup, request and shutdown sequence PHP uses elsewhere, compressed into a single process run.
A host could in principle keep the process alive and run many units of work. The README does not document such a worker pattern, so treat it as your own design responsibility. If you build one, state leakage becomes your problem. In a persistent process, anything that survives between units of work can bleed into the next one: static properties, global variables, singletons, open handles, caches and modified INI settings. A classic request lifetime discards these at request shutdown. A long-lived host must instead do deliberate cleanup. That means resetting or avoiding mutable globals, closing resources, and recycling the process when state cannot be cleaned reliably. The source packet gives no details on how to do this safely, so verify against the PHP internals documentation and your own tests.
Practical use cases
The README supports these uses:
- Running PHP scripts from a C program, as in the script execution example.
- Calling individual PHP functions from native code.
- Evaluating PHP snippets and supplying INI defaults from the host.
- Reaching PHP from languages that have C bindings.
The source does not discuss production patterns, performance, or which application types suit it. Those points are unknown here.
Installation approach
The embed SAPI is disabled by default. To see which SAPIs your PHP installation provides, run php-config --php-sapis. If embed is missing, the README says you need to rebuild PHP with ./configure --enable-embed. This guide does not cover the full PHP build. Use the official build documentation for that, and select the PHP 8.4 sources if you follow this track. The README gives the compile command shape:
php-config --includessupplies the header paths.- The linker needs
-Lpointing at$(php-config --prefix)/lib, plus-lphp. - An rpath entry lets the runtime loader find
libphp.so.
The examples below reuse that shape. They need a C compiler and a PHP build with the embed SAPI enabled. No Composer, Docker or network service is involved, because the source does not describe a container workflow. These are not browser exercises.
Strengths
- It uses the same Zend Engine as the rest of PHP, reached through a small C interface.
- Host-controlled INI defaults let you set the lowest-precedence configuration. INI files can still override them.
- The macro pair makes the startup and shutdown phases explicit.
Limitations
- It is disabled by default, so you must have or build a PHP with
--enable-embed. - It requires C-level knowledge of Zend Engine structures such as
zvalandzend_fcall_info. - The README gives no guidance on threading, persistent workers, memory behavior or supported platforms. Treat all of these as unknown.
- There are no packaged versions, license or support-status statements in the source packet. Lifecycle is marked unknown for that reason.
- Embedded scripts run with the host process's privileges, so only run code you trust.
Deployment notes
Deployment means shipping a native executable that depends on a matching libphp.so and its PHP configuration. The rpath in the README command points at the PHP prefix on the build machine, so a deployment target must provide a compatible library at a known path. The embed SAPI does not speak FastCGI or HTTP. If you wrap it in a network service, that service is your own code, and you should keep development listeners on 127.0.0.1 and never expose a FastCGI endpoint publicly.
Examples overview
The first example runs a PHP file from a C host. The second sets an INI default from the host. Both follow the README. Run them from inside the example directory, because the script path is relative.
Links
The official source is the embed SAPI directory in php-src on the PHP-8.4 branch, listed below.
Practical examples
These examples have not been run in a live environment yet. Commands and versions follow the official documentation linked on this page; check them before relying on them, and report a problem if one does not work.
Run a PHP script from a C host with the embed SAPI
A native C program starts the Zend Engine through the embed macros and executes example.php from the current directory, following the README script execution example. Not a browser exercise.
embed-example/embed_run_script.c
#include <sapi/embed/php_embed.h>
int main(int argc, char **argv)
{
PHP_EMBED_START_BLOCK(argc, argv)
zend_file_handle file_handle;
zend_stream_init_filename(&file_handle, "example.php");
if (php_execute_script(&file_handle) == FAILURE) {
php_printf("Failed to execute PHP script.\n");
}
PHP_EMBED_END_BLOCK()
}
embed-example/example.php
# example.php
echo 'Hello from userland!' . PHP_EOL;
Install and run
cd embed-example
php-config --php-sapis
cc $(php-config --includes) -L$(php-config --prefix)/lib embed_run_script.c -o embed_run_script -lphp -Wl,-rpath,$(php-config --prefix)/lib
./embed_run_scriptExpected behavior
The php-config output should list embed. After compiling, running the program should print 'Hello from userland!' from example.php. If the script cannot be executed, the host prints 'Failed to execute PHP script.'
Requires PHP built with --enable-embed so libphp.so exists. The script path is relative, so run from the embed-example directory. One request-shaped run per process; no persistent worker, HTTP server or FastCGI is involved.
Set an INI default from the host with ini_defaults
A C host assigns php_embed_module.ini_defaults to supply error_prepend_string before startup, then evaluates code that triggers a warning, following the README INI defaults example.
embed-ini/embed_ini_defaults.c
#include <sapi/embed/php_embed.h>
/* Invoked as soon as the configuration hash table is allocated, so values
* added here have the lowest precedence and INI files can overwrite them.
*/
static void example_ini_defaults(HashTable *configuration_hash)
{
zval ini_value;
ZVAL_NEW_STR(&ini_value, zend_string_init(ZEND_STRL("Embed SAPI error:"), /* persistent */ 1));
zend_hash_str_update(configuration_hash, ZEND_STRL("error_prepend_string"), &ini_value);
}
int main(int argc, char **argv)
{
php_embed_module.ini_defaults = example_ini_defaults;
PHP_EMBED_START_BLOCK(argc, argv)
zval retval;
/* Generates an error by accessing an undefined variable '$a'. */
if (zend_eval_stringl(ZEND_STRL("var_dump($a);"), &retval, "example") == FAILURE) {
php_printf("Failed to eval PHP.\n");
}
PHP_EMBED_END_BLOCK()
}
Install and run
cd embed-ini
cc $(php-config --includes) -L$(php-config --prefix)/lib embed_ini_defaults.c -o embed_ini_defaults -lphp -Wl,-rpath,$(php-config --prefix)/lib
./embed_ini_defaultsExpected behavior
Per the README, output should show 'Embed SAPI error:' followed by a warning for undefined variable $a on line 1 of 'example', then NULL. If a loaded INI file sets error_prepend_string, that value should replace the host default without recompiling.
Requires PHP built with --enable-embed. The php --ini command may report a different PHP installation than the one linked here, so confirm which INI files apply. Warning wording may vary by build.