-->

Displaying custom error page in PHP for errors whi

2020-07-24 04:40发布

问题:

I would like to be able to discard a partially rendered page and show an error page in PHP.

I already know about set_error_handler(), but it can only trap certain types of errors. I would like to know how to show an error page when an error type which can't be trapped by set_error_handler() is raised.

Unfortunately, it seems that the following code, when run with PHP 5.3.2 on Apache 2.2, doesn't do what I would expect it to do:

<?php

// Start the output buffer
ob_start();

// Output something into the buffer.
// I only want this to be displayed if I call one of the 
// ob_flush functions or echo the buffer myself later.
echo "yep";

// Call a function I know not to exist in order to raise 
// an error which cannot be trapped by set_error_handler() 
// and would, if display_errors was On, output "Fatal 
// error: Call to undefined function fwee()..." 
function_which_does_not_exist();

// This will never be executed.
$out = ob_get_clean();

The output of the script is:

yep

Whereas I would expect it to output nothing (or spew error info and only error info if display_errors() is on).

I have confirmed using LiveHTTPHeaders that PHP 5.3.2 does send a 500 error to the browser when display_errors is off (and a 200 when it's on) using the version of apache supplied by MacPorts, but it only ever spits 200s when using PHP 5.3.1 on XAMPP.

I tried setting ErrorDocument 500 "test" in the apache configuration (confirmed to be working by doing the same for 404) but PHP never shows the custom error, even when the entire contents of the script is just header('HTTP/1.1 500 Internal Server Error');

I'm not sure what else to do to make sure a partially rendered page is replaced with a simple error.

I can also confirm that this happens in the Yii framework. If I edit the view for the "about" page in the blog demo to have a line which reads <?php echo function_which_does_not_exist() ?>, I get a partially rendered page.

回答1:

You could pass ob_start the name of a callback function, that is executed before the output is flushed on ob_get_clean().

This callback function seams to be executed even if an error occured on the page.

This way you could do something like this:

<?php
$endReached = 0;

function outpu_cb($buffer) {
  global $endReached;

  if ($endReached) return $buffer;
  else return 'Your error message';
}

// Start the output buffer
ob_start('outpu_cb');

// Output something into the buffer.
// I only want this to be displayed if I call one of the
// ob_flush functions or echo the buffer myself later.
echo "yep";

// Call a function I know not to exist in order to raise
// an error which cannot be trapped by set_error_handler()
// and would, if display_errors was On, output "Fatal
// error: Call to undefined function fwee()..."
function_which_does_not_exist();

// This will never be executed.
$endReached = 1;
echo ob_get_clean();
?>


回答2:

I think the only right way to do this is by using correct output buffering, than you don't have to rely on specific webserver or browser behaviour.

Best you'd use a MVC framework to handle this for you. All output is buffered until all systems are go, so when an error occurs you can take another route, clear the current buffer and display some nice error message.

You can also use the ob_*() family of functions.

  • You have to call ob_start() as the very first thing in your script (well, before any output is generated)
  • Install an error_handler to fetch errors
  • When an error occured, clean the buffer and re-route your app logic to display some nice userfriendly error message


回答3:

If your talking about E_FATAL or other such errors yes you can catch them with a custom error handler using set_error_handler().

All you need to add is a shutdown function.

// Set the error handler
set_error_handler(array('error', 'handler'));

// Catch E_FATAL errors too!
register_shutdown_function(array('error', 'catch_fatal'));

// Set the exception handler
set_exception_handler(array('error', 'exception'));

// Manually return a new exception 
function catch_fatal()
{
    if($e=error_get_last())Error::exception(new ErrorException($e['message'],$e['type'],0,$e['file'],$e['line']));
}

Take a look at http://micromvc.com or http://kohanaphp.com/ to see how it's done.



回答4:

An old question, but for the record I'd suggest avoiding this issue rather than handling it.

My own approach is to build the response in a response object rather than echoing it as you go along, and only echo the output once the full response has been processed without error. This requires a template system that parses your template and builds your response into a string, in contrast to a classic PHP template which echoes output from your placeholders.

This way you entirely avoid PHP's crufty management of the output cache in error states.