When you’re building something in PHP, whether it’s for a simple website or a much larger web application, you always need to be on the lookout for errors cropping up.
Nasty little bugs can creep in when you least expect them.
An error occurs whenever PHP tries to execute an instruction which either results in an impossible outcome, or otherwise prevents the script from executing in the manner intended by the programmer.
Errors range from simple fatal errors, where program execution is halted at the point of the fault – usually a syntax error or a call to a function that doesn’t exist – to more complex errors where the cause of the problem isn’t immediately clear, and an indepth review of the code must be carried out to pinpoint the flaw.
Dealing with errors in a meaningful and graceful manner is a major consideration when developing PHP code.
It’s important to ensure that when something goes wrong (and it likely will, eventually), your users aren’t treated to an unsightly page of technical jargon.
The default behaviour of PHP when encountering an error is to output a very simple line of text, with very basic formatting. It’s really quite ugly.
echo ($hello_world);
Here, we are trying to access a variable $hello_world which has not been defined. The resulting output will be a screen with the following text
Notice: Undefined variable: hello_world in C:localmyphp.php on line 5
You might not see this appear on screen right away. PHP sometimes is not fully set up to display all errors as they happen.
To change PHP’s default behaviour, add the following lines before the echo statement above
error_reporting(E_ALL|E_STRICT);
ini_set("display_errors", TRUE);
Line 1 tells PHP to report all errors (including “strict” errors, which aren’t included as part of the E_ALL argument).
Line 2 sets a PHP configuration variable which instructs PHP to always display errors. This value is also hidden away in the php.ini configuration file, but that’s not always accessible, so this way of setting it is perfectly valid.
One way to get around this error message cropping up in the first place is to make sure you’ve properly defined the variable before you use it. And if you’re uncertain whether or not the variable is defined, you can always place checks before calling it, like so
//the isset function checks if a given variable is defined or not
if(isset($hello_world){
echo $hello_world;
} else {
echo "The variable 'hello world' couldn't be located";
}
But that’s not terribly practical when you have to do the same checks for every variable, and the error message displayed still makes it all too clear to the visiting public that something’s gone wrong.
The following example is a very common instance of an error message when something goes wrong
Fatal error: Call to undefined function hello_world() in C:localmyphp.php on line 1
The error displayed above isn’t terribly pretty to look at. In fact, if a visitor saw that on your site they’d wonder why it was there in the first place, and would probably be discouraged from exploring the site any more, even if the error wasn’t a fatal error, and they could still browse.
A good way to work around this is to use a custom error handler, which allows you to intercept PHP’s error commands, and replace them with your own, nicely formatted ones, even hiding them away completely from visitors, and displaying them only to yourself.
function custom_error_function($level, $message, $file, $line, $context){
Creating a custom error handler is as simple as creating a new function with up to 5 seperate arguments, defined as follows
The specific error levels generated by an error are outlined below. These are the values passed into the first variable of the custom error handler, along with their associated numerical value
Lets try creating our own customised error function, to try and make the default error message a little bit friendlier to look at
function customError($level, $message){
//note that the final three arguments are optional
echo "An error has occured! The error level is $level and the message was '$message'";
echo "Please get in touch with the site administrator at admin@email.com to notify them of this fault";
}
The function above is still only just that, a function, but when set to be the default error handler for PHP, as the following code will do, it prints out a much friendlier error message, and lets the user know of a course of action they can take to get the problem remedied.
//this function will set the custom error handler to our function above
set_error_handler("customError");
//now, let's try triggering that error again...
echo ($hello_world); /* our undefined variable */
Which produces
An error has occured! The error level is 8 and the message was 'Undefined variable: hello_world'
Please get in touch with the site administrator at admin@email.com to notify them of this fault
A somewhat friendlier thing to see, but still not the best solution. Ideally, we’d prefer for the visitor never to see these error messages, as they indicate that something has gone wrong with the code, reducing their confidence in the site.
When errors do occur, as they inevitably will, it helps to be able to take as much control of the error as you can.
This normally includes shielding the website visitor from the full effects of the error, while giving yourself as much information as possible about the problem so you can set about fixing it in a timely manner.
Error logging in PHP is a very helpful tool you can use to record messages about errors that have occured on your site.
This way, you don’t have to rely on your site visitors notifying you when things go wrong. Instead, you have a means of automatically noting down anything that goes awry.
Let’s modify our custom error handler function from the previous section to use a useful function of PHP, error_log(). This function usually takes three variables, and a fourth when we are sending error logs via email.
Custom headers : When message type is set to 1 (send via email) this variable holds the custom mail headers used by mail() – see the tutorial on PHP and email
function customError($level, $message, $file, $line)
{
//here we’ll append the error to the local file error.log
error_log("Error level $level occured : ‘$message’",3, "error.log");
//now that we’ve safely recorded the error, we don’t need to rely on the visitor
//to inform us of any faults
}
set_error_handler("customError");
//let’s trigger the error again…
echo($hello_world);
Now, we won’t see anything appear on screen, but the error will be recorded within the error.log file. Here, if we look on the first line of that file, we will see
Error level 8 occured : 'Undefined variable: hello_world'
Good stuff! We now have a handler in place to notify us of any warnings or errors that crop up on the site. You can use a file like this to keep track of any serious errors that occur
Important note: This method of error logging, especially when used to just track undefined variables, can be very system intensive if you have a lot of visitors. Use this method wisely, and only for the more serious forms of error.
We can modify the behaviour of our custom error handler to deal with the errors more wisely
function customError($level, $message, $file, $line)
{
if($level >= E_USER_ERROR)
error_log("Error level $level occured : '$message'",3, "error.log");
}
Now, this function will ignore all errors less serious than E_USER_ERROR and log only those errors which pose a risk to the application. We can test this by use of the trigger_error() function, which will generate a user-triggered error
trigger_error("User generated error");
This will cause the following line to appear in error.log
Error level 1024 occured : 'User generated error'
Fatal errors : Unfortunately, PHP doesn’t allow fatal errors to be handled with custom error handlers – the assumption here being that a fatal error will pretty much have killed your script anyway, with no hope of recovery.
Fatal errors can be created by calling functions that don’t exist, for example
hello_world();
Gives, again, the messy error string
Fatal error: Call to undefined function hello_world() in C:localmyphp.php on line 1
The solution to this little scenario : Check that you’ve defined your functions Fatal errors are usually a sign that something’s gone really wrong and you should probably get it sorted quickish!
Sometimes it’s useful to be able to see basic errors, especially while you’re developing your site.
Nothing provides quite as many headaches as a problem that seems to have no obvious cause and having error messages popping up all over the place, while not looking pretty, is a pretty effective way of pinpointing the offending code.
However, at the same time, you certainly don’t want all these error messages cropping up on your site for the average visitor, on the off-chance that something goes wrong.
To prevent this from happening, it’s possible to create a custom error handler which obeys one set of rules for yourself, the developer, and one set of rules for the visiting public.
There’s a couple of simple ways of specifying "who you are" to your website while you’re developing it, so that the site can distinguish between yourself, the developer, and a member of the public.
The first way involves your unique IP address, which is stored in the $_SERVER variable on the webserver, and can be accessed simply inside your script.
First, grab your IP address from a site like What’s My IP. Once you’ve got that you can make some simple adjustments to your error handling function like so
function customError($level, $message, $file, $line)
{
if($_SERVER['REMOTE_ADDR'] == "164.25.66.42"){
echo "An error occurred ($level) : '$message'";
} else {
if($level >= E_USER_ERROR) {
//write critical errors to the log for normal users
error_log("nError level $level occured : '$message'",3, "error.log");
}
}
}
set_error_handler("customError");
//let's trigger the error again...
echo $hello_world;
Here, the variable $_SERVER['REMOTE_ADDR']
holds the IP address of the visiting computer (yourself, in this case) and if it matches the value you’ve entered, then an error is displayed.
If not, then the script knows you’re just a member of the public browsing the site and won’t display an error, although the error will still be logged if it’s serious enough.
A second way, which doesn’t rely on storing your IP address uses the $_GET variable of PHP, which passes data from the URL into the script.
Very handy for passing in variables as required, and this has a couple of benefits over the previous code
You don’t need to keep track of your IP address in the code, which can be troublesome if you develop from multiple locations, or your IP address changes frequently. You can turn debugging on and off easily, which allows you to see the site as a visitor would
Take the following URL, for example
http://www.mysite.com/myphp.php?debug=on
By typing the website URL like so, we have access to a variable $_GET[‘debug’] from within the script. Modifying our custom error handler again…
function customError($level, $message, $file, $line)
{
if($_GET['debug'] == "on"){
echo "An error occurred ($level) : '$message'";
} else {
if($level >= E_USER_ERROR) {
//write critical errors to the log for normal users
error_log("nError level $level occured : '$message'",3, "error.log");
}
}
}
set_error_handler("customError");
//let's trigger the error again...
echo $hello_world;
A small change, but one which allows us to control our debugging status more easily.
There’s a lot that can be done in the realms of error handling and, especially for large scale applications, it’s wise to invest a decent amount of time and effort into ensuring your application functions well even when things come crashing down behind the scenes.
This article only covers the basics of PHP error handling, but hopefully it’ll give you a good idea of what to consider when you’re writing your code, and a few ways you can gracefully alert your users to a fault when the servers burst into flames!
A neater way of handling errors in PHP is to use a feature called exceptions.
Exceptions can be used when we wish to impose conditions on the execution of our scripts, or when we want to be able to recover gracefully from conditions where, otherwise, execution of the program would grind to a halt
Exceptions also offer a more Object Oriented way of dealing with errors, and they allow us to build error handling into our class definitions so that any errors that occur will cause the code to degrade gracefully and provide detailed feedback about the exact location of the fault.
Since they are based around a root Exception object we can also extend the Exception class to deal with errors in a more versatile way than the simpler methods outlined on the previous page.
Let’s look first at a simple of use the Exception class to prevent a function from completing execution whenever it is passed an argument outwith certain boundaries.
function tryValue($v){
if($v < 1 || $v > 6){
throw new Exception("the value is not between 1 and 6 inclusive");
}
else return $v*2;
}
try {
echo tryValue(4);
echo tryValue(10);
} catch(Exception $e) {
echo "Exception : ".$e->getMessage();
}
echo "All done";
This code produces the output
8
Exception : the value is not between 1 and 6 inclusive
All done
We need a little explanation of what’s going on here…
Line 12 : The code within the catch block is executed whenever an exception is thrown by code within the try block. It’s almost as if you’re saying
try {this code} and if that throws an (Exception, $e) {do this instead (catch the throw)}
Think of it as if the catch block is a safety net, to catch any failing functions, and prevent the code from hitting the ground hard!
Inside the catch block you also have access to the Exception object $e, which contains some useful information about the particular error that was thrown, including the message defined at the time the exception was thrown.
One useful application of exceptions is to isolate the location of an error within a complex class that depends on many internal functions during run-time.
We can encase the whole function call within a try…catch block to ensure that if an exception is thrown at any point during the operation of the class, then the class will immediately exit and return control to the try…catch block where the error will be reported.
This is in comparison to our earlier error handling functions using set_error_handler(), which would simply output the information there and then, without leaving the scope of the class
class BaseClass{
function tryValue($v){
if($v < 1 || $v > 6){
throw new Exception("the value is not between 1 and 6 inclusive");
}
else return $v*2;
}
}
class MyClass extends BaseClass{
function doubler($x){
parent::tryValue($x);
}
}
function printException(&$e){
$trace = $e->getTrace();
echo "Exception on line ".$e->getLine()." : ".$e->getMessage()."<br/>";
$count = 1;
foreach($trace as $k => $v){
echo "Line {$v['line']} : {$v['class']}::{$v['function']}()<br/>";
$count++;
}
echo "-----------------------<br/>";
}
$baseclass = new BaseClass;
$myclass = new MyClass;
try {
echo $baseclass->tryValue(64);
} catch(Exception $e) {
printException($e);
}
try {
echo $myclass->doubler(10);
} catch(Exception $e) {
printException($e);
}
echo "All done";
This produces useful feedback as shown below
Exception on line 5 : the value is not between 1 and 6 inclusive
Line 27 : BaseClass::tryValue()
-----------------------
Exception on line 5 : the value is not between 1 and 6 inclusive
Line 11 : BaseClass::tryValue()
Line 32 : MyClass::doubler()
-----------------------
All done
Let’s see what this seemingly complicated tangle of code is telling us…
Both function calls have failed – first on line 27 and then the second on line 32. Since the function tryValue
throws exceptions whenever the value is out of range, we’ve been provided with some useful debugging information
The first exception tells us that it occurred on line 5, exactly at the point we’ve specified the exception should be thrown, We also have access to a trace, which we can use to step back through the code to see the path of execution. We can see here that the function call came from line 27, just as our code says.
The second exception also tells us that it occurred on line 5. This is correct, since the function call came from elsewhere in the code, but it also came from an extended version of the first class. We can see that the initial call, of MyClass::doubler() came from line 32, but then doubler() calls the function tryValue (belonging to it’s parent BaseClass) from line 11
When an exception is thrown, PHP is nice enough to give us an instance of the Exception class $e (you can name the variable whatever you like here) complete with a collection of useful methods you can utilise to retrieve more information about the error that just occurred
Hopefully from that example you can see how an Exception based error handler can provide you with ever-useful detail about your code which makes the difference between hours of hair-tearing debugging hell, and ten minutes of peaceful script inspection!
These are only a few of the ways in which you can gracefully let your users be aware of faults that occur within your application. Error handling and fault prevention is a detailed subject though, so I’d recommend digging down further into the subject if you’re keen to make sure that your code is solid and reliable.
Robin is the dedicated developer behind Solarise.dev. With years of experience in web development, he's committed to delivering high-quality solutions and ensuring websites run smoothly. Always eager to learn and adapt to the ever-changing digital landscape, Robin believes in the power of collaboration and sharing knowledge. Outside of coding, he enjoys diving into tech news and exploring new tools in the industry.
If you'd like to get in touch to discuss a project, or if you'd just like to ask a question, fill in the form below.
Send me a message and I'll get back to you as soon as possible. Ask me about anything - web development, project proposals or just say hi!