It happens to every developer. You’ve added your new functionality, checked the code, uploaded it to the server (if necessary), and then you load it up in your browser. Aaaaand… nothing but an empty white screen. One of the hardest parts of dealing with bugs in code is identifying the PHP error and where it can be found. Once you have that information, the fix is often trivial. Unfortunately, the white screen of death is no help at all.
Why no output?
In fact, a lack of output is often a feature and not a bug. Unless you’re working in a secure development environment, you won’t want error messages to appear on your users’ screens. Error messages provide a peek behind the curtain that can reveal details you’d perhaps rather keep to yourself, like the names and dispositions of your files, the extensions you have installed – even fragments of SQL code.
So where is the PHP Error Information?
The answer to this is.. it depends. There is an excellent chance that fatal error information is finding its way to an error log. If you already know where that is, and if your PHP installation is reporting errors then it’s just a matter of looking.
Let’s kick things off with a real error:
class User
{
function getName()
{
}
}
$user = new User();
print $u->getName();
On my current set up that generates an empty screen. I happen to know that my PHP app is logging at /var/log/httpd/twitdev-error_log
so I can monitor monitor from the command line:
$ sudo tail -f /var/log/httpd/twitdev-error_log
[Wed Apr 19 19:43:07 2017] [error] [client 192.168.33.1] PHP Notice: Undefined variable: u in /var/www/twitdev/web/errors.php on line 11
[Wed Apr 19 19:43:07 2017] [error] [client 192.168.33.1] PHP Fatal error: Uncaught Error: Call to a member function getName() on null in /var/www/twitdev/web/errors.php:11\nStack trace:\n#0 {main}\n thrown in /var/www/twitdev/web/errors.php on line 11
And that tells me all I need. It warns me that the variable $u
is undefined. It records the script’s collapse when I attempt to call a method on this non-existent object. It passes on the file name and line number.
By the way, note that shell command: tail -f
. On its own tail
will print the last 10 lines of a given file. The useful -f
or ‘follow’ flag tracks new lines as they’re added. During development you can set up a console to use tail -f
to monitor PHP error information as it becomes available. (ctl-c
will get you back to the prompt afterwards).
Finding the error log
What if you don’t know where to find PHP error information? Unfortunately, there isn’t always a simple answer to this. The first place to look is your php configuration settings. You can access these in a browser by calling the function phpinfo()
. On the command line you can get the ame information by running php -i
.
First of all, look for the error_log
entry. It’s empty by default, but if it’s set, your error information will be logged at that location. Here’s what it might look like in the web view if it’s set.
If this item is not set, then your errors are most likely ending up in the server’s error log. The location for this depends upon your server configuration. For Apache, you’ll usually find your configuration files under /etc/httpd/
. You may find the logfile you’re looking for specified in the central configuration file at something like: /etc/httpd/conf/httpd.conf
or in one of the configuration files in the /etc/httpd/conf.d/
directory. The wrinkle here is that one Apache instance can manage multiple domains – each with its own set of logs – so you may need to dig about to find the correct configuration for your current environment. The configuration item you are looking for is ErrorLog
. This will specify either an absolute file path to a logfile or a relative path from a common log directory. In practical terms this common root is usually one of /var/log/httpd
or /var/log/apache2
.
And now for some bad news if you don’t have root access (or sudo – which allows you to run commands as root) – you can’t usually access apache’s error logs for reading. If that’s the situation, all is not lost for you, though. Read on!
If you do have sudo privileges on your server, then you can sometimes save yourself some digging around in configuration files by temporily tailing all the log files under the common server log directory
$ sudo tail -f /var/log/httpd/*
before kicking off your error condition by reloading the page. If you’re lucky, you’ll see some movement. You’ll see your error messages and learn which log you should be tailing in future.
What if no PHP error information is generated?
Perhaps you know where your error log is, but you’re seeing no output at all. This can happen in systems which are configured with production in mind (or, in some cases, systems that are just plain misconfigured). There are two steps to take here. Firstly you need to confirm that errors are being suppressed. Secondly, you need to re-enable error reporting. Return either to your PHP info output or your php.ini
file to check for the these directives:
log_errors
This should be set to On
– otherwise you’ll not see errors in the log. Even if you’ve exposed errors to the browser, it’s a good idea to track fatal errors in the log. Where errors are not exposed, this is even more important. You can change the log_errors
setting in the php.ini
file.
error_reporting
This setting is best viewed in your php.ini
file because it’s arrived at by combining error constants using binary arithmetic. Rather unhelpfully, the phpinfo()
output only shows the final numeric value. However, in the php.ini
file the constants should tell a much more illuminating story. If you see something like
error_reporting = E_ALL & ~E_NOTICE & ~E_STRICT & ~E_DEPRECATED
then you are set up for the recommended production configuration – all fatal errors are logged and warnings are suppressed. In a development environment you’d want to see something like
error_reporting = E_ALL
so that you get as much information as possible. If you don’t see E_ALL
in your error_reporting
setting, then errors have been suppressed. You should change the setting to include E_ALL
.
Remember that, if you’re changing the main php.ini
file, you’ll likely have to restart apache in order for your change to take effect. How you do that will depend upon your platform.
But wait! How do I find php.ini?
That’s easily answered. phpinfo()
(or php -i
) will tell you. Just look for the phrase Loaded Configuration File. The answer is usually /etc/php.ini
$ php -i | grep 'Loaded Conf'
Loaded Configuration File => /etc/php.ini
Your system could have a local version. Note also that PHP can load an overriding configuration file. Look at Scan this dir for additional .ini files and Additional .ini files parsed for information about those.
Displaying the PHP error in the browser
This is not recommended in a production environment for reasons we have already covered, but during development it can be useful. To get errors to display in the browser you simply set the display_errors
directive to On
in the php.ini
file.
What to do if you do not have root access
Your main problems here are, firstly, that you may not be able to change the php.ini
file and, secondly, that you may not have read access to the server log file. Also for additions to the main php.ini
file to take effect, you may need to restart apache which, guess what?, requires root privileges. What can you do to get your eyeballs on php error data if you do not have root access?
Change ini settings locally
There are a few ways of doing this. If your server supports AllowOverride
for your web directory, you use an .htaccess
file. Simply create a file named .htaccess
and place it in your web root directory. Then you can override central PHP ini items like this:
php_value error_log /tmp/my_htaccess_error.log
php_value display_errors On
You can see whether this worked by causing an error or with a quick look at the phpinfo()
output.
The first column in this grab shows the local (ie overriding) value, the second shows the default.
If .htaccess
won’t work for you, then all is not lost. You can set many ini values from within your script. In a shared initialisation script simply call ini_set()
with your key and value.
ini_set("error_log", "/tmp/my_ini_set.log");
ini_set("display_errors", "On");
Once again, you will override the default setting. Neither of these approaches require you to restart the server.
Image: Danial Novta Creative Commons