xref: /PHP-8.2/sapi/embed/php_embed.c (revision b5db594f)
1 /*
2    +----------------------------------------------------------------------+
3    | Copyright (c) The PHP Group                                          |
4    +----------------------------------------------------------------------+
5    | This source file is subject to version 3.01 of the PHP license,      |
6    | that is bundled with this package in the file LICENSE, and is        |
7    | available through the world-wide-web at the following url:           |
8    | https://www.php.net/license/3_01.txt                                 |
9    | If you did not receive a copy of the PHP license and are unable to   |
10    | obtain it through the world-wide-web, please send a note to          |
11    | license@php.net so we can mail you a copy immediately.               |
12    +----------------------------------------------------------------------+
13    | Author: Edin Kadribasic <edink@php.net>                              |
14    +----------------------------------------------------------------------+
15 */
16 
17 #include "php_embed.h"
18 #include "ext/standard/php_standard.h"
19 #include "ext/standard/dl_arginfo.h"
20 
21 #ifdef PHP_WIN32
22 #include <io.h>
23 #include <fcntl.h>
24 #endif
25 
26 const char HARDCODED_INI[] =
27 	"html_errors=0\n"
28 	"register_argc_argv=1\n"
29 	"implicit_flush=1\n"
30 	"output_buffering=0\n"
31 	"max_execution_time=0\n"
32 	"max_input_time=-1\n\0";
33 
34 #if defined(PHP_WIN32) && defined(ZTS)
ZEND_TSRMLS_CACHE_DEFINE()35 ZEND_TSRMLS_CACHE_DEFINE()
36 #endif
37 
38 static char* php_embed_read_cookies(void)
39 {
40 	return NULL;
41 }
42 
php_embed_deactivate(void)43 static int php_embed_deactivate(void)
44 {
45 	fflush(stdout);
46 	return SUCCESS;
47 }
48 
49 /* Here we prefer to use write(), which is unbuffered, over fwrite(), which is
50  * buffered. Using an unbuffered write operation to stdout will ensure PHP's
51  * output buffering feature does not compete with a SAPI output buffer and
52  * therefore we avoid situations wherein flushing the output buffer results in
53  * nondeterministic behavior.
54  */
php_embed_single_write(const char * str,size_t str_length)55 static inline size_t php_embed_single_write(const char *str, size_t str_length)
56 {
57 #ifdef PHP_WRITE_STDOUT
58 	zend_long ret;
59 
60 	ret = write(STDOUT_FILENO, str, str_length);
61 	if (ret <= 0) return 0;
62 	return ret;
63 #else
64 	size_t ret;
65 
66 	ret = fwrite(str, 1, MIN(str_length, 16384), stdout);
67 	return ret;
68 #endif
69 }
70 
71 /* SAPIs only have unbuffered write operations. This is because PHP's output
72  * buffering feature will handle any buffering of the output and invoke the
73  * SAPI unbuffered write operation when it flushes the buffer.
74  */
php_embed_ub_write(const char * str,size_t str_length)75 static size_t php_embed_ub_write(const char *str, size_t str_length)
76 {
77 	const char *ptr = str;
78 	size_t remaining = str_length;
79 	size_t ret;
80 
81 	while (remaining > 0) {
82 		ret = php_embed_single_write(ptr, remaining);
83 		if (!ret) {
84 			php_handle_aborted_connection();
85 		}
86 		ptr += ret;
87 		remaining -= ret;
88 	}
89 
90 	return str_length;
91 }
92 
php_embed_flush(void * server_context)93 static void php_embed_flush(void *server_context)
94 {
95 	if (fflush(stdout)==EOF) {
96 		php_handle_aborted_connection();
97 	}
98 }
99 
php_embed_send_header(sapi_header_struct * sapi_header,void * server_context)100 static void php_embed_send_header(sapi_header_struct *sapi_header, void *server_context)
101 {
102 }
103 
104 /* The SAPI error logger that is called when the 'error_log' INI setting is not
105  * set.
106  *
107  * https://www.php.net/manual/en/errorfunc.configuration.php#ini.error-log
108  */
php_embed_log_message(const char * message,int syslog_type_int)109 static void php_embed_log_message(const char *message, int syslog_type_int)
110 {
111 	fprintf(stderr, "%s\n", message);
112 }
113 
php_embed_register_variables(zval * track_vars_array)114 static void php_embed_register_variables(zval *track_vars_array)
115 {
116 	php_import_environment_variables(track_vars_array);
117 }
118 
119 /* Module initialization (MINIT) */
php_embed_startup(sapi_module_struct * sapi_module)120 static int php_embed_startup(sapi_module_struct *sapi_module)
121 {
122 	return php_module_startup(sapi_module, NULL);
123 }
124 
125 EMBED_SAPI_API sapi_module_struct php_embed_module = {
126 	"embed",                       /* name */
127 	"PHP Embedded Library",        /* pretty name */
128 
129 	php_embed_startup,             /* startup */
130 	php_module_shutdown_wrapper,   /* shutdown */
131 
132 	NULL,                          /* activate */
133 	php_embed_deactivate,          /* deactivate */
134 
135 	php_embed_ub_write,            /* unbuffered write */
136 	php_embed_flush,               /* flush */
137 	NULL,                          /* get uid */
138 	NULL,                          /* getenv */
139 
140 	php_error,                     /* error handler */
141 
142 	NULL,                          /* header handler */
143 	NULL,                          /* send headers handler */
144 	php_embed_send_header,         /* send header handler */
145 
146 	NULL,                          /* read POST data */
147 	php_embed_read_cookies,        /* read Cookies */
148 
149 	php_embed_register_variables,  /* register server variables */
150 	php_embed_log_message,         /* Log message */
151 	NULL,                          /* Get request time */
152 	NULL,                          /* Child terminate */
153 
154 	STANDARD_SAPI_MODULE_PROPERTIES
155 };
156 /* }}} */
157 
158 static const zend_function_entry additional_functions[] = {
159 	ZEND_FE(dl, arginfo_dl)
160 	ZEND_FE_END
161 };
162 
php_embed_init(int argc,char ** argv)163 EMBED_SAPI_API int php_embed_init(int argc, char **argv)
164 {
165 #if defined(SIGPIPE) && defined(SIG_IGN)
166 	signal(SIGPIPE, SIG_IGN); /* ignore SIGPIPE in standalone mode so
167 								 that sockets created via fsockopen()
168 								 don't kill PHP if the remote site
169 								 closes it.  in apache|apxs mode apache
170 								 does that for us!  thies@thieso.net
171 								 20000419 */
172 #endif
173 
174 #ifdef ZTS
175 	php_tsrm_startup();
176 # ifdef PHP_WIN32
177 	ZEND_TSRMLS_CACHE_UPDATE();
178 # endif
179 #endif
180 
181 	zend_signal_startup();
182 
183 	/* SAPI initialization (SINIT)
184 	 *
185 	 * Initialize the SAPI globals (memset to 0). After this point we can set
186 	 * SAPI globals via the SG() macro.
187 	 *
188 	 * Reentrancy startup.
189 	 *
190 	 * This also sets 'php_embed_module.ini_entries = NULL' so we cannot
191 	 * allocate the INI entries until after this call.
192 	 */
193 	sapi_startup(&php_embed_module);
194 
195 #ifdef PHP_WIN32
196 	_fmode = _O_BINARY;			/*sets default for file streams to binary */
197 	setmode(_fileno(stdin), O_BINARY);		/* make the stdio mode be binary */
198 	setmode(_fileno(stdout), O_BINARY);		/* make the stdio mode be binary */
199 	setmode(_fileno(stderr), O_BINARY);		/* make the stdio mode be binary */
200 #endif
201 
202 	/* This hard-coded string of INI settings is parsed and read into PHP's
203 	 * configuration hash table at the very end of php_init_config(). This
204 	 * means these settings will overwrite any INI settings that were set from
205 	 * an INI file.
206 	 *
207 	 * To provide overwritable INI defaults, hook the ini_defaults function
208 	 * pointer that is part of the sapi_module_struct
209 	 * (php_embed_module.ini_defaults).
210 	 *
211 	 *     void (*ini_defaults)(HashTable *configuration_hash);
212 	 *
213 	 * This callback is invoked as soon as the configuration hash table is
214 	 * allocated so any INI settings added via this callback will have the
215 	 * lowest precedence and will allow INI files to overwrite them.
216 	 */
217 	php_embed_module.ini_entries = malloc(sizeof(HARDCODED_INI));
218 	memcpy(php_embed_module.ini_entries, HARDCODED_INI, sizeof(HARDCODED_INI));
219 
220 	/* SAPI-provided functions. */
221 	php_embed_module.additional_functions = additional_functions;
222 
223 	if (argv) {
224 		php_embed_module.executable_location = argv[0];
225 	}
226 
227 	/* Module initialization (MINIT) */
228 	if (php_embed_module.startup(&php_embed_module) == FAILURE) {
229 		return FAILURE;
230 	}
231 
232 	/* Do not chdir to the script's directory. This is akin to calling the CGI
233 	 * SAPI with '-C'.
234 	 */
235 	SG(options) |= SAPI_OPTION_NO_CHDIR;
236 
237 	SG(request_info).argc=argc;
238 	SG(request_info).argv=argv;
239 
240 	/* Request initialization (RINIT) */
241 	if (php_request_startup() == FAILURE) {
242 		php_module_shutdown();
243 		return FAILURE;
244 	}
245 
246 	SG(headers_sent) = 1;
247 	SG(request_info).no_headers = 1;
248 	php_register_variable("PHP_SELF", "-", NULL);
249 
250 	return SUCCESS;
251 }
252 
php_embed_shutdown(void)253 EMBED_SAPI_API void php_embed_shutdown(void)
254 {
255 	/* Request shutdown (RSHUTDOWN) */
256 	php_request_shutdown((void *) 0);
257 
258 	/* Module shutdown (MSHUTDOWN) */
259 	php_module_shutdown();
260 
261 	/* SAPI shutdown (SSHUTDOWN) */
262 	sapi_shutdown();
263 
264 #ifdef ZTS
265 	tsrm_shutdown();
266 #endif
267 
268 	if (php_embed_module.ini_entries) {
269 		free(php_embed_module.ini_entries);
270 		php_embed_module.ini_entries = NULL;
271 	}
272 }
273