Well, now that I've gotten a bit further into it, I realize that this is a stupid question, and wrong. Turns out that the author of the legacy code I maintain was hi-jacking the error log to a different file with a php_init statement. The hi-jacking occurred at the same time as the output buffering was turned on, making it appear as though output buffering was throwing away my error messages.
So, Mr. Moderator, feel free to delete this. Thanks to those who answered in good faith.
Given the following PHP script:
<?php
error_log('test');
ob_start();
error_log('test2');
ob_end_flush();
?>
I get the following error log output:
[04-Feb-2010 11:30:38] test
Why is output buffering eating my error messages? How can I make it stop?
Alternately, is there another way to smuggle messages out of an output buffer, or is it simply a black hole?
(Using PHP 5.2.4-2ubuntu5.10)
Exceptions penetrate ob_start() shield
If you want to stop PHP script execution, rather throw an Exception, which will penetrate the ob_start(), ob_end_flush() shield
echo 'before output buffer';
ob_start();
throw new Exception('this will be seen');
ob_end_flush();
Consider creating a Logger class
Don't output directly with your method, rather use a class or a holder which incorporates the log (or error method in your case), eg:
class Logger
{
private $_messages = array();
public function __construct()
{
$this->_messages['errors'] = array();
$this->_messages['debug'] = array();
}
public function error($msg)
{
$this->_messages['errors'][] = $msg;
}
public function debug($msg)
{
$this->_messages['debug'] = $msg;
}
public function getErrors()
{
return $this->_messages['errors'];
}
}
$logger = new Logger();
$logger->error('error1');
ob_start();
$logger->error('error2');
ob_end_flush();
print_r($logger->getErrors());
this way you can rely on the holder object, it will not discard messages and get all errors, that you wanted to display
I've never done this in practice, but this should work:
You would have to build a wrapper around error_log() that
stores the output you are buffering using ob_get_contents()
erases the output buffer using ob_clean()
writes out the error message and ob_flush()es it
writes back the stored output using echo()
Related
1) I have a view (a neseted one) that triggers a notice-level error
2) I have set up a custom error handler, which is called. Code goes below
3) I want visitor seeing only the "error happened" string in his browser, instead, he sees the half-baked page with this error message appended
4) messing with php's native ob_end_clean only made me clear the contents of the deepest view involved, yet the higher-level views are still half-shown
5) the contents of final_output var of CI_Ouput class is empty string at the moment of error. That's strange is CI is said to pre-buffer everything befoew outputting to the client.
essentially, what I need seems to be discarding any content having been collected so far and replacing it with error message only.
how can I do it?
the code of the handler:
function _error_handler($errno, $errstr) {
echo "error happened";
die();
}
Try to use a Try Catch system?
Try{
//Do something
} catch(Exception $e){
echo "Error has occured!";
}
as suggested in the discussion this had to be related to php's native output buffering wich we can control with ob_* family of functions. Buffering in php can be nested, and CI seems to use it, opening a buffer for each view being loaded.
If we load a view from inside a view, the nesting gets deeper, and generally, when an error occures in the view we don't know how deep we are.
So that's what seems to solve the problem (the code of error handler):
function _error_handler($errno, $errstr) {
$currentLevel=ob_get_level();
for($i=0; $i<$currentLevel-1; $i++) {
ob_end_clean();
}
print "errorrrr!";
die();
}
so it detects how deep it is and cleans as many buffers as needed. After all, we end up with "errorrrr" message being the only thing on the page.
Is it possible to exit the script, say on a fatal error and still output my buffer? I am using a template system that executes the header/content/footer. So I am trying to make it so it'll stop code executing on the fatal_error() function but still output the buffer template (which sends out the error message to the user while still maintaining the website template.) I use ob_start("ob_gzhandler") and in my fatal_error() function I use ob_flush() and I end up with a white screen when fatal_error() is called. Here's my function
function fatal_error($error_message, $log = true)
{
setup_error($error_message);
ob_flush();
exit();
}
setup_error() tells the script to change the content body to the error message (so it doesn't parse 1/2 the body when the error occurs). All the examples I've looked through says ob_flush() or ob_end_flush() can achieve this, though I'm not having any luck.
Use the set_error_handler() function. This way you can assign a user-defined function, in your case fatal_error(), to execute when PHP encounters an error. You can do whatever you like in your function, including flushing the buffer and exiting.
EDIT:
The following code should do what you want:
set_error_handler('fatal_error', E_ERROR, E_USER_ERROR);
function fatal_error($errno, $errstr, $errfile, $errline)
{
ob_flush();
exit();
}
EDIT 2:
You can trigger an E_ERROR and therefore test the fatal_error() function like so:
trigger_error('This is a test error', E_ERROR);
I would try also adding a call to flush() before the ob_flush(). Sometimes, both are needed.
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.
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();
?>
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
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.
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.
I'm having some problems with the output buffer. I am buffering my script and printing the result using a callback. The problem is that if a error is thrown at any point, nothing is being shown and I am getting a blank screen. I have tried setting my own custom error handlers but nothing seems to work. I have a feeling this is because the errors are causing my buffer to call the callback method instead of my error handler. Either that or it's because I have the error handler as a static method, but changing that causes issues elsewhere.
I'd really appreciate any help because this one has me stumped!
public function constructor()
{
ob_start(array(__CLASS__, 'render'));
self::$buffer_level = ob_get_level();
set_error_handler(array(__CLASS__, 'exception_handler'));
set_exception_handler(array(_C_LASS__, 'exception_handler'));
RUNNING MY SCRIPT HERE
ob_end_flush();
}
public static function exception_handler($exception, $message = NULL, $file = NULL, $line = NULL)
{
while (ob_get_level() > self::$buffer_level)
{
ob_end_clean();
}
echo $exception.' - '.$message.' - '.$file.' - '.$line.'<br/>';
}
_C_LASS__
Looks like you misplaced a _ there
Damn, that is actually a typo which isn't in the script, so that isn't what is causing the problem. Good spot though!
I'm having some problems with the output buffer. I am buffering my script and printing the result using a callback. The problem is that if a error is thrown at any point, nothing is being shown and I am getting a blank screen. I have tried setting my own custom error handlers but nothing seems to work. I have a feeling this is because the errors are causing my buffer to call the callback method instead of my error handler. Either that or it's because I have the error handler as a static method, but changing that causes issues elsewhere.
I'd really appreciate any help because this one has me stumped!
public function constructor()
{
ob_start(array(__CLASS__, 'render'));
self::$buffer_level = ob_get_level();
set_error_handler(array(__CLASS__, 'exception_handler'));
set_exception_handler(array(_CLASS__, 'exception_handler'));
RUNNING MY SCRIPT HERE
ob_end_flush();
}
public static function exception_handler($exception, $message = NULL, $file = NULL, $line = NULL)
{
while (ob_get_level() > self::$buffer_level)
{
ob_end_clean();
}
echo $exception.' - '.$message.' - '.$file.' - '.$line.'<br/>';
}
I would suggest turning on error logging in PHP which will send errors to the apache error log by default. You could also try turning on track_errors, but I think the log is the best bet. If you don't have access to the apache log, you might have to log things manually.
Log files and tracing strategies are essential when using output buffering and other "behind the scenes" stuff (like ajax).
You might also have a look at the output_buffering setting. See this article: http://thinkpositivesoftware.blogspot.com/2009/03/have-blank-php-page.html