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