xref: /unit/src/nxt_php_sapi.c (revision 743:e0f0cd7d244a)
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 
12 #include <nxt_main.h>
13 #include <nxt_router.h>
14 #include <nxt_unit.h>
15 #include <nxt_unit_request.h>
16 
17 
18 typedef struct nxt_php_run_ctx_s  nxt_php_run_ctx_t;
19 
20 static nxt_int_t nxt_php_init(nxt_task_t *task, nxt_common_app_conf_t *conf);
21 
22 static void nxt_php_str_trim_trail(nxt_str_t *str, u_char t);
23 static void nxt_php_str_trim_lead(nxt_str_t *str, u_char t);
24 nxt_inline u_char *nxt_realpath(const void *c);
25 
26 static void nxt_php_request_handler(nxt_unit_request_info_t *req);
27 
28 #if PHP_MAJOR_VERSION >= 7
29 #   define NXT_PHP7 1
30 #   if PHP_MINOR_VERSION >= 1
31 #       define NXT_HAVE_PHP_LOG_MESSAGE_WITH_SYSLOG_TYPE 1
32 #   else
33 #       define NXT_HAVE_PHP_INTERRUPTS 1
34 #   endif
35 #   define NXT_HAVE_PHP_IGNORE_CWD 1
36 #else
37 #   define NXT_HAVE_PHP_INTERRUPTS 1
38 #   if PHP_MINOR_VERSION >= 4
39 #       define NXT_HAVE_PHP_IGNORE_CWD 1
40 #   endif
41 #endif
42 
43 static int nxt_php_startup(sapi_module_struct *sapi_module);
44 static void nxt_php_set_options(nxt_task_t *task, nxt_conf_value_t *options,
45     int type);
46 static nxt_int_t nxt_php_alter_option(nxt_str_t *name, nxt_str_t *value,
47     int type);
48 static int nxt_php_send_headers(sapi_headers_struct *sapi_headers);
49 static char *nxt_php_read_cookies(void);
50 static void nxt_php_set_sptr(nxt_unit_request_info_t *req, const char *name,
51     nxt_unit_sptr_t *v, uint32_t len, zval *track_vars_array TSRMLS_DC);
52 nxt_inline void nxt_php_set_str(nxt_unit_request_info_t *req, const char *name,
53     nxt_str_t *s, zval *track_vars_array TSRMLS_DC);
54 static void nxt_php_set_cstr(nxt_unit_request_info_t *req, const char *name,
55     char *str, uint32_t len, zval *track_vars_array TSRMLS_DC);
56 static void nxt_php_register_variables(zval *track_vars_array);
57 #ifdef NXT_HAVE_PHP_LOG_MESSAGE_WITH_SYSLOG_TYPE
58 static void nxt_php_log_message(char *message, int syslog_type_int);
59 #else
60 static void nxt_php_log_message(char *message);
61 #endif
62 
63 #ifdef NXT_PHP7
64 static size_t nxt_php_unbuffered_write(const char *str,
65     size_t str_length TSRMLS_DC);
66 static size_t nxt_php_read_post(char *buffer, size_t count_bytes TSRMLS_DC);
67 #else
68 static int nxt_php_unbuffered_write(const char *str, uint str_length TSRMLS_DC);
69 static int nxt_php_read_post(char *buffer, uint count_bytes TSRMLS_DC);
70 #endif
71 
72 
73 static sapi_module_struct  nxt_php_sapi_module =
74 {
75     (char *) "cli-server",
76     (char *) "unit",
77 
78     nxt_php_startup,             /* startup */
79     php_module_shutdown_wrapper, /* shutdown */
80 
81     NULL,                        /* activate */
82     NULL,                        /* deactivate */
83 
84     nxt_php_unbuffered_write,    /* unbuffered write */
85     NULL,                        /* flush */
86     NULL,                        /* get uid */
87     NULL,                        /* getenv */
88 
89     php_error,                   /* error handler */
90 
91     NULL,                        /* header handler */
92     nxt_php_send_headers,        /* send headers handler */
93     NULL,                        /* send header handler */
94 
95     nxt_php_read_post,           /* read POST data */
96     nxt_php_read_cookies,        /* read Cookies */
97 
98     nxt_php_register_variables,  /* register server variables */
99     nxt_php_log_message,         /* log message */
100     NULL,                        /* get request time */
101     NULL,                        /* terminate process */
102 
103     NULL,                        /* php_ini_path_override */
104 #ifdef NXT_HAVE_PHP_INTERRUPTS
105     NULL,                        /* block_interruptions */
106     NULL,                        /* unblock_interruptions */
107 #endif
108     NULL,                        /* default_post_reader */
109     NULL,                        /* treat_data */
110     NULL,                        /* executable_location */
111 
112     0,                           /* php_ini_ignore */
113 #ifdef NXT_HAVE_PHP_IGNORE_CWD
114     1,                           /* php_ini_ignore_cwd */
115 #endif
116     NULL,                        /* get_fd */
117 
118     NULL,                        /* force_http_10 */
119 
120     NULL,                        /* get_target_uid */
121     NULL,                        /* get_target_gid */
122 
123     NULL,                        /* input_filter */
124 
125     NULL,                        /* ini_defaults */
126     0,                           /* phpinfo_as_text */
127 
128     NULL,                        /* ini_entries */
129     NULL,                        /* additional_functions */
130     NULL                         /* input_filter_init */
131 };
132 
133 
134 struct nxt_php_run_ctx_s {
135     char                       *cookie;
136     nxt_str_t                  script;
137     nxt_unit_request_info_t    *req;
138 };
139 
140 
141 static nxt_str_t nxt_php_path;
142 static nxt_str_t nxt_php_root;
143 static nxt_str_t nxt_php_script;
144 static nxt_str_t nxt_php_index = nxt_string("index.php");
145 
146 
147 static uint32_t  compat[] = {
148     NXT_VERNUM, NXT_DEBUG,
149 };
150 
151 
152 NXT_EXPORT nxt_app_module_t  nxt_app_module = {
153     sizeof(compat),
154     compat,
155     nxt_string("php"),
156     PHP_VERSION,
157     nxt_php_init,
158 };
159 
160 
161 static nxt_task_t  *nxt_php_task;
162 
163 
164 static nxt_int_t
165 nxt_php_init(nxt_task_t *task, nxt_common_app_conf_t *conf)
166 {
167     u_char              *p;
168     nxt_str_t           rpath, ini_path;
169     nxt_str_t           *root, *path, *script, *index;
170     nxt_port_t          *my_port, *main_port;
171     nxt_runtime_t       *rt;
172     nxt_unit_ctx_t      *unit_ctx;
173     nxt_unit_init_t     php_init;
174     nxt_conf_value_t    *value;
175     nxt_php_app_conf_t  *c;
176 
177     static nxt_str_t  file_str = nxt_string("file");
178     static nxt_str_t  user_str = nxt_string("user");
179     static nxt_str_t  admin_str = nxt_string("admin");
180 
181     nxt_php_task = task;
182 
183     c = &conf->u.php;
184 
185     if (c->root == NULL) {
186         nxt_alert(task, "php root is empty");
187         return NXT_ERROR;
188     }
189 
190     root = &nxt_php_root;
191     path = &nxt_php_path;
192     script = &nxt_php_script;
193     index = &nxt_php_index;
194 
195     root->start = nxt_realpath(c->root);
196     if (nxt_slow_path(root->start == NULL)) {
197         nxt_alert(task, "root realpath(%s) failed %E", c->root, nxt_errno);
198         return NXT_ERROR;
199     }
200 
201     root->length = nxt_strlen(root->start);
202 
203     nxt_php_str_trim_trail(root, '/');
204 
205     if (c->script.length > 0) {
206         nxt_php_str_trim_lead(&c->script, '/');
207 
208         path->length = root->length + 1 + c->script.length;
209         path->start = nxt_malloc(path->length + 1);
210         if (nxt_slow_path(path->start == NULL)) {
211             return NXT_ERROR;
212         }
213 
214         p = nxt_cpymem(path->start, root->start, root->length);
215         *p++ = '/';
216 
217         p = nxt_cpymem(p, c->script.start, c->script.length);
218         *p = '\0';
219 
220         rpath.start = nxt_realpath(path->start);
221         if (nxt_slow_path(rpath.start == NULL)) {
222             nxt_alert(task, "script realpath(%V) failed %E", path, nxt_errno);
223             return NXT_ERROR;
224         }
225 
226         rpath.length = nxt_strlen(rpath.start);
227 
228         if (!nxt_str_start(&rpath, root->start, root->length)) {
229             nxt_alert(task, "script is not under php root");
230             return NXT_ERROR;
231         }
232 
233         nxt_free(path->start);
234 
235         *path = rpath;
236 
237         script->length = c->script.length + 1;
238         script->start = nxt_malloc(script->length);
239         if (nxt_slow_path(script->start == NULL)) {
240             return NXT_ERROR;
241         }
242 
243         script->start[0] = '/';
244         nxt_memcpy(script->start + 1, c->script.start, c->script.length);
245 
246         nxt_log_error(NXT_LOG_INFO, task->log,
247                       "(ABS_MODE) php script \"%V\" root: \"%V\"",
248                       script, root);
249 
250     } else {
251         nxt_log_error(NXT_LOG_INFO, task->log,
252                       "(non ABS_MODE) php root: \"%V\"", root);
253     }
254 
255     if (c->index.length > 0) {
256         index->length = c->index.length;
257         index->start = nxt_malloc(index->length);
258         if (nxt_slow_path(index->start == NULL)) {
259             return NXT_ERROR;
260         }
261 
262         nxt_memcpy(index->start, c->index.start, c->index.length);
263     }
264 
265     sapi_startup(&nxt_php_sapi_module);
266 
267     if (c->options != NULL) {
268         value = nxt_conf_get_object_member(c->options, &file_str, NULL);
269 
270         if (value != NULL) {
271             nxt_conf_get_string(value, &ini_path);
272 
273             p = nxt_malloc(ini_path.length + 1);
274             if (nxt_slow_path(p == NULL)) {
275                 return NXT_ERROR;
276             }
277 
278             nxt_php_sapi_module.php_ini_path_override = (char *) p;
279 
280             p = nxt_cpymem(p, ini_path.start, ini_path.length);
281             *p = '\0';
282         }
283     }
284 
285     nxt_php_startup(&nxt_php_sapi_module);
286 
287     if (c->options != NULL) {
288         value = nxt_conf_get_object_member(c->options, &admin_str, NULL);
289         nxt_php_set_options(task, value, ZEND_INI_SYSTEM);
290 
291         value = nxt_conf_get_object_member(c->options, &user_str, NULL);
292         nxt_php_set_options(task, value, ZEND_INI_USER);
293     }
294 
295     nxt_memzero(&php_init, sizeof(nxt_unit_init_t));
296 
297     rt = task->thread->runtime;
298 
299     main_port = rt->port_by_type[NXT_PROCESS_MAIN];
300     if (nxt_slow_path(main_port == NULL)) {
301         return NXT_ERROR;
302     }
303 
304     my_port = nxt_runtime_port_find(rt, nxt_pid, 0);
305     if (nxt_slow_path(my_port == NULL)) {
306         return NXT_ERROR;
307     }
308 
309     php_init.callbacks.request_handler = nxt_php_request_handler;
310     php_init.ready_port.id.pid = main_port->pid;
311     php_init.ready_port.id.id = main_port->id;
312     php_init.ready_port.out_fd = main_port->pair[1];
313 
314     nxt_fd_blocking(task, main_port->pair[1]);
315 
316     php_init.ready_stream = my_port->process->init->stream;
317 
318     php_init.read_port.id.pid = my_port->pid;
319     php_init.read_port.id.id = my_port->id;
320     php_init.read_port.in_fd = my_port->pair[0];
321 
322     nxt_fd_blocking(task, my_port->pair[0]);
323 
324     php_init.log_fd = 2;
325 
326     unit_ctx = nxt_unit_init(&php_init);
327     if (nxt_slow_path(unit_ctx == NULL)) {
328         return NXT_ERROR;
329     }
330 
331     nxt_unit_run(unit_ctx);
332 
333     nxt_unit_done(unit_ctx);
334 
335     exit(0);
336 
337     return NXT_OK;
338 }
339 
340 
341 static void
342 nxt_php_set_options(nxt_task_t *task, nxt_conf_value_t *options, int type)
343 {
344     uint32_t          next;
345     nxt_str_t         name, value;
346     nxt_conf_value_t  *value_obj;
347 
348     if (options != NULL) {
349         next = 0;
350 
351         for ( ;; ) {
352             value_obj = nxt_conf_next_object_member(options, &name, &next);
353             if (value_obj == NULL) {
354                 break;
355             }
356 
357             nxt_conf_get_string(value_obj, &value);
358 
359             if (nxt_php_alter_option(&name, &value, type) != NXT_OK) {
360                 nxt_log(task, NXT_LOG_ERR,
361                         "setting PHP option \"%V: %V\" failed", &name, &value);
362             }
363         }
364     }
365 }
366 
367 
368 #if (NXT_PHP7)
369 
370 static nxt_int_t
371 nxt_php_alter_option(nxt_str_t *name, nxt_str_t *value, int type)
372 {
373     zend_string     *zs;
374     zend_ini_entry  *ini_entry;
375 
376     ini_entry = zend_hash_str_find_ptr(EG(ini_directives),
377                                        (char *) name->start, name->length);
378 
379     if (ini_entry == NULL) {
380         return NXT_ERROR;
381     }
382 
383     /* PHP exits on memory allocation errors. */
384     zs = zend_string_init((char *) value->start, value->length, 1);
385 
386     if (ini_entry->on_modify
387         && ini_entry->on_modify(ini_entry, zs, ini_entry->mh_arg1,
388                                 ini_entry->mh_arg2, ini_entry->mh_arg3,
389                                 ZEND_INI_STAGE_ACTIVATE)
390            != SUCCESS)
391     {
392         zend_string_release(zs);
393         return NXT_ERROR;
394     }
395 
396     ini_entry->value = zs;
397     ini_entry->modifiable = type;
398 
399     return NXT_OK;
400 }
401 
402 #else  /* PHP 5. */
403 
404 static nxt_int_t
405 nxt_php_alter_option(nxt_str_t *name, nxt_str_t *value, int type)
406 {
407     char            *cstr;
408     zend_ini_entry  *ini_entry;
409     char            buf[256];
410 
411     if (nxt_slow_path(name->length >= sizeof(buf))) {
412         return NXT_ERROR;
413     }
414 
415     nxt_memcpy(buf, name->start, name->length);
416     buf[name->length] = '\0';
417 
418     if (zend_hash_find(EG(ini_directives), buf, name->length + 1,
419                        (void **) &ini_entry)
420         == FAILURE)
421     {
422         return NXT_ERROR;
423     }
424 
425     cstr = nxt_malloc(value->length + 1);
426     if (nxt_slow_path(cstr == NULL)) {
427         return NXT_ERROR;
428     }
429 
430     nxt_memcpy(cstr, value->start, value->length);
431     cstr[value->length] = '\0';
432 
433     if (ini_entry->on_modify
434         && ini_entry->on_modify(ini_entry, cstr, value->length,
435                                 ini_entry->mh_arg1, ini_entry->mh_arg2,
436                                 ini_entry->mh_arg3, ZEND_INI_STAGE_ACTIVATE)
437            != SUCCESS)
438     {
439         nxt_free(cstr);
440         return NXT_ERROR;
441     }
442 
443     ini_entry->value = cstr;
444     ini_entry->value_length = value->length;
445     ini_entry->modifiable = type;
446 
447     return NXT_OK;
448 }
449 
450 #endif
451 
452 
453 static void
454 nxt_php_str_trim_trail(nxt_str_t *str, u_char t)
455 {
456     while (str->length > 0 && str->start[str->length - 1] == t) {
457         str->length--;
458     }
459 
460     str->start[str->length] = '\0';
461 }
462 
463 
464 static void
465 nxt_php_str_trim_lead(nxt_str_t *str, u_char t)
466 {
467     while (str->length > 0 && str->start[0] == t) {
468         str->length--;
469         str->start++;
470     }
471 }
472 
473 
474 nxt_inline u_char *
475 nxt_realpath(const void *c)
476 {
477     return (u_char *) realpath(c, NULL);
478 }
479 
480 
481 static void
482 nxt_php_request_handler(nxt_unit_request_info_t *req)
483 {
484     int                 rc;
485     u_char              *p;
486     nxt_str_t           path, script_name;
487     nxt_unit_field_t    *f;
488     zend_file_handle    file_handle;
489     nxt_php_run_ctx_t   run_ctx, *ctx;
490     nxt_unit_request_t  *r;
491 
492     nxt_memzero(&run_ctx, sizeof(run_ctx));
493 
494     ctx = &run_ctx;
495     ctx->req = req;
496 
497     r = req->request;
498 
499     path.length = r->path_length;
500     path.start = nxt_unit_sptr_get(&r->path);
501 
502     if (nxt_php_path.start == NULL) {
503         if (path.start[path.length - 1] == '/') {
504             script_name = nxt_php_index;
505 
506         } else {
507             script_name.length = 0;
508             script_name.start = NULL;
509         }
510 
511         ctx->script.length = nxt_php_root.length + path.length
512                              + script_name.length;
513         p = ctx->script.start = nxt_malloc(ctx->script.length + 1);
514         if (nxt_slow_path(p == NULL)) {
515             nxt_unit_request_done(req, NXT_UNIT_ERROR);
516 
517             return;
518         }
519 
520         p = nxt_cpymem(p, nxt_php_root.start, nxt_php_root.length);
521         p = nxt_cpymem(p, path.start, path.length);
522 
523         if (script_name.length > 0) {
524             p = nxt_cpymem(p, script_name.start, script_name.length);
525         }
526 
527         *p = '\0';
528 
529     } else {
530         ctx->script = nxt_php_path;
531     }
532 
533     SG(server_context) = ctx;
534     SG(request_info).request_uri = nxt_unit_sptr_get(&r->target);
535     SG(request_info).request_method = nxt_unit_sptr_get(&r->method);
536 
537     SG(request_info).proto_num = 1001;
538 
539     SG(request_info).query_string = r->query.offset
540                                     ? nxt_unit_sptr_get(&r->query) : NULL;
541     SG(request_info).content_length = r->content_length;
542 
543     if (r->content_type_field != NXT_UNIT_NONE_FIELD) {
544         f = r->fields + r->content_type_field;
545 
546         SG(request_info).content_type = nxt_unit_sptr_get(&f->value);
547     }
548 
549     if (r->cookie_field != NXT_UNIT_NONE_FIELD) {
550         f = r->fields + r->cookie_field;
551 
552         ctx->cookie = nxt_unit_sptr_get(&f->value);
553     }
554 
555     SG(sapi_headers).http_response_code = 200;
556 
557     SG(request_info).path_translated = NULL;
558 
559     file_handle.type = ZEND_HANDLE_FILENAME;
560     file_handle.filename = (char *) ctx->script.start;
561     file_handle.free_filename = 0;
562     file_handle.opened_path = NULL;
563 
564     nxt_unit_req_debug(req, "handle.filename = '%s'", ctx->script.start);
565 
566     if (nxt_php_path.start != NULL) {
567         nxt_unit_req_debug(req, "run script %.*s in absolute mode",
568                            (int) nxt_php_path.length,
569                            (char *) nxt_php_path.start);
570 
571     } else {
572         nxt_unit_req_debug(req, "run script %.*s", (int) ctx->script.length,
573                            (char *) ctx->script.start);
574     }
575 
576     if (nxt_slow_path(php_request_startup() == FAILURE)) {
577         nxt_unit_req_debug(req, "php_request_startup() failed");
578         rc = NXT_UNIT_ERROR;
579 
580         goto fail;
581     }
582 
583     rc = NXT_UNIT_OK;
584 
585     php_execute_script(&file_handle TSRMLS_CC);
586     php_request_shutdown(NULL);
587 
588 fail:
589 
590     nxt_unit_request_done(req, rc);
591 
592     if (ctx->script.start != nxt_php_path.start) {
593         nxt_free(ctx->script.start);
594     }
595 }
596 
597 
598 static int
599 nxt_php_startup(sapi_module_struct *sapi_module)
600 {
601    return php_module_startup(sapi_module, NULL, 0);
602 }
603 
604 
605 #ifdef NXT_PHP7
606 static size_t
607 nxt_php_unbuffered_write(const char *str, size_t str_length TSRMLS_DC)
608 #else
609 static int
610 nxt_php_unbuffered_write(const char *str, uint str_length TSRMLS_DC)
611 #endif
612 {
613     int                rc;
614     nxt_php_run_ctx_t  *ctx;
615 
616     ctx = SG(server_context);
617 
618     rc = nxt_unit_response_write(ctx->req, str, str_length);
619     if (nxt_fast_path(rc == NXT_UNIT_OK)) {
620         return str_length;
621     }
622 
623     php_handle_aborted_connection();
624     return 0;
625 }
626 
627 
628 static int
629 nxt_php_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC)
630 {
631     int                      rc, fields_count;
632     char                     *colon, *status_line, *value;
633     uint16_t                 status;
634     uint32_t                 resp_size;
635     nxt_php_run_ctx_t        *ctx;
636     sapi_header_struct       *h;
637     zend_llist_position      zpos;
638     nxt_unit_request_info_t  *req;
639 
640     ctx = SG(server_context);
641     req = ctx->req;
642 
643     nxt_unit_req_debug(req, "nxt_php_send_headers");
644 
645     if (SG(request_info).no_headers == 1) {
646         rc = nxt_unit_response_init(req, 200, 0, 0);
647         if (nxt_slow_path(rc != NXT_UNIT_OK)) {
648             return SAPI_HEADER_SEND_FAILED;
649         }
650 
651         return SAPI_HEADER_SENT_SUCCESSFULLY;
652     }
653 
654     resp_size = 0;
655     fields_count = zend_llist_count(&sapi_headers->headers);
656 
657     for (h = zend_llist_get_first_ex(&sapi_headers->headers, &zpos);
658          h;
659          h = zend_llist_get_next_ex(&sapi_headers->headers, &zpos))
660     {
661         resp_size += h->header_len;
662     }
663 
664     if (SG(sapi_headers).http_status_line) {
665         status_line = SG(sapi_headers).http_status_line;
666 
667         status = nxt_int_parse((u_char *) status_line + 9, 3);
668 
669     } else if (SG(sapi_headers).http_response_code) {
670         status = SG(sapi_headers).http_response_code;
671 
672     } else {
673         status = 200;
674     }
675 
676     rc = nxt_unit_response_init(req, status, fields_count, resp_size);
677     if (nxt_slow_path(rc != NXT_UNIT_OK)) {
678         return SAPI_HEADER_SEND_FAILED;
679     }
680 
681     for (h = zend_llist_get_first_ex(&sapi_headers->headers, &zpos);
682          h;
683          h = zend_llist_get_next_ex(&sapi_headers->headers, &zpos))
684     {
685         nxt_unit_req_debug(req, "header: %.*s", (int) h->header_len, h->header);
686 
687         colon = memchr(h->header, ':', h->header_len);
688         if (nxt_slow_path(colon == NULL)) {
689             nxt_unit_req_warn(req, "colon not found in header '%.*s'",
690                               (int) h->header_len, h->header);
691             continue;
692         }
693 
694         value = colon + 1;
695         while(isspace(*value)) {
696             value++;
697         }
698 
699         nxt_unit_response_add_field(req, h->header, colon - h->header,
700                                     value,
701                                     h->header_len - (value - h->header));
702     }
703 
704     rc = nxt_unit_response_send(req);
705     if (nxt_slow_path(rc != NXT_UNIT_OK)) {
706         nxt_unit_req_debug(req, "failed to send response");
707 
708         return SAPI_HEADER_SEND_FAILED;
709     }
710 
711     return SAPI_HEADER_SENT_SUCCESSFULLY;
712 }
713 
714 
715 #ifdef NXT_PHP7
716 static size_t
717 nxt_php_read_post(char *buffer, size_t count_bytes TSRMLS_DC)
718 #else
719 static int
720 nxt_php_read_post(char *buffer, uint count_bytes TSRMLS_DC)
721 #endif
722 {
723     nxt_php_run_ctx_t  *ctx;
724 
725     ctx = SG(server_context);
726 
727     nxt_unit_req_debug(ctx->req, "nxt_php_read_post %d", (int) count_bytes);
728 
729     return nxt_unit_request_read(ctx->req, buffer, count_bytes);
730 }
731 
732 
733 static char *
734 nxt_php_read_cookies(TSRMLS_D)
735 {
736     nxt_php_run_ctx_t  *ctx;
737 
738     ctx = SG(server_context);
739 
740     nxt_unit_req_debug(ctx->req, "nxt_php_read_cookies");
741 
742     return ctx->cookie;
743 }
744 
745 
746 static void
747 nxt_php_register_variables(zval *track_vars_array TSRMLS_DC)
748 {
749     char                     *host_start, *port_start;
750     uint32_t                 host_length, port_length;
751     const char               *name;
752     nxt_unit_field_t         *f, *f_end;
753     nxt_php_run_ctx_t        *ctx;
754     nxt_unit_request_t       *r;
755     nxt_unit_request_info_t  *req;
756 
757     ctx = SG(server_context);
758 
759     req = ctx->req;
760     r = req->request;
761 
762     nxt_unit_req_debug(req, "nxt_php_register_variables");
763 
764     php_register_variable_safe((char *) "SERVER_SOFTWARE",
765                                (char *) nxt_server.start,
766                                nxt_server.length, track_vars_array TSRMLS_CC);
767 
768     nxt_php_set_sptr(req, "SERVER_PROTOCOL", &r->version, r->version_length,
769                      track_vars_array TSRMLS_CC);
770 
771 /*
772  * 'SCRIPT_NAME'
773  * Contains the current script's path. This is useful for pages which need to
774  * point to themselves. The __FILE__ constant contains the full path and
775  * filename of the current (i.e. included) file.
776  */
777 
778 /*
779  * 'SCRIPT_FILENAME'
780  * The absolute pathname of the currently executing script.
781  */
782 
783 /*
784  * 'DOCUMENT_ROOT'
785  * The document root directory under which the current script is executing,
786  * as defined in the server's configuration file.
787  */
788 
789     if (nxt_php_script.start != NULL) {
790     // ABS_MODE
791 /*
792  * 'PHP_SELF'
793  * The filename of the currently executing script, relative to the document
794  * root. For instance, $_SERVER['PHP_SELF'] in a script at the address
795  * http://example.com/foo/bar.php would be /foo/bar.php. The __FILE__ constant
796  * contains the full path and filename of the current (i.e. included) file.
797  * If PHP is running as a command-line processor this variable contains the
798  * script name since PHP 4.3.0. Previously it was not available.
799  */
800         nxt_php_set_str(req, "PHP_SELF", &nxt_php_script,
801                         track_vars_array TSRMLS_CC);
802         nxt_php_set_str(req, "SCRIPT_NAME", &nxt_php_script,
803                         track_vars_array TSRMLS_CC);
804 
805     } else {
806         nxt_php_set_sptr(req, "PHP_SELF", &r->path, r->path_length,
807                          track_vars_array TSRMLS_CC);
808         nxt_php_set_sptr(req, "SCRIPT_NAME", &r->path, r->path_length,
809                          track_vars_array TSRMLS_CC);
810     }
811 
812     nxt_php_set_str(req, "SCRIPT_FILENAME", &ctx->script,
813                     track_vars_array TSRMLS_CC);
814     nxt_php_set_str(req, "DOCUMENT_ROOT", &nxt_php_root,
815                     track_vars_array TSRMLS_CC);
816 
817     nxt_php_set_sptr(req, "REQUEST_METHOD", &r->method, r->method_length,
818                      track_vars_array TSRMLS_CC);
819     nxt_php_set_sptr(req, "REQUEST_URI", &r->target, r->target_length,
820                      track_vars_array TSRMLS_CC);
821     if (r->query.offset) {
822         nxt_php_set_sptr(req, "QUERY_STRING", &r->query, r->query_length,
823                          track_vars_array TSRMLS_CC);
824     }
825 
826     nxt_php_set_sptr(req, "REMOTE_ADDR", &r->remote, r->remote_length,
827                      track_vars_array TSRMLS_CC);
828     nxt_php_set_sptr(req, "SERVER_ADDR", &r->local, r->local_length,
829                      track_vars_array TSRMLS_CC);
830 
831     f_end = r->fields + r->fields_count;
832     for (f = r->fields; f < f_end; f++) {
833         name = nxt_unit_sptr_get(&f->name);
834 
835         nxt_php_set_sptr(req, name, &f->value, f->value_length,
836                          track_vars_array TSRMLS_CC);
837     }
838 
839     if (r->content_length_field != NXT_UNIT_NONE_FIELD) {
840         f = r->fields + r->content_length_field;
841 
842         nxt_php_set_sptr(req, "CONTENT_LENGTH", &f->value, f->value_length,
843                          track_vars_array TSRMLS_CC);
844     }
845 
846     if (r->content_type_field != NXT_UNIT_NONE_FIELD) {
847         f = r->fields + r->content_type_field;
848 
849         nxt_php_set_sptr(req, "CONTENT_TYPE", &f->value, f->value_length,
850                          track_vars_array TSRMLS_CC);
851     }
852 
853     if (r->host_field != NXT_UNIT_NONE_FIELD) {
854         f = r->fields + r->host_field;
855 
856         host_start = nxt_unit_sptr_get(&f->value);
857         host_length = f->value_length;
858 
859     } else {
860         host_start = NULL;
861         host_length = 0;
862     }
863 
864     nxt_unit_split_host(host_start, host_length, &host_start, &host_length,
865                         &port_start, &port_length);
866 
867     nxt_php_set_cstr(req, "SERVER_NAME", host_start, host_length,
868                     track_vars_array TSRMLS_CC);
869     nxt_php_set_cstr(req, "SERVER_PORT", port_start, port_length,
870                     track_vars_array TSRMLS_CC);
871 }
872 
873 
874 static void
875 nxt_php_set_sptr(nxt_unit_request_info_t *req, const char *name,
876     nxt_unit_sptr_t *v, uint32_t len, zval *track_vars_array TSRMLS_DC)
877 {
878     char  *str;
879 
880     str = nxt_unit_sptr_get(v);
881 
882     nxt_unit_req_debug(req, "php: register %s='%.*s'", name, (int) len, str);
883 
884     php_register_variable_safe((char *) name, str, len,
885                                track_vars_array TSRMLS_CC);
886 }
887 
888 
889 nxt_inline void
890 nxt_php_set_str(nxt_unit_request_info_t *req, const char *name,
891     nxt_str_t *s, zval *track_vars_array TSRMLS_DC)
892 {
893     nxt_php_set_cstr(req, name, (char *) s->start, s->length,
894                      track_vars_array TSRMLS_CC);
895 }
896 
897 
898 static void
899 nxt_php_set_cstr(nxt_unit_request_info_t *req, const char *name,
900     char *cstr, uint32_t len, zval *track_vars_array TSRMLS_DC)
901 {
902     if (nxt_slow_path(cstr == NULL)) {
903         return;
904     }
905 
906     nxt_unit_req_debug(req, "php: register %s='%.*s'", name, (int) len, cstr);
907 
908     php_register_variable_safe((char *) name, cstr, len,
909                                track_vars_array TSRMLS_CC);
910 }
911 
912 
913 #ifdef NXT_HAVE_PHP_LOG_MESSAGE_WITH_SYSLOG_TYPE
914 static void
915 nxt_php_log_message(char *message, int syslog_type_int)
916 #else
917 static void
918 nxt_php_log_message(char *message)
919 #endif
920 {
921     nxt_log(nxt_php_task, NXT_LOG_NOTICE, "php message: %s", message);
922 }
923