xref: /unit/src/nxt_php_sapi.c (revision 2597:9dd704cc466f)
1 /*
2  * Copyright (C) Max Romanov
3  * Copyright (C) Valentin V. Bartenev
4  * Copyright (C) NGINX, Inc.
5  */
6 
7 #include "php.h"
8 #include "SAPI.h"
9 #include "php_main.h"
10 #include "php_variables.h"
11 #include "ext/standard/php_standard.h"
12 
13 #include <nxt_main.h>
14 #include <nxt_router.h>
15 #include <nxt_unit.h>
16 #include <nxt_unit_request.h>
17 #include <nxt_http.h>
18 
19 
20 #if (PHP_VERSION_ID >= 50400)
21 #define NXT_HAVE_PHP_IGNORE_CWD 1
22 #endif
23 
24 #if (PHP_VERSION_ID >= 70100)
25 #define NXT_HAVE_PHP_LOG_MESSAGE_WITH_SYSLOG_TYPE 1
26 #else
27 #define NXT_HAVE_PHP_INTERRUPTS 1
28 #endif
29 
30 #if (PHP_VERSION_ID >= 70000)
31 #define NXT_PHP7 1
32 #endif
33 #if (PHP_VERSION_ID >= 80000)
34 #define NXT_PHP8 1
35 #endif
36 
37 /* PHP 8 */
38 #ifndef TSRMLS_CC
39 #define TSRMLS_CC
40 #define TSRMLS_DC
41 #define TSRMLS_D  void
42 #define TSRMLS_C
43 #endif
44 
45 
46 typedef struct {
47     nxt_str_t  root;
48     nxt_str_t  index;
49     nxt_str_t  script_name;
50     nxt_str_t  script_dirname;
51     nxt_str_t  script_filename;
52 } nxt_php_target_t;
53 
54 
55 typedef struct {
56     char                     *cookie;
57     nxt_str_t                *root;
58     nxt_str_t                *index;
59     nxt_str_t                path_info;
60     nxt_str_t                script_name;
61     nxt_str_t                script_filename;
62     nxt_str_t                script_dirname;
63     nxt_unit_request_info_t  *req;
64 
65     uint8_t                  chdir;  /* 1 bit */
66 } nxt_php_run_ctx_t;
67 
68 
69 #if NXT_PHP8
70 typedef int (*nxt_php_disable_t)(const char *p, size_t size);
71 #elif NXT_PHP7
72 typedef int (*nxt_php_disable_t)(char *p, size_t size);
73 #else
74 typedef int (*nxt_php_disable_t)(char *p, uint TSRMLS_DC);
75 #endif
76 
77 #if (PHP_VERSION_ID < 70200)
78 typedef void (*zif_handler)(INTERNAL_FUNCTION_PARAMETERS);
79 #endif
80 
81 
82 static nxt_int_t nxt_php_setup(nxt_task_t *task, nxt_process_t *process,
83     nxt_common_app_conf_t *conf);
84 static nxt_int_t nxt_php_start(nxt_task_t *task, nxt_process_data_t *data);
85 static nxt_int_t nxt_php_set_target(nxt_task_t *task, nxt_php_target_t *target,
86     nxt_conf_value_t *conf);
87 static nxt_int_t nxt_php_set_ini_path(nxt_task_t *task, nxt_str_t *path,
88     char *workdir);
89 static void nxt_php_set_options(nxt_task_t *task, nxt_conf_value_t *options,
90     int type);
91 static nxt_int_t nxt_php_alter_option(nxt_str_t *name, nxt_str_t *value,
92     int type);
93 #ifdef NXT_PHP8
94 static void nxt_php_disable_functions(nxt_str_t *str);
95 #endif
96 static void nxt_php_disable(nxt_task_t *task, const char *type,
97     nxt_str_t *value, char **ptr, nxt_php_disable_t disable);
98 
99 static nxt_int_t nxt_php_dirname(const nxt_str_t *file, nxt_str_t *dir);
100 static void nxt_php_str_trim_trail(nxt_str_t *str, u_char t);
101 static void nxt_php_str_trim_lead(nxt_str_t *str, u_char t);
102 nxt_inline u_char *nxt_realpath(const void *c);
103 
104 static nxt_int_t nxt_php_do_301(nxt_unit_request_info_t *req);
105 static nxt_int_t nxt_php_handle_fs_err(nxt_unit_request_info_t *req);
106 
107 static void nxt_php_request_handler(nxt_unit_request_info_t *req);
108 static void nxt_php_dynamic_request(nxt_php_run_ctx_t *ctx,
109     nxt_unit_request_t *r);
110 #if (PHP_VERSION_ID < 70400)
111 static void nxt_zend_stream_init_fp(zend_file_handle *handle, FILE *fp,
112     const char *filename);
113 #endif
114 static void nxt_php_execute(nxt_php_run_ctx_t *ctx, nxt_unit_request_t *r);
115 nxt_inline void nxt_php_vcwd_chdir(nxt_unit_request_info_t *req, u_char *dir);
116 
117 static int nxt_php_startup(sapi_module_struct *sapi_module);
118 static int nxt_php_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC);
119 static void *nxt_php_hash_str_find_ptr(const HashTable *ht,
120     const nxt_str_t *str);
121 static char *nxt_php_read_cookies(TSRMLS_D);
122 static void nxt_php_set_sptr(nxt_unit_request_info_t *req, const char *name,
123     nxt_unit_sptr_t *v, uint32_t len, zval *track_vars_array TSRMLS_DC);
124 nxt_inline void nxt_php_set_str(nxt_unit_request_info_t *req, const char *name,
125     nxt_str_t *s, zval *track_vars_array TSRMLS_DC);
126 static void nxt_php_set_cstr(nxt_unit_request_info_t *req, const char *name,
127     const char *str, uint32_t len, zval *track_vars_array TSRMLS_DC);
128 static void nxt_php_register_variables(zval *track_vars_array TSRMLS_DC);
129 #if NXT_PHP8
130 static void nxt_php_log_message(const char *message, int syslog_type_int);
131 #else
132 #ifdef NXT_HAVE_PHP_LOG_MESSAGE_WITH_SYSLOG_TYPE
133 static void nxt_php_log_message(char *message, int syslog_type_int);
134 #else
135 static void nxt_php_log_message(char *message TSRMLS_DC);
136 #endif
137 #endif
138 
139 #ifdef NXT_PHP7
140 static size_t nxt_php_unbuffered_write(const char *str,
141     size_t str_length TSRMLS_DC);
142 static size_t nxt_php_read_post(char *buffer, size_t count_bytes TSRMLS_DC);
143 #else
144 static int nxt_php_unbuffered_write(const char *str, uint str_length TSRMLS_DC);
145 static int nxt_php_read_post(char *buffer, uint count_bytes TSRMLS_DC);
146 #endif
147 
148 
149 #ifdef NXT_PHP7
150 #if (PHP_VERSION_ID < 70200)
151 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_fastcgi_finish_request, 0, 0,
152                                         _IS_BOOL, NULL, 0)
153 #else
154 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_fastcgi_finish_request, 0, 0,
155                                         _IS_BOOL, 0)
156 #endif
157 #else /* PHP5 */
158 ZEND_BEGIN_ARG_INFO_EX(arginfo_fastcgi_finish_request, 0, 0, 0)
159 #endif
160 ZEND_END_ARG_INFO()
161 
162 ZEND_FUNCTION(fastcgi_finish_request);
163 
164 PHP_MINIT_FUNCTION(nxt_php_ext);
165 ZEND_NAMED_FUNCTION(nxt_php_chdir);
166 
167 
168 static const zend_function_entry  nxt_php_ext_functions[] = {
169     ZEND_FE(fastcgi_finish_request, arginfo_fastcgi_finish_request)
170     ZEND_FE_END
171 };
172 
173 
174 zif_handler       nxt_php_chdir_handler;
175 zend_auto_global  *nxt_php_server_ag;
176 
177 
178 static zend_module_entry  nxt_php_unit_module = {
179     STANDARD_MODULE_HEADER,
180     "unit",
181     nxt_php_ext_functions,       /* function table */
182     PHP_MINIT(nxt_php_ext),      /* initialization */
183     NULL,                        /* shutdown */
184     NULL,                        /* request initialization */
185     NULL,                        /* request shutdown */
186     NULL,                        /* information */
187     NXT_VERSION,
188     STANDARD_MODULE_PROPERTIES
189 };
190 
191 
PHP_MINIT_FUNCTION(nxt_php_ext)192 PHP_MINIT_FUNCTION(nxt_php_ext)
193 {
194     zend_function  *func;
195 
196     static const nxt_str_t  chdir = nxt_string("chdir");
197 
198     func = nxt_php_hash_str_find_ptr(CG(function_table), &chdir);
199     if (nxt_slow_path(func == NULL)) {
200         return FAILURE;
201     }
202 
203     nxt_php_chdir_handler = func->internal_function.handler;
204     func->internal_function.handler = nxt_php_chdir;
205 
206     return SUCCESS;
207 }
208 
209 
ZEND_NAMED_FUNCTION(nxt_php_chdir)210 ZEND_NAMED_FUNCTION(nxt_php_chdir)
211 {
212     nxt_php_run_ctx_t  *ctx;
213 
214     ctx = SG(server_context);
215 
216     if (nxt_fast_path(ctx != NULL)) {
217         ctx->chdir = 1;
218     }
219 
220     nxt_php_chdir_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU);
221 }
222 
223 
PHP_FUNCTION(fastcgi_finish_request)224 PHP_FUNCTION(fastcgi_finish_request)
225 {
226     zend_auto_global   *ag;
227     nxt_php_run_ctx_t  *ctx;
228 
229     if (nxt_slow_path(zend_parse_parameters_none() == FAILURE)) {
230 #ifdef NXT_PHP8
231         RETURN_THROWS();
232 #else
233         return;
234 #endif
235     }
236 
237     ctx = SG(server_context);
238 
239     if (nxt_slow_path(ctx == NULL || ctx->req == NULL)) {
240         RETURN_FALSE;
241     }
242 
243 #ifdef NXT_PHP7
244     php_output_end_all();
245     php_header();
246 #else
247 #ifdef PHP_OUTPUT_NEWAPI
248     php_output_end_all(TSRMLS_C);
249 #else
250     php_end_ob_buffers(1 TSRMLS_CC);
251 #endif
252 
253     php_header(TSRMLS_C);
254 #endif
255 
256     ag = nxt_php_server_ag;
257 
258     if (ag->armed) {
259 #ifdef NXT_PHP7
260         ag->armed = ag->auto_global_callback(ag->name);
261 #else
262         ag->armed = ag->auto_global_callback(ag->name, ag->name_len TSRMLS_CC);
263 #endif
264     }
265 
266     nxt_unit_request_done(ctx->req, NXT_UNIT_OK);
267     ctx->req = NULL;
268 
269     PG(connection_status) = PHP_CONNECTION_ABORTED;
270 #ifdef NXT_PHP7
271     php_output_set_status(PHP_OUTPUT_DISABLED);
272 #else
273 #ifdef PHP_OUTPUT_NEWAPI
274     php_output_set_status(PHP_OUTPUT_DISABLED TSRMLS_CC);
275 #else
276     php_output_set_status(0 TSRMLS_CC);
277 #endif
278 #endif
279 
280     RETURN_TRUE;
281 }
282 
283 
284 static sapi_module_struct  nxt_php_sapi_module =
285 {
286     (char *) "cli-server",
287     (char *) "unit",
288 
289     nxt_php_startup,             /* startup */
290     php_module_shutdown_wrapper, /* shutdown */
291 
292     NULL,                        /* activate */
293     NULL,                        /* deactivate */
294 
295     nxt_php_unbuffered_write,    /* unbuffered write */
296     NULL,                        /* flush */
297     NULL,                        /* get uid */
298     NULL,                        /* getenv */
299 
300     php_error,                   /* error handler */
301 
302     NULL,                        /* header handler */
303     nxt_php_send_headers,        /* send headers handler */
304     NULL,                        /* send header handler */
305 
306     nxt_php_read_post,           /* read POST data */
307     nxt_php_read_cookies,        /* read Cookies */
308 
309     nxt_php_register_variables,  /* register server variables */
310     nxt_php_log_message,         /* log message */
311     NULL,                        /* get request time */
312     NULL,                        /* terminate process */
313 
314     NULL,                        /* php_ini_path_override */
315 #ifdef NXT_HAVE_PHP_INTERRUPTS
316     NULL,                        /* block_interruptions */
317     NULL,                        /* unblock_interruptions */
318 #endif
319     NULL,                        /* default_post_reader */
320     NULL,                        /* treat_data */
321     NULL,                        /* executable_location */
322 
323     0,                           /* php_ini_ignore */
324 #ifdef NXT_HAVE_PHP_IGNORE_CWD
325     1,                           /* php_ini_ignore_cwd */
326 #endif
327     NULL,                        /* get_fd */
328 
329     NULL,                        /* force_http_10 */
330 
331     NULL,                        /* get_target_uid */
332     NULL,                        /* get_target_gid */
333 
334     NULL,                        /* input_filter */
335 
336     NULL,                        /* ini_defaults */
337     0,                           /* phpinfo_as_text */
338 
339     NULL,                        /* ini_entries */
340     NULL,                        /* additional_functions */
341     NULL                         /* input_filter_init */
342 };
343 
344 
345 static uint32_t  compat[] = {
346     NXT_VERNUM, NXT_DEBUG,
347 };
348 
349 
350 NXT_EXPORT nxt_app_module_t  nxt_app_module = {
351     sizeof(compat),
352     compat,
353     nxt_string("php"),
354     PHP_VERSION,
355     NULL,
356     0,
357     nxt_php_setup,
358     nxt_php_start,
359 };
360 
361 
362 static nxt_php_target_t  *nxt_php_targets;
363 static nxt_int_t         nxt_php_last_target = -1;
364 
365 static nxt_unit_ctx_t    *nxt_php_unit_ctx;
366 #if defined(ZTS) && (PHP_VERSION_ID < 70400)
367 static void              ***tsrm_ls;
368 #endif
369 
370 
371 static nxt_int_t
nxt_php_setup(nxt_task_t * task,nxt_process_t * process,nxt_common_app_conf_t * conf)372 nxt_php_setup(nxt_task_t *task, nxt_process_t *process,
373     nxt_common_app_conf_t *conf)
374 {
375     nxt_str_t           ini_path;
376     nxt_int_t           ret;
377     nxt_conf_value_t    *value;
378     nxt_php_app_conf_t  *c;
379 
380     static nxt_str_t  file_str = nxt_string("file");
381     static nxt_str_t  user_str = nxt_string("user");
382     static nxt_str_t  admin_str = nxt_string("admin");
383 
384     c = &conf->u.php;
385 
386 #ifdef ZTS
387 
388 #if (PHP_VERSION_ID >= 70400)
389     php_tsrm_startup();
390 #else
391     tsrm_startup(1, 1, 0, NULL);
392     tsrm_ls = ts_resource(0);
393 #endif
394 
395 #endif
396 
397 #if defined(NXT_PHP7) && defined(ZEND_SIGNALS)
398 
399 #if (NXT_ZEND_SIGNAL_STARTUP)
400     zend_signal_startup();
401 #elif defined(ZTS)
402 #error PHP is built with thread safety and broken signals.
403 #endif
404 
405 #endif
406 
407     sapi_startup(&nxt_php_sapi_module);
408 
409     if (c->options != NULL) {
410         value = nxt_conf_get_object_member(c->options, &file_str, NULL);
411 
412         if (value != NULL) {
413             nxt_conf_get_string(value, &ini_path);
414 
415             ret = nxt_php_set_ini_path(task, &ini_path,
416                                        conf->working_directory);
417 
418             if (nxt_slow_path(ret != NXT_OK)) {
419                 return NXT_ERROR;
420             }
421         }
422     }
423 
424     if (nxt_slow_path(nxt_php_startup(&nxt_php_sapi_module) == FAILURE)) {
425         nxt_alert(task, "failed to initialize SAPI module and extension");
426         return NXT_ERROR;
427     }
428 
429     if (c->options != NULL) {
430         value = nxt_conf_get_object_member(c->options, &admin_str, NULL);
431         nxt_php_set_options(task, value, ZEND_INI_SYSTEM);
432 
433         value = nxt_conf_get_object_member(c->options, &user_str, NULL);
434         nxt_php_set_options(task, value, ZEND_INI_USER);
435     }
436 
437 #ifdef NXT_PHP7
438     nxt_php_server_ag = zend_hash_str_find_ptr(CG(auto_globals), "_SERVER",
439                                                nxt_length("_SERVER"));
440 #else
441     zend_hash_quick_find(CG(auto_globals), "_SERVER", sizeof("_SERVER"),
442                          zend_hash_func("_SERVER", sizeof("_SERVER")),
443                          (void **) &nxt_php_server_ag);
444 #endif
445     if (nxt_slow_path(nxt_php_server_ag == NULL)) {
446         nxt_alert(task, "failed to find $_SERVER auto global");
447         return NXT_ERROR;
448     }
449 
450     return NXT_OK;
451 }
452 
453 
454 static nxt_int_t
nxt_php_start(nxt_task_t * task,nxt_process_data_t * data)455 nxt_php_start(nxt_task_t *task, nxt_process_data_t *data)
456 {
457     uint32_t               next;
458     nxt_int_t              ret;
459     nxt_str_t              name;
460     nxt_uint_t             n;
461     nxt_unit_ctx_t         *unit_ctx;
462     nxt_unit_init_t        php_init;
463     nxt_conf_value_t       *value;
464     nxt_php_app_conf_t     *c;
465     nxt_common_app_conf_t  *conf;
466 
467     conf = data->app;
468     c = &conf->u.php;
469 
470     n = (c->targets != NULL) ? nxt_conf_object_members_count(c->targets) : 1;
471 
472     nxt_php_targets = nxt_zalloc(sizeof(nxt_php_target_t) * n);
473     if (nxt_slow_path(nxt_php_targets == NULL)) {
474         return NXT_ERROR;
475     }
476 
477     if (c->targets != NULL) {
478         next = 0;
479 
480         for (n = 0; /* void */; n++) {
481             value = nxt_conf_next_object_member(c->targets, &name, &next);
482             if (value == NULL) {
483                 break;
484             }
485 
486             ret = nxt_php_set_target(task, &nxt_php_targets[n], value);
487             if (nxt_slow_path(ret != NXT_OK)) {
488                 return NXT_ERROR;
489             }
490         }
491 
492     } else {
493         ret = nxt_php_set_target(task, &nxt_php_targets[0], conf->self);
494         if (nxt_slow_path(ret != NXT_OK)) {
495             return NXT_ERROR;
496         }
497     }
498 
499     ret = nxt_unit_default_init(task, &php_init, conf);
500     if (nxt_slow_path(ret != NXT_OK)) {
501         nxt_alert(task, "nxt_unit_default_init() failed");
502         return ret;
503     }
504 
505     php_init.callbacks.request_handler = nxt_php_request_handler;
506 
507     unit_ctx = nxt_unit_init(&php_init);
508     if (nxt_slow_path(unit_ctx == NULL)) {
509         return NXT_ERROR;
510     }
511 
512     nxt_php_unit_ctx = unit_ctx;
513 
514     nxt_unit_run(nxt_php_unit_ctx);
515     nxt_unit_done(nxt_php_unit_ctx);
516 
517     exit(0);
518 
519     return NXT_OK;
520 }
521 
522 
523 static nxt_int_t
nxt_php_set_target(nxt_task_t * task,nxt_php_target_t * target,nxt_conf_value_t * conf)524 nxt_php_set_target(nxt_task_t *task, nxt_php_target_t *target,
525     nxt_conf_value_t *conf)
526 {
527     u_char            *tmp, *p;
528     nxt_str_t         str;
529     nxt_int_t         ret;
530     nxt_conf_value_t  *value;
531 
532     static nxt_str_t  root_str = nxt_string("root");
533     static nxt_str_t  script_str = nxt_string("script");
534     static nxt_str_t  index_str = nxt_string("index");
535 
536     value = nxt_conf_get_object_member(conf, &root_str, NULL);
537 
538     nxt_conf_get_string(value, &str);
539 
540     tmp = nxt_malloc(str.length + 1);
541     if (nxt_slow_path(tmp == NULL)) {
542         return NXT_ERROR;
543     }
544 
545     p = tmp;
546 
547     p = nxt_cpymem(p, str.start, str.length);
548     *p = '\0';
549 
550     p = nxt_realpath(tmp);
551     if (nxt_slow_path(p == NULL)) {
552         nxt_alert(task, "root realpath(%s) failed %E", tmp, nxt_errno);
553         return NXT_ERROR;
554     }
555 
556     nxt_free(tmp);
557 
558     target->root.length = nxt_strlen(p);
559     target->root.start = p;
560 
561     nxt_php_str_trim_trail(&target->root, '/');
562 
563     value = nxt_conf_get_object_member(conf, &script_str, NULL);
564 
565     if (value != NULL) {
566         nxt_conf_get_string(value, &str);
567 
568         nxt_php_str_trim_lead(&str, '/');
569 
570         tmp = nxt_malloc(target->root.length + 1 + str.length + 1);
571         if (nxt_slow_path(tmp == NULL)) {
572             return NXT_ERROR;
573         }
574 
575         p = tmp;
576 
577         p = nxt_cpymem(p, target->root.start, target->root.length);
578         *p++ = '/';
579 
580         p = nxt_cpymem(p, str.start, str.length);
581         *p = '\0';
582 
583         p = nxt_realpath(tmp);
584         if (nxt_slow_path(p == NULL)) {
585             nxt_alert(task, "script realpath(%s) failed %E", tmp, nxt_errno);
586             return NXT_ERROR;
587         }
588 
589         nxt_free(tmp);
590 
591         target->script_filename.length = nxt_strlen(p);
592         target->script_filename.start = p;
593 
594         if (!nxt_str_start(&target->script_filename,
595                            target->root.start, target->root.length))
596         {
597             nxt_alert(task, "script is not under php root");
598             return NXT_ERROR;
599         }
600 
601         ret = nxt_php_dirname(&target->script_filename,
602                               &target->script_dirname);
603         if (nxt_slow_path(ret != NXT_OK)) {
604             return NXT_ERROR;
605         }
606 
607         target->script_name.length = target->script_filename.length
608                                      - target->root.length;
609         target->script_name.start = target->script_filename.start
610                                     + target->root.length;
611 
612     } else {
613         value = nxt_conf_get_object_member(conf, &index_str, NULL);
614 
615         if (value != NULL) {
616             nxt_conf_get_string(value, &str);
617 
618             tmp = nxt_malloc(str.length);
619             if (nxt_slow_path(tmp == NULL)) {
620                 return NXT_ERROR;
621             }
622 
623             nxt_memcpy(tmp, str.start, str.length);
624 
625             target->index.length = str.length;
626             target->index.start = tmp;
627 
628         } else {
629             nxt_str_set(&target->index, "index.php");
630         }
631     }
632 
633     return NXT_OK;
634 }
635 
636 
637 static nxt_int_t
nxt_php_set_ini_path(nxt_task_t * task,nxt_str_t * ini_path,char * workdir)638 nxt_php_set_ini_path(nxt_task_t *task, nxt_str_t *ini_path, char *workdir)
639 {
640     size_t  wdlen;
641     u_char  *p, *start;
642 
643     if (ini_path->start[0] == '/' || workdir == NULL) {
644         p = nxt_malloc(ini_path->length + 1);
645         if (nxt_slow_path(p == NULL)) {
646             return NXT_ERROR;
647         }
648 
649         start = p;
650 
651     } else {
652         wdlen = nxt_strlen(workdir);
653 
654         p = nxt_malloc(wdlen + ini_path->length + 2);
655         if (nxt_slow_path(p == NULL)) {
656             return NXT_ERROR;
657         }
658 
659         start = p;
660 
661         p = nxt_cpymem(p, workdir, wdlen);
662 
663         if (workdir[wdlen - 1] != '/') {
664             *p++ = '/';
665         }
666     }
667 
668     p = nxt_cpymem(p, ini_path->start, ini_path->length);
669     *p = '\0';
670 
671     nxt_php_sapi_module.php_ini_path_override = (char *) start;
672 
673     return NXT_OK;
674 }
675 
676 
677 static void
nxt_php_set_options(nxt_task_t * task,nxt_conf_value_t * options,int type)678 nxt_php_set_options(nxt_task_t *task, nxt_conf_value_t *options, int type)
679 {
680     uint32_t          next;
681     nxt_str_t         name, value;
682     nxt_conf_value_t  *value_obj;
683 
684     if (options != NULL) {
685         next = 0;
686 
687         for ( ;; ) {
688             value_obj = nxt_conf_next_object_member(options, &name, &next);
689             if (value_obj == NULL) {
690                 break;
691             }
692 
693             nxt_conf_get_string(value_obj, &value);
694 
695             if (nxt_php_alter_option(&name, &value, type) != NXT_OK) {
696                 nxt_log(task, NXT_LOG_ERR,
697                         "setting PHP option \"%V: %V\" failed", &name, &value);
698                 continue;
699             }
700 
701             if (nxt_str_eq(&name, "disable_functions", 17)) {
702 #ifdef NXT_PHP8
703                 nxt_php_disable_functions(&value);
704 #else
705                 nxt_php_disable(task, "function", &value,
706                                 &PG(disable_functions),
707                                 zend_disable_function);
708 #endif
709                 continue;
710             }
711 
712             if (nxt_str_eq(&name, "disable_classes", 15)) {
713                 nxt_php_disable(task, "class", &value,
714                                 &PG(disable_classes),
715                                 zend_disable_class);
716                 continue;
717             }
718         }
719     }
720 }
721 
722 
723 #ifdef NXT_PHP7
724 
725 static nxt_int_t
nxt_php_alter_option(nxt_str_t * name,nxt_str_t * value,int type)726 nxt_php_alter_option(nxt_str_t *name, nxt_str_t *value, int type)
727 {
728     zend_string     *zs;
729     zend_ini_entry  *ini_entry;
730 
731     ini_entry = nxt_php_hash_str_find_ptr(EG(ini_directives), name);
732     if (nxt_slow_path(ini_entry == NULL)) {
733         return NXT_ERROR;
734     }
735 
736     /* PHP exits on memory allocation errors. */
737     zs = zend_string_init((char *) value->start, value->length, 1);
738 
739     if (ini_entry->on_modify
740         && ini_entry->on_modify(ini_entry, zs, ini_entry->mh_arg1,
741                                 ini_entry->mh_arg2, ini_entry->mh_arg3,
742                                 ZEND_INI_STAGE_ACTIVATE)
743            != SUCCESS)
744     {
745         zend_string_release(zs);
746         return NXT_ERROR;
747     }
748 
749     ini_entry->value = zs;
750     ini_entry->modifiable = type;
751 
752     return NXT_OK;
753 }
754 
755 #else  /* PHP 5. */
756 
757 static nxt_int_t
nxt_php_alter_option(nxt_str_t * name,nxt_str_t * value,int type)758 nxt_php_alter_option(nxt_str_t *name, nxt_str_t *value, int type)
759 {
760     char            *cstr;
761     zend_ini_entry  *ini_entry;
762 
763     ini_entry = nxt_php_hash_str_find_ptr(EG(ini_directives), name);
764     if (nxt_slow_path(ini_entry == NULL)) {
765         return NXT_ERROR;
766     }
767 
768     cstr = nxt_malloc(value->length + 1);
769     if (nxt_slow_path(cstr == NULL)) {
770         return NXT_ERROR;
771     }
772 
773     nxt_memcpy(cstr, value->start, value->length);
774     cstr[value->length] = '\0';
775 
776     if (ini_entry->on_modify
777         && ini_entry->on_modify(ini_entry, cstr, value->length,
778                                 ini_entry->mh_arg1, ini_entry->mh_arg2,
779                                 ini_entry->mh_arg3, ZEND_INI_STAGE_ACTIVATE
780                                 TSRMLS_CC)
781            != SUCCESS)
782     {
783         nxt_free(cstr);
784         return NXT_ERROR;
785     }
786 
787     ini_entry->value = cstr;
788     ini_entry->value_length = value->length;
789     ini_entry->modifiable = type;
790 
791     return NXT_OK;
792 }
793 
794 #endif
795 
796 
797 #ifdef NXT_PHP8
798 
799 static void
nxt_php_disable_functions(nxt_str_t * str)800 nxt_php_disable_functions(nxt_str_t *str)
801 {
802     char  *p;
803 
804     p = nxt_malloc(str->length + 1);
805     if (nxt_slow_path(p == NULL)) {
806         return;
807     }
808 
809     nxt_memcpy(p, str->start, str->length);
810     p[str->length] = '\0';
811 
812     zend_disable_functions(p);
813 
814     nxt_free(p);
815 }
816 
817 #endif
818 
819 
820 static void
nxt_php_disable(nxt_task_t * task,const char * type,nxt_str_t * value,char ** ptr,nxt_php_disable_t disable)821 nxt_php_disable(nxt_task_t *task, const char *type, nxt_str_t *value,
822     char **ptr, nxt_php_disable_t disable)
823 {
824     char  c, *p, *start;
825 
826     p = nxt_malloc(value->length + 1);
827     if (nxt_slow_path(p == NULL)) {
828         return;
829     }
830 
831     /*
832      * PHP frees this memory on module shutdown.
833      * See core_globals_dtor() for details.
834      */
835     *ptr = p;
836 
837     nxt_memcpy(p, value->start, value->length);
838     p[value->length] = '\0';
839 
840     start = p;
841 
842     do {
843         c = *p;
844 
845         if (c == ' ' || c == ',' || c == '\0') {
846 
847             if (p != start) {
848                 *p = '\0';
849 
850 #ifdef NXT_PHP7
851                 if (disable(start, p - start)
852 #else
853                 if (disable(start, p - start TSRMLS_CC)
854 #endif
855                     != SUCCESS)
856                 {
857                     nxt_log(task, NXT_LOG_ERR,
858                             "PHP: failed to disable \"%s\": no such %s",
859                             start, type);
860                 }
861             }
862 
863             start = p + 1;
864         }
865 
866         p++;
867 
868     } while (c != '\0');
869 }
870 
871 
872 static nxt_int_t
nxt_php_dirname(const nxt_str_t * file,nxt_str_t * dir)873 nxt_php_dirname(const nxt_str_t *file, nxt_str_t *dir)
874 {
875     size_t  length;
876 
877     if (file->length == 0 || file->start[0] != '/') {
878         nxt_unit_alert(NULL, "php_dirname: invalid file name "
879                        "(not starts from '/')");
880         return NXT_ERROR;
881     }
882 
883     length = file->length;
884 
885     while (file->start[length - 1] != '/') {
886         length--;
887     }
888 
889     dir->length = length;
890     dir->start = nxt_malloc(length + 1);
891     if (nxt_slow_path(dir->start == NULL)) {
892         return NXT_ERROR;
893     }
894 
895     nxt_memcpy(dir->start, file->start, length);
896 
897     dir->start[length] = '\0';
898 
899     return NXT_OK;
900 }
901 
902 
903 static void
nxt_php_str_trim_trail(nxt_str_t * str,u_char t)904 nxt_php_str_trim_trail(nxt_str_t *str, u_char t)
905 {
906     while (str->length > 0 && str->start[str->length - 1] == t) {
907         str->length--;
908     }
909 
910     str->start[str->length] = '\0';
911 }
912 
913 
914 static void
nxt_php_str_trim_lead(nxt_str_t * str,u_char t)915 nxt_php_str_trim_lead(nxt_str_t *str, u_char t)
916 {
917     while (str->length > 0 && str->start[0] == t) {
918         str->length--;
919         str->start++;
920     }
921 }
922 
923 
924 nxt_inline u_char *
nxt_realpath(const void * c)925 nxt_realpath(const void *c)
926 {
927     return (u_char *) realpath(c, NULL);
928 }
929 
930 
931 static nxt_int_t
nxt_php_do_301(nxt_unit_request_info_t * req)932 nxt_php_do_301(nxt_unit_request_info_t *req)
933 {
934     char                *p, *url, *port;
935     uint32_t            size;
936     const char          *proto;
937     nxt_unit_request_t  *r;
938 
939     r = req->request;
940 
941     url = nxt_malloc(sizeof("https://") - 1
942                      + r->server_name_length
943                      + r->local_port_length + 1
944                      + r->path_length + 1
945                      + r->query_length + 1
946                      + 1);
947     if (nxt_slow_path(url == NULL)) {
948         return NXT_UNIT_ERROR;
949     }
950 
951     proto = r->tls ? "https://" : "http://";
952     p = nxt_cpymem(url, proto, strlen(proto));
953     p = nxt_cpymem(p, nxt_unit_sptr_get(&r->server_name),
954                    r->server_name_length);
955 
956     port = nxt_unit_sptr_get(&r->local_port);
957     if (r->local_port_length > 0
958         && !(r->tls && strcmp(port, "443") == 0)
959         && !(!r->tls && strcmp(port, "80") == 0))
960     {
961         *p++ = ':';
962         p = nxt_cpymem(p, port, r->local_port_length);
963     }
964 
965     p = nxt_cpymem(p, nxt_unit_sptr_get(&r->path), r->path_length);
966     *p++ = '/';
967 
968     if (r->query_length > 0) {
969         *p++ = '?';
970         p = nxt_cpymem(p, nxt_unit_sptr_get(&r->query), r->query_length);
971     }
972 
973     *p = '\0';
974 
975     size = p - url;
976 
977     nxt_unit_response_init(req, NXT_HTTP_MOVED_PERMANENTLY, 1,
978                            nxt_length("Location") + size);
979     nxt_unit_response_add_field(req, "Location", nxt_length("Location"),
980                                 url, size);
981 
982     nxt_free(url);
983 
984     return NXT_UNIT_OK;
985 }
986 
987 
988 static nxt_int_t
nxt_php_handle_fs_err(nxt_unit_request_info_t * req)989 nxt_php_handle_fs_err(nxt_unit_request_info_t *req)
990 {
991     switch (nxt_errno) {
992     case ELOOP:
993     case EACCES:
994     case ENFILE:
995         return nxt_unit_response_init(req, NXT_HTTP_FORBIDDEN, 0, 0);
996     case ENOENT:
997     case ENOTDIR:
998     case ENAMETOOLONG:
999         return nxt_unit_response_init(req, NXT_HTTP_NOT_FOUND, 0, 0);
1000     }
1001 
1002     return NXT_UNIT_ERROR;
1003 }
1004 
1005 
1006 static void
nxt_php_request_handler(nxt_unit_request_info_t * req)1007 nxt_php_request_handler(nxt_unit_request_info_t *req)
1008 {
1009     nxt_php_target_t    *target;
1010     nxt_php_run_ctx_t   ctx;
1011     nxt_unit_request_t  *r;
1012 
1013     r = req->request;
1014     target = &nxt_php_targets[r->app_target];
1015 
1016     nxt_memzero(&ctx, sizeof(ctx));
1017 
1018     ctx.req = req;
1019     ctx.root = &target->root;
1020     ctx.index = &target->index;
1021 
1022     if (target->script_filename.length == 0) {
1023         nxt_php_dynamic_request(&ctx, r);
1024         return;
1025     }
1026 
1027     ctx.script_filename = target->script_filename;
1028     ctx.script_dirname = target->script_dirname;
1029     ctx.script_name = target->script_name;
1030 
1031     ctx.chdir = (r->app_target != nxt_php_last_target);
1032 
1033     nxt_php_execute(&ctx, r);
1034 
1035     nxt_php_last_target = ctx.chdir ? -1 : r->app_target;
1036 }
1037 
1038 
1039 static void
nxt_php_dynamic_request(nxt_php_run_ctx_t * ctx,nxt_unit_request_t * r)1040 nxt_php_dynamic_request(nxt_php_run_ctx_t *ctx, nxt_unit_request_t *r)
1041 {
1042     u_char     *p;
1043     nxt_str_t  path, script_name;
1044     nxt_int_t  ret;
1045 
1046     path.length = r->path_length;
1047     path.start = nxt_unit_sptr_get(&r->path);
1048 
1049     nxt_str_null(&script_name);
1050 
1051     ctx->path_info.start = memmem(path.start, path.length, ".php/",
1052                                   strlen(".php/"));
1053     if (ctx->path_info.start != NULL) {
1054         ctx->path_info.start += 4;
1055         path.length = ctx->path_info.start - path.start;
1056 
1057         ctx->path_info.length = r->path_length - path.length;
1058 
1059     } else if (path.start[path.length - 1] == '/') {
1060         script_name = *ctx->index;
1061 
1062     } else if (path.length < 4
1063                || memcmp(path.start + (path.length - 4), ".php", 4) != 0)
1064     {
1065         char         tpath[PATH_MAX];
1066         nxt_int_t    ec;
1067         struct stat  sb;
1068 
1069         ec = NXT_UNIT_ERROR;
1070 
1071         if (ctx->root->length + path.length + 1 > PATH_MAX) {
1072             nxt_unit_request_done(ctx->req, ec);
1073 
1074             return;
1075         }
1076 
1077         p = nxt_cpymem(tpath, ctx->root->start, ctx->root->length);
1078         p = nxt_cpymem(p, path.start, path.length);
1079         *p = '\0';
1080 
1081         ret = stat(tpath, &sb);
1082         if (ret == 0 && S_ISDIR(sb.st_mode)) {
1083             ec = nxt_php_do_301(ctx->req);
1084         } else if (ret == -1) {
1085             ec = nxt_php_handle_fs_err(ctx->req);
1086         }
1087 
1088         nxt_unit_request_done(ctx->req, ec);
1089 
1090         return;
1091     }
1092 
1093     ctx->script_filename.length = ctx->root->length
1094                                   + path.length
1095                                   + script_name.length;
1096 
1097     p = nxt_malloc(ctx->script_filename.length + 1);
1098     if (nxt_slow_path(p == NULL)) {
1099         nxt_unit_request_done(ctx->req, NXT_UNIT_ERROR);
1100 
1101         return;
1102     }
1103 
1104     ctx->script_filename.start = p;
1105 
1106     ctx->script_name.length = path.length + script_name.length;
1107     ctx->script_name.start = p + ctx->root->length;
1108 
1109     p = nxt_cpymem(p, ctx->root->start, ctx->root->length);
1110     p = nxt_cpymem(p, path.start, path.length);
1111 
1112     if (script_name.length > 0) {
1113         p = nxt_cpymem(p, script_name.start, script_name.length);
1114     }
1115 
1116     *p = '\0';
1117 
1118     ctx->chdir = 1;
1119 
1120     ret = nxt_php_dirname(&ctx->script_filename, &ctx->script_dirname);
1121     if (nxt_slow_path(ret != NXT_OK)) {
1122         nxt_unit_request_done(ctx->req, NXT_UNIT_ERROR);
1123         nxt_free(ctx->script_filename.start);
1124 
1125         return;
1126     }
1127 
1128     nxt_php_execute(ctx, r);
1129 
1130     nxt_free(ctx->script_filename.start);
1131     nxt_free(ctx->script_dirname.start);
1132 
1133     nxt_php_last_target = -1;
1134 }
1135 
1136 
1137 #if (PHP_VERSION_ID < 70400)
1138 static void
nxt_zend_stream_init_fp(zend_file_handle * handle,FILE * fp,const char * filename)1139 nxt_zend_stream_init_fp(zend_file_handle *handle, FILE *fp,
1140                         const char *filename)
1141 {
1142     nxt_memzero(handle, sizeof(zend_file_handle));
1143     handle->type = ZEND_HANDLE_FP;
1144     handle->handle.fp = fp;
1145     handle->filename = filename;
1146 }
1147 #else
1148 #define nxt_zend_stream_init_fp  zend_stream_init_fp
1149 #endif
1150 
1151 
1152 static void
nxt_php_execute(nxt_php_run_ctx_t * ctx,nxt_unit_request_t * r)1153 nxt_php_execute(nxt_php_run_ctx_t *ctx, nxt_unit_request_t *r)
1154 {
1155     FILE              *fp;
1156 #if (PHP_VERSION_ID < 50600)
1157     void              *read_post;
1158 #endif
1159     const char        *filename;
1160     nxt_unit_field_t  *f;
1161     zend_file_handle  file_handle;
1162 
1163     filename = (const char *) ctx->script_filename.start;
1164 
1165     nxt_unit_req_debug(ctx->req, "PHP execute script %s", filename);
1166 
1167     fp = fopen(filename, "re");
1168     if (fp == NULL) {
1169         nxt_int_t  ec;
1170 
1171         nxt_unit_req_debug(ctx->req, "PHP fopen(\"%s\") failed", filename);
1172 
1173         ec = nxt_php_handle_fs_err(ctx->req);
1174         nxt_unit_request_done(ctx->req, ec);
1175         return;
1176     }
1177 
1178     SG(server_context) = ctx;
1179     SG(options) |= SAPI_OPTION_NO_CHDIR;
1180     SG(request_info).request_uri = nxt_unit_sptr_get(&r->target);
1181     SG(request_info).request_method = nxt_unit_sptr_get(&r->method);
1182 
1183     SG(request_info).proto_num = 1001;
1184 
1185     SG(request_info).query_string = r->query.offset
1186                                     ? nxt_unit_sptr_get(&r->query) : NULL;
1187     SG(request_info).content_length = r->content_length;
1188 
1189     if (r->content_type_field != NXT_UNIT_NONE_FIELD) {
1190         f = r->fields + r->content_type_field;
1191 
1192         SG(request_info).content_type = nxt_unit_sptr_get(&f->value);
1193     }
1194 
1195     if (r->cookie_field != NXT_UNIT_NONE_FIELD) {
1196         f = r->fields + r->cookie_field;
1197 
1198         ctx->cookie = nxt_unit_sptr_get(&f->value);
1199     }
1200 
1201     if (r->authorization_field != NXT_UNIT_NONE_FIELD) {
1202         f = r->fields + r->authorization_field;
1203 
1204 #ifdef NXT_PHP7
1205         php_handle_auth_data(nxt_unit_sptr_get(&f->value));
1206 #else
1207         php_handle_auth_data(nxt_unit_sptr_get(&f->value) TSRMLS_CC);
1208 #endif
1209 
1210     } else {
1211         SG(request_info).auth_digest = NULL;
1212         SG(request_info).auth_user = NULL;
1213         SG(request_info).auth_password = NULL;
1214     }
1215 
1216     SG(sapi_headers).http_response_code = 200;
1217 
1218     SG(request_info).path_translated = NULL;
1219 
1220 #ifdef NXT_PHP7
1221     if (nxt_slow_path(php_request_startup() == FAILURE)) {
1222 #else
1223     if (nxt_slow_path(php_request_startup(TSRMLS_C) == FAILURE)) {
1224 #endif
1225         nxt_unit_req_debug(ctx->req, "php_request_startup() failed");
1226 
1227         nxt_unit_request_done(ctx->req, NXT_UNIT_ERROR);
1228         fclose(fp);
1229 
1230         return;
1231     }
1232 
1233     if (ctx->chdir) {
1234         ctx->chdir = 0;
1235         nxt_php_vcwd_chdir(ctx->req, ctx->script_dirname.start);
1236     }
1237 
1238     nxt_zend_stream_init_fp(&file_handle, fp, filename);
1239 
1240     php_execute_script(&file_handle TSRMLS_CC);
1241 
1242 #if (PHP_VERSION_ID >= 80100)
1243     zend_destroy_file_handle(&file_handle);
1244 #endif
1245 
1246     /* Prevention of consuming possible unread request body. */
1247 #if (PHP_VERSION_ID < 50600)
1248     read_post = sapi_module.read_post;
1249     sapi_module.read_post = NULL;
1250 #else
1251     SG(post_read) = 1;
1252 #endif
1253 
1254     php_request_shutdown(NULL);
1255 
1256     if (ctx->req != NULL) {
1257         nxt_unit_request_done(ctx->req, NXT_UNIT_OK);
1258     }
1259 
1260 #if (PHP_VERSION_ID < 50600)
1261     sapi_module.read_post = read_post;
1262 #endif
1263 }
1264 
1265 
1266 nxt_inline void
1267 nxt_php_vcwd_chdir(nxt_unit_request_info_t *req, u_char *dir)
1268 {
1269     if (nxt_slow_path(VCWD_CHDIR((char *) dir) != 0)) {
1270         nxt_unit_req_alert(req, "VCWD_CHDIR(%s) failed (%d: %s)",
1271                            dir, errno, strerror(errno));
1272     }
1273 }
1274 
1275 
1276 static int
1277 nxt_php_startup(sapi_module_struct *sapi_module)
1278 {
1279 #if (PHP_VERSION_ID < 80200)
1280     return php_module_startup(sapi_module, &nxt_php_unit_module, 1);
1281 #else
1282     return php_module_startup(sapi_module, &nxt_php_unit_module);
1283 #endif
1284 }
1285 
1286 
1287 #ifdef NXT_PHP7
1288 static size_t
1289 nxt_php_unbuffered_write(const char *str, size_t str_length TSRMLS_DC)
1290 #else
1291 static int
1292 nxt_php_unbuffered_write(const char *str, uint str_length TSRMLS_DC)
1293 #endif
1294 {
1295     int                rc;
1296     nxt_php_run_ctx_t  *ctx;
1297 
1298     ctx = SG(server_context);
1299 
1300     rc = nxt_unit_response_write(ctx->req, str, str_length);
1301     if (nxt_fast_path(rc == NXT_UNIT_OK)) {
1302         return str_length;
1303     }
1304 
1305     php_handle_aborted_connection();
1306     return 0;
1307 }
1308 
1309 
1310 static int
1311 nxt_php_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC)
1312 {
1313     int                      rc, fields_count;
1314     char                     *colon, *value;
1315     uint16_t                 status;
1316     uint32_t                 resp_size;
1317     nxt_php_run_ctx_t        *ctx;
1318     sapi_header_struct       *h;
1319     zend_llist_position      zpos;
1320     nxt_unit_request_info_t  *req;
1321 
1322     ctx = SG(server_context);
1323     req = ctx->req;
1324 
1325     nxt_unit_req_debug(req, "nxt_php_send_headers");
1326 
1327     if (SG(request_info).no_headers == 1) {
1328         rc = nxt_unit_response_init(req, 200, 0, 0);
1329         if (nxt_slow_path(rc != NXT_UNIT_OK)) {
1330             return SAPI_HEADER_SEND_FAILED;
1331         }
1332 
1333         return SAPI_HEADER_SENT_SUCCESSFULLY;
1334     }
1335 
1336     resp_size = 0;
1337     fields_count = zend_llist_count(&sapi_headers->headers);
1338 
1339     for (h = zend_llist_get_first_ex(&sapi_headers->headers, &zpos);
1340          h;
1341          h = zend_llist_get_next_ex(&sapi_headers->headers, &zpos))
1342     {
1343         resp_size += h->header_len;
1344     }
1345 
1346     status = SG(sapi_headers).http_response_code;
1347 
1348     rc = nxt_unit_response_init(req, status, fields_count, resp_size);
1349     if (nxt_slow_path(rc != NXT_UNIT_OK)) {
1350         return SAPI_HEADER_SEND_FAILED;
1351     }
1352 
1353     for (h = zend_llist_get_first_ex(&sapi_headers->headers, &zpos);
1354          h;
1355          h = zend_llist_get_next_ex(&sapi_headers->headers, &zpos))
1356     {
1357         colon = memchr(h->header, ':', h->header_len);
1358         if (nxt_slow_path(colon == NULL)) {
1359             nxt_unit_req_warn(req, "colon not found in header '%.*s'",
1360                               (int) h->header_len, h->header);
1361             continue;
1362         }
1363 
1364         value = colon + 1;
1365         while(isspace(*value)) {
1366             value++;
1367         }
1368 
1369         nxt_unit_response_add_field(req, h->header, colon - h->header,
1370                                     value,
1371                                     h->header_len - (value - h->header));
1372     }
1373 
1374     rc = nxt_unit_response_send(req);
1375     if (nxt_slow_path(rc != NXT_UNIT_OK)) {
1376         nxt_unit_req_debug(req, "failed to send response");
1377 
1378         return SAPI_HEADER_SEND_FAILED;
1379     }
1380 
1381     return SAPI_HEADER_SENT_SUCCESSFULLY;
1382 }
1383 
1384 
1385 #ifdef NXT_PHP7
1386 static size_t
1387 nxt_php_read_post(char *buffer, size_t count_bytes TSRMLS_DC)
1388 #else
1389 static int
1390 nxt_php_read_post(char *buffer, uint count_bytes TSRMLS_DC)
1391 #endif
1392 {
1393     nxt_php_run_ctx_t  *ctx;
1394 
1395     ctx = SG(server_context);
1396 
1397     nxt_unit_req_debug(ctx->req, "nxt_php_read_post %d", (int) count_bytes);
1398 
1399     return nxt_unit_request_read(ctx->req, buffer, count_bytes);
1400 }
1401 
1402 
1403 static char *
1404 nxt_php_read_cookies(TSRMLS_D)
1405 {
1406     nxt_php_run_ctx_t  *ctx;
1407 
1408     ctx = SG(server_context);
1409 
1410     nxt_unit_req_debug(ctx->req, "nxt_php_read_cookies");
1411 
1412     return ctx->cookie;
1413 }
1414 
1415 
1416 static void
1417 nxt_php_register_variables(zval *track_vars_array TSRMLS_DC)
1418 {
1419     const char               *name;
1420     nxt_unit_field_t         *f, *f_end;
1421     nxt_php_run_ctx_t        *ctx;
1422     nxt_unit_request_t       *r;
1423     nxt_unit_request_info_t  *req;
1424 
1425     ctx = SG(server_context);
1426 
1427     req = ctx->req;
1428     r = req->request;
1429 
1430     nxt_unit_req_debug(req, "nxt_php_register_variables");
1431 
1432     php_register_variable_safe((char *) "SERVER_SOFTWARE",
1433                                (char *) nxt_server.start,
1434                                nxt_server.length, track_vars_array TSRMLS_CC);
1435 
1436     nxt_php_set_sptr(req, "SERVER_PROTOCOL", &r->version, r->version_length,
1437                      track_vars_array TSRMLS_CC);
1438 
1439     /*
1440      * 'PHP_SELF'
1441      * The filename of the currently executing script, relative to the document
1442      * root.  For instance, $_SERVER['PHP_SELF'] in a script at the address
1443      * http://example.com/foo/bar.php would be /foo/bar.php.  The __FILE__
1444      * constant contains the full path and filename of the current (i.e.
1445      * included) file.  If PHP is running as a command-line processor this
1446      * variable contains the script name since PHP 4.3.0. Previously it was not
1447      * available.
1448      */
1449 
1450     if (ctx->path_info.length != 0) {
1451         nxt_php_set_sptr(req, "PHP_SELF", &r->path, r->path_length,
1452                          track_vars_array TSRMLS_CC);
1453 
1454         nxt_php_set_str(req, "PATH_INFO", &ctx->path_info,
1455                         track_vars_array TSRMLS_CC);
1456 
1457     } else {
1458         nxt_php_set_str(req, "PHP_SELF", &ctx->script_name,
1459                         track_vars_array TSRMLS_CC);
1460     }
1461 
1462     /*
1463      * 'SCRIPT_NAME'
1464      * Contains the current script's path.  This is useful for pages which need
1465      * to point to themselves.  The __FILE__ constant contains the full path and
1466      * filename of the current (i.e. included) file.
1467      */
1468 
1469     nxt_php_set_str(req, "SCRIPT_NAME", &ctx->script_name,
1470                     track_vars_array TSRMLS_CC);
1471 
1472     /*
1473      * 'SCRIPT_FILENAME'
1474      * The absolute pathname of the currently executing script.
1475      */
1476 
1477     nxt_php_set_str(req, "SCRIPT_FILENAME", &ctx->script_filename,
1478                     track_vars_array TSRMLS_CC);
1479 
1480     /*
1481      * 'DOCUMENT_ROOT'
1482      * The document root directory under which the current script is executing,
1483      * as defined in the server's configuration file.
1484      */
1485 
1486     nxt_php_set_str(req, "DOCUMENT_ROOT", ctx->root,
1487                     track_vars_array TSRMLS_CC);
1488 
1489     nxt_php_set_sptr(req, "REQUEST_METHOD", &r->method, r->method_length,
1490                      track_vars_array TSRMLS_CC);
1491     nxt_php_set_sptr(req, "REQUEST_URI", &r->target, r->target_length,
1492                      track_vars_array TSRMLS_CC);
1493     nxt_php_set_sptr(req, "QUERY_STRING", &r->query, r->query_length,
1494                      track_vars_array TSRMLS_CC);
1495 
1496     nxt_php_set_sptr(req, "REMOTE_ADDR", &r->remote, r->remote_length,
1497                      track_vars_array TSRMLS_CC);
1498     nxt_php_set_sptr(req, "SERVER_ADDR", &r->local_addr, r->local_addr_length,
1499                      track_vars_array TSRMLS_CC);
1500 
1501     nxt_php_set_sptr(req, "SERVER_NAME", &r->server_name, r->server_name_length,
1502                      track_vars_array TSRMLS_CC);
1503     nxt_php_set_cstr(req, "SERVER_PORT", "80", 2, track_vars_array TSRMLS_CC);
1504 
1505     if (r->tls) {
1506         nxt_php_set_cstr(req, "HTTPS", "on", 2, track_vars_array TSRMLS_CC);
1507     }
1508 
1509     f_end = r->fields + r->fields_count;
1510     for (f = r->fields; f < f_end; f++) {
1511         name = nxt_unit_sptr_get(&f->name);
1512 
1513         nxt_php_set_sptr(req, name, &f->value, f->value_length,
1514                          track_vars_array TSRMLS_CC);
1515     }
1516 
1517     if (r->content_length_field != NXT_UNIT_NONE_FIELD) {
1518         f = r->fields + r->content_length_field;
1519 
1520         nxt_php_set_sptr(req, "CONTENT_LENGTH", &f->value, f->value_length,
1521                          track_vars_array TSRMLS_CC);
1522     }
1523 
1524     if (r->content_type_field != NXT_UNIT_NONE_FIELD) {
1525         f = r->fields + r->content_type_field;
1526 
1527         nxt_php_set_sptr(req, "CONTENT_TYPE", &f->value, f->value_length,
1528                          track_vars_array TSRMLS_CC);
1529     }
1530 }
1531 
1532 
1533 static void
1534 nxt_php_set_sptr(nxt_unit_request_info_t *req, const char *name,
1535     nxt_unit_sptr_t *v, uint32_t len, zval *track_vars_array TSRMLS_DC)
1536 {
1537     char          *str;
1538 #if NXT_PHP7
1539     size_t        new_len;
1540 #else
1541     unsigned int  new_len;
1542 #endif
1543 
1544     str = nxt_unit_sptr_get(v);
1545 
1546     nxt_unit_req_debug(req, "php: register %s='%.*s'", name, (int) len, str);
1547 
1548     if (sapi_module.input_filter(PARSE_SERVER, (char *) name, &str, len,
1549                                  &new_len TSRMLS_CC))
1550     {
1551         php_register_variable_safe((char *) name, str, new_len,
1552                                    track_vars_array TSRMLS_CC);
1553     }
1554 }
1555 
1556 
1557 nxt_inline void
1558 nxt_php_set_str(nxt_unit_request_info_t *req, const char *name,
1559     nxt_str_t *s, zval *track_vars_array TSRMLS_DC)
1560 {
1561     nxt_php_set_cstr(req, name, (char *) s->start, s->length,
1562                      track_vars_array TSRMLS_CC);
1563 }
1564 
1565 
1566 #ifdef NXT_PHP7
1567 
1568 static void *
1569 nxt_php_hash_str_find_ptr(const HashTable *ht, const nxt_str_t *str)
1570 {
1571     return zend_hash_str_find_ptr(ht, (const char *) str->start, str->length);
1572 }
1573 
1574 #else
1575 
1576 static void *
1577 nxt_php_hash_str_find_ptr(const HashTable *ht, const nxt_str_t *str)
1578 {
1579     int   ret;
1580     void  *entry;
1581     char  buf[256];
1582 
1583     if (nxt_slow_path(str->length >= (sizeof(buf) - 1))) {
1584         return NULL;
1585     }
1586 
1587     nxt_memcpy(buf, str->start, str->length);
1588     buf[str->length] = '\0';
1589 
1590     ret = zend_hash_find(ht, buf, str->length + 1, &entry);
1591     if (nxt_fast_path(ret == SUCCESS)) {
1592         return entry;
1593     }
1594 
1595     return NULL;
1596 }
1597 
1598 #endif
1599 
1600 
1601 static void
1602 nxt_php_set_cstr(nxt_unit_request_info_t *req, const char *name,
1603     const char *cstr, uint32_t len, zval *track_vars_array TSRMLS_DC)
1604 {
1605     if (nxt_slow_path(cstr == NULL)) {
1606         return;
1607     }
1608 
1609     nxt_unit_req_debug(req, "php: register %s='%.*s'", name, (int) len, cstr);
1610 
1611     php_register_variable_safe((char *) name, (char *) cstr, len,
1612                                track_vars_array TSRMLS_CC);
1613 }
1614 
1615 
1616 #if NXT_PHP8
1617 static void
1618 nxt_php_log_message(const char *message, int syslog_type_int)
1619 #else
1620 #ifdef NXT_HAVE_PHP_LOG_MESSAGE_WITH_SYSLOG_TYPE
1621 static void
1622 nxt_php_log_message(char *message, int syslog_type_int)
1623 #else
1624 static void
1625 nxt_php_log_message(char *message TSRMLS_DC)
1626 #endif
1627 #endif
1628 {
1629     nxt_php_run_ctx_t  *ctx;
1630 
1631     ctx = SG(server_context);
1632 
1633     if (ctx != NULL) {
1634         nxt_unit_req_log(ctx->req, NXT_UNIT_LOG_NOTICE,
1635                          "php message: %s", message);
1636 
1637     } else {
1638         nxt_unit_log(nxt_php_unit_ctx, NXT_UNIT_LOG_NOTICE,
1639                      "php message: %s", message);
1640     }
1641 }
1642