php error post: a pencil eraser

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.

php error post: phpinfo output

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.

php error post: error in browser

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.

php error post: 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");

php error post: phpinfo output

Once again, you will override the default setting. Neither of these approaches require you to restart the server.

Image: Danial Novta Creative Commons