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