Hi,
We started a discussion about the current error handling mechanism and the
possible improvements in the "Why do disabled functions / classes generate
a WARNING" thread[1], but that is a little bit offtopic there, so I decided
to create a separate thread for it.
Basically Etienne mentioned that the original issue was a good example why
would we reconsider throwing exceptions from the core, which is currently
discouraged.[2]
Stan replied with the idea of turning the current error handling mechanism
in php into using Exceptions for everything, but keeping some error types
as is/uncatchable. [3]
This was brought up on the lists multiple times, we even have two separate
RFC with similar idea.[4][5]
I tried to explain why I think is impossible to achieve the same
functionality by turning the php errors into exceptions without a major
overhaul in the engine[6][7], but it seems that I failed to do so.
First of all, I would like to clear up the few misunderstandings:
Stan:
You mentioned that E_STRICT
(and E_ERROR
but I see no problem with that)
should be instant fatal error, I think that was only a typo on your part,
could you please clear that up?
I would also ask you to confirm that you would like to see every error
levels[8] turned into exceptions except the non-recoverable ones. It seems
that you and Andrew disagree on that.
Andrew:
From your mails, it seems that you don't agree with Stan on turning
everything but fatals into exceptions[9].
I'm really confused about that, as if we take away warnings (and by that
logic everything else which is less serious than a warning) the we would
only have the fatal errors (which we already discussed to not turn into
exceptions and you also seemed to agree[10]), E_USER
and E_RECOVERABLE_ERROR.
Is this what you propose?
My opinion (and this is what I tried to explain to you) is that it is
pretty common in the core and the bundled extensions to raise notices and
warnings left and right, and still do something,
If we would turn anything less serious than E_RECOVERABLE_ERROR
(so any
notice, deprecation notice, strict notice or warning)into an exception,
that would be a huuuuuuge BC break.
Basically silecing errors through setting error_reporting level, custom
error handlers or the @ operator would stop working, and one should put
everything into a try-catch block to not have his code blown into his code
constantly.
Another thing that I mentioned: even if you catch everything, there would
be some things that you couldn't do anymore.
Any method which throws notices or strict/deprecated notices or warnings
would be broken, as the execution would be halted (as the execution jumps
into the catch) which would be a major PITA to the userland developers, and
without further work, it could make the userland/engine in a more fragile
state, as the original call was interrupted but the execution still
continues from a different spot.
With the current error handling the execution either resumes after the
error handler is called, or the execution is terminates gracefully
The current error handling provides a way to trigger multiple
errors/warnings for an operation, and allows to still return and continue
the execution.
Turning everything into exceptions would kill both of those, and without
providing something similar suite, we can't do that imo.
So basically these are our boundaries:
- Fatal errors can't be turned into Exceptions, but it was mentioned
multiple times, that there are some fatals, which could be turned into
E_RECOVERABLE_ERROR. - Most/all non-fatal errors could be safe to be turned into Exceptions
as without explicit measures(try-catch) on the caller's side, it would
still stop the execution. - Every other warning/notice/etc. which are just holds additional
information but not indicating unsuccessful execution in itself cannot be
turned into Exceptions.
I have a few ideas about how to proceed from here, but I need some time to
gather my thoughts.
- internals@lists.php.net/msg60043.html" rel="nofollow" target="_blank">http://www.mail-archive.com/internals@lists.php.net/msg60043.html
- internals@lists.php.net/msg60054.html" rel="nofollow" target="_blank">http://www.mail-archive.com/internals@lists.php.net/msg60054.html
- internals@lists.php.net/msg60056.html" rel="nofollow" target="_blank">http://www.mail-archive.com/internals@lists.php.net/msg60056.html
- https://wiki.php.net/rfc/errors_as_exceptions
- https://wiki.php.net/rfc/enhanced_error_handling
- internals@lists.php.net/msg60060.html" rel="nofollow" target="_blank">http://www.mail-archive.com/internals@lists.php.net/msg60060.html
- internals@lists.php.net/msg60063.html" rel="nofollow" target="_blank">http://www.mail-archive.com/internals@lists.php.net/msg60063.html
- http://www.php.net/manual/en/errorfunc.constants.php
- internals@lists.php.net/msg60061.html" rel="nofollow" target="_blank">http://www.mail-archive.com/internals@lists.php.net/msg60061.html
- internals@lists.php.net/msg60065.html" rel="nofollow" target="_blank">http://www.mail-archive.com/internals@lists.php.net/msg60065.html
--
Ferenc Kovács
@Tyr43l - http://tyrael.hu
Andrew:
From your mails, it seems that you don't agree with Stan on turning
everything but fatals into exceptions[9].
I'm really confused about that, as if we take away warnings (and by
that logic everything else which is less serious than a warning) the
we would only have the fatal errors (which we already discussed to not
turn into exceptions and you also seemed to agree[10]), E_USER
and E_RECOVERABLE_ERROR.
Is this what you propose?
Warnings aren't "errors" as such (IMO they are little more than
glorified debugging print statements). Making them into exceptions would
mean you'd have to catch them, whilst at the moment they are loud but
harmless.
However, I think more serious errors should be exceptions. But perhaps
in PHP 6, because obviously this would break a lot of things.My opinion (and this is what I tried to explain to you) is that it is
pretty common in the core and the bundled extensions to raise notices
and warnings left and right, and still do something,
By that I thought you meant errors of the kind E_STRICT, not warnings.
Misunderstanding.
If we would turn anything less serious thanE_RECOVERABLE_ERROR
(so
any notice, deprecation notice, strict notice or warning)into an
exception, that would be a huuuuuuge BC break.
Of course. I think they are something the current errors system should
remain for. It should be for that kind of thing, but proper "errors"
(you know, things going wrong, not warnings etc.) should be exceptions,
I think.
Basically silecing errors through setting error_reporting level,
custom error handlers or the @ operator would stop working, and one
should put everything into a try-catch block to not have his code
blown into his code constantly.
Another thing that I mentioned: even if you catch everything, there
would be some things that you couldn't do anymore.
Any method which throws notices or strict/deprecated notices or
warnings would be broken, as the execution would be halted (as the
execution jumps into the catch) which would be a major PITA to the
userland developers, and without further work, it could make the
userland/engine in a more fragile state, as the original call was
interrupted but the execution still continues from a different spot.
With the current error handling the execution either resumes after the
error handler is called, or the execution is terminates gracefullyThe current error handling provides a way to trigger multiple
errors/warnings for an operation, and allows to still return and
continue the execution.
Turning everything into exceptions would kill both of those, and
without providing something similar suite, we can't do that imo.So basically these are our boundaries:
- Fatal errors can't be turned into Exceptions, but it was mentioned
multiple times, that there are some fatals, which could be turned
into E_RECOVERABLE_ERROR.
Truly fatal errors can't become exceptions. But some fatal errors
shouldn't be fatal and are recoverable (and I think these should become
exceptions).
- Most/all non-fatal errors could be safe to be turned into
Exceptions as without explicit measures(try-catch) on the caller's
side, it would still stop the execution.- Every other warning/notice/etc. which are just holds additional
information but not indicating unsuccessful execution in itself
cannot be turned into Exceptions.I have a few ideas about how to proceed from here, but I need some
time to gather my thoughts.
- internals@lists.php.net/msg60043.html" rel="nofollow" target="_blank">http://www.mail-archive.com/internals@lists.php.net/msg60043.html
- internals@lists.php.net/msg60054.html" rel="nofollow" target="_blank">http://www.mail-archive.com/internals@lists.php.net/msg60054.html
- internals@lists.php.net/msg60056.html" rel="nofollow" target="_blank">http://www.mail-archive.com/internals@lists.php.net/msg60056.html
- https://wiki.php.net/rfc/errors_as_exceptions
- https://wiki.php.net/rfc/enhanced_error_handling
- internals@lists.php.net/msg60060.html" rel="nofollow" target="_blank">http://www.mail-archive.com/internals@lists.php.net/msg60060.html
- internals@lists.php.net/msg60063.html" rel="nofollow" target="_blank">http://www.mail-archive.com/internals@lists.php.net/msg60063.html
- http://www.php.net/manual/en/errorfunc.constants.php
- internals@lists.php.net/msg60061.html" rel="nofollow" target="_blank">http://www.mail-archive.com/internals@lists.php.net/msg60061.html
- internals@lists.php.net/msg60065.html" rel="nofollow" target="_blank">http://www.mail-archive.com/internals@lists.php.net/msg60065.html
To sum up my thoughts:
- "real" (i.e., things going wrong, code doing something bad) errors
should become exceptions, with the possible exception of fatal errors
that it is impossible to recover from - notices, warnings, deprecations, etc. should stay as PHP errors, so
you can just suppress them if you want, and they don't require you to
handle them
So fatal errors and notices/warnings/deprecations stay errors,
everything else becomes an exception.
Clear?
--
Ferenc Kovács
@Tyr43l - http://tyrael.hu
--
Andrew Faulds
http://ajf.me/
Andrew:
From your mails, it seems that you don't agree with Stan on turning
everything but fatals into exceptions[9].
I'm really confused about that, as if we take away warnings (and by that
logic everything else which is less serious than a warning) the we would
only have the fatal errors (which we already discussed to not turn into
exceptions and you also seemed to agree[10]), E_USER
and E_RECOVERABLE_ERROR.
Is this what you propose?Warnings aren't "errors" as such (IMO they are little more than glorified
debugging print statements). Making them into exceptions would mean you'd
have to catch them, whilst at the moment they are loud but harmless.
errors and warnings/notices/etc. share the same infrastructure(called error
handling. :P) so it is a little bit easy to get confused when someone talks
about error handling in generals by errors, or only just a subset of the
error levels.
However, I think more serious errors should be exceptions. But perhaps in
PHP 6, because obviously this would break a lot of things.
okay, then it is clear.
My opinion (and this is what I tried to explain to you) is that it is
pretty common in the core and the bundled extensions to raise notices and
warnings left and right, and still do something,By that I thought you meant errors of the kind E_STRICT, not warnings.
Misunderstanding.
I was referring to notices/warnings, sorry for the confusion.
But it seems from your reply that you take E_STRICT
messages as errors?
Why? Those can only happen on successful operations, and until 5.4, they
weren't part of E_ALL, so they are the less known and less serious of all
of the error levels.
If we would turn anything less serious than
E_RECOVERABLE_ERROR
(so any
notice, deprecation notice, strict notice or warning)into an exception,
that would be a huuuuuuge BC break.Of course. I think they are something the current errors system should
remain for. It should be for that kind of thing, but proper "errors" (you
know, things going wrong, not warnings etc.) should be exceptions, I think.
Yeah, errors are meant to be explicit, hard to not notice/handle and for
that description, Exceptions works just fine.
Basically silecing errors through setting error_reporting level, custom
error handlers or the @ operator would stop working, and one should put
everything into a try-catch block to not have his code blown into his code
constantly.Another thing that I mentioned: even if you catch everything, there
would be some things that you couldn't do anymore.
Any method which throws notices or strict/deprecated notices or warnings
would be broken, as the execution would be halted (as the execution jumps
into the catch) which would be a major PITA to the userland developers, and
without further work, it could make the userland/engine in a more fragile
state, as the original call was interrupted but the execution still
continues from a different spot.
With the current error handling the execution either resumes after the
error handler is called, or the execution is terminates gracefullyThe current error handling provides a way to trigger multiple
errors/warnings for an operation, and allows to still return and continue
the execution.
Turning everything into exceptions would kill both of those, and without
providing something similar suite, we can't do that imo.So basically these are our boundaries:
- Fatal errors can't be turned into Exceptions, but it was mentioned
multiple times, that there are some fatals, which could be turned into
E_RECOVERABLE_ERROR.Truly fatal errors can't become exceptions. But some fatal errors
shouldn't be fatal and are recoverable (and I think these should become
exceptions).
yeah, this is in line with what I said.
... skip the links ...
To sum up my thoughts:
- "real" (i.e., things going wrong, code doing something bad) errors
should become exceptions, with the possible exception of fatal errors that
it is impossible to recover from
I think that is the further where can we go without breaking every code out
there.
- notices, warnings, deprecations, etc. should stay as PHP errors, so you
can just suppress them if you want, and they don't require you to handle
them
I'm thinking about some kind of new infrastructure which would allow the
developer to handle/fetch the errors locally (similar to how you can fetch
the last error with error_get_last()
, but with the ability to fetch
multiple items and only those which were generated for a given piece of
code.
of course we could still keep the set_error_handler()
as I see two
different use case for handling warnings:
- you want to log all errors in some other format what the log_errors
and error_log ini directives provide - you want to get the errors/warnings in the same scope where the error
occured and you want to handle that error in that context
for 1, the current set_error_handler is perfect (maybe some improvements
would be welcome, like having multiple handlers at the same time as we
allow it for spl_autoload_register, etc.), for 2, it would be nice having a
better infrastructure, currently it isn't as easy to fetch the warnings for
a specific operation as it could be.
So fatal errors and notices/warnings/deprecations stay errors, everything
else becomes an exception.Clear?
yep.
ps: in the meantime we discussed with you on IRC, and it seems that after
that, you would also put E_STRICT
to the warnings group.
--
Ferenc Kovács
@Tyr43l - http://tyrael.hu
Hi,
We started a discussion about the current error handling mechanism and the
possible improvements in the "Why do disabled functions / classes generate
a WARNING" thread[1], but that is a little bit offtopic there, so I decided
to create a separate thread for it.
I think there are several types of errors in PHP that have to be
handled differently:
- There are "debugging" class errors, like notices etc. They are
normally programming mistakes, but don't really prevent further
execution of the script. Those should stay as they are. Throwing
exceptions here would make things more complicated for the caller. - There are real errors, which should actually stop execution, but
which currently don't because throwing a fatal error would be overkill
due to it's incatchability (and even recoverable fatals are hard to
catch). Many warnings fall into this category. E.g. if a function is
called with incorrect parameters (e.g. too little of them) then an
E_WARNING
is thrown. The wrong function call is clearly an error, but
PHP chooses to continue execution there. In my eyes this is a mistake
and these cases should be exceptions. (As exceptions are easily
catchable one can combine the best of both worlds here). - There is a small number of recoverable fatal errors. Those are
obviously the clearest candidates for exceptions, because recoverable
fatal is basically an exception with very ugly catching. - There are a large number of fatal errors in places which aren't
really fatal. They probably should be recoverable fatals now, and as
such exceptions in the future. - There is a small number of truly fatal errors, i.e. some kind of
problem deep in the engine. There is probably no way to turn those
into exceptions. - Another special category are parse and compilations errors. Those
are not inherently fatal (e.g. a parse error in an eval() does not
have to stop script execution), but they would be really hard to turn
into exceptions with the current architecture.
To summarize, what I'd like to see:
- Not-really fatal errors (and currently recoverable fatal errors) to
be converted into exceptions - Some of the warnings be converted to exceptions (depends on the
exact context). - Leave notices etc alone.
Nikita
When I said I'd like to see E_STRICT
be fatal/exceptions it wasn't a typo.
My choice isn't based as much on what the current error severity is, or what
the error severity is supposed to represent in general, because I've found
in PHP often the error severity has no connection with the error that's
being reported. So I decided this by observing the real-world errors that
use a certain severity.
Many warnings and all E_STRICT
errors clearly point to bugs in the code,
wrong method signatures, non-existing variables and constants being used,
which can easily do actual data damage if the script keeps running in
undefined state (even if the engine is just fine with it).
PHP should not split the language semantic into loose, less loose and
strict, there should be just one set of semantics: the PHP semantics, and
when code does something that doesn't fit, it shouldn't be an ignorable
warning.
I've found this speeds up the development process, keeps bug count down and
protects the site/app's data in case of bugs.
Stan
When I said I'd like to see
E_STRICT
be fatal/exceptions it wasn't a typo.
My choice isn't based as much on what the current error severity is, or
what the error severity is supposed to represent in general, because I've
found in PHP often the error severity has no connection with the error
that's being reported. So I decided this by observing the real-world errors
that use a certain severity.Many warnings and all
E_STRICT
errors clearly point to bugs in the code,
wrong method signatures, non-existing variables and constants being used,
which can easily do actual data damage if the script keeps running in
undefined state (even if the engine is just fine with it).
are you sure about that?
E_STRICT
is more about code that works but relying on some quirk or
side-effect or simply does a stupid thing.
see internals@lists.php.net/msg24665.html" rel="nofollow" target="_blank">http://www.mail-archive.com/internals@lists.php.net/msg24665.html about
the original proposal and the discussion and you can see the list of
E_STRICT
errors in the 5.4 branch here:
http://lxr.php.net/search?q=E_STRICT&defs=&refs=&path=%28c%29+OR+%28h%29&hist=&project=PHP_5_4
stuff like using non-existing variables and constants are E_NOTICE, method
signature mismatches are either fatal errors or warnings AFAIR.
PHP should not split the language semantic into loose, less loose and
strict, there should be just one set of semantics: the PHP semantics, and
when code does something that doesn't fit, it shouldn't be an ignorable
warning.
it is the opposite, we allowed sloppy code in the past, and introduced
E_STRICT
to allow the pedantic people to find and fix the quirks and
E_DEPRECATED
for finding and migrating the code which will go away.
from all of the errors levels only E_DEPRECATED
and E_STRICT
is what means
that atm your code works as intended.
but for E_DEPRECATED
it will not work in the future, and for E_STRICT
it is
either does it's job sub optimally or the requested operation is just
stupid (but doable).
at least this is the theory, if you know something with doesn't in line
with this, we could talk about it. :)
I've found this speeds up the development process, keeps bug count down
and protects the site/app's data in case of bugs.
I agree that developing with E_ALL
(including E_STRICT) is a good developer
mentality.
But I would find it too intrusive to start forcing everybody with the next
version to either fix every E_STRICT
in their code, and even more weird if
we do that but still allow them to write code with full of E_WARNING
and
E_NOTICE
and get away with that.
--
Ferenc Kovács
@Tyr43l - http://tyrael.hu
When I said I'd like to see `E_STRICT` be fatal/exceptions it wasn't a typo. My choice isn't based as much on what the current error severity is, or what the error severity is supposed to represent in general, because I've found in PHP often the error severity has no connection with the error that's being reported. So I decided this by observing the real-world errors that use a certain severity.
Many warnings and all `E_STRICT` errors clearly point to bugs in the code, wrong method signatures, non-existing variables and constants being used, which can easily do actual data damage if the script keeps running in undefined state (even if the engine is just fine with it).
are you sure about that?
E_STRICT
is more about code that works but relying on some quirk or side-effect or simply does a stupid thing.
Well think about it. Why is it a goal to let quirky/stide-effect/stupid code run? This almost always ends badly.
It also reduces compatibility between code libraries. I.e.:
it is the opposite, we allowed sloppy code in the past, and introduced E_STRICT
to allow the pedantic people to find and fix the quirks and E_DEPRECATED
for finding and migrating the code which will go away.
If you use a well tested, popular library by "sloppy" coders who don't mind E_STRICT
and E_NOTICE, but you're "pedantic", what happens? You have to live with your error log filled with noise from the library or fork the library.
And when the "sloppy" coders miss bugs because they've decided the real PHP is about sloppy code that still works somehow, they miss bugs and then blame themselves or PHP for being a language that allows buggy code.
This is the end result if trying to turn PHP into everything for everybody. Instead, PHP should make up it's mind and stop littering the php.ini with semantics/behavior configuration for the language.
Have one behavior. Stick with it. Make it obvious.
**
When I said I'd like to see
E_STRICT
be fatal/exceptions it wasn't a
typo. My choice isn't based as much on what the current error severity is,
or what the error severity is supposed to represent in general, because
I've found in PHP often the error severity has no connection with the error
that's being reported. So I decided this by observing the real-world errors
that use a certain severity.Many warnings and all
E_STRICT
errors clearly point to bugs in the code,
wrong method signatures, non-existing variables and constants being used,
which can easily do actual data damage if the script keeps running in
undefined state (even if the engine is just fine with it).are you sure about that?
E_STRICT
is more about code that works but relying on some quirk or
side-effect or simply does a stupid thing.Well think about it. Why is it a goal to let quirky/stide-effect/stupid
code run? This almost always ends badly.It also reduces compatibility between code libraries. I.e.:
my words aren't getting through to you.
it is the opposite, we allowed sloppy code in the past, and introduced
E_STRICT
to allow the pedantic people to find and fix the quirks and
E_DEPRECATED
for finding and migrating the code which will go away.If you use a well tested, popular library by "sloppy" coders who don't
mindE_STRICT
and E_NOTICE, but you're "pedantic", what happens? You have
to live with your error log filled with noise from the library or fork the
library.And when the "sloppy" coders miss bugs because they've decided the real
PHP is about sloppy code that still works somehow, they miss bugs and then
blame themselves or PHP for being a language that allows buggy code.This is the end result if trying to turn PHP into everything for
everybody. Instead, PHP should make up it's mind and stop littering the
php.ini with semantics/behavior configuration for the language.Have one behavior. Stick with it. Make it obvious.
if we turn E_STRICT
to behave exactly as E_ERROR
there is no point keeping
the E_STRICT
level.
personally I disagree with turning something which was a pedantic notice in
one version into an unsupported feature which fatals out if you try to use
it in the next.
maybe E_STRICT->E_DEPRECATED->E_ERROR
--
Ferenc Kovács
@Tyr43l - http://tyrael.hu
if we turn E_STRICT
to behave exactly as E_ERROR
there is no point keeping the E_STRICT
level.
personally I disagree with turning something which was a pedantic notice in one version into an unsupported feature which fatals out if you try to use it in the next.
maybe E_STRICT->E_DEPRECATED->E_ERROR
I do think there's no point keeping the E_STRICT
level. In fact, there's no point keeping any level except
-
E_WARNING
(strictly for developer "tips" like deprecated functionality, and not for say division by zero errors or Stream I/O errors like it is today); -
E_ERROR
(everything that's not a developer "tip" but an error, should be this type, not a notice/warning/strict); - E_FATAL_ERROR (direct script halt).
That's just how I see it. Today we have these instead:
-
E_ERROR
-
E_WARNING
-
E_PARSE
-
E_NOTICE
-
E_CORE_ERROR
-
E_CORE_WARNING
-
E_COMPILE_ERROR
-
E_COMPILE_WARNING
-
E_USER_ERROR
-
E_USER_WARNING
- E_USE_NOTICE
-
E_STRICT
-
E_RECOVERABLE_ERROR
-
E_DEPRECATED
-
E_USER_DEPRECATED
Most of those have no reason to exist and especially they have no reason to be exposed as different severities at the user level. It seems like the severity levels have become a poor man's error type codes, which are needed, and at much more granular level, but as this is a severity, that's not the place to differentiate them.
I never spoke about E_STRICT
going fatal in the next version of PHP. I'm saying this in the context of my advice about using an error handler. I do this in my error handler (not just development, but also production), but still I think it could be a nice long-term goal to cleaning up PHP.
Stan
Hi,
having worked on the user land side of error handling
[1]https://github.com/nicolas-grekas/Patchwork-Logger/blob/master/class/Patchwork/PHP/ErrorHandler.php,
I wanted to share some more ideas that could help enhance the current error
handling mechanism.
Concerning throwing vs not throwing exceptions, the current error handling
mechanism allows everyone to code what they prefer, thanks to custom error
handling. Personally, I always throw on E_RECOVERABLE_ERROR
and
E_USER_ERROR. I've found that it's the only safe and compatible choice if I
want to reuse some code from others, like a PEAR lib for example, while
allowing to recover from situations that otherwise halt script's execution.
The really cool thing about error handling in PHP is that we can customize
it quite extensively. The bad thing is that we can't customize the behavior
for non-catchable errors (E_ERROR, E_CORE_ERROR, E_COMPILE_ERROR, E_PARSE,
E_CORE_WARNING
and E_COMPILE_WARNING). Of course, I understand that the PHP
engine can't be made to recover from all errors. But my feeling is that
some which are currently fatal could be made recoverable
[2]https://bugs.php.net/bug.php?id=55428.
It would be awesome if as much E_ERROR
as possible could be converted to
E_RECOVERABLE_ERROR! As a side note for E_PARSE: it is fatal, but only when
including and not when inside an eval(). This dual E_PARSE
behavior is
strange, and I wonder if "E_PARSE when including" could be made
E_RECOVERABLE_ERROR? Then, could also "E_PARSE when eval()" be made
user-catchable?
The second idea I wanted to share concerns error silencing. The @ operator
can sometimes make debugging a nightmare, especially when the error it
hides is one of the non-catchable ones. I know about xdebug.scream, but it
works by removing @s, so it makes impossible to differentiate between
"would be silenced" and "is not silenced" errors and as such it can't be
enabled all the time. On my side, to work around silencing, I use a custom
error handler [1]https://github.com/nicolas-grekas/Patchwork-Logger/blob/master/class/Patchwork/PHP/ErrorHandler.phpfor
catchable errors, and some special code [3] around includes. In my
error handler, I use a bit field to configure a set of error levels that
are never silenced. The hack [3] for non-catchable errors works, but needs
special care. A more general approach would be to add a "minimal error
reporting level" or otherwise named "screamed errors" bit field in PHP
core. Would this be possible? Is it a good idea?
Then comes the third idea I wanted to share: to me, the last area where PHP
doesn't help debugging is at shutdown time, with the "Exception thrown
without a stack frame in Unknown on line 0" error. Before shutdown time,
uncaught exceptions are transfered to the exception handler (php default or
user callback). But at shutdown time, this cryptic error message is always
reported instead. I know that shutdown time is very special and code
running there should be kept rare, but it happens... So, could we get rid
of this cryptic error message, and replace it with the regular exception
handling path? I tried and somewhat managed to enhance the situation user
land side: if you replace every single call to register_shutdown_function()
with [4], and every single __destruct() with [5], then this fixes most of
the pb. AFAIK, the only other case where an uncaught exception could be
triggered is in a remaining output buffering handler. But as in [4], it
wouldn't be hard to encapsulate output buffering handlers in a catching
closure. As it looks possible in user land, is the same doable on the
engine side?
I'm over with my ideas, thanks for reading :)
Nicolas
[1]
https://github.com/nicolas-grekas/Patchwork-Logger/blob/master/class/Patchwork/PHP/ErrorHandler.phphttps://github.com/nicolas-grekas/Patchwork-Logger/blob/master/class/Patchwork/PHP/ErrorHandler.php
[2] https://bugs.php.net/bug.php?id=55428https://bugs.php.net/bug.php?id=55428
[3] :
<?php
$error_level_bak = error_reporting(error_reporting() | E_PARSE
| E_ERROR
|
E_CORE_ERROR
| E_COMPILE_ERROR);
include $file;
error_reporting($error_level_bak);
?>
[4] :
<?php
function my_register_shutdown_function($callback)
{
$args = func_get_args()
;
$callback = function() use (&$args)
{
try
{
call_user_func_array(array_shift($args), $args);
}
catch (\Exception $e)
{
$h = set_exception_handler('var_dump');
// "var_dump" is never used as per the next line
restore_exception_handler()
;
if (null !== $h) call_user_func($h, $e);
else user_error("Uncaught exception '" . get_class($e) . "' in {
$e->getFile()} on line {$e->getLine()}", E_USER_WARNING);
exit(255);
}
}
return register_shutdown_function($callback);
}
?>
[5] :
<?php
class foo
{
function __destruct()
{
try
{
// destructor's implementation here
}
catch (\Exception $e)
{
unset($t);
if (empty($e->isDestructorException))
{
$t = array_slice($e->getTrace(), -1);
if (isset($t[0]['line']) || !isset($t[0]['class']) ||
strcasecmp('__destruct', $t[0]['function']))
{
$e->isDestructorException = 1;
}
else
{
// This code path is taken only when the $e exception has been
// thrown while PHP was calling remaining objects'
destructors,
// at the very end of script's execution.
$e->isDestructorException = $t[0]['class'];
}
}
if (isset($e->isDestructorException) && __CLASS__ === $e->
isDestructorException && 1 === count(debug_backtrace(
DEBUG_BACKTRACE_IGNORE_ARGS, 2)))
{
$t = set_exception_handler('var_dump');
// "var_dump" is never used as per the next line
restore_exception_handler()
;
if (null !== $t) call_user_func($t, $e);
else user_error("Uncaught exception '" . get_class($e) .
"' in {$e->getFile()} on line {$e->getLine()}", E_USER_WARNING);
exit(255);
}
throw $e;
}
}
}
?
The overall mood seems to be that since PHP has an error handler, everyone is free to handle errors any way they want.
Everyone is surprisingly ignoring the two giant holes in that theory:
-
PHP Errors come with a severity code and a string message. You want to handle specific errors in a specific way? You better start writing giant regexes parsing the string messages.
-
When everyone starts handling errors in their own way with error handlers, you can't reliably use third party code. You are in your own universe.
Stan
**
The overall mood seems to be that since PHP has an error handler, everyone
is free to handle errors any way they want.
- When everyone starts handling errors in their own way with error
handlers, you can't reliably use third party code. You are in your own
universe.
I think that's the main point that makes any change to the way PHP
currently handles errors difficult:
The current behavior is to continue the execution flow (except for fatal
errors of course) whereas an exception always breaks it. Throwing an
exception, either in a custom or in a new internal handler, introduces a
major portability + backward compatibility issue.
But do you think it could be possible to introduce a per script declaration
of the way it has been coded regarding error handling ?
We could imagine for example that any script that is coded assuming
exceptions instead of errors could be force to declare with a :
<?php declare(throw_errors=1); ?> or maybe : <?php
declare(throw_errors=E_WARNING | E_NOTICE
/* | ...*/);
- PHP Errors come with a severity code and a string message. You want to
handle specific errors in a specific way? You better start writing giant
regexes parsing the string messages.
This is a problem that could also be addressed with the current error
handling mechanism: that would require that any error comes with some
unique number for example. Is this possible? Independently of the exception
vs classical error discussion, that would be amazing.
Regards,
Nicolas
2012/8/24 Nicolas Grekas nicolas.grekas+php@gmail.com:
**
The overall mood seems to be that since PHP has an error handler, everyone
is free to handle errors any way they want.
- When everyone starts handling errors in their own way with error
handlers, you can't reliably use third party code. You are in your own
universe.I think that's the main point that makes any change to the way PHP
currently handles errors difficult:The current behavior is to continue the execution flow (except for fatal
errors of course) whereas an exception always breaks it. Throwing an
exception, either in a custom or in a new internal handler, introduces a
major portability + backward compatibility issue.
I think we should talk first over the general handling and then go
into the details (how to handle the different types of errors/warnings
etc.)
Maybe I repeat some things. What do we want?
A) Handling errors with exceptions instead error-handler
B) handling warnings/notices in some way.
That are 2 completely different things...
For A: Errors (something which stops execution) could be just handled
as an exception. I see no problem with this: If not handled, they
behave like now.
For B - the warnings etc. - I suggest something like this:
// This is pseudocode:
abstract class WarningAbstract implements Exception
{
public function __construct()
{
error_log($this->message()......);
unset($this); // I just want to say, that the exception is
destroyed in any way and so not handled any more
}
...
}
// A warning looks like this - this is created by PHP for example when
turning on E_WARNING
in php.ini
class Warning extends WarningAbstract
{
}
// and when created it does this:
if (class_exists('Warning')) {
$warning = new Warning();
if (isset($warning)) {
throw $warning;
}
}
Logically, when the exception is created the constructor is called. In
this example (by default) PHP could handle a warning etc. (write
something into a error-log) and then "destructs itself", to stop the
handling. (Of course this doesn't work now, when unseting myself the
object is not unseted; I wrote this code only so that PHP-programmers
could understands what I mean.)
[Not so important: could be configured like now. If E_WARNING
is
turned on in the php.ini, PHP creates the default handler for this
class itself. If not, the class doesn't exists and so nothing happens.
Or I create my own Warning class, and implement my own way to handle
the warnings.]
This has the big advantage, that we don't need to care about the
mentioned different handling of the different type of errors (see
above: A, B). I think it is important, that the way how it is done is
for every kind of error the same.
Now the details:
A file-error is defined
abstract class FileHandlerWarningAbstract extends if exists Warning //
of course "if exists" is no PHP - just to explain that it should work
as a "hint" for the compiler to create the "hierarchy"
{
}
The code to create the exception needs to recursively look up: If no
FileHandlerWarning exists it must know the "hirarchy of it's abstract"
(in this case the Warning) and look if that class exists. And so on.
So you can handle file-errors different from all other warnings
without try/catch everything. Not perfect for now, and I think a
little bit too complicated, but this is a brainstorming.
--
Alex Aulbach
Hi!
Basically Etienne mentioned that the original issue was a good example why
would we reconsider throwing exceptions from the core, which is currently
discouraged.[2]
Stan replied with the idea of turning the current error handling mechanism
in php into using Exceptions for everything, but keeping some error types
as is/uncatchable. [3]
Exceptions are different from PHP errors. For example, if you try to
open a file and the file isn't there, throwing exception is a very
annoying behavior (yes I know some languages do that, IMO it's wrong).
The reason is that it's pretty normal and within normal set of
situations to which code should be prepared, so you will either have to
obsessively wrap everything with try/catch blocks or do exception typing
like Java does. Both options are quite annoying. I think it's much
better to just open file and check if the result is OK. Converting it to
exception doesn't really improve situation, as if downstream code didn't
handle it the upstream probably won't know what to do with that
exception either. This leads to code like try { whatever(); }
catch(Exception e) {}. I saw tons of that in Java.
Stanislav Malyshev, Software Architect
SugarCRM: http://www.sugarcrm.com/
(408)454-6900 ext. 227
. . .For example, if you try to
open a file and the file isn't there, throwing exception is a very
annoying behavior (yes I know some languages do that, IMO it's wrong).
Because checking that the returned variable is !== FALSE
is way
better than throwing an exception, right?
This type of thing is one of the main reasons I like PDO more than
MySQLi. In MySQLi I'm constantly checking return values. In PDO I
just wrap it all up one try/catch. It's not like if my query fails
I'm going to try a different one. Most of the time it's just logging
that something went wrong and reporting it upstream somehow.
I understand that opening a file is a bit different than querying a
database, but I have to disagree with your entire previous post.
Exceptions are much nicer than errors.
Of course, everyone is entitled to have and express their own opinion.
I'm just sharing mine.
Hi!
Because checking that the returned variable is
!== FALSE
is way
better than throwing an exception, right?
Yes, it is. You can control it, unlike the exception which you can not,
unless, again, you wrap everything into try/catch on every kind of
exception possible.
This type of thing is one of the main reasons I like PDO more than
MySQLi. In MySQLi I'm constantly checking return values. In PDO I
just wrap it all up one try/catch. It's not like if my query fails
I'm going to try a different one. Most of the time it's just logging
that something went wrong and reporting it upstream somehow.
You are using exceptions for normal flow control. It is not what
exceptions should be used for. They are called exceptions for a reason.
--
Stanislav Malyshev, Software Architect
SugarCRM: http://www.sugarcrm.com/
(408)454-6900 ext. 227
Hi all,
Am 06.08.2012 20:41, schrieb Stas Malyshev:
Hi!
Because checking that the returned variable is
!== FALSE
is way
better than throwing an exception, right?
Yes, it is. You can control it, unlike the exception which you can not,
unless, again, you wrap everything into try/catch on every kind of
exception possible.
Well, if you use return codes, you have to wrap everything in
if-statements (maybe more than one, for multiple error codes), so you
don't actually gain anything here.
If you really need very fine-grained error reporting, you can also use
error codes in the exceptions (although you might get a strange mix of
both then - I would rather use typed exceptions... I mean, how many
functions do you know, where you actually have to deal with 20 different
types of errors [without using exceptions for control flow]).
You can avoid the mentioned problems with accidentally missing an
exception by just using multiple catch blocks:
try {
// ...
}
catch (FileNotFoundException $e)
{
// do something for this exact error message
}
catch (Exception $e)
{
// catch all other errors
}
What I really like when working with exceptions is that you have a
"natural way of error bubbling". If you have a call stack of about 10
functions and the last one returns null as error value, every of the
other 9 functions needs to check for null and return null too (or do
something else to hint for an error). If you use exceptions, all this
work is done for you, so that you can handle the error on exactly the
level, where you want it to be handled.
What Andrew also tried to say is, that with some kind of errors you just
want the execution of your application to stop (and probably present an
error message to the user by using a try-catch around your complete
application) and not keep running, just because you forget to check a
single return value and the program keeps running in an undefined state.
But I guess this boils down to the quite old question: do I want my
application to crash (hard) to prevent bugs/corrupt data but create a
bad UX for the user; or do I want it to keep running by any means, even
if the handled and produced data may be just garbage? In the past, PHP
did choose the second alternative (which can be - depending on the case
- be both good and bad).
Just my thoughts.
Cheers,
Jannik
Because checking that the returned variable is
!== FALSE
is way
better than throwing an exception, right?Yes, it is. You can control it, unlike the exception which you can not,
unless, again, you wrap everything into try/catch on every kind of
exception possible.This type of thing is one of the main reasons I like PDO more than
MySQLi. In MySQLi I'm constantly checking return values. In PDO I
just wrap it all up one try/catch. It's not like if my query fails
I'm going to try a different one. Most of the time it's just logging
that something went wrong and reporting it upstream somehow.You are using exceptions for normal flow control. It is not what
exceptions should be used for. They are called exceptions for a reason.
It should be an exceptional situation if my database queries don't
work properly. I've tested my code locally, on a staging environment
and sometimes even ran tests on the live server. It is absolutely
100% an exception if something goes wrong, my friend.
Opening a file? I have to agree that it should not throw an exception.
I was replying more to explain the benefit of exceptions than to
refute that one possible use-case.
Levi et al:
On Mon, Aug 6, 2012 at 8:55 PM, Levi Morrison morrison.levi@gmail.comwrote:
Because checking that the returned variable is
!== FALSE
is way
better than throwing an exception, right?Yes, it is. You can control it, unlike the exception which you can not,
unless, again, you wrap everything into try/catch on every kind of
exception possible.This type of thing is one of the main reasons I like PDO more than
MySQLi. In MySQLi I'm constantly checking return values. In PDO I
just wrap it all up one try/catch. It's not like if my query fails
I'm going to try a different one. Most of the time it's just logging
that something went wrong and reporting it upstream somehow.You are using exceptions for normal flow control. It is not what
exceptions should be used for. They are called exceptions for a reason.It should be an exceptional situation if my database queries don't
work properly. I've tested my code locally, on a staging environment
and sometimes even ran tests on the live server. It is absolutely
100% an exception if something goes wrong, my friend.Opening a file? I have to agree that it should not throw an exception.
I was replying more to explain the benefit of exceptions than to
refute that one possible use-case.
It should absolutely throw an exception. If you're opening a file, and the
file doesn't exist (in read mode), that's definitely an exceptional
circumstance. If it's not, you should be checking for existence first (via
file_exists()
or is_readable()
, etc)...
The only situations I consider non-exceptional in terms of PHP errors are
those that don't alter the flow via error return. So if fopen errors, you
can't just fread the return value. You need to change your execution flow
based on that error. Therefore, it is exceptional.
However, cases where you can logically continue on would not be
exceptional. Cases such as substr($string, 100) on a 2 character string,
currently raise a warning, but getting an empty string back from it allows
you to continue on. The rest of your code won't be effected by the error in
a significant way (since the case where the string is 2 characters is the
same as if it's 100 characters).
So, as a gross overgeneralization,
Can I continue on without having to check the return value?
Yes: Not an exceptional circumstance
No: An exceptional circumstance
Again, as a gross overgeneralization. Not as a hard rule, but as a quick
check...
Anthony
Hi!
circumstance. If it's not, you should be checking for existence first
(viafile_exists()
oris_readable()
, etc)...
This is exactly how we got into this mess with $x =
isset($a['x'])?$a['x']:null;
We're trying to get out of this mess and you're proposing to build
another mess just like that.
errors, you can't just fread the return value. You need to change your
execution flow based on that error. Therefore, it is exceptional.
That's exactly what I am saying. Exceptions should not be a means of
flow control, and that's exactly what are you doing here.
Stanislav Malyshev, Software Architect
SugarCRM: http://www.sugarcrm.com/
(408)454-6900 ext. 227
errors, you can't just fread the return value. You need to change your
execution flow based on that error. Therefore, it is exceptional.That's exactly what I am saying. Exceptions should not be a means of
flow control, and that's exactly what are you doing here.
I agree that exceptions should not be a means of flow control except
when something exceptional has happened. The difference is between
what you think is exceptional and what I do.
Of course you are welcome to believe what you want to and voice your
opinion, but I think I speak for the rest of us when I say, "We've
heard your opinion, loud and clear." It's okay. Let the rest of us
voice our opinions.
Stas,
On Tue, Aug 7, 2012 at 1:46 AM, Stas Malyshev smalyshev@sugarcrm.comwrote:
Hi!
circumstance. If it's not, you should be checking for existence first
(viafile_exists()
oris_readable()
, etc)...This is exactly how we got into this mess with $x =
isset($a['x'])?$a['x']:null;
We're trying to get out of this mess and you're proposing to build
another mess just like that.
A situation like that is not exceptional. It's null either way, therefore
no exception should be thrown. (Which is why I said earlier that I wouldn't
want most Notices to become exceptions)...
errors, you can't just fread the return value. You need to change your
execution flow based on that error. Therefore, it is exceptional.That's exactly what I am saying. Exceptions should not be a means of
flow control, and that's exactly what are you doing here.
Exceptions by very definition are a means of flow control. That's their
very purpose. The difference here is that it's a means of flow control when
an error means you literally can't continue on in the current flow of
execution (such as the fopen case). To continue the current flow would
break lots of stuff, because it's expecting a resource. Therefore, the
resource not being opened is exceptional...
Anthony
Hi!
Because checking that the returned variable is
!== FALSE
is way
better than throwing an exception, right?Yes, it is. You can control it, unlike the exception which you can not,
unless, again, you wrap everything into try/catch on every kind of
exception possible.
Have you stopped for a moment to think this opinion through? Look at two
typical patterns of error handling. The examples below are generalized from
my file cache code. Having no cache is exceptional, because 99.999% of the
time I run this, there is cache, except the first time when there's not.
Tell me again how you using try..catch is very verbose and how you need to
supply multiple catch blocks for "every kind of exception possible"?
With exceptions:
try {
fileop1();
fileop2();
fileop3();
normal_actions();
} catch (IOException $e) {
exceptional_actions();
}
Without exceptions:
$valid = true;
if (fileop1() !== FALSE) {
$valid = false;
}
if ($valid && fileop2() !== FALSE) {
$valid = false;
}
if ($valid && fileop3() !== FALSE) {
$valid = false;
}
if ($valid) {
normal_actions();
} else {
exceptional_actions();
}
Hi!
Have you stopped for a moment to think this opinion through? Look at two
Of course not. Why would I bother thinking? It is always safe to assume
nobody thinks before writing anything to the list.
typical patterns of error handling. The examples below are generalized from
my file cache code. Having no cache is exceptional, because 99.999% of the
time I run this, there is cache, except the first time when there's not.Tell me again how you using try..catch is very verbose and how you need to
supply multiple catch blocks for "every kind of exception possible"?
You are either purposefully exaggerating or not doing it right.
if(fileop1() && fileop2() && fileop3()) {
// do valid stuff
} else {
// do error stuff
}
It's not that hard.
With exceptions:
try {
fileop1();
fileop2();
fileop3();
normal_actions();
} catch (IOException $e) {
exceptional_actions();
}
Now imagine fileop1 throws some other exception - note that you have no
idea which exceptions are there and nothing guarantees you fileop1
throws only IOException, and by the proposal here, any problem in any
code whatsoever results in exception now, even if the problem was in
converting log message to utf-8 while writing some log inside some
function called by fileop2(). Now your whole app has failed, even though
you don't really care about that log message at all.
--
Stanislav Malyshev, Software Architect
SugarCRM: http://www.sugarcrm.com/
(408)454-6900 ext. 227
You are either purposefully exaggerating or not doing it right.
if(fileop1() && fileop2() && fileop3()) {
// do valid stuff
} else {
// do error stuff
}It's not that hard.
I guess it was my mistake that I simplified my actual code for simplicity's
sake. Please show me how would my actual code look stuffed in an "if"
statement:
try {
$cacheBaseFile = Core\CACHE_PATH . '/Compiled/' . \str_replace('\', '-',
$symbolName);
// The validator checks whether the cache is fresh.
$cacheIsFresh = include $cacheFileBase . '.val.php';
// Attempt to load the cache (it should be there if the validator is
there, unless someone messed with the files).
if ($cacheIsFresh) {
$result = include $cacheFileBase . '.main.php';
} else {
$this->generateCache($symbolName);
...
}
return $result;
} catch (IncludeIOError $e) { // One or more of the files were damaged or
missing.
...
}
When you have:
- comments
- multiple statements
- assign variables
- call functions/methods with long arguments
etc.
...you can't just stuff it in an if (op() && op() && op()) and pretend this
is in any way a maintainable code style.
With exceptions:
try {
fileop1();
fileop2();
fileop3();
normal_actions();
} catch (IOException $e) {
exceptional_actions();
}Now imagine fileop1 throws some other exception - note that you have no
idea which exceptions are there and nothing guarantees you fileop1
throws only IOException, and by the proposal here, any problem in any
code whatsoever results in exception now, even if the problem was in
converting log message to utf-8 while writing some log inside some
function called by fileop2(). Now your whole app has failed, even though
you don't really care about that log message at all.
Exceptions aren't about log messages, they're about exceptions - error
conditions that have to be handled.
If my code above generates another type of exception there are only two
options:
- Either I catch that exception because I know how to handle it -or-
- I don't catch it, and the app should correctly stop right there before
data damage is done.
I know PHP's model is all messed up, but no one here, I believe, is asking
about putting non-error log messages in Exceptions. IO failure is an
exception.
If your IO operation fails, you can't just log it and plow forward
blissfully without handling the problem.
Stan
I know PHP's model is all messed up, but no one here, I believe, is
asking about putting non-error log messages in Exceptions. IO failure is
an exception.If your IO operation fails, you can't just log it and plow forward
blissfully without handling the problem.Stan
Exceptions allow you to decide:
I know how to handle it -> deal with it,
I don't know how to handle it -> pop up to higher layer who might know
I didn't know this case needs to be handled -> pop up to handler of last
resort, either crash or catch-all and at least present some meaningful
info on what happened.
That's managable. Of course you can probably do most stuff with error
code handling, PEAR_Error & Friends, error handlers for really bad
stuff. We've been doing this before Exceptions. But wherever it makes
sense, I tend to use them.
2012/8/6 Stas Malyshev smalyshev@sugarcrm.com
Exceptions are different from PHP errors. For example, if you try to
open a file and the file isn't there, throwing exception is a very
annoying behavior (yes I know some languages do that, IMO it's wrong).
The reason is that it's pretty normal and within normal set of
situations to which code should be prepared, so you will either have to
obsessively wrap everything with try/catch blocks or do exception typing
like Java does. Both options are quite annoying. I think it's much
better to just open file and check if the result is OK. Converting it to
exception doesn't really improve situation, as if downstream code didn't
handle it the upstream probably won't know what to do with that
exception either. This leads to code like try { whatever(); }
catch(Exception e) {}. I saw tons of that in Java.
Even a simple file opening can fail for different kind of reasons (the file
doesn't exists, it is not readable, on some OS it could be already opened
and thus locked). Most of the time, you don't care the reason, but
sometimes you want to be more precise. With exceptions, we have an elegant
way to manage all failures as a whole, or to differenciate each reason.
But you are right, it could be very annoying to write a lot of try/catch
blocks. Maybe we could think about something inspired from Lua's "protected
call" function [1]. A convenient mean to say «OK, I know this expression
may raise an exception, but I don't care, discard it silently please. I
will check if my variable has been set or not.».
Basically Etienne mentioned that the original issue was a good example why
would we reconsider throwing exceptions from the core, which is currently
discouraged.[2]
Stan replied with the idea of turning the current error handling mechanism
in php into using Exceptions for everything, but keeping some error types
as is/uncatchable. [3]
This is going to be the biggest BC break ever. Instead of harmless
notices and warnings that people will and can (and sometimes should)
ignore, the moment any of those happens, the script will suddendly
because the exception isn't caught.
Andrew:
From your mails, it seems that you don't agree with Stan on turning
everything but fatals into exceptions[9].
That's a funny one. The only thing that currently makes sense to use an
exception for is the E_RECOVERABLE - that is, without potentionally
breaking any script that's ever been written.
So basically these are our boundaries:
- Fatal errors can't be turned into Exceptions, but it was mentioned
multiple times, that there are some fatals, which could be turned into
E_RECOVERABLE_ERROR.
Some, but definitely not many. When we introduced E_RECOVERABLE we had a
good look at them all.
- Most/all non-fatal errors could be safe to be turned into Exceptions
as without explicit measures(try-catch) on the caller's side, it would
still stop the execution.
And hence break the script...
cheers,
Derick
--
http://derickrethans.nl | http://xdebug.org
Like Xdebug? Consider a donation: http://xdebug.org/donate.php
twitter: @derickr and @xdebug