Advanced PHP Error Handling via PHP

.htaccess made easy

In my previous articles on PHP error handling, I explain the process whereby PHP error handling may be achieved using htaccess. Handling (logging, reporting) PHP errors via htaccess requires the following:

  • Access/editing privileges for htaccess files
  • A server running PHP via Apache, not CGI (e.g., phpSuExec1
  • Ability to edit/change permissions for files on your server

If you are having trouble handling PHP errors using htaccess, these three items are the first things to check. If it turns out that you are unable to use htaccess to work with PHP errors, don’t despair — this article explains how to achieve the same goals using local php.ini files. To implement this strategy, the following is required:

  • Ability to create/edit a php.ini file in your public_html directory
  • A server running PHP via CGI (e.g., phpSuExec), not Apache 2
  • Ability to edit/change permissions for files on your server
  • Access/editing privileges for htaccess files (not required)

Assuming satisfaction of the preceding requirements, the remainder of this tutorial explains how to enable global (sitewide) PHP error handling using a custom php.ini file. After explaining the implementation process for production environments, we will explore several useful functional customizations for both production and development servers 3. Excited? Great, let’s begin..

Step 1: Create a custom php.ini file in your site’s root directory

Using a text editor, create a file named “php.ini” and add the following PHP directives 4:

;;; php error handling for production servers
display_startup_errors = off
display_errors = off
html_errors = off
log_errors = on
docref_root = 0
docref_ext = 0
error_log = /var/log/php/errors/php_error.log

Here, we are disabling all public error displays and enabling private error logging in the specified file. After editing the path and file name of the error log in the last line, save the file and upload it to the root directory of your domain. Generally, this directory is named “public_html”, but it may be different on your server. Then, create the specified log file and ensure that it is writable (via 755 or 777) by the server.

Step 2: Enable subdirectory inheritance of custom settings

At this point, error logging should be working, but only for the same directory in which you have placed the php.ini file. Unfortunately, by default, locally specified php.ini directives only affect the directory in which they are located — they are not inherited by subdirectories as they are for htaccess directives. Thus, each directory for which you would like to log errors requires its own copy of the php.ini file. Fortunately, if you are able to access and edit your site’s root htaccess file, there is an easy way to enable subdirectory inheritance of your custom php.ini settings. Simply add the following code to your site’s root htaccess file:

# enable subdirectory inheritance of custom php settings
suPHP_ConfigPath /home/path/public_html

This trick takes advantage of htaccess’ inheritance properties by using them to “point” from each subdirectory to the custom php.ini file in the root (e.g., public_html) directory. Note that you may override the root php.ini directives by placing alternate php.ini files in the target subdirectory.

Step 3: Secure your custom php.ini and log files

Once everything is working, it is important to protect your domain by securing your newly created files. In addition to setting permissions to 600 for your custom php.ini file(s), you may also want to add the following directives to your root htaccess file:

# deny access to php.ini
<Files php.ini>
 order allow,deny
 deny from all
 satisfy all
</Files>

# deny access to php error log
<Files php_error.log>
 order allow,deny
 deny from all
 satisfy all
</Files>

And that’s it — PHP error logging should now be securely enabled on your domain. Now let’s explore some useful functional customizations for both production and development servers..

Controlling the level of PHP error reporting

Using your custom php.ini file, it is possible to set the level of error reporting to suit your particular needs. The general format for controlling the level of PHP errors is as follows:

;;; general directive for setting php error level
error_reporting = integer

There are several common values used for “integer”, including:

  • Complete error reporting — for complete PHP error logging, use an error-reporting integer value of “8191”, which will enable logging of everything except run-time notices. 5
  • Zend error reporting — to record both fatal and non-fatal compile-time warnings generated by the Zend scripting engine, use an error-reporting integer value of “128”.
  • Basic error reporting — to record run-time notices, compile-time parse errors, as well as run-time errors and warnings, use “8” for the error-reporting integer value.
  • Minimal error reporting — to record only fatal run-time errors, use an error-reporting integer value of “1”, which will enable logging of unrecoverable errors.

Of course, there are many more error-reporting values to use, depending on your particular error-logging needs. For more information on logging PHP errors, refer to the Error Handling and Logging Functions page at php.net.

Setting the maximum file size for your error strings

Using your custom php.ini file, you may specify a maximum size for your PHP errors. This controls the size of each logged error, not the overall file size. Here is the general syntax:

;;; general directive for setting max error size
log_errors_max_len = integer

Here, “integer” represents the maximum size of each recorded error string as measured in bytes. The default value is “1024” (i.e., 1 kilobyte). To unleash your logging powers to their fullest extent, you may use a zero value, “0”, to indicate “no maximum” and thus remove all limits. Note that this value is also applied to displayed errors when they are enabled (e.g., during development).

Disable logging of repeated errors

If you remember the last time you examined a healthy (or sick, depending on your point of view) PHP error log, you may recall countless entries of nearly identical errors, where the only difference for each line is the timestamp of the event. If you would like to disable this redundancy, throw down the following code in your custom php.ini file:

;;; disable repeated error logging
ignore_repeated_errors = true
ignore_repeated_source = true

With these lines in place, repeated errors will not be logged, even if they are from different sources or locations. If you only want to disable repeat errors from the same source or file, simply comment out or delete the last line [note: comments must begin with a semicolon ( ; ) in php.ini files]. Conversely, to ensure that your log file includes all repeat errors, change both of the true values to false.

Putting it all together — Production Environment

Having discussed a few of the useful ways to customize our PHP error-logging experience, let’s wrap it all up with a solid, php.ini-based error-handling strategy for generalized production environments. Here is the code for your custom php.ini file:

;;; php error handling for production servers
display_startup_errors = false
display_errors = false
html_errors = false
log_errors = true
ignore_repeated_errors = false
ignore_repeated_source = false
report_memleaks = true
track_errors = true
docref_root = 0
docref_ext = 0
error_log = /var/log/php/errors/php_error.log
error_reporting = 999999999
log_errors_max_len = 0

Or, if you prefer, an explanatory version of the same code, using comments to explain each line:

;;; php error handling for production servers

; disable display of startup errors
display_startup_errors = false

; disable display of all other errors
display_errors = false

; disable html markup of errors
html_errors = false

; enable logging of errors
log_errors = true

; disable ignoring of repeat errors
ignore_repeated_errors = false

; disable ignoring of unique source errors
ignore_repeated_source = false

; enable logging of php memory leaks
report_memleaks = true

; preserve most recent error via php_errormsg
track_errors = true

; disable formatting of error reference links
docref_root = 0

; disable formatting of error reference links
docref_ext = 0

; specify path to php error log
error_log = /var/log/php/errors/php_error.log

; specify recording of all php errors
error_reporting = 999999999

; disable max error string length
log_errors_max_len = 0

This PHP error-handling strategy is ideal for a generalized production environment. In a nutshell, this code secures your server by disabling public display of error messages, yet also enables complete error transparency for the administrator via private error log. Of course, you may wish to customize this code to suit your specific needs. As always, please share your thoughts, ideas, tips and tricks with our fellow readers. Now, let’s take a look at a generalized error-handling strategy for development environments..

Putting it all together — Development Environment

During project development, when public access to your project is unavailable, you may find it beneficial to catch PHP errors in real time, where moment-by-moment circumstances continue to evolve. Here is a generalized, php.ini-based PHP error-handling strategy for development environments. Place this code in your custom php.ini file:

;;; php error handling for production servers
display_startup_errors = true
display_errors = true
html_errors = true
log_errors = true
ignore_repeated_errors = false
ignore_repeated_source = false
report_memleaks = true
track_errors = true
docref_root = 0
docref_ext = 0
error_log = /var/log/php/errors/php_error.log
error_reporting = 999999999
log_errors_max_len = 0

For this code, we will forego the line-by-line explanations, as they may be extrapolated from the previous section. This PHP error-handling strategy is ideal for a generalized development environment. In a nutshell, this code enables real-time error-handling via public display of error messages, while also enabling complete error transparency for the administrator via private error log. Of course, you may wish to customize this code to suit your specific needs. As always, please share your thoughts, ideas, tips and tricks with our fellow readers.

Whew! That about does it for this article.. — see you next time!

Footnotes

  • 1 To determine if your server is running PHP via phpSuExec (i.e., CGI) instead of Apache, upload a phpinfo() file and check the “Server API” near the top of the file. If it says “Apache”, PHP is running on Apache; if it says “CGI”, PHP is running via phpSuExec.
  • 2 This is important because it is impossible to manipulate php.ini directives via htaccess while running PHP on phpSuExec.
  • 3 For more information, check out the manual on Error Handling and Logging Functions at php.net
  • 4 Many thanks to Jeff N. at A Small Orange for helping with the information provided in this article :)
  • 5 Due to the bitwise nature of the various error-reporting values, the value for logging all errors continues to increase. For example, in PHP 5.2.x, its value is 6143, and before that, its value was 2047. Thus, to ensure comprehensive error logging well into the future, it is advisable to set a very large value for error_reporting, such as 2147483647.