This is a discussion on Exception vs error handling within the PHP Language forums, part of the PHP Programming Forums category; Coming from the .Net world, I am used to the try...catch...finally approach to error handling. And PHP 5 ...
|
|||||||
| FAQ | Members List | Calendar | Search | Today's Posts | Mark Forums Read |
|
|||
|
Coming from the .Net world, I am used to the try...catch...finally
approach to error handling. And PHP 5 now supports this approach. But I am not clear what happens to unhandled errors/exceptioins? Do I define a default error handler as well as do my try/catch? Can I mix error handling with the exceptions? Is one approach better than the other? TIA John |
|
|||
|
"john6630" <john6630@hotmail.com> wrote in message news:1b83ceb7-9188-42ce-ad19-4c9e075a10cb@r15g2000prd.googlegroups.com... > Coming from the .Net world, I am used to the try...catch...finally > approach to error handling. And PHP 5 now supports this approach. But > I am not clear what happens to unhandled errors/exceptioins? Do I > define a default error handler as well as do my try/catch? Can I mix > error handling with the exceptions? Is one approach better than the > other? try/catch is not .NET exlusive. further, you are coming from a VB.NET background...as try/catch has been a part of MOST *other* languages supported by .net, i.e. c, c++, c#, perl, etc. since before the advent of ..NET. anyway, what happens when you don't handle an exception in any other language? you get an unrecoverable crash...or your language's error handler handles it as best it can. php is no different. you should plan and design the way you want errors handled. if you want to beautify the output of the default error handler, by all means, tie into it. as for other methods...*always* try and handle any error you might encounter. if you don't, you will find yourself with a potential memory leak and your errors will show to your customers in an ugly fashion with jibberish-for-a-description of the problem. but, that's just me. cheers |
|
|||
|
On Aug 19, 7:06 pm, john6630 <john6...@hotmail.com> wrote:
> Coming from the .Net world, I am used to the try...catch...finally > approach to error handling. And PHP 5 now supports this approach. But > I am not clear what happens to unhandled errors/exceptioins? Do I > define a default error handler as well as do my try/catch? Can I mix > error handling with the exceptions? Is one approach better than the > other? > > TIA > John PHP doesn't support "finally." But in its most commonly deployed configuration, it does like to dump warning/error messages in the middle of your output, as Dale alluded to. My advice: Turn up error_reporting as far as it can go: E_ALL | E_STRICT. Turn all PHP warnings/errors into exceptions using something like the following: set_error_handler( create_function( '$x, $y', 'throw new PHPError( $y, $x );' ), E_ALL | E_STRICT ); Then handle the exceptions as appropriate. (If you use 3rd party libraries, you'll find they aren't always so strict about their error handling. If you find that's the case, post again here and I'll describe how I handle it.) Walter |
|
|||
|
I agree with your post, but want to add some things to it.
On Aug 20, 7:45*am, WalterGR <walte...@gmail.com> wrote: > But in its most commonly deployed configuration, it does like to dump > warning/error messages in the middle of your output, as Dale alluded > to. Indeed. In development environments this is probably fine and you want errors to stand out. In a production environment it is typical to disable error messages altogether. You may handle uncatched exceptions by using set_exception_handler() By default, PHP functions do not throw exceptions but indicate failure by their return value. For example, fopen() returns a file handle on success and FALSE on failure. However, you do want exceptions to be thrown instead, so Walter proposes this: > Turn all PHP warnings/errors into exceptions using something like the > following: > > set_error_handler( create_function( '$x, $y', 'throw new PHPError( $y, > $x );' ), E_ALL | E_STRICT ); The created function indeed throws an exception. It is called whenever an error occurs. Note that this will not use the settings of error_reporting(). |
|
|||
|
On Aug 20, 12:20 am, Sjoerd <sjoer...@gmail.com> wrote:
> I agree with your post, but want to add some things to it. > > On Aug 20, 7:45 am, WalterGR <walte...@gmail.com> wrote: > > > But in its most commonly deployed configuration, it does like to dump > > warning/error messages in the middle of your output, as Dale alluded > > to. > > Indeed. In development environments this is probably fine and you want > errors to stand out. In a production environment it is typical to > disable error messages altogether. > > You may handle uncatched exceptions by using set_exception_handler() > > By default, PHP functions do not throw exceptions but indicate failure > by their return value. For example, fopen() returns a file handle on > success and FALSE on failure. However, you do want exceptions to be > thrown instead, so Walter proposes this: > > > Turn all PHP warnings/errors into exceptions using something like the > > following: > > > set_error_handler( create_function( '$x, $y', 'throw new PHPError( $y, > > $x );' ), E_ALL | E_STRICT ); > > The created function indeed throws an exception. It is called whenever > an error occurs. Note that this will not use the settings of > error_reporting(). Yes, my programming is in VB.Net and, indeed, the purpose of my question is so I can create an error handling framework for my application before I begin writing code. The confusion for me is in the concept of "errors" vs "exceptions". We have set_error_handler and set_exception_handler. We have trigger_error and "throw new exception". Understanding which occurs when is my question. Thank you for the responses and I believe I am beginning to understand. I would like to summarize what I understand just to be sure I have it correctly. Exceptions are a result of my anticipation of an error. For example, if I anticipate a problem connecting to a database, I can enclose that code in a try/catch and throw an exception within the code block if I detect the connection failed. Errors on the other hand are generated by PHP automatically. So I need to define an error handler (assuming I do not want to use the default handler). And then the good advice here was to use set_error_handler to throw all errors as exceptions to a generic handler (which is analogous to my UnhandledExceptionHandler in vb.net) Do I have that correct? If so, I have another question. In the set_error_handler example above, you only pass the error code and error string. Wouldn't it be good to also pass the file, line and context? I saw some code that automatically detected if the app is running locally and uses the added info for a "debug" mode of display but shortens to a user friendly display if the site is deployed. I hope my questions are not too sophomoric, but being new to the web world, there is a lot to learn and a lot left to the developer that was handled by the OS or IDE. I also find it interesting that even though error handling is a key requirement of any app, most of the books I have read give it only passing consideration. Thanks again, John |
|
|||
|
On Aug 20, 8:08 am, john6630 <john6...@hotmail.com> wrote:
> If so, I have another question. In the set_error_handler example > above, you only pass the error code and error string. Wouldn't it be > good to also pass the file, line and context? I know you asked a bunch of other questions, but I'll stick to my area of expertise. :) With PHP exceptions, you don't need to pass the additional information. They come along with the exception. Using the code I shared for set_error_handler, the default uncaught-exception handler will print (to the web browser) a stack trace. Here's an example from my logs: [18-Aug-2008 14:23:01] PHP Fatal error: Uncaught exception 'PHPError' with message 'SQLiteDatabase::queryExec() [<a href='sqlitedatabase.queryexec'>sqlitedatabase.que ryexec</a>]: columns SubmissionLowered, RefUser, RefLocation are not unique' in E:\dev\wamp \source\main.php(21) : runtime-created function:1 Stack trace: #0 [internal function]: __lambda_func(2, 'SQLiteDatabase:...', 'E:\dev \wamp\sou...', 45, Array) #1 E:\dev\wamp\source\DB.php(45): SQLiteDatabase->queryExec(' INSERT INTO Su...', 'columns Submiss...') #2 E:\dev\wamp\source\misc.php(109): CPSDB::queryExec(' INSERT INTO Su...') #3 E:\dev\wamp\www\map.php5(28): addUserToMap('foo') #4 {main} thrown in E:\dev\wamp\source\main.php(21) : runtime-created function on line 1 It's somewhat difficult to read because of the way errors are converted to exceptions. In the example above, ignore #0 and #4. The call order is #3 -> #2 -> #1. (addUserToMap called CPSDB::queryExec called SQLiteDatabase->queryExec.) #1 is where the error occurred, and hence where the exception was thrown. I believe exceptions also come with the context, so you could write your own default exception handler to, for example: * Show the entire contents of function parameters, rather than the "..." cut off text above. * Show local variables for each stack frame. > I saw some code that > automatically detected if the app is running locally and uses the > added info for a "debug" mode of display but shortens to a user > friendly display if the site is deployed. Excellent idea. The great thing about exceptions is that they pop the stack, so you can do any cleanup you need, rather than just dieing at the first PHP error. Then when you encounter an uncaught exception, you can log the exception details and display a generic error message to the user. > I hope my questions are not too sophomoric <snip> Absolutely not. Exceptions are new to PHP5, and foreign to the way that PHP has handled errors since its inception. The documentation is sparse about how to bridge the two worlds of PHP error handling. Let me know if any of the above isn't clear, and I'll clarify. Walter |
|
|||
|
On Aug 20, 8:35 am, WalterGR <walte...@gmail.com> wrote:
> On Aug 20, 8:08 am, john6630 <john6...@hotmail.com> wrote: > > > If so, I have another question. In the set_error_handler example > > above, you only pass the error code and error string. Wouldn't it be > > good to also pass the file, line and context? > > I know you asked a bunch of other questions, but I'll stick to my area > of expertise. :) > > With PHP exceptions, you don't need to pass the additional > information. They come along with the exception. Using the code I > shared for set_error_handler, the default uncaught-exception handler > will print (to the web browser) a stack trace. > > Here's an example from my logs: > > [18-Aug-2008 14:23:01] PHP Fatal error: Uncaught exception 'PHPError' > with message 'SQLiteDatabase::queryExec() [<a > href='sqlitedatabase.queryexec'>sqlitedatabase.que ryexec</a>]: columns > SubmissionLowered, RefUser, RefLocation are not unique' in E:\dev\wamp > \source\main.php(21) : runtime-created function:1 > Stack trace: > #0 [internal function]: __lambda_func(2, 'SQLiteDatabase:...', 'E:\dev > \wamp\sou...', 45, Array) > #1 E:\dev\wamp\source\DB.php(45): SQLiteDatabase->queryExec(' INSERT > INTO Su...', 'columns Submiss...') > #2 E:\dev\wamp\source\misc.php(109): CPSDB::queryExec(' INSERT INTO > Su...') > #3 E:\dev\wamp\www\map.php5(28): addUserToMap('foo') > #4 {main} > thrown in E:\dev\wamp\source\main.php(21) : runtime-created function > on line 1 > > It's somewhat difficult to read because of the way errors are > converted to exceptions. In the example above, ignore #0 and #4. > > The call order is #3 -> #2 -> #1. (addUserToMap called > CPSDB::queryExec called SQLiteDatabase->queryExec.) #1 is where the > error occurred, and hence where the exception was thrown. > > I believe exceptions also come with the context, so you could write > your own default exception handler to, for example: > > * Show the entire contents of function parameters, rather than the > "..." cut off text above. > > * Show local variables for each stack frame. > > > I saw some code that > > automatically detected if the app is running locally and uses the > > added info for a "debug" mode of display but shortens to a user > > friendly display if the site is deployed. > > Excellent idea. The great thing about exceptions is that they pop the > stack, so you can do any cleanup you need, rather than just dieing at > the first PHP error. Then when you encounter an uncaught exception, > you can log the exception details and display a generic error message > to the user. > > > I hope my questions are not too sophomoric <snip> > > Absolutely not. Exceptions are new to PHP5, and foreign to the way > that PHP has handled errors since its inception. The documentation is > sparse about how to bridge the two worlds of PHP error handling. > > Let me know if any of the above isn't clear, and I'll clarify. > > Walter Hi Walter, Thank you for your time and input. I am still confused by this: > set_error_handler( create_function( '$x, $y', 'throw new PHPError( $y, > $x );' ), E_ALL | E_STRICT ); I believe this causes any error to throw an exception of type PHPError. But I do not understand the arguments $x and $y. Can you explain? And what does PHPError look like? Was the example log output you shared created by PHPError? Thank you again for all your help, John |
|
|||
|
On Aug 20, 9:44*am, john6630 <john6...@hotmail.com> wrote:
> On Aug 20, 8:35 am, WalterGR <walte...@gmail.com> wrote: > > > > > On Aug 20, 8:08 am, john6630 <john6...@hotmail.com> wrote: > > > > If so, I have another question. In the set_error_handler example > > > above, you only pass the error code and error string. Wouldn't it be > > > good to also pass the file, line and context? > > > I know you asked a bunch of other questions, but I'll stick to my area > > of expertise. :) > > > With PHP exceptions, you don't need to pass the additional > > information. *They come along with the exception. *Using the code I > > shared for set_error_handler, the default uncaught-exception handler > > will print (to the web browser) a stack trace. > > > Here's an example from my logs: > > > [18-Aug-2008 14:23:01] PHP Fatal error: *Uncaught exception 'PHPError' > > with message 'SQLiteDatabase::queryExec() [<a > > href='sqlitedatabase.queryexec'>sqlitedatabase.que ryexec</a>]: columns > > SubmissionLowered, RefUser, RefLocation are not unique' in E:\dev\wamp > > \source\main.php(21) : runtime-created function:1 > > Stack trace: > > #0 [internal function]: __lambda_func(2, 'SQLiteDatabase:...', 'E:\dev > > \wamp\sou...', 45, Array) > > #1 E:\dev\wamp\source\DB.php(45): SQLiteDatabase->queryExec(' INSERT > > INTO Su...', 'columns Submiss...') > > #2 E:\dev\wamp\source\misc.php(109): CPSDB::queryExec(' INSERT INTO > > Su...') > > #3 E:\dev\wamp\www\map.php5(28): addUserToMap('foo') > > #4 {main} > > * thrown in E:\dev\wamp\source\main.php(21) : runtime-created function > > on line 1 > > > It's somewhat difficult to read because of the way errors are > > converted to exceptions. *In the example above, ignore #0 and #4. > > > The call order is #3 -> #2 -> #1. *(addUserToMap called > > CPSDB::queryExec called SQLiteDatabase->queryExec.) *#1 is where the > > error occurred, and hence where the exception was thrown. > > > I believe exceptions also come with the context, so you could write > > your own default exception handler to, for example: > > > * Show the entire contents of function parameters, rather than the > > "..." cut off text above. > > > * Show local variables for each stack frame. > > > > I saw some code that > > > automatically detected if the app is running locally and uses the > > > added info for a "debug" mode of display but shortens to a user > > > friendly display if the site is deployed. > > > Excellent idea. *The great thing about exceptions is that they pop the > > stack, so you can do any cleanup you need, rather than just dieing at > > the first PHP error. *Then when you encounter an uncaught exception, > > you can log the exception details and display a generic error message > > to the user. > > > > I hope my questions are not too sophomoric <snip> > > > Absolutely not. *Exceptions are new to PHP5, and foreign to the way > > that PHP has handled errors since its inception. *The documentation is > > sparse about how to bridge the two worlds of PHP error handling. > > > Let me know if any of the above isn't clear, and I'll clarify. > > > Walter > > Hi Walter, > Thank you for your time and input. > > I am still confused by this: > > > set_error_handler( create_function( '$x, $y', 'throw new PHPError( $y, > > $x );' ), E_ALL | E_STRICT ); > > I believe this causes any error to throw an exception of type > PHPError. But I do not understand the arguments $x and $y. Can you > explain? And what does PHPError look like? > > Was the example log output you shared created by PHPError? > > Thank you again for all your help, > John Yes, it does causes any error to throw an exception of type PHPError. Right, I forgot to define PHPError. It just looks like this: class PHPError extends Exception { } Here's a description in words of what the set_error_handler call does: 1. Create a function that takes 2 arguments, $x and $y. 2. When that function is called, it creates a new PHPError exception, passing to its constructor $y and $x. 3. When there's _any_ error (E_ALL|E_STRICT), call the function created in #1. To get into the details, first look up the documentation for set_error_handler. http://www.php.net/set_error_handler You'll find that the error handler function "needs to accept two parameters: the error code, and a string describing the error." So $x is the error code and $y is the string describing the error. But you'll notice that $x and $y are passed in reverse order to the PHPError constructor. Look up the documentation for PHP exceptions... http://www.php.net/exceptions ...and you'll find that the constructor for the Exception base class (from which PHPError derives) expects the first argument to be the error string and the second to be the error code. (Reverse of how set_error_handler does it.) On additional note: the function created via create_function *could* throw the base Exception class, rather than some derived exception class (PHPError in this case.) I just like to define new exception classes for different categories of errors. And your final question: the log output I showed wasn't created by PHPError. It's created by the default PHP-wide uncaught exception handler. You'll get similar output for any uncaught exception. For example, a script as simple as <?php throw new Exception(); ?> will give similar output. Walter |
|
|||
|
On Aug 20, 10:04 am, WalterGR <walte...@gmail.com> wrote:
> On Aug 20, 9:44 am, john6630 <john6...@hotmail.com> wrote: > > > > > On Aug 20, 8:35 am, WalterGR <walte...@gmail.com> wrote: > > > > On Aug 20, 8:08 am, john6630 <john6...@hotmail.com> wrote: > > > > > If so, I have another question. In the set_error_handler example > > > > above, you only pass the error code and error string. Wouldn't it be > > > > good to also pass the file, line and context? > > > > I know you asked a bunch of other questions, but I'll stick to my area > > > of expertise. :) > > > > With PHP exceptions, you don't need to pass the additional > > > information. They come along with the exception. Using the code I > > > shared for set_error_handler, the default uncaught-exception handler > > > will print (to the web browser) a stack trace. > > > > Here's an example from my logs: > > > > [18-Aug-2008 14:23:01] PHP Fatal error: Uncaught exception 'PHPError' > > > with message 'SQLiteDatabase::queryExec() [<a > > > href='sqlitedatabase.queryexec'>sqlitedatabase.que ryexec</a>]: columns > > > SubmissionLowered, RefUser, RefLocation are not unique' in E:\dev\wamp > > > \source\main.php(21) : runtime-created function:1 > > > Stack trace: > > > #0 [internal function]: __lambda_func(2, 'SQLiteDatabase:...', 'E:\dev > > > \wamp\sou...', 45, Array) > > > #1 E:\dev\wamp\source\DB.php(45): SQLiteDatabase->queryExec(' INSERT > > > INTO Su...', 'columns Submiss...') > > > #2 E:\dev\wamp\source\misc.php(109): CPSDB::queryExec(' INSERT INTO > > > Su...') > > > #3 E:\dev\wamp\www\map.php5(28): addUserToMap('foo') > > > #4 {main} > > > thrown in E:\dev\wamp\source\main.php(21) : runtime-created function > > > on line 1 > > > > It's somewhat difficult to read because of the way errors are > > > converted to exceptions. In the example above, ignore #0 and #4. > > > > The call order is #3 -> #2 -> #1. (addUserToMap called > > > CPSDB::queryExec called SQLiteDatabase->queryExec.) #1 is where the > > > error occurred, and hence where the exception was thrown. > > > > I believe exceptions also come with the context, so you could write > > > your own default exception handler to, for example: > > > > * Show the entire contents of function parameters, rather than the > > > "..." cut off text above. > > > > * Show local variables for each stack frame. > > > > > I saw some code that > > > > automatically detected if the app is running locally and uses the > > > > added info for a "debug" mode of display but shortens to a user > > > > friendly display if the site is deployed. > > > > Excellent idea. The great thing about exceptions is that they pop the > > > stack, so you can do any cleanup you need, rather than just dieing at > > > the first PHP error. Then when you encounter an uncaught exception, > > > you can log the exception details and display a generic error message > > > to the user. > > > > > I hope my questions are not too sophomoric <snip> > > > > Absolutely not. Exceptions are new to PHP5, and foreign to the way > > > that PHP has handled errors since its inception. The documentation is > > > sparse about how to bridge the two worlds of PHP error handling. > > > > Let me know if any of the above isn't clear, and I'll clarify. > > > > Walter > > > Hi Walter, > > Thank you for your time and input. > > > I am still confused by this: > > > > set_error_handler( create_function( '$x, $y', 'throw new PHPError( $y, > > > $x );' ), E_ALL | E_STRICT ); > > > I believe this causes any error to throw an exception of type > > PHPError. But I do not understand the arguments $x and $y. Can you > > explain? And what does PHPError look like? > > > Was the example log output you shared created by PHPError? > > > Thank you again for all your help, > > John > > Yes, it does causes any error to throw an exception of type > PHPError. > > Right, I forgot to define PHPError. It just looks like this: > > class PHPError extends Exception { } > > Here's a description in words of what the set_error_handler call does: > > 1. Create a function that takes 2 arguments, $x and $y. > 2. When that function is called, it creates a new PHPError exception, > passing to its constructor $y and $x. > 3. When there's _any_ error (E_ALL|E_STRICT), call the function > created in #1. > > To get into the details, first look up the documentation for > set_error_handler. > > http://www.php.net/set_error_handler > > You'll find that the error handler function "needs to accept two > parameters: the error code, and a string describing the error." > > So $x is the error code and $y is the string describing the error. > > But you'll notice that $x and $y are passed in reverse order to the > PHPError constructor. Look up the documentation for PHP exceptions... > > http://www.php.net/exceptions > > ...and you'll find that the constructor for the Exception base class > (from which PHPError derives) expects the first argument to be the > error string and the second to be the error code. (Reverse of how > set_error_handler does it.) > > On additional note: the function created via create_function *could* > throw the base Exception class, rather than some derived exception > class (PHPError in this case.) I just like to define new exception > classes for different categories of errors. > > And your final question: the log output I showed wasn't created by > PHPError. It's created by the default PHP-wide uncaught exception > handler. You'll get similar output for any uncaught exception. For > example, a script as simple as > > <?php > > throw new Exception(); > > ?> > > will give similar output. > > Walter Fantastic Walter...it is beginning to make sense. I had reviewed the PHP manual but did not understand how to use the info. So I have one last question...do I need to use both set_error_handler and set_exception_handler to prevent any unhandled errors or exceptions? I was thinking that since you used set_error_handler, that would take care of everything, but you indicate your log was from an uncaught exception, so now I think I need to allow for both errors and exceptions. Thanks again, John |
|
|||
|
On Aug 20, 10:19*am, john6630 <john6...@hotmail.com> wrote:
> Fantastic Walter...it is beginning to make sense. I had reviewed the > PHP manual but did not understand how to use the info. > > So I have one last question...do I need to use both set_error_handler > and set_exception_handler to prevent any unhandled errors or > exceptions? I was thinking that since you used set_error_handler, that > would take care of everything, but you indicate your log was from an > uncaught exception, so now I think I need to allow for both errors and > exceptions. > > Thanks again, > John Very glad I can help. set_error_handler only handles PHP errors, so yes, you do need a way to handle uncaught exceptions. The easiest way is - as you mention - set_exception_handler. Now, this is where my knowledge gets shaky: As I understand it - and I hope someone will correct me if I'm wrong - the unfortunate thing about defining your own exception handler is that you lose the built-in functionality of PHP's default exception handler. That means: you lose the fancy stack trace output, and you lose PHP's automatic logging of that output. (Beware that there are configuration settings to turn on logging, which may be off by default in your configuration.) You may have to write a default exception handler that replicates that functionality. My hypothesis - which I haven't yet checked - is that you may be able to call the default exception handler from your user-defined exception handler. (When you call set_exception_handler it gives you the name of the previous exception handler.) But the default exception handler halts execution, so you'd have to make *sure* that the HTML error page has been fully sent to the user before calling it. Walter |