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