Jan Kudlicka

Ta kontakt med Jan Kudlicka

Jan had been working as an independent consultant for many years, specializing in web application development, before he joined eZ Systems in 2004 where he worked together with both the core and project team. As a part of his job he also concentrated on performance boosting on large projects implemented with eZ Publish. In 2007 he joined Seeds Consulting AS.

Hjem > eZ Publish > eZ Publish Tips And Tricks > Custom Fatal Error Page

Did you know that we offer expert eZ Publish services to eZ Publish partners with 10% discount?

30 mai 08

Jan Kudlicka

Custom Fatal Error Page

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.

Files to download

Custom Fatal Error Page

Version 1.0.1. Compatible with: eZ Publish 3. License: GNU GPL version 2.

The extension allows to display a custom fatal error page instead of the default message.