Description
Description
The following (very invalid) code:
<?php
bug
Results in this output:
$ php -S 0:3000 router.php
[Wed Jan 10 19:37:13 2024] PHP 8.2.7 Development Server (http://0:3000) started
[Wed Jan 10 19:37:14 2024] 127.0.0.1:55760 Accepted
[Wed Jan 10 19:37:14 2024] 127.0.0.1:55760 Closing
And:
$ curl 127.0.0.1:3000 -vvv
* Trying 127.0.0.1:3000...
* Connected to 127.0.0.1 (127.0.0.1) port 3000 (#0)
> GET / HTTP/1.1
> Host: 127.0.0.1:3000
> User-Agent: curl/7.88.1
> Accept: */*
>
< HTTP/1.1 200 OK
< Host: 127.0.0.1:3000
< Date: Wed, 10 Jan 2024 07:51:07 GMT
< Connection: close
< X-Powered-By: PHP/8.2.7
< Content-type: text/html; charset=UTF-8
<
* Closing connection 0
But I expected output along these lines instead:
[Wed Jan 10 19:37:13 2024] PHP 8.2.7 Development Server (http://0:3000) started [Wed Jan 10 19:37:14 2024] 127.0.0.1:55760 Accepted [Wed Jan 10 19:37:14 2024] 127.0.0.1:55760 [200] (some appropriate preamble) - syntax error, unexpected end of file in router.php (router) on line 3 [Wed Jan 10 19:37:14 2024] 127.0.0.1:55760 Closing
And something similar to:
$ curl 127.1:3000/ -vvv
* Trying 127.0.0.1:3000...
* Connected to 127.0.0.1 (127.0.0.1) port 3000 (#0)
> GET / HTTP/1.1
> Host: 127.0.0.1:3000
> User-Agent: curl/7.88.1
> Accept: */*
>
* HTTP 1.0, assume close after body
< HTTP/1.0 500 Internal Server Error
< Host: 127.0.0.1:3000
< Date: Wed, 10 Jan 2024 08:58:08 GMT
< Connection: close
< X-Powered-By: PHP/8.2.7
< Content-type: text/html; charset=UTF-8
<
* Closing connection 0
Analyses/discussion
I was curious and did a bit of code spelunking. I realized very quickly that my cursory glance around (an hour or so) wasn't going to quickly grasp the whole bigger picture so I just identified some basics.
It looks like the router path is handled by php_cli_server_dispatch_router()
, while the regular URI-based path is handled by php_cli_server_dispatch_script()
.
The regular URI-based path has a crucial call to php_cli_server_log_response()
, which is responsible for logging the request and its status code - while the router path doesn't have any equivalent! Given that the router effectively replaces file lookup, I vote that similar logging should be added to the router path. It feels like obvious common sense.
As noted in a comment in the function, php_cli_server_dispatch_router()
uses zend_execute_scripts()
instead of php_execute_scripts()
so that you can return false;
from the top level of the router script and PHP will serve a generic 404 page for you. That's pretty neat.
I think what needs to happen is that the logic that presently just checks to see if the return value IS_FALSE
needs to be extended to handle script errors.
Some extremely cheeky overreaching to put a surreptitious php_debug_zval_dump(&retval, 0);
just above } zend_end_try();
produces some interesting bits of signal: I can return 2;
and see int(2)
- or I can drop a syntax error or two and get UNKNOWN:0
instead.
There's probably a more properly idiomatic and unambiguous way to detect and surface errors though. I'm just wandering for fun :)
Generally, if I were to soapbox about what should be happening here, I'd say that the router path should be brought up to scratch to be consistent with the rest of PHP wrt. respecting error_reporting
and show_errors
et al, including (and particularly) to the point of dumping parse/syntax errors.
Thanks for reading!
PHP Version
8.2.7
Operating System
Debian Stable