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