PHP currently has two separate approaches to handling errors:
- Errors
- Exceptions
Both have their issues.
Using and responding to errors requires that the returned value (if
there is one) perform double duty as both a potential valid response
AND, in the case of error, a flag that something went wrong. It's easy
to forget to check that a particular call succeeded and specifically
handle the error. I find myself constantly revisiting the
documentation to ensure I know the specific error flag for functions,
and performing post-hoc analysis of error logs to make sure I didn't
miss an error flag that I would be better served by handling in the
standard flow of my app.
Using and responding to exceptions is a heavy-handed approach, as any
particular exception can bring the entire application down.
Additionally, the process of raising and catching exceptions loses
context of the error, often requiring many different subclasses of
exceptions to maintain the error state. Issues arise when the language
constructs for exceptions are liberally used in situations that are
not truly "exceptional," and where the the exception handling becomes
a glorified goto.
I've been programming in Go, and the ability to pass back multiple
arguments, one of which is conventionally the error state, is
wonderful. It's simple, clear, concise, and powerful. The code calling
into the error-capable function knows the context and can handle
errors appropriately according to the application requirements.
In PHP, we could provide the ability to automatically pack up a
comma-separated list of values as an array:
return $var1, $var2;
// returns [$var1, $var2];
And, we could provide the ability to automatically unpack an array
into separate variables:
$var1, $var2 = myFunction();
// same as list($var1, $var2) = myFunction();
This functionality would allow us to embrace the convention that an
error is passed back as one of the return values:
$dbh, $err = db\connect();
// check for error
if ($err) {
// handle db connection error
}
// carry on normally
$dbh->query("blah blah blah");
The simplicity (just passing back values), clarity (the code to handle
errors is proximal to the code that caused the error), and efficiency
(not having to create bunches of exception subclasses to maintain
context/information, and exceptions are expensive) of this approach is
a beautiful thing. You can see an explanation of Go error handling
here:
http://blog.golang.org/2011/07/error-handling-and-go.html
Concern 1: Isn't this just saving a few keystrokes?
Allowing developers to pass back an array and then access the members
of the array isn't the same thing, and we're not just saving a few
keystrokes if this is implemented. Providing this ability promotes
usage and clarity, and I'm confident Go would not have gotten
developers to follow this convention if the syntax didn't facilitate
the approach. I now write my Python code to handle errors in this same
manner, taking advantage of its ability to handle comma-separated
lists as tuples (something I didn't know about until recently, I must
confess):
http://stackoverflow.com/questions/423710/return-more-than-one-value-from-a-function-in-python
Concern 2: Why don't you just use Python or Go?
I really like using PHP for web development.
Concern 3: Are you saying we should get rid of errors and exceptions?
To be clear, errors have their place (maybe an extension failed to
load, or a warning about usage of undeclared vars, etc.), and so do
exceptions (for events that are truly "exceptional", that should cause
a major revision to the branching/flow of the application and/or stop
the application in its tracks if left unhandled.)
Adam
So is the feature you're describing is tuples and a use case of that
feature is an easier way to do error handling?
On Fri, May 3, 2013 at 8:00 AM, Adam Jon Richardson adamjonr@gmail.comwrote:
PHP currently has two separate approaches to handling errors:
- Errors
- Exceptions
Both have their issues.
Using and responding to errors requires that the returned value (if
there is one) perform double duty as both a potential valid response
AND, in the case of error, a flag that something went wrong. It's easy
to forget to check that a particular call succeeded and specifically
handle the error. I find myself constantly revisiting the
documentation to ensure I know the specific error flag for functions,
and performing post-hoc analysis of error logs to make sure I didn't
miss an error flag that I would be better served by handling in the
standard flow of my app.Using and responding to exceptions is a heavy-handed approach, as any
particular exception can bring the entire application down.
Additionally, the process of raising and catching exceptions loses
context of the error, often requiring many different subclasses of
exceptions to maintain the error state. Issues arise when the language
constructs for exceptions are liberally used in situations that are
not truly "exceptional," and where the the exception handling becomes
a glorified goto.I've been programming in Go, and the ability to pass back multiple
arguments, one of which is conventionally the error state, is
wonderful. It's simple, clear, concise, and powerful. The code calling
into the error-capable function knows the context and can handle
errors appropriately according to the application requirements.In PHP, we could provide the ability to automatically pack up a
comma-separated list of values as an array:return $var1, $var2;
// returns [$var1, $var2];And, we could provide the ability to automatically unpack an array
into separate variables:$var1, $var2 = myFunction();
// same as list($var1, $var2) = myFunction();This functionality would allow us to embrace the convention that an
error is passed back as one of the return values:$dbh, $err = db\connect();
// check for error
if ($err) {
// handle db connection error
}
// carry on normally
$dbh->query("blah blah blah");The simplicity (just passing back values), clarity (the code to handle
errors is proximal to the code that caused the error), and efficiency
(not having to create bunches of exception subclasses to maintain
context/information, and exceptions are expensive) of this approach is
a beautiful thing. You can see an explanation of Go error handling
here:
http://blog.golang.org/2011/07/error-handling-and-go.htmlConcern 1: Isn't this just saving a few keystrokes?
Allowing developers to pass back an array and then access the members
of the array isn't the same thing, and we're not just saving a few
keystrokes if this is implemented. Providing this ability promotes
usage and clarity, and I'm confident Go would not have gotten
developers to follow this convention if the syntax didn't facilitate
the approach. I now write my Python code to handle errors in this same
manner, taking advantage of its ability to handle comma-separated
lists as tuples (something I didn't know about until recently, I must
confess):http://stackoverflow.com/questions/423710/return-more-than-one-value-from-a-function-in-python
Concern 2: Why don't you just use Python or Go?
I really like using PHP for web development.
Concern 3: Are you saying we should get rid of errors and exceptions?
To be clear, errors have their place (maybe an extension failed to
load, or a warning about usage of undeclared vars, etc.), and so do
exceptions (for events that are truly "exceptional", that should cause
a major revision to the branching/flow of the application and/or stop
the application in its tracks if left unhandled.)Adam
So is the feature you're describing is tuples and a use case of that feature
is an easier way to do error handling?
No.
Tuples are an implementation detail specific to how Python allows one
to conveniently return multiple values. I merely mentioned this
because PHP's ordered hash maps (arrays) provide a similar avenue of
implementation.
The ability to conveniently pass back multiple values is necessary to
implement and promote an error handling convention similar to Go's, a
convention that seems to work really well for many cases. I was
proposing that this approach be considered as an addition to PHP.
Adam
On Thu, May 2, 2013 at 6:00 PM, Adam Jon Richardson adamjonr@gmail.comwrote:
PHP currently has two separate approaches to handling errors:
- Errors
- Exceptions
Both have their issues.
Just to clarify, PHP doesn't offer two separate approaches of handling
errors. We need to first distinguish between what is meant by a the terms
"Errors" and "Exceptions" so that we don't throw around ambigous claims
here. PHP has an error handler, which is where any part of your code can
send error reporting information to at any point in the execution stack.
This can be done both from user space as well as the core. PHP also has
Exceptions, but nothing in PHP's core throws Exceptions. They are normally
only used by extensions and classes, in user space, or those that extend
PHP.
So to be fair, PHP errors aren't really useful for user space error
handling. They're mostly informative. For example an E_NOTICE
of Undefined
variable/index/offset just lets you know the variable/index/offset was not
defined within that scope up to that point. It doesn't really offer you any
way of handling that error since it's unlikely you would have a well
defined error handling path for it to that point. Which is where constructs
like isset() and/or empty() help you out. But this means the programmer has
to write their code with that error checking approach in mind. Just seeing
the error only informs of a potential for a bug. It can't really address
your requirements to handle application-level errors in that regard.
However, many PHP functions/classes do return different values to indicate
and error (some even offer additinoal error information such as
json_last_error, preg_last_error, PDO::errorInfo, etc...) where it makes
sense. So a function like json_encode will indicate failure by returning a
boolean false and a function like json_decode will indicate failure by
returning NULL. You have to go out and find the extended error information
yourself, but they will also send accompanying E_WARNING
errors to the PHP
error handler to inform the user. Let's not get the purpose of using the
return value to test for failure and the purpose of the error handler
confused.
Using and responding to errors requires that the returned value (if
there is one) perform double duty as both a potential valid response
AND, in the case of error, a flag that something went wrong. It's easy
This is all true, and I agree that this makes testing for error/failure
cases is convoluted in PHP.
to forget to check that a particular call succeeded and specifically
handle the error. I find myself constantly revisiting the
documentation to ensure I know the specific error flag for functions,
and performing post-hoc analysis of error logs to make sure I didn't
miss an error flag that I would be better served by handling in the
standard flow of my app.Using and responding to exceptions is a heavy-handed approach, as any
particular exception can bring the entire application down.
Additionally, the process of raising and catching exceptions loses
context of the error, often requiring many different subclasses of
exceptions to maintain the error state. Issues arise when the language
constructs for exceptions are liberally used in situations that are
not truly "exceptional," and where the the exception handling becomes
a glorified goto.I've been programming in Go, and the ability to pass back multiple
arguments, one of which is conventionally the error state, is
wonderful. It's simple, clear, concise, and powerful. The code calling
into the error-capable function knows the context and can handle
errors appropriately according to the application requirements.In PHP, we could provide the ability to automatically pack up a
comma-separated list of values as an array:return $var1, $var2;
// returns [$var1, $var2];And, we could provide the ability to automatically unpack an array
into separate variables:$var1, $var2 = myFunction();
// same as list($var1, $var2) = myFunction();This functionality would allow us to embrace the convention that an
error is passed back as one of the return values:$dbh, $err = db\connect();
// check for error
if ($err) {
// handle db connection error
}
// carry on normally
$dbh->query("blah blah blah");The simplicity (just passing back values), clarity (the code to handle
errors is proximal to the code that caused the error), and efficiency
(not having to create bunches of exception subclasses to maintain
context/information, and exceptions are expensive) of this approach is
a beautiful thing. You can see an explanation of Go error handling
here:
http://blog.golang.org/2011/07/error-handling-and-go.htmlConcern 1: Isn't this just saving a few keystrokes?
Allowing developers to pass back an array and then access the members
of the array isn't the same thing, and we're not just saving a few
keystrokes if this is implemented. Providing this ability promotes
usage and clarity, and I'm confident Go would not have gotten
developers to follow this convention if the syntax didn't facilitate
the approach. I now write my Python code to handle errors in this same
manner, taking advantage of its ability to handle comma-separated
lists as tuples (something I didn't know about until recently, I must
confess):http://stackoverflow.com/questions/423710/return-more-than-one-value-from-a-function-in-python
Concern 2: Why don't you just use Python or Go?
I really like using PHP for web development.
Concern 3: Are you saying we should get rid of errors and exceptions?
To be clear, errors have their place (maybe an extension failed to
load, or a warning about usage of undeclared vars, etc.), and so do
exceptions (for events that are truly "exceptional", that should cause
a major revision to the branching/flow of the application and/or stop
the application in its tracks if left unhandled.)Adam
--
The problem with all of this is that the parser just isn't up to the task
of list comprehension. Be that as it may, that's not to say it can't ever
be done if someone wanted to take on the challenge of rewriting the parser
or providing an actual working implementation. The problem is you still
have a lot more challenges past just getting the list comprehension into
the engine. What you're talking about has many flaws just the same as the
existing error handling approach.
-
It depends on order of return values
-
It requires breaking BC in a way that will effect every single PHP user
out there. -
It requires re-factoring virtually every PHP function, of which PHP has
thousands upon thousands. -
It doesn't make error handling that much easier in PHP, because you
still have to check the error state regardless of how it's obtained.
Believe it or not passing back an array is the same thing. All you're
describing here is syntax sugar that allows the engine to do list
comprehension for you on the fly.
I agree that there needs to be a better approach to error handling in PHP.
I disagree that this is it or that it solves the problem any better than we
do today.
Just to clarify, PHP doesn't offer two separate approaches of handling
errors. We need to first distinguish between what is meant by a the terms
"Errors" and "Exceptions" so that we don't throw around ambigous claims
here. PHP has an error handler, which is where any part of your code can
send error reporting information to at any point in the execution stack.
This can be done both from user space as well as the core. PHP also has
Exceptions, but nothing in PHP's core throws Exceptions. They are normally
only used by extensions and classes, in user space, or those that extend
PHP.
A few points:
- I'm speaking to the approach of throwing exceptions vs. the process
of returning what both serves as values and indicators of error state
(similar to C's convention.) - In terms of exceptions in the core, that depends, would PDO be
considered part of the core?
internals@lists.php.net/msg60652.html" rel="nofollow" target="_blank">http://www.mail-archive.com/internals@lists.php.net/msg60652.html
So to be fair, PHP errors aren't really useful for user space error
handling. They're mostly informative.
The file()
function returns either an array or false on failure. This
is an example of what I was referring to as the error approach.
However, many PHP functions/classes do return different values to indicate
and error (some even offer additinoal error information such as
json_last_error, preg_last_error, PDO::errorInfo, etc...) where it makes
sense. So a function like json_encode will indicate failure by returning a
boolean false and a function like json_decode will indicate failure by
returning NULL. You have to go out and find the extended error information
yourself, but they will also send accompanyingE_WARNING
errors to the PHP
error handler to inform the user. Let's not get the purpose of using the
return value to test for failure and the purpose of the error handler
confused.
Responding to errors requires that you know when an error occurred.
The approach (i.e., providing a dual-function return value) provides
this. It's also helpful to know the context, and the error approach
does a nice job here, too. However, if I want the specifics of the
error (maybe I want to better prompt the user to avoid the error
again), I have to use something like error_get_last()
or I have to
record the error state in an custom error handler and then view the
info there if I want the specific error details.
The problem with all of this is that the parser just isn't up to the task of
list comprehension.
Currently, sure. Limiting this functionality to the return statement
seems doable.
What you're talking about has many flaws just the same as the
existing error handling approach.
- It depends on order of return values
Sure, but I've found the convention in Go to be one that hasn't caused
me any trouble (really, the only time I return multiple values is when
I'm returning an error for impure functions.)
- It requires breaking BC in a way that will effect every single PHP user
out there.
How so?
- It requires re-factoring virtually every PHP function, of which PHP has
thousands upon thousands.
How so? Even just making this possible would allow libraries to wrap
calls to functions like file()
and provide a multiple return
equivalent that includes the error information. The core functions
wouldn't have to change.
- It doesn't make error handling that much easier in PHP, because you still
have to check the error state regardless of how it's obtained. Believe it or
not passing back an array is the same thing. All you're describing here is
syntax sugar that allows the engine to do list comprehension for you on the
fly.
I might have agreed with you half a year ago, but after coding in a Go
a while, I disagree. This approach has merit.
Adam
On Fri, May 3, 2013 at 1:25 AM, Adam Jon Richardson adamjonr@gmail.comwrote:
Currently, sure. Limiting this functionality to the return statement
seems doable.
It's already possible to do so now without any modifications to the core:
function myFunc() {
$result = array(null, null);
if ($failureCase) {
$result[1] = $errorState;
} else {
$result[0] = $value;
{
return $result;
}
list($result, $error) = myFunc();
Again, all you're asking for beyond that is syntax sugar.
What you're talking about has many flaws just the same as the
existing error handling approach.
- It depends on order of return values
Sure, but I've found the convention in Go to be one that hasn't caused
me any trouble (really, the only time I return multiple values is when
I'm returning an error for impure functions.)
And what works in Go may not necessarily work well in PHP.
- It requires breaking BC in a way that will effect every single PHP
user
out there.How so?
Because you would be changing the return value of every single function in
PHP. Anytime you change what a function returns you break BC.
- It requires re-factoring virtually every PHP function, of which PHP
has
thousands upon thousands.How so? Even just making this possible would allow libraries to wrap
calls to functions likefile()
and provide a multiple return
equivalent that includes the error information. The core functions
wouldn't have to change.
If the functions are wrapped to return an array we have to update the
documentation for thousands of functions as well as break user space for
millions of users. Now array_map('file',$files) has a convoluted error
state problem. This isn't as simple of a change as you're making it out to
be.
- It doesn't make error handling that much easier in PHP, because you
still
have to check the error state regardless of how it's obtained. Believe
it or
not passing back an array is the same thing. All you're describing here
is
syntax sugar that allows the engine to do list comprehension for you on
the
fly.I might have agreed with you half a year ago, but after coding in a Go
a while, I disagree. This approach has merit.
I'm sure every approach has merit. The problem here is the return on
investment for implementing such a change in PHP. It's a lot of work for
very little benefit.
Adam
It's already possible to do so now without any modifications to the core:
It was already possible to declare arrays, but the new, shorter array
syntax still brings a smile to my face :) Few new language features
actually add things that weren't possible before.
If the functions are wrapped to return an array we have to update the
documentation for thousands of functions as well as break user space for
millions of users. Now array_map('file',$files) has a convoluted error state
problem. This isn't as simple of a change as you're making it out to be.
I'm sure every approach has merit. The problem here is the return on
investment for implementing such a change in PHP. It's a lot of work for
very little benefit.
I was speaking of user-space libraries/code. I'm not expecting all of
PHP core to change. I believe making the changes to the parser to
facilitating this approach in user-space code would be worth the
effort, and user-space libraries could normalize the error approach as
they see fit.
Adam