1 2 /* 3 * Copyright (C) Max Romanov 4 * Copyright (C) Valentin V. Bartenev 5 * Copyright (C) NGINX, Inc. 6 */ 7 8 #include "php.h" 9 #include "SAPI.h" 10 #include "php_main.h" 11 #include "php_variables.h" 12 13 #include <nxt_main.h> 14 #include <nxt_router.h> 15 16 17 static nxt_int_t nxt_php_init(nxt_task_t *task, nxt_common_app_conf_t *conf); 18 19 static nxt_int_t nxt_php_run(nxt_task_t *task, 20 nxt_app_rmsg_t *rmsg, nxt_app_wmsg_t *wmsg); 21 22 #if PHP_MAJOR_VERSION >= 7 23 # define NXT_PHP7 1 24 # if PHP_MINOR_VERSION >= 1 25 # define NXT_HAVE_PHP_LOG_MESSAGE_WITH_SYSLOG_TYPE 1 26 # else 27 # define NXT_HAVE_PHP_INTERRUPTS 1 28 # endif 29 # define NXT_HAVE_PHP_IGNORE_CWD 1 30 #else 31 # define NXT_HAVE_PHP_INTERRUPTS 1 32 # if PHP_MINOR_VERSION >= 4 33 # define NXT_HAVE_PHP_IGNORE_CWD 1 34 # endif 35 #endif 36 37 static int nxt_php_startup(sapi_module_struct *sapi_module); 38 static int nxt_php_send_headers(sapi_headers_struct *sapi_headers); 39 static char *nxt_php_read_cookies(void); 40 static void nxt_php_register_variables(zval *track_vars_array); 41 #ifdef NXT_HAVE_PHP_LOG_MESSAGE_WITH_SYSLOG_TYPE 42 static void nxt_php_log_message(char *message, int syslog_type_int); 43 #else 44 static void nxt_php_log_message(char *message); 45 #endif 46 47 #ifdef NXT_PHP7 48 static size_t nxt_php_unbuffered_write(const char *str, 49 size_t str_length TSRMLS_DC); 50 static size_t nxt_php_read_post(char *buffer, size_t count_bytes TSRMLS_DC); 51 #else 52 static int nxt_php_unbuffered_write(const char *str, uint str_length TSRMLS_DC); 53 static int nxt_php_read_post(char *buffer, uint count_bytes TSRMLS_DC); 54 #endif 55 56 static void nxt_php_flush(void *server_context); 57 58 59 static sapi_module_struct nxt_php_sapi_module = 60 { 61 (char *) "cli-server", 62 (char *) "unit", 63 64 nxt_php_startup, /* startup */ 65 php_module_shutdown_wrapper, /* shutdown */ 66 67 NULL, /* activate */ 68 NULL, /* deactivate */ 69 70 nxt_php_unbuffered_write, /* unbuffered write */ 71 nxt_php_flush, /* flush */ 72 NULL, /* get uid */ 73 NULL, /* getenv */ 74 75 php_error, /* error handler */ 76 77 NULL, /* header handler */ 78 nxt_php_send_headers, /* send headers handler */ 79 NULL, /* send header handler */ 80 81 nxt_php_read_post, /* read POST data */ 82 nxt_php_read_cookies, /* read Cookies */ 83 84 nxt_php_register_variables, /* register server variables */ 85 nxt_php_log_message, /* log message */ 86 NULL, /* get request time */ 87 NULL, /* terminate process */ 88 89 NULL, /* php_ini_path_override */ 90 #ifdef NXT_HAVE_PHP_INTERRUPTS 91 NULL, /* block_interruptions */ 92 NULL, /* unblock_interruptions */ 93 #endif 94 NULL, /* default_post_reader */ 95 NULL, /* treat_data */ 96 NULL, /* executable_location */ 97 98 0, /* php_ini_ignore */ 99 #ifdef NXT_HAVE_PHP_IGNORE_CWD 100 0, /* php_ini_ignore_cwd */ 101 #endif 102 NULL, /* get_fd */ 103 104 NULL, /* force_http_10 */ 105 106 NULL, /* get_target_uid */ 107 NULL, /* get_target_gid */ 108 109 NULL, /* input_filter */ 110 111 NULL, /* ini_defaults */ 112 0, /* phpinfo_as_text */ 113 114 NULL, /* ini_entries */ 115 NULL, /* additional_functions */ 116 NULL /* input_filter_init */ 117 }; 118 119 typedef struct { 120 nxt_task_t *task; 121 nxt_app_rmsg_t *rmsg; 122 nxt_app_request_t r; 123 nxt_str_t script; 124 nxt_app_wmsg_t *wmsg; 125 126 size_t body_preread_size; 127 } nxt_php_run_ctx_t; 128 129 nxt_inline nxt_int_t nxt_php_write(nxt_php_run_ctx_t *ctx, 130 const u_char *data, size_t len, 131 nxt_bool_t flush, nxt_bool_t last); 132 133 134 static nxt_str_t nxt_php_path; 135 static nxt_str_t nxt_php_root; 136 static nxt_str_t nxt_php_script; 137 static nxt_str_t nxt_php_index = nxt_string("index.php"); 138 139 140 static void 141 nxt_php_str_trim_trail(nxt_str_t *str, u_char t) 142 { 143 while (str->length > 0 && str->start[str->length - 1] == t) { 144 str->length--; 145 } 146 147 str->start[str->length] = '\0'; 148 } 149 150 151 static void 152 nxt_php_str_trim_lead(nxt_str_t *str, u_char t) 153 { 154 while (str->length > 0 && str->start[0] == t) { 155 str->length--; 156 str->start++; 157 } 158 } 159 160 static uint32_t compat[] = { 161 NXT_VERNUM, NXT_DEBUG, 162 }; 163 164 165 NXT_EXPORT nxt_application_module_t nxt_app_module = { 166 sizeof(compat), 167 compat, 168 nxt_string("php"), 169 PHP_VERSION, 170 nxt_php_init, 171 nxt_php_run, 172 NULL, 173 }; 174 175 176 nxt_inline u_char * 177 nxt_realpath(const void *c) 178 { 179 return (u_char *) realpath(c, NULL); 180 } 181 182 183 static nxt_int_t 184 nxt_php_init(nxt_task_t *task, nxt_common_app_conf_t *conf) 185 { 186 u_char *p; 187 nxt_str_t rpath; 188 nxt_str_t *root, *path, *script, *index; 189 nxt_php_app_conf_t *c; 190 191 c = &conf->u.php; 192 193 if (c->root == NULL) { 194 nxt_alert(task, "php root is empty"); 195 return NXT_ERROR; 196 } 197 198 root = &nxt_php_root; 199 path = &nxt_php_path; 200 script = &nxt_php_script; 201 index = &nxt_php_index; 202 203 root->start = nxt_realpath(c->root); 204 if (nxt_slow_path(root->start == NULL)) { 205 nxt_alert(task, "root realpath(%s) failed %E", c->root, nxt_errno); 206 return NXT_ERROR; 207 } 208 209 root->length = nxt_strlen(root->start); 210 211 nxt_php_str_trim_trail(root, '/'); 212 213 if (c->script.length > 0) { 214 nxt_php_str_trim_lead(&c->script, '/'); 215 216 path->length = root->length + 1 + c->script.length; 217 path->start = nxt_malloc(path->length + 1); 218 if (nxt_slow_path(path->start == NULL)) { 219 return NXT_ERROR; 220 } 221 222 p = nxt_cpymem(path->start, root->start, root->length); 223 *p++ = '/'; 224 225 p = nxt_cpymem(p, c->script.start, c->script.length); 226 *p = '\0'; 227 228 rpath.start = nxt_realpath(path->start); 229 if (nxt_slow_path(rpath.start == NULL)) { 230 nxt_alert(task, "script realpath(%V) failed %E", path, nxt_errno); 231 return NXT_ERROR; 232 } 233 234 rpath.length = nxt_strlen(rpath.start); 235 236 if (!nxt_str_start(&rpath, root->start, root->length)) { 237 nxt_alert(task, "script is not under php root"); 238 return NXT_ERROR; 239 } 240 241 nxt_free(path->start); 242 243 *path = rpath; 244 245 script->length = c->script.length + 1; 246 script->start = nxt_malloc(script->length); 247 if (nxt_slow_path(script->start == NULL)) { 248 return NXT_ERROR; 249 } 250 251 script->start[0] = '/'; 252 nxt_memcpy(script->start + 1, c->script.start, c->script.length); 253 254 nxt_log_error(NXT_LOG_INFO, task->log, 255 "(ABS_MODE) php script \"%V\" root: \"%V\"", 256 script, root); 257 258 } else { 259 nxt_log_error(NXT_LOG_INFO, task->log, 260 "(non ABS_MODE) php root: \"%V\"", root); 261 } 262 263 if (c->index.length > 0) { 264 index->length = c->index.length; 265 index->start = nxt_malloc(index->length); 266 if (nxt_slow_path(index->start == NULL)) { 267 return NXT_ERROR; 268 } 269 270 nxt_memcpy(index->start, c->index.start, c->index.length); 271 } 272 273 sapi_startup(&nxt_php_sapi_module); 274 nxt_php_startup(&nxt_php_sapi_module); 275 276 return NXT_OK; 277 } 278 279 280 static nxt_int_t 281 nxt_php_read_request(nxt_task_t *task, nxt_app_rmsg_t *rmsg, 282 nxt_php_run_ctx_t *ctx) 283 { 284 u_char *p; 285 size_t s; 286 nxt_int_t rc; 287 nxt_str_t script_name; 288 nxt_app_request_header_t *h; 289 290 h = &ctx->r.header; 291 292 #define RC(S) \ 293 do { \ 294 rc = (S); \ 295 if (nxt_slow_path(rc != NXT_OK)) { \ 296 goto fail; \ 297 } \ 298 } while(0) 299 300 #define NXT_READ(dst) \ 301 RC(nxt_app_msg_read_str(task, rmsg, (dst))) 302 303 NXT_READ(&h->method); 304 NXT_READ(&h->target); 305 NXT_READ(&h->path); 306 307 RC(nxt_app_msg_read_size(task, rmsg, &s)); 308 if (s > 0) { 309 s--; 310 h->query.start = h->target.start + s; 311 h->query.length = h->target.length - s; 312 313 if (h->path.start == NULL) { 314 h->path.start = h->target.start; 315 h->path.length = s - 1; 316 } 317 } 318 319 if (h->path.start == NULL) { 320 h->path = h->target; 321 } 322 323 if (nxt_php_path.start == NULL) { 324 if (h->path.start[h->path.length - 1] == '/') { 325 script_name = nxt_php_index; 326 327 } else { 328 script_name.length = 0; 329 script_name.start = NULL; 330 } 331 332 ctx->script.length = nxt_php_root.length + h->path.length 333 + script_name.length; 334 p = ctx->script.start = nxt_malloc(ctx->script.length + 1); 335 if (nxt_slow_path(p == NULL)) { 336 return NXT_ERROR; 337 } 338 339 p = nxt_cpymem(p, nxt_php_root.start, nxt_php_root.length); 340 p = nxt_cpymem(p, h->path.start, h->path.length); 341 342 if (script_name.length > 0) { 343 p = nxt_cpymem(p, script_name.start, script_name.length); 344 } 345 346 *p = '\0'; 347 348 } else { 349 ctx->script = nxt_php_path; 350 } 351 352 NXT_READ(&h->version); 353 354 NXT_READ(&ctx->r.remote); 355 NXT_READ(&ctx->r.local); 356 357 NXT_READ(&h->host); 358 NXT_READ(&h->cookie); 359 NXT_READ(&h->content_type); 360 NXT_READ(&h->content_length); 361 362 RC(nxt_app_msg_read_size(task, rmsg, &s)); 363 h->parsed_content_length = s; 364 365 RC(nxt_app_msg_read_size(task, ctx->rmsg, &ctx->body_preread_size)); 366 367 #undef NXT_READ 368 #undef RC 369 370 /* Further headers read moved to nxt_php_register_variables. */ 371 return NXT_OK; 372 373 fail: 374 375 return rc; 376 } 377 378 379 static nxt_int_t 380 nxt_php_run(nxt_task_t *task, 381 nxt_app_rmsg_t *rmsg, nxt_app_wmsg_t *wmsg) 382 { 383 nxt_int_t rc; 384 zend_file_handle file_handle; 385 nxt_php_run_ctx_t run_ctx; 386 nxt_app_request_header_t *h; 387 388 nxt_memzero(&run_ctx, sizeof(run_ctx)); 389 390 run_ctx.task = task; 391 run_ctx.rmsg = rmsg; 392 run_ctx.wmsg = wmsg; 393 394 h = &run_ctx.r.header; 395 396 rc = nxt_php_read_request(task, rmsg, &run_ctx); 397 398 if (nxt_slow_path(rc != NXT_OK)) { 399 goto fail; 400 } 401 402 SG(server_context) = &run_ctx; 403 SG(request_info).request_uri = (char *) h->target.start; 404 SG(request_info).request_method = (char *) h->method.start; 405 406 SG(request_info).proto_num = 1001; 407 408 SG(request_info).query_string = (char *) h->query.start; 409 SG(request_info).content_length = h->parsed_content_length; 410 411 if (h->content_type.start != NULL) { 412 SG(request_info).content_type = (char *) h->content_type.start; 413 } 414 415 SG(sapi_headers).http_response_code = 200; 416 417 SG(request_info).path_translated = NULL; 418 419 file_handle.type = ZEND_HANDLE_FILENAME; 420 file_handle.filename = (char *) run_ctx.script.start; 421 file_handle.free_filename = 0; 422 file_handle.opened_path = NULL; 423 424 nxt_debug(task, "handle.filename = '%s'", run_ctx.script.start); 425 426 if (nxt_php_path.start != NULL) { 427 nxt_debug(task, "run script %V in absolute mode", &nxt_php_path); 428 429 } else { 430 nxt_debug(task, "run script %V", &run_ctx.script); 431 } 432 433 if (nxt_slow_path(php_request_startup() == FAILURE)) { 434 nxt_debug(task, "php_request_startup() failed"); 435 rc = NXT_ERROR; 436 goto fail; 437 } 438 439 php_execute_script(&file_handle TSRMLS_CC); 440 php_request_shutdown(NULL); 441 442 nxt_app_msg_flush(task, wmsg, 1); 443 444 rc = NXT_OK; 445 446 fail: 447 448 if (run_ctx.script.start != nxt_php_path.start) { 449 nxt_free(run_ctx.script.start); 450 } 451 452 return rc; 453 } 454 455 456 nxt_inline nxt_int_t 457 nxt_php_write(nxt_php_run_ctx_t *ctx, const u_char *data, size_t len, 458 nxt_bool_t flush, nxt_bool_t last) 459 { 460 nxt_int_t rc; 461 462 if (len > 0) { 463 rc = nxt_app_msg_write_raw(ctx->task, ctx->wmsg, data, len); 464 465 } else { 466 rc = NXT_OK; 467 } 468 469 if (flush || last) { 470 rc = nxt_app_msg_flush(ctx->task, ctx->wmsg, last); 471 } 472 473 return rc; 474 } 475 476 477 static int 478 nxt_php_startup(sapi_module_struct *sapi_module) 479 { 480 return php_module_startup(sapi_module, NULL, 0); 481 } 482 483 484 #ifdef NXT_PHP7 485 static size_t 486 nxt_php_unbuffered_write(const char *str, size_t str_length TSRMLS_DC) 487 #else 488 static int 489 nxt_php_unbuffered_write(const char *str, uint str_length TSRMLS_DC) 490 #endif 491 { 492 nxt_int_t rc; 493 nxt_php_run_ctx_t *ctx; 494 495 ctx = SG(server_context); 496 497 rc = nxt_php_write(ctx, (u_char *) str, str_length, 1, 0); 498 499 if (nxt_fast_path(rc == NXT_OK)) { 500 return str_length; 501 } 502 503 // TODO handle NXT_AGAIN 504 php_handle_aborted_connection(); 505 return 0; 506 } 507 508 509 static void 510 nxt_php_flush(void *server_context) 511 { 512 nxt_php_run_ctx_t *ctx; 513 514 ctx = server_context; 515 516 (void) nxt_app_msg_flush(ctx->task, ctx->wmsg, 0); 517 } 518 519 520 static int 521 nxt_php_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC) 522 { 523 size_t len; 524 u_char *status, buf[64]; 525 nxt_int_t rc; 526 nxt_php_run_ctx_t *ctx; 527 sapi_header_struct *h; 528 zend_llist_position zpos; 529 530 static const u_char default_repsonse[] 531 = "Status: 200\r\n" 532 "\r\n"; 533 534 static const u_char status_200[] = "Status: 200"; 535 static const u_char cr_lf[] = "\r\n"; 536 537 ctx = SG(server_context); 538 539 #define RC(S) \ 540 do { \ 541 rc = (S); \ 542 if (nxt_slow_path(rc != NXT_OK)) { \ 543 goto fail; \ 544 } \ 545 } while(0) 546 547 if (SG(request_info).no_headers == 1) { 548 RC(nxt_php_write(ctx, default_repsonse, sizeof(default_repsonse) - 1, 549 1, 0)); 550 return SAPI_HEADER_SENT_SUCCESSFULLY; 551 } 552 553 if (SG(sapi_headers).http_status_line) { 554 status = (u_char *) SG(sapi_headers).http_status_line; 555 len = nxt_strlen(status); 556 557 if (len < 12) { 558 goto fail; 559 } 560 561 RC(nxt_php_write(ctx, status_200, sizeof(status_200) - 4, 0, 0)); 562 RC(nxt_php_write(ctx, status + 9, 3, 0, 0)); 563 564 } else if (SG(sapi_headers).http_response_code) { 565 status = nxt_sprintf(buf, buf + sizeof(buf), "%03d", 566 SG(sapi_headers).http_response_code); 567 len = status - buf; 568 569 RC(nxt_php_write(ctx, status_200, sizeof(status_200) - 4, 0, 0)); 570 RC(nxt_php_write(ctx, buf, len, 0, 0)); 571 572 } else { 573 RC(nxt_php_write(ctx, status_200, sizeof(status_200) - 1, 0, 0)); 574 } 575 576 RC(nxt_php_write(ctx, cr_lf, sizeof(cr_lf) - 1, 0, 0)); 577 578 h = zend_llist_get_first_ex(&sapi_headers->headers, &zpos); 579 580 while (h) { 581 RC(nxt_php_write(ctx, (u_char *) h->header, h->header_len, 0, 0)); 582 RC(nxt_php_write(ctx, cr_lf, sizeof(cr_lf) - 1, 0, 0)); 583 584 h = zend_llist_get_next_ex(&sapi_headers->headers, &zpos); 585 } 586 587 RC(nxt_php_write(ctx, cr_lf, sizeof(cr_lf) - 1, 1, 0)); 588 589 #undef RC 590 591 return SAPI_HEADER_SENT_SUCCESSFULLY; 592 593 fail: 594 595 // TODO handle NXT_AGAIN 596 return SAPI_HEADER_SEND_FAILED; 597 } 598 599 600 #ifdef NXT_PHP7 601 static size_t 602 nxt_php_read_post(char *buffer, size_t count_bytes TSRMLS_DC) 603 #else 604 static int 605 nxt_php_read_post(char *buffer, uint count_bytes TSRMLS_DC) 606 #endif 607 { 608 size_t size, rest; 609 nxt_php_run_ctx_t *ctx; 610 nxt_app_request_header_t *h; 611 612 ctx = SG(server_context); 613 h = &ctx->r.header; 614 615 rest = (size_t) h->parsed_content_length - SG(read_post_bytes); 616 617 nxt_debug(ctx->task, "nxt_php_read_post %O", rest); 618 619 if (rest == 0) { 620 return 0; 621 } 622 623 rest = nxt_min(ctx->body_preread_size, (size_t) count_bytes); 624 size = nxt_app_msg_read_raw(ctx->task, ctx->rmsg, buffer, rest); 625 626 ctx->body_preread_size -= size; 627 628 return size; 629 } 630 631 632 static char * 633 nxt_php_read_cookies(TSRMLS_D) 634 { 635 nxt_php_run_ctx_t *ctx; 636 637 ctx = SG(server_context); 638 639 nxt_debug(ctx->task, "nxt_php_read_cookies"); 640 641 return (char *) ctx->r.header.cookie.start; 642 } 643 644 645 static void 646 nxt_php_register_variables(zval *track_vars_array TSRMLS_DC) 647 { 648 u_char *colon; 649 nxt_str_t n, v; 650 nxt_int_t rc; 651 nxt_str_t host, server_name, server_port; 652 nxt_task_t *task; 653 nxt_php_run_ctx_t *ctx; 654 nxt_app_request_header_t *h; 655 656 static nxt_str_t def_host = nxt_string("localhost"); 657 static nxt_str_t def_port = nxt_string("80"); 658 659 ctx = SG(server_context); 660 661 h = &ctx->r.header; 662 task = ctx->task; 663 664 nxt_debug(task, "php register variables"); 665 666 #define NXT_PHP_SET(n, v) \ 667 nxt_debug(task, "php: register %s='%V'", n, &v); \ 668 php_register_variable_safe((char *) (n), (char *) (v).start, \ 669 (v).length, track_vars_array TSRMLS_CC) \ 670 671 NXT_PHP_SET("SERVER_SOFTWARE", nxt_server); 672 673 NXT_PHP_SET("SERVER_PROTOCOL", h->version); 674 675 /* 676 * 'SCRIPT_NAME' 677 * Contains the current script's path. This is useful for pages which need to 678 * point to themselves. The __FILE__ constant contains the full path and 679 * filename of the current (i.e. included) file. 680 */ 681 682 /* 683 * 'SCRIPT_FILENAME' 684 * The absolute pathname of the currently executing script. 685 */ 686 687 /* 688 * 'DOCUMENT_ROOT' 689 * The document root directory under which the current script is executing, 690 * as defined in the server's configuration file. 691 */ 692 693 if (nxt_php_script.start != NULL) { 694 // ABS_MODE 695 /* 696 * 'PHP_SELF' 697 * The filename of the currently executing script, relative to the document 698 * root. For instance, $_SERVER['PHP_SELF'] in a script at the address 699 * http://example.com/foo/bar.php would be /foo/bar.php. The __FILE__ constant 700 * contains the full path and filename of the current (i.e. included) file. 701 * If PHP is running as a command-line processor this variable contains the 702 * script name since PHP 4.3.0. Previously it was not available. 703 */ 704 NXT_PHP_SET("PHP_SELF", nxt_php_script); 705 NXT_PHP_SET("SCRIPT_NAME", nxt_php_script); 706 707 } else { 708 NXT_PHP_SET("PHP_SELF", h->path); 709 NXT_PHP_SET("SCRIPT_NAME", h->path); 710 } 711 712 NXT_PHP_SET("SCRIPT_FILENAME", ctx->script); 713 NXT_PHP_SET("DOCUMENT_ROOT", nxt_php_root); 714 715 NXT_PHP_SET("REQUEST_METHOD", h->method); 716 NXT_PHP_SET("REQUEST_URI", h->target); 717 718 if (h->query.start != NULL) { 719 NXT_PHP_SET("QUERY_STRING", h->query); 720 } 721 722 if (h->content_type.start != NULL) { 723 NXT_PHP_SET("CONTENT_TYPE", h->content_type); 724 } 725 726 if (h->content_length.start != NULL) { 727 NXT_PHP_SET("CONTENT_LENGTH", h->content_length); 728 } 729 730 host = h->host; 731 if (host.length == 0) { 732 host = def_host; 733 } 734 735 server_name = host; 736 colon = nxt_memchr(host.start, ':', host.length); 737 738 if (colon != NULL) { 739 server_name.length = colon - host.start; 740 741 server_port.start = colon + 1; 742 server_port.length = host.length - server_name.length - 1; 743 744 } else { 745 server_port = def_port; 746 } 747 748 NXT_PHP_SET("SERVER_NAME", server_name); 749 NXT_PHP_SET("SERVER_PORT", server_port); 750 751 NXT_PHP_SET("REMOTE_ADDR", ctx->r.remote); 752 NXT_PHP_SET("SERVER_ADDR", ctx->r.local); 753 754 while (nxt_app_msg_read_str(task, ctx->rmsg, &n) == NXT_OK) { 755 if (nxt_slow_path(n.length == 0)) { 756 break; 757 } 758 759 rc = nxt_app_msg_read_str(task, ctx->rmsg, &v); 760 if (nxt_slow_path(rc != NXT_OK)) { 761 break; 762 } 763 764 NXT_PHP_SET(n.start, v); 765 } 766 767 #undef NXT_PHP_SET 768 } 769 770 771 #ifdef NXT_HAVE_PHP_LOG_MESSAGE_WITH_SYSLOG_TYPE 772 static void 773 nxt_php_log_message(char *message, int syslog_type_int) 774 #else 775 static void 776 nxt_php_log_message(char *message) 777 #endif 778 { 779 nxt_thread_log_error(NXT_LOG_NOTICE, "php message: %s", message); 780 } 781