Even on the production servers, fatal errors do occur. And when such an error happens, visitors of your web site are confronted with the following page:
If you have not switched off displaying errors (i.e. you have display_errors enabled in the PHP configuration), a visitor will also see something like Fatal error: Call to a member function on a non-object in path/to/the/php/file on line xyz, which reveals information that user is not interested in and it might also be considered as a security risk.
There are no links the visitor can use to continue browsing the site – the only choices to get back to the site are the Back button or re-entering the web address. The error page looks rather unprofessional. The following picture shows how a suitable fatal error page should look like:
The message is user friendly, there is navigation on the page, a search box as well as contact information. As we will show in this article, it is rather easy to achieve this. First, download, install and enable the fatalerrorpage extension attached to this article. Then you have to apply a patch from the extension's patches directory: if you are running eZ Publish 3.9, apply fatalerror_3.9.patch, if 3.10, apply fatalerror_3.10.patch. (If you are running earlier versions, try to apply fatalerror_3.9.patch, if you have problems, look at the patch and make the changes in index.php manually.)
After patching index.php, eZ Publish will, in the case of a fatal error, try to return the content of the file "fatalerror_", followed by the name of the current siteaccess and ".html" (for example "fatalerror_my_site.html" for the my_site siteaccess) and if it does not exist, it will try to return the content of the file "fatalerror.html". If neither exists, it will return the default text "Fatal error: eZ Publish did not finish its request". If you have display_errors enabled, the error message, together with the filename and line number, will be added to the debug output.
Note: if you have just one front siteaccess, use fatalerror_siteaccess.html rather than fatalerror.html. In the latter case you would see the same error page in the administration siteaccess, which might be confusing to the administrators. If you have several front siteaccesses and you want to show the very same page, place it in fatalerror.html and prepare a special page for the administration siteaccess.
So, the next step is to make the files. You can either prepare the files yourself or you can use the output of the extension's fatal/error view. (This view will not show debug even if it is enabled). If you need to change the error message, you can either do it manually in the saved file or you can override the design:fatal/error.tpl template. Place the files to the eZ Publish root directory.
The custom error pages might also contain PHP code but in that case make 100% sure that your code does not contain any code which could lead to an error.
You can test the fatal error page by using the fatal/test view. The code for this view will cause the fatal error ("Call to a member function on a non-object in .../extension/fatalerror/modules/fatal/test.php on line 4") and you should be able to see your custom error page.
The patch introduces one more thing: any content which is output during processing of a request (using functions like echo, print, vardump, print_r, etc.) will not be shown before the output of a pagelayout but will be appended as an HTML comment. This can be useful when you are forced to debug a production site.
Tip: if your pagelayout contains elements which are either random or frequently updated (for example latest news, weather forecast etc.), you can use the cron daemon to automatically update the custom error pages.
If your custom error pages do not link to the fatal/error view and you are not going to update them, you can disable the fatalerrorpage extension.
How it works
Let's have a look at the patch for index.php (version for eZ Publish 3.10, the only difference from 3.9 is that eZ Publish is with the capital P):
--- index.php.orig 2007-10-03 16:05:18.000000000 +0200
+++ index.php 2008-05-29 16:16:47.000000000 +0200
@@ -58,8 +58,14 @@
}
}
+function scOutputBufferCallback( $buffer )
+{
+ $GLOBALS['scOutputBuffer'] = $buffer;
+ return '';
+}
+
$scriptStartTime = microtime();
-ob_start();
+ob_start( 'scOutputBufferCallback' );
$use_external_css = true;
$show_page_layout = true;
@@ -218,9 +224,13 @@
function eZFatalError()
{
+ eZDebug::writeError( $GLOBALS['scOutputBuffer'], 'Buffered Output' );
eZDebug::setHandleType( EZ_HANDLE_NONE );
- print( "<b>Fatal error</b>: eZ Publish did not finish its request<br/>" );
- print( "<p>The execution of eZ Publish was abruptly ended, the debug output is present below.</p>" );
+ if ( !( @include "fatalerror_{$GLOBALS['eZCurrentAccess']['name']}.html" ) && !( @include 'fatalerror.html' ) )
+ {
+ print( "<b>Fatal error</b>: eZ Publish did not finish its request<br/>" );
+ print( "<p>The execution of eZ Publish was abruptly ended, the debug output is present below.</p>" );
+ }
$templateResult = null;
eZDisplayResult( $templateResult );
}
@@ -1140,7 +1150,11 @@
eZDebug::addTimingPoint( "End" );
-ob_end_flush();
+ob_end_clean();
+if ( $GLOBALS['scOutputBuffer'] )
+{
+ $templateResult .= "<!-- {$GLOBALS['scOutputBuffer']} -->";
+}
eZDB::checkTransactionCounter();
We altered the ob_start() call to use callback scOutputBufferCallback() before calling ob_end_flush() or ob_end_clean(). This function will also be called when a fatal error occurs (this method is often used in PHP projects to catch fatal errors). We store the content of the output buffer in a global variable and remove the content of the buffer by returning an empty string.
If a fatal error occurs, the eZFatalError() function is called (after calling scOutputBufferCallback()) and the content of the buffer, stored in the global variable, is appended to the debug output. Then we try to include custom error pages, first the error page for the current siteaccess, then the global one. If unsuccessfully, the original fatal error message is printed.
We have changed ob_end_flush() to ob_end_clean() even if it was not necessary as the content of the output buffer was changed to an empty string, because it is more logical. After this call we are appending the eventual content of the output buffer (before it was changed) as an HTML comment.
To be able to see the custom error page in the case of a database transaction failure, we need to patch lib/ezdb/classes/ezdbinterface.php too:
--- lib/ezdb/classes/ezdbinterface.php.orig 2007-08-22 16:02:19.000000000 +0200
+++ lib/ezdb/classes/ezdbinterface.php 2008-06-03 08:30:49.000000000 +0200
@@ -953,20 +953,24 @@
if ( $htmlErrors )
{
- print( "<div class=\"fatal-error\" style=\"" );
- print( 'margin: 0.5em 0 1em 0; ' .
- 'padding: 0.25em 1em 0.75em 1em;' .
- 'border: 4px solid #000000;' .
- 'background-color: #f8f8f4;' .
- 'border-color: #f95038;" >' );
- print( "<b>Fatal error</b>: A database transaction in eZ Publish failed.<br/>" );
- print( "<p>" );
- print( "The current execution was stopped to prevent further problems.<br/>\n" .
- "You should contact the <a href=\"mailto:$adminEmail?subject=Transaction failed on $site and URI $uri with ID $transID\">System Administrator</a> of this site with the information on this page.<br/>\n" .
- "The current transaction ID is <b>$transID</b> and has been logged.<br/>\n" .
- "Please include the transaction ID and the current URL when contacting the system administrator.<br/>\n" );
- print( "</p>" );
- print( "</div>" );
+ @ob_end_clean();
+ if ( !( @include "fatalerror_{$GLOBALS['eZCurrentAccess']['name']}.html" ) && !( @include 'fatalerror.html' ) )
+ {
+ print( "<div class=\"fatal-error\" style=\"" );
+ print( 'margin: 0.5em 0 1em 0; ' .
+ 'padding: 0.25em 1em 0.75em 1em;' .
+ 'border: 4px solid #000000;' .
+ 'background-color: #f8f8f4;' .
+ 'border-color: #f95038;" >' );
+ print( "<b>Fatal error</b>: A database transaction in eZ Publish failed.<br/>" );
+ print( "<p>" );
+ print( "The current execution was stopped to prevent further problems.<br/>\n" .
+ "You should contact the <a href=\"mailto:$adminEmail?subject=Transaction failed on $site and URI $uri with ID $transID\">System Administrator</a> of this site with the information on this page.<br/>\n" .
+ "The current transaction ID is <b>$transID</b> and has been logged.<br/>\n" .
+ "Please include the transaction ID and the current URL when contacting the system administrator.<br/>\n" );
+ print( "</p>" );
+ print( "</div>" );
+ }
$templateResult = null;
if ( function_exists( 'eZDisplayResult' ) )
We turn off output buffering by calling ob_end_clean() before showing any message and then try to include custom error pages instead of showing the default message if possible.
Comments
To add a comment, fill in and submit the form below.
Mandy Vang on Thursday 07 January 2010 12:47:15 pm
Darrin Franco on Thursday 07 January 2010 6:37:18 pm
Herschel Salinas on Thursday 07 January 2010 11:41:27 pm
Carmine Duncan on Friday 08 January 2010 7:27:14 am
Major Elliott on Friday 08 January 2010 9:15:28 am
Johanna Brown on Friday 08 January 2010 5:49:45 pm
Alma Jacobs on Friday 08 January 2010 11:56:21 pm
Stacia Harding on Saturday 09 January 2010 4:14:53 am
Lizette Dennis on Saturday 09 January 2010 10:52:54 am
Lillie Heath on Saturday 09 January 2010 2:31:12 pm
Gay Rivas on Saturday 09 January 2010 6:36:42 pm
Nick Henderson on Sunday 10 January 2010 12:45:17 am
Myles Chambers on Sunday 10 January 2010 7:24:30 am
Penny Valenzuela on Sunday 10 January 2010 12:24:15 pm
Marguerite Terrell on Sunday 10 January 2010 8:11:00 pm
Dell Hooper on Monday 11 January 2010 2:45:40 am
Kieth Larsen on Monday 11 January 2010 6:29:30 am
Deana Odonnell on Monday 11 January 2010 2:16:18 pm
Ernesto Carr on Monday 11 January 2010 8:24:37 pm
Angelo Britt on Tuesday 12 January 2010 3:16:46 am
Estella Caldwell on Tuesday 12 January 2010 9:49:38 am
Tricia Foster on Tuesday 12 January 2010 3:26:09 pm
Lauren Fulton on Tuesday 12 January 2010 10:32:27 pm
Alejandro Leon on Wednesday 13 January 2010 2:24:59 am
Desiree Figueroa on Wednesday 13 January 2010 9:38:33 am
Myles Warren on Wednesday 13 January 2010 2:42:34 pm
Elton Morrison on Wednesday 13 January 2010 10:34:27 pm
Rene Lott on Thursday 14 January 2010 2:38:14 am
Claudette Paul on Thursday 14 January 2010 9:15:58 am
Ina Strong on Thursday 14 January 2010 2:44:52 pm
Darell Wright on Thursday 14 January 2010 9:01:41 pm
Jimmy Cooke on Friday 15 January 2010 2:50:06 am
Lillian Glenn on Saturday 16 January 2010 1:36:00 pm
Tammy Berger on Saturday 16 January 2010 6:12:15 pm
Christine Russo on Saturday 16 January 2010 11:40:39 pm
Scott Chavez on Sunday 17 January 2010 6:58:01 am
Cherie Francis on Sunday 17 January 2010 10:20:28 am
Orlando Leach on Sunday 17 January 2010 5:31:44 pm
Jerrold Patel on Sunday 17 January 2010 11:04:37 pm
Barton Anderson on Monday 18 January 2010 5:59:45 am
Lionel Marsh on Wednesday 20 January 2010 10:36:10 am
Gregg Armstrong on Wednesday 20 January 2010 2:48:34 pm
Luke Pruitt on Wednesday 20 January 2010 8:05:35 pm
Bessie Acosta on Thursday 21 January 2010 2:14:59 am
Tabatha Benson on Thursday 21 January 2010 6:20:14 am
Bambi Ewing on Thursday 21 January 2010 10:55:52 am
Terese Garza on Thursday 21 January 2010 4:25:19 pm
Randolph Vincent on Thursday 21 January 2010 10:29:34 pm
Donny Mckinney on Friday 22 January 2010 3:52:43 am
Brady Moreno on Friday 22 January 2010 8:09:49 am
Luz Rasmussen on Friday 22 January 2010 1:28:19 pm
Monroe Medina on Friday 22 January 2010 5:58:33 pm
Bill Mueller on Friday 22 January 2010 10:04:21 pm
Nichole Stevens on Saturday 23 January 2010 4:14:54 am
Juliet Harvey on Saturday 23 January 2010 7:47:47 am
Magdalena Guerrero on Saturday 23 January 2010 12:46:51 pm
Rubin Fleming on Saturday 23 January 2010 7:50:22 pm
Brooke Rush on Saturday 23 January 2010 11:10:12 pm
Lesa Forbes on Sunday 24 January 2010 4:38:39 am
Sallie Watts on Sunday 24 January 2010 10:52:41 am
Dorinda Osborn on Sunday 24 January 2010 2:11:11 pm
Duke Craft on Sunday 24 January 2010 7:26:09 pm
Mechelle Patton on Monday 25 January 2010 12:26:37 am
Alyson Young on Monday 25 January 2010 11:20:15 am
Trevor Luna on Monday 25 January 2010 5:06:14 pm
Jared Avila on Monday 25 January 2010 9:44:54 pm
Alma Hawkins on Tuesday 26 January 2010 3:32:40 am
Leona Salinas on Tuesday 26 January 2010 8:32:13 am
Parrish Patrick on Tuesday 26 January 2010 12:49:14 pm
Delia Chandler on Tuesday 26 January 2010 7:21:51 pm
Duane Mercer on Wednesday 27 January 2010 12:35:51 am
Kyle Sharp on Wednesday 27 January 2010 6:22:09 am
Kenny Atkinson on Wednesday 27 January 2010 11:23:22 am
Mark Vance on Wednesday 27 January 2010 4:56:04 pm
Cristina Garcia on Wednesday 27 January 2010 8:55:21 pm
Jeffery Chandler on Thursday 28 January 2010 2:50:49 am
Booker Chapman on Thursday 28 January 2010 8:55:55 am
Brandi Potts on Thursday 28 January 2010 2:21:55 pm
Sanford Hancock on Thursday 28 January 2010 6:40:54 pm
Keenan Mason on Thursday 28 January 2010 11:24:50 pm
Courtney Cooper on Friday 29 January 2010 4:36:58 am
Add a comment