Converting errors to exceptions: design flaw? - php

I came across some code recently that used a custom error handler to turn any PHP errors into an generalized application exception. A custom exception handler was also defined that would log the exception if it was within a particular error code range. Example:
class AppException extends Exception
{
}
function error_handler($errno, $errstr, $errfile, $errline)
{
throw new AppException($errstr, $errno);
}
function exception_handler($exception)
{
$min = ...;
$max = ...;
if ($exception->getCode() >= $min && $exception->getCode() <= $max)
{
// log exception
}
}
set_error_handler('error_handler');
set_exception_handler('exception_handler');
$a[1]; // throws exception
The problem is that I saw things like:
try
{
do_something();
}
catch (AppException $exception)
{
}
Which implies that there is no distinction between actual programming errors and "Exceptional" behavior. Upon further digging, I came across parts of the code that were designed around the idea that PHP errors represented "Exceptional" behavior such as:
...
function my_function($param1, $param2)
{
// do something great
}
try
{
my_function('only_one_param');
}
catch (AppException $exception)
{
}
Which ends up obfuscating errors and the design of the application's interface.
What is your opinion on handling errors this way? Is it worth turning PHP's native errors into exceptions? What do you do in situations like the above where a codebase is designed around this idea?

Personally, I do this all the time. The only difference is that in my error_handler function, I check to see if the error is an E_NOTICE first, and only throw if it is not (I log the notice anyway)...
I would change the AppException to something that extends ErrorException... Something like: PhpRuntimeErrorException extends ErrorException which you ONLY use for PHP errors... The reason is so that it's more readable (it's easier to tell what a PhpRuntimeErrorException is without needing to figure out where it's thrown). The other reason, is that the ErrorException will store the generating line/file/etc information, where it would not be stored elsewhere (since the backtrace starts from the throw line)...
So, then you can "try" code like this:
try {
$f = fopen('foo.bar', 'r');
$ret = '';
while ($data = fread($f)) {
$ret .= process($data);
}
fclose($f);
return '';
} catch (PHPRuntimeErrorException $e) {
throw new RuntimeException('Could not open file');
} catch (ProcessException $e) {
fclose($f);
throw new RuntimeException('Could not process data');
}
return $ret;
I also make my default exception handler generate a 500 server error page. That's because any exceptions should be caught, and if they were not, it really is a server error...
Just my experience and opinion...

There has been some debate about this in the PHP community. I believe the general thinking is that errors are things thrown by internal PHP functions, and are coding problems you really need to fix; whereas exceptions are things thrown by your application code that you may just need to 'handle'.
However some of the newer SPL extensions (which tend to be more object oriented) do use exceptions for things previously might have thrown errors, so there is some grey area.
There's also a PHP core class ErrorException - http://www.php.net/manual/en/class.errorexception.php which looks a little easier to use than your code example, if this is a route you want to go down.

I convert everything, including notices to exceptions. There's no reason to allow exceptions to continue normally as it does. Undefined variable or offset? That's a problem.
https://github.com/KyleWolfe/PHPErrorNet

Related

How to call error_handler automatically?

From my previous question I understand that isn't a good practice manage each exception with try/catch block, 'cause if I've hundred functions I should put hundred try/catch block.
Now I noticed the set_error_handler function, if I've understand correctly (never used it), allow me to swith in a file or function all the error generated in the whole scripts. So instead of put try catch block this function should automatically intercept the error and call a function, is right?
Now I already have a Log class that help me to write a stack trace in a file. This file is daily so I can see all system transaction in separated file.
My Log class is a SingleTon, so in each classes if I want write some trace in the log file I just need to do this:
Log::warning('some parameter here');
My goal is create an error.php file where all the error are switched in the Log::warning('...');. I think that this set_error_handler should be placed in the system core. As I said I never worked with it, someone could help me to achieve this with a bit example? I'll be glad.
set_error_handler is used to handle errors in a script not exceptions.
If you want to catch all exceptions from your application to apply the same process you have to call set_exception_handler PHPDoc.
This function takes a callable in argument, so your handler must be defined in another function.
The main difference between this function and a try catch block is that
Execution will stop after the exception_handler is called.
It's a also good practice to keep existing exception handlers possibly introduce by an included lib.
You can create a class to do this
class ErrorHandler
{
private $previousExceptionHandler;
public function registerExceptionHandler($callPrevious = true)
{
$prev = set_exception_handler(array($this, 'handleException'));
if ($callPrevious && $prev) {
$this->previousExceptionHandler = $prev;
}
}
public function handleException(\Exception $e)
{
// DO YOUR STUFF
if ($this->previousExceptionHandler) {
call_user_func($this->previousExceptionHandler, $e);
}
}
}
And to use it
$errorHandler = new ErrorHandler();
$errorHandler->registerExceptionHandler();
There are some good libs to do that and more, especially if you want to catch your exceptions for logging purpose. You can try the excellent Monolog lib wihich is widely used, and its ErrorHandler class

What's the advantage of throwing an exception?

function foo() {
// Some code.
throw new Exception('Ouch'); // Why don't I just output "Ouch" here?
// Code continued.
}
function bar() {
try {
foo()
} catch (Exception $e) {
echo $e->getMessage(); // Ouch
}
}
The code above demonstrates a basic use of the Exception class in PHP. What's the advantage of throwing an exception? I mean, what's the advantage of deferring the problem and not dealing with it immediately inside foo()?
The answers here don't address this question.
Consider this example:
function parseInt($string) {
// Return the int represented by this string
// If the string does not represent an int, throw an exception
}
Now imagine that $string == "foo". What does your function return? It can't return an int, because that would not make any sense. It can't return something that is not an int, because it would surely break something (consider this call $score = parseInt("foo") + parseInt("42")).
That's where an Exception is useful. It breaks in a predictable way, and avoid leaving you in an unknown state.
When you write function foo(), you don't necessarily know that the developer who will be calling it (who might just be Future You) has thought of everything. So if they, for example, forgot to validate user input and tried to call myAwesomeDivisionFunction(3,0), your awesome division function would throw some kind of Exception (possibly a DivideByZeroException extends RangeException). This would stop the code there, and the developer would be forced to fix it.
While you could just do echo "Divide by zero error";, what logical step would you take next? There isn't one. You can't assume anything any more, and so you throw the Exception.

Cleaning up code for a custom exception class in PHP

I'm playing around with custom PHP exceptions for the first time, and would like some help with cleaning up some code. In particular, I'm catching PDO errors, and have written a class to mail myself a stack trace of the errors. The way that I'm currently doing things is as follows:
try {
//db stuff
} catch (PDOException $e) {
throw new My_Own_Exception($e);
exit;
}
where my My_Own_Exception does the job with:
class My_Own_Exception extends Exception
{
/*code to mail myself error info: this part works!
}
While the above works, I feel like I should be able to just write something cleaner such as:
try {
} catch (My_Own_Exception $e) {
exit;
}
where My_Own_Exception is an extension of the PDOException class. A few questions about this: First, is the second way the better approach? Second, if so, is it possible? Third, if it is possible, how do I let PHP know that My_Own_Exception "exists" if My_Own_Exception is never instantiated anywhere? Hopefully the third question makes some sense: my gut tells me that if I can make that happen, then my approach should be possible.
I don't think that an exception is the correct place for logic, it should contain information about the error. A PDOException is useful because you know it originates from your PDO code, if you throw a MyException instead, you need to at least give more (useful) information.
This being set, you should read BiVOC's comment on your original question.
If you have a custom exception handler, you can then differentiate via instanceof.
function exception_handler($exception) {
if ($exception instanceof PDOException) {
//mail
}
//always log/etc.
}
What you are trying to do wont work, because nothing in the PDO code will throw your exception, unfortunately. So you are going to have to throw it yourself, like you were in the first example.
Also, the exit in the first example will never be hit.

How to point all notices and warnings to exceptions?

Is it possible to point all notices and warnings inside try/catch in exceptions. But do it only in try, the rest of the code should use default error handling. The main problem is that the try is in the while loop and can be called several times so i think it isnt a good idea to use set_error_handler and restore_error_handler several times. Maybe php have something like in java?
The only way is to use error handler. But you can configure the error handler to throw or not to throw exceptions:
class ErrorHandler {
public static $throw = false;
public static function handler() {
if(self::$throw) throw new Exception();
}
}
set_error_handler(array('ErrorHandler', 'handler'));
ErrorHandler::$throw = true;

Is there a way to catch an Exception without having to create a variable?

In PHP, I sometimes catch some exceptions with try/catch :
try {
...
} catch (Exception $e) {
// Nothing, this is a test that an exception is thrown.
}
With that kind of code, I end up with the variable $e that is created for nothing (lots of resources), and PHP_MD (PHP Mess Detector) creates a warning because of an unused variable.
Starting with PHP 8, it is possible to use a non-capturing catch.
This is the relevant RFC, which was voted favourably 48-1.
Now it will be possible to do something like this:
try {
readFile($file);
} catch (FileDoesNotExist) {
echo "File does not exist";
} catch (UnauthorizedAccess) {
echo "User does not have the appropriate permissions to access the file";
log("User attempted to access $file");
}
With this, for some edge cases where the exception details are not relevant and exception type already provides all the necessary context, it will be possible to catch the exception without creating a new variable.
You can with PHP 8 #see
PHP 5,7
No, but you can unset it.
try {
...
} catch (Exception $e) {
// Nothing, this is normal
unset($e);
}
If it is PHPMD causing this issue then you can suppress the warning.
PHPMD suppress-warnings
class Bar {
/**
* This will suppress UnusedLocalVariable
* warnings in this method
*
* #SuppressWarnings(PHPMD.UnusedLocalVariable)
*/
public function foo() {
try {
...
} catch (Exception $e) {
// Nothing, this is normal
unset($e);
}
}
}
I'm assuming you are only catching the exception because you need to not because you want to.
With PHP 5,7 you have to use a catch if you want to use try and if you use a catch you have to declare a variable.
That's the whole point of exceptions - you can have multiple different catch blocks to catch any exceptions you'd want to handle. The exception's data has to be assigned somewhere, hence the variable. You could just do something like unset($e) inside the catch block if you really don't want to see those warnings... or disable the warnings (generally a bad idea).
I disagree fundamentally with Marc B's and Artefacto's answers. There are cases where ommitting the catch is better or even the only option. Especially when using external libraries (where you have no control over what exceptions are thrown) and/or async operations.
For example:
I want to create a file only if it doesn't exist yet. I'm using an external I/O library. Imagine it has File::exists($fileName) and File::create($fileName) methods.
Option 1 (if ommitting the catch was possible):
try {
File::create($fileName);
}
// Go on with the rest of the code.
Option 2 (without try/catch):
if (!File::exists($fileName))
File::create($fileName);
Here, option 1 is perfectly valid, since option 2 has two important issues:
If multiple threads are running and going through this code section at the same time, it could be that thread A first checks if the file exists. Next, thread B checks if the file exists. They both find that it doesn't exist. Thread A creates the file. Thread B then attempts to create it again and throws an exception even though you're using the if check.
It's very likely that the library itself already performs the !File::exists($fileName) check. Therefore you're wasting a call that is already made.
Note that if File::create throws other exceptions that might be unexpected it would be good to catch those.
Conclusion
Stating that something is never a good idea, is almost never a good idea. There are always exceptions (hehe) to the rule. Like any convention or design pattern, it's just a rule of thumb meant to help less experienced developers make the right decision.
No.
In any case, it's generally a bad idea to catch an exception and do nothing; exceptions exist precisely to force you to handle the exceptional circumstance (otherwise execution is aborted), so it's comprehensible the language doesn't facilitate such a use case.
As of PHP 8.0 it may be typed without variables, but the general case for each Exception is now Throwable. Class Exception implements Throwable.
try {
...
} catch (CustomException) {
// CustomException
} catch (Throwable) {
//All other classes implementing Throwable interface
}

Categories