xref: /PHP-8.4/sapi/embed/README.md (revision c44834d8)
1# The embed SAPI
2
3A 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 other languages that have C bindings.
4
5## Basic Example
6
7Below is a basic example in C that uses the embed SAPI to boot up the Zend Engine, start a request, and print the number of functions loaded in the function table.
8
9```c
10/* embed_sapi_basic_example.c */
11
12#include <sapi/embed/php_embed.h>
13
14int main(int argc, char **argv)
15{
16	/* Invokes the Zend Engine initialization phase: SAPI (SINIT), modules
17	 * (MINIT), and request (RINIT). It also opens a 'zend_try' block to catch
18	 * a zend_bailout().
19	 */
20	PHP_EMBED_START_BLOCK(argc, argv)
21
22	php_printf(
23		"Number of functions loaded: %d\n",
24		zend_hash_num_elements(EG(function_table))
25	);
26
27	/* Close the 'zend_try' block and invoke the shutdown phase: request
28	 * (RSHUTDOWN), modules (MSHUTDOWN), and SAPI (SSHUTDOWN).
29	 */
30	PHP_EMBED_END_BLOCK()
31}
32```
33
34To compile this, we must point the compiler to the PHP header files. The paths to the header files are listed from `php-config --includes`.
35
36We must also point the linker and the runtime loader to the `libphp.so` shared lib for linking PHP (`-lphp`) which is located at `$(php-config --prefix)/lib`. So the complete command to compile ends up being:
37
38```bash
39$  gcc \
40	$(php-config --includes) \
41	-L$(php-config --prefix)/lib \
42	embed_sapi_basic_example.c \
43	-lphp \
44	-Wl,-rpath=$(php-config --prefix)/lib
45```
46
47> :memo: The embed SAPI is disabled by default. In order for the above example to compile, PHP must be built with the embed SAPI enabled. To see what SAPIs are installed, run `php-config --php-sapis`. If you don't see `embed` in the list, you'll need to rebuild PHP with `./configure --enable-embed`. The PHP shared library `libphp.so` is built when the embed SAPI is enabled.
48
49If all goes to plan you should be able to run the program.
50
51```bash
52$ ./a.out
53Number of functions loaded: 1046
54```
55
56## Function call example
57
58The following example calls `mt_rand()` and `var_dump()`s the return value.
59
60```c
61#include <main/php.h>
62#include <ext/standard/php_var.h>
63#include <sapi/embed/php_embed.h>
64
65int main(int argc, char **argv)
66{
67	PHP_EMBED_START_BLOCK(argc, argv)
68
69	zval retval = {0};
70	zend_fcall_info fci = {0};
71	zend_fcall_info_cache fci_cache = {0};
72
73	zend_string *func_name = zend_string_init(ZEND_STRL("mt_rand"), 0);
74	ZVAL_STR(&fci.function_name, func_name);
75
76	fci.size = sizeof fci;
77	fci.retval = &retval;
78
79	if (zend_call_function(&fci, &fci_cache) == SUCCESS) {
80		php_var_dump(&retval, 1);
81	}
82
83	zend_string_release(func_name);
84
85	PHP_EMBED_END_BLOCK()
86}
87```
88
89## Execute a PHP script example
90
91```php
92<?php
93
94# example.php
95
96echo 'Hello from userland!' . PHP_EOL;
97```
98
99```c
100#include <sapi/embed/php_embed.h>
101
102int main(int argc, char **argv)
103{
104	PHP_EMBED_START_BLOCK(argc, argv)
105
106	zend_file_handle file_handle;
107	zend_stream_init_filename(&file_handle, "example.php");
108
109	if (php_execute_script(&file_handle) == FAILURE) {
110		php_printf("Failed to execute PHP script.\n");
111	}
112
113	PHP_EMBED_END_BLOCK()
114}
115```
116
117## INI defaults
118
119The default value for 'error_prepend_string' is 'NULL'. The following example sets the INI default for 'error_prepend_string' to 'Embed SAPI error:'.
120
121```c
122#include <sapi/embed/php_embed.h>
123
124/* This callback is invoked as soon as the configuration hash table is
125 * allocated so any INI settings added via this callback will have the lowest
126 * precedence and will allow INI files to overwrite them.
127 */
128static void example_ini_defaults(HashTable *configuration_hash)
129{
130	zval ini_value;
131	ZVAL_NEW_STR(&ini_value, zend_string_init(ZEND_STRL("Embed SAPI error:"), /* persistent */ 1));
132	zend_hash_str_update(configuration_hash, ZEND_STRL("error_prepend_string"), &ini_value);
133}
134
135int main(int argc, char **argv)
136{
137	php_embed_module.ini_defaults = example_ini_defaults;
138
139	PHP_EMBED_START_BLOCK(argc, argv)
140
141	zval retval;
142
143	/* Generates an error by accessing an undefined variable '$a'. */
144	if (zend_eval_stringl(ZEND_STRL("var_dump($a);"), &retval, "example") == FAILURE) {
145		php_printf("Failed to eval PHP.\n");
146	}
147
148	PHP_EMBED_END_BLOCK()
149}
150```
151
152After compiling and running, you should see:
153
154```
155Embed SAPI error:
156Warning: Undefined variable $a in example on line 1
157NULL
158```
159
160This default value is overwritable from INI files. We'll update one of the INI files (which can be found by running `$ php --ini`), and set `error_prepend_string="Oops!"`. We don't have to recompile the program, we can just run it again and we should see:
161
162```
163Oops!
164Warning: Undefined variable $a in example on line 1
165NULL
166```
167