Php Errors and Exceptions
For example:
$ php -a
php > echo $foo;
Notice: Undefined variable: foo in php shell code on line 1
This is only a notice error, and PHP will happily carry on. This can be confusing for those coming from “exception-heavy” languages, because referencing a missing variable in Python for example will throw an exception:
$ python
>>> print foo
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'foo' is not defined
The only real difference is that Python will freak out over any small thing, so that developers can be super sure any potential issue or edge-case is caught, whereas PHP will keep on processing unless something extreme happens, at which point it will throw an error and report it.
Error Severity
PHP has several levels of error severity. The three most common types of messages are errors, notices and warnings. These have different levels of severity; E_ERROR, E_NOTICE, and E_WARNING. Errors are fatal run-time errors and are usually caused by faults in your code and need to be fixed as they’ll cause PHP to stop executing. Notices are advisory messages caused by code that may or may not cause problems during the execution of the script, execution is not halted. Warnings are non-fatal errors, execution of the script will not be halted.
Another type of error message reported at compile time are E_STRICT messages. These messages are used to suggest changes to your code to help ensure best interoperability and forward compatibility with upcoming versions of PHP.
Changing PHP’s Error Reporting Behaviour
Error Reporting can be changed by using PHP settings and/or PHP function calls. Using the built in PHP function error_reporting() you can set the level of errors for the duration of the script execution by passing one of the predefined error level constants, meaning if you only want to see Errors and Warnings - but not Notices - then you can configure that:
<?php
error_reporting(E_ERROR | E_WARNING);
You can also control whether or not errors are displayed to the screen (good for development) or hidden, and logged (good for production). For more information on this check out the Error Reporting section.
Inline Error Suppression
You can also tell PHP to suppress specific errors with the Error Control Operator @. You put this operator at the beginning of an expression, and any error that’s a direct result of the expression is silenced.
<?php
echo @$foo['bar'];
This will output $foo['bar'] if it exists, but will simply return a null and print nothing if the variable $foo or 'bar' key does not exist. Without the error control operator, this expression could create a PHP Notice: Undefined variable: foo or PHP Notice: Undefined index: bar error.
This might seem like a good idea, but there are a few undesirable tradeoffs. PHP handles expressions using an @ in a less performant way than expressions without an @. Premature optimization may be the root of all programming arguments, but if performance is particularly important for your application/library it’s important to understand the error control operator’s performance implications.
Secondly, the error control operator completely swallows the error. The error is not displayed, and the error is not sent to the error log. Also, stock/production PHP systems have no way to turn off the error control operator. While you may be correct that the error you’re seeing is harmless, a different, less harmless error will be just as silent.
If there’s a way to avoid the error suppression operator, you should consider it. For example, our code above could be rewritten like this:
<?php
echo isset($foo['bar']) ? $foo['bar'] : '';
One instance where error suppression might make sense is where fopen() fails to find a file to load. You could check for the existence of the file before you try to load it, but if the file is deleted after the check and before the fopen() (which might sound impossible, but it can happen) then fopen() will return false and throw an error. This is potentially something PHP should resolve, but is one case where error suppression might seem like the only valid solution.
Earlier we mentioned there’s no way in a stock PHP system to turn off the error control operator. However, Xdebug has an xdebug.scream ini setting which will disable the error control operator. You can set this via your php.ini file with the following.
xdebug.scream = On
You can also set this value at runtime with the ini_set function
<?php
ini_set('xdebug.scream', '1')
The “Scream” PHP extension offers similar functionality to Xdebug’s, although Scream’s ini setting is named scream.enabled.
This is most useful when you’re debugging code and suspect an informative error is suppressed. Use scream with care, and as a temporary debugging tool. There’s lots of PHP library code that may not work with the error control operator disabled.
Error Control Operators
SitePoint
Xdebug
Scream
ErrorException
PHP is perfectly capable of being an “exception-heavy” programming language, and only requires a few lines of code to make the switch. Basically you can throw your “errors” as “exceptions” using the ErrorException class, which extends the Exception class.
This is a common practice implemented by a large number of modern frameworks such as Symfony and Laravel. By default Laravel will display all errors as exceptions using the Whoops! package if the app.debug switch is turned on, then hide them if the switch is turned off.
By throwing errors as exceptions in development you can handle them better than the usual result, and if you see an exception during development you can wrap it in a catch statement with specific instructions on how to handle the situation. Each exception you catch instantly makes your application that little bit more robust.
More information on this and details on how to use ErrorException with error handling can be found at ErrorException Class.
Error Control Operators
Predefined Constants for Error Handling
error_reporting()
Reporting
Exceptions
Exceptions are a standard part of most popular programming languages, but they are often overlooked by PHP programmers. Languages like Ruby are extremely Exception heavy, so whenever something goes wrong such as a HTTP request failing, or a DB query goes wrong, or even if an image asset could not be found, Ruby (or the gems being used) will throw an exception to the screen meaning you instantly know there is a mistake.
PHP itself is fairly lax with this, and a call to file_get_contents() will usually just get you a FALSE and a warning. Many older PHP frameworks like CodeIgniter will just return a false, log a message to their proprietary logs and maybe let you use a method like $this->upload->get_error() to see what went wrong. The problem here is that you have to go looking for a mistake and check the docs to see what the error method is for this class, instead of having it made extremely obvious.
Another problem is when classes automatically throw an error to the screen and exit the process. When you do this you stop another developer from being able to dynamically handle that error. Exceptions should be thrown to make a developer aware of an error; they then can choose how to handle this. E.g.:
<?php
$email = new Fuel\Email;
$email->subject('My Subject');
$email->body('How the heck are you?');
$email->to('guy@example.com', 'Some Guy');
try
{
$email->send();
}
catch(Fuel\Email\ValidationFailedException $e)
{
// The validation failed
}
catch(Fuel\Email\SendingFailedException $e)
{
// The driver could not send the email
}
finally
{
// Executed regardless of whether an exception has been thrown, and before normal execution resumes
}
SPL Exceptions
The generic Exception class provides very little debugging context for the developer; however, to remedy this, it is possible to create a specialized Exception type by sub-classing the generic Exception class:
<?php
class ValidationException extends Exception {}
This means you can add multiple catch blocks and handle different Exceptions differently. This can lead to the creation of a lot of custom Exceptions, some of which could have been avoided using the SPL Exceptions provided in the SPL extension.
If for example you use the __call() Magic Method and an invalid method is requested then instead of throwing a standard Exception which is vague, or creating a custom Exception just for that, you could just throw new BadMethodCallException;.