Repository navigation
Improve the reporting of parsing errors #24112
Description
Activity
Hi. It's not clear to me what you're asking for. Would you like syntax errors output to HTML by default? Or how else does PHP not behave like other interpreters? Can you explain in a bit more detail what exactly should change and how?
PHP currently reports parse errors (syntax errors) incorrectly (differently from all other PHP error types, such as fatal errors), by raising an HTTP 500 error instead of using the existing PHP error reporting mechanism. My proposal is to fix this problem, which requires significant change to the first pass of PHP over the user code. This would improve developer and user experiences with PHP.
That's not accurate. See https://3v4l.org/Ik06I. PHP already throws a catchable exception for syntax errors. You get a 500 because you're not catching it, or because it occurs in your primary index file. That exception it's later turned into an error if uncaught. Presumably you're also using the
display_errors=0ini setting in production, which you should. Leaking implementation details with the default configuration, which is a potential attack surface, is really not a good idea.Reporting parsing errors in the same way as other errors would benefit far more users than it would burden
It is doing that. If you're not seeing it, but you're seeing other errors, it's possible it occurs before any of your code runs, which might include a
ini_set('display_errors');call, or something along those lines.I'm struggling to understand myself where else other than the display_errors/error_reporting INI options you could set if your main script isn't parseable.
Thanks for the correction and the 3v4l link — you're right that I overstated it, and I appreciate you taking the time to demonstrate it concretely. I'd missed that a ParseError from a required/included file is already a catchable exception like any other error type; that part of my report was wrong as written, and I'll narrow the proposal accordingly.
There's a specific case left that I think the demo doesn't quite cover, though: a syntax error in the file PHP is directly invoked on (the script passed to php, or the one the web server's DirectoryIndex/front-controller config points at). That file can never catch its own parse error, no matter what try/catch you put in it, because the error occurs during PHP's first-pass compilation of that file — before any of its own code, including any catch block, has started executing. The only way around it is to make sure your real application code is never that literal top-level file — e.g., a thin bootstrap/index.php that does nothing but require the real code inside a try/catch. That works well, and plenty of apps already have this structure for other reasons, but it means the "catchability" of a syntax error becomes dependent on how the entry point happens to be organized, rather than being a property of the error itself — which is inconsistent with how every other PHP error type behaves, and easy for a developer to get wrong (or inherit wrong) without realizing it.
So I'd like to narrow the proposal to just that case: either make the top-level file's own parse error reportable through the standard error-handling mechanism as well, or at minimum, document this gap clearly so people know the stub-file pattern isn't just a style choice but a requirement if they want that guarantee.
On display_errors=0: agreed, and yes, that's what I run in production. But I'd note the 500 behavior shows up regardless of that setting — it's about when in the error-handling pipeline the failure becomes visible/catchable, not about whether the details are displayed. A generic 500 with no stack trace is still a different code path and a different HTTP status story than a handled application error, which can matter for monitoring, custom error pages, and logging, independent of what's leaked to the client.
either make the top-level file's own parse error reportable through the standard error-handling mechanism as well
That's exactly what happens though. If you're using development settings, i.e.
display_errors=1, the error will appear on your screen, regardless of whether it's a syntax error or some other uncaught exception. Ifdisplay_errors=0, you will not see the error, even if it's not a syntax error.E.g.:
$ php -d display_errors=0 -r "throw new Exception('I will never see this');" $ echo $? 255
So, whether the uncaught error is a syntax error or not,
display_errors=0will hide it,display_errors=1will show it.On display_errors=0: agreed, and yes, that's what I run in production. But I'd note the 500 behavior shows up regardless of that setting — it's about when in the error-handling pipeline the failure becomes visible/catchable, not about whether the details are displayed. A generic 500 with no stack trace is still a different code path and a different HTTP status story than a handled application error, which can matter for monitoring, custom error pages, and logging, independent of what's leaked to the client.
I don't understand that last paragraph.
display_errors=0will absolutely change whether you get an empty 500, or will have the exception message embedded into the HTML document.Regardless, this would be a forceful weakening of security onto the entire community, which I cannot imagine they'd be happy to see. In any case, this would need to be discussed with the community, i.e. the internals mailing list.
True, my display_errors claim was in error. You're right that display_errors alone doesn't distinguish syntax errors from other uncaught errors; both get hidden the same way by that setting.
The actual asymmetry is about whether application code ever gets the chance to run set_exception_handler() before the failure. I tested both cases:
php
set_exception_handler
(
function($e)
{
echo "CUSTOM HANDLER RAN: ".$e->getMessage()."\n";
}
);
throw new Exception('boom');Run with display_errors=0, this still prints CUSTOM HANDLER RAN: boom and exits 0 — the custom handler runs and fully controls the output, logging, and exit/status behavior, regardless of display_errors. But when the same handler-registration line sits in a file that itself has a syntax error, it never executes at all, because the whole file fails to compile before any of its statements — including the handler registration — can run. So with a literal top-level parse error, there is no way to install a custom handler no matter what your config is; you're stuck with PHP's built-in behavior. That's the real (and narrower) distinction — not visibility of output, but whether your error-handling code ever gets a chance to take over.
Given that this is already fully achievable today with the bootstrap/stub-file pattern (keep the literally-invoked entry file trivial, with real code only reached via require), I take your point that a core-engine change here is a hard sell, and I agree that's a discussion for the internals list, not something to push as a given. Unfortunately, the pressure of work prevents me from presenting and defending the issue there, but at least I had a chance to state my case here and answer a few objections.
Yes, that is true, whatever error handling your main script sets up will fail if the file doesn't parse. The log should still land in your SAPI log though.
Description
(I'm posting here to capture this idea, as I don't have the time to shepherd the steps of a formal RFC request.)
Currently, a parse error is reported to an error file and an HTTP 500 error is raised by all recent versions of PHP, unless php.ini is changed (which is not always possible, and which might require distinguishing between production and development access, which is currently not supported except by workarounds like using an environment variable). Reporting error settings are ignored. There is no perfect workaround available via .htaccess under Apache. There is a workaround: changing all main programs to include/require the real main programs, but this is arguably ugly and adds to the learning hurdle.
These problems could be solved by a rewrite of the parser and how the parser is called, so it works like many other interpreters or compilers.
It could be argued that this is a major change that would impact many users. However, major changes that impact more users than would be impacted by this change have already been done. An example is the mysql_* removal (deprecated in 5.5, gone in 7.0). This required rewriting almost all MySQL code, an incredible impact, justified only by a perceived urgent need to increase security.
And there were other PHP changes that involved deprecation with much, much lower urgency, such as the $string{0} curly-brace offset syntax being removed in 8.0 after a deprecation period.
Reporting parsing errors in the same way as other errors would benefit far more users than it would burden, by improving the reporting of this category of error and by reducing the PHP learning hurdle and several maintenance impacts.