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