Jump to content

fatal error handling


Destramic

Recommended Posts

hey guys i read about being able to catch E_ERROR's using:

register_shutdown_function('fatal_error_catcher');

function fatal_error_catcher()
{
        $last_error = error_get_last();
        
        if ($last_error['type'] === E_ERROR) 
        {
            print_r($last_error);
        }
}

now I've tried to test it when triggering but it'll just show a warning message

trigger_error('test',  E_ERROR);

my questions are

 

1. is it even possible to catch a fatal error?...as a fatal error would be caused by the PHP engine itself, which makes me think it  wouldn't execute this error catcher?

 

2. or is it not catching because you can't manual trigger a E_ERROR.

 

some advise on this would be helpful thank you

 

Link to comment
Share on other sites

I'm going to guess it's either cause you can't trigger it that way or because you're using print_r() on the error result.  I use 3 error handlers in my cms to db log anything that happens, including fatal errors.

 

This is my error handling function page

// Strips the SERVER HOST to main domain name
function stripHost()
{
	if(!empty($_SERVER['HTTP_HOST']))
	{
		if(substr($_SERVER['HTTP_HOST'], 0, 4) == "www.")
		{$server = htmlentities(substr($_SERVER['HTTP_HOST'], 4), ENT_QUOTES);}
		else {$server = htmlentities($_SERVER['HTTP_HOST'], ENT_QUOTES);}
	}
	else{ $server = $_SERVER['SERVER_NAME']; }
	
	return $server;
}

// Determines if the host is your development localhost
function isLocalhost()
{
	if(strpos(stripHost(), 'localhost') !== FALSE)
	{ return TRUE; }
	else{ return FALSE; }
}

// Attempts to get an accurate IP address from the client
function get_ip_address() {
    $ip_keys = array('HTTP_CLIENT_IP', 'HTTP_X_FORWARDED_FOR', 'HTTP_X_FORWARDED', 'HTTP_X_CLUSTER_CLIENT_IP', 'HTTP_FORWARDED_FOR', 'HTTP_FORWARDED', 'REMOTE_ADDR');
    foreach ($ip_keys as $key) {
        if (array_key_exists($key, $_SERVER) === true) {
            foreach (explode(',', $_SERVER[$key]) as $ip) {
                // trim for safety measures
                $ip = trim($ip);
                // attempt to validate IP
                if (validate_ip($ip)) {
                    return $ip;
                }
            }
        }
    }

    return isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : false;
}


/**
 * Ensures an ip address is both a valid IP and does not fall within
 * a private network range.
 */
function validate_ip($ip)
{
    if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) === false) {
        return false;
    }
    return true;
}

function checkUserAgent()
{
	$agent = (!empty($_SERVER['HTTP_USER_AGENT'])) ? $_SERVER['HTTP_USER_AGENT'] : ''; 

	if(!empty($agent))
	{
		$apikey = '92a7f5ba';
		$useragent = urlencode($agent);
		$request = file_get_contents("http://useragentapi.com/api/v2/json/$apikey/$useragent");
		$result = json_decode($request, true);
	
		return $result;
	}	
}

// Custom error handler
function cmsErrorHandler($code, $message, $file, $line)
{
	$code = (int)$code;
	$line = (int)$line;
	$user_agent = checkUserAgent();
	$fields = ['platform_type', 'platform_name', 'platform_version', 'browser_name', 'browser_version', 'engine_name', 'engine_version'];
	
	if(!empty($user_agent))
	{
		foreach($user_agent as $key => $val)
		{
			if(in_array($key, $fields))
			{ $agent[$key] = $val; }
		}
	}
	
	$last_page = (isset($_SESSION['last_page_queried'])) ? $_SESSION['last_page_queried'] : '';
	
	$params = [
		'error_type' => [$code, 'INT'],
		'error_time' => date("Y-m-d H:i:s"),
		'error_string' => $message,
		'error_file' => $file,
		'error_line' => [$line, 'INT'],
		'error_ip' => get_ip_address(),
		'queried_page' => $last_page
	];
	
	$params = (!empty($agent)) ? array_merge($params, $agent) : $params;
	
	DB::getInstance()->insert('cms_errors', $params);
}

function cmsExceptionErrorHandler($e)
{
	cmsErrorHandler($e->getCode(), $e->getMessage(), $e->getFile(), $e->getLine());
}

// Custom Fatal error handler
function cmsFatalErrorShutdownHandler()
{
	$last_error = error_get_last();
	if ($last_error['type'] === E_ERROR) 
	{
		// fatal error
		cmsErrorHandler(E_ERROR, $last_error['message'], $last_error['file'], $last_error['line']);
		
		header('Location: http://'.stripHost().'/500.html');
	}
}

And this is the set handlers

if(isLocalhost() === FALSE)
{
    // Setup custom error_handlers
    set_error_handler('cmsErrorHandler');
    set_exception_handler('cmsExceptionErrorHandler');
    register_shutdown_function('cmsFatalErrorShutdownHandler');    
}

One note, the header() in the fatal error function will only work if the error occurs before any output to the browser, typical cause of the buffer.  I made a stripped down 500.html page to redirect to if it's able to.

Link to comment
Share on other sites

yeah i've used mine to catch exceptions also...although i do like the redirect to 500 error page if occurs...the problem is with my code is that when i try to get the last error

print_r(error_get_last());

it returns nothing even when there's a error....any idea why?

Link to comment
Share on other sites

Thinking, I believe it won't echo anything out cause with a fatal error, the parser stops immediately when the error happens.  So it can't output anything after that point, it can perform some other things, but output is a no go.

Link to comment
Share on other sites

here's what i've made just need to add database now...the one problem i did see though is if using a PHP vesion > 5.3  then you're unable to use the

error_get_last():

function...therefor being unable to capture any fatal errors which sucks!

<?php

class Error_Handler
{
    protected $_handler;

    protected static $_error_reporting;
    
    public function __construct($handler = "Unkown")
    {
        set_exception_handler(array($this, 'exception_catcher'));
        set_error_handler(array($this, 'error_catcher'));
        
        $this->_handler = $handler;
    }
    
    public function set_error_reporting($error_reporting = true)
    {    
        if ($error_reporting !== false)
        {
            ini_set('display_startup_errors', 1);
            ini_set('display_errors', 1);
            error_reporting(E_ALL);
            
            self::$_error_reporting = true;
        }
        else
        {
            ini_set('display_startup_errors', 0);
            ini_set('display_errors', 0);
            error_reporting(0);
            
            self::$_error_reporting = false;
        }
    }
    
    public function debug($name, $data)
    {
        $data = $this->desensitize_data($data);
        
        if (self::$_error_reporting === true)
        {
            if (is_array($data) || 
               is_object($data))
            {
                $data = json_encode($data);
            }
            
            echo "<script>console.log('PHP: " . $name . " - " .  $data . "');</script>";
        }
        else
        {
            echo "log debug in db.";
        }
    }
    
    public function log($message, $data)
    {  
        $handler = $this->_handler;
       
        if ($data instanceof Exception)
        {
            $traces = $this->trace($data->getTrace());
            
            // log traces
            // log exception
                        
        }
        else
        {
            $traces = debug_backtrace();
            $traces = array_slice($traces, 3);          
            $traces = $this->trace($traces);
            
            // log traces
            // log exception
        }
    }
    
    public function exception_catcher($exception)
    {        
        $this->log('Uncaught Exception', $exception);
    }
    
    public function error_catcher($error_number, $message, $filename, $line, $parameters)
    {       
        switch ($error_number) 
        {
            case "2":
                $type = "E_WARNING";
            break;

            case "8":
                $type = "E_NOTICE";
            break;
            
            case "256":
                $type = "E_USER_ERROR";
            break;
        
            case "512":
                $type = "E_USER_WARNING";
            break;
        
            case "1024":
                $type = "E_USER_NOTICE";
            break;
        
            case "16384":
                $type = "E_USER_DEPRECATED";
            break;
            
            default:
                $type = "Unknown";
            break;
        }

        if (empty($parameters))
        {
            $parameters = null;
        }
        else
        {
            $parameters = json_encode($parameters);
        }
        
        $this->log($type, array('filename'     => $filename,
                                'line'         => $line,
                                'message'      => $message,
                                'error_number' => $error_number,
                                'type'         => $type,
                                'parameters'   => $parameters
        ));
    }
    
    protected function trace($data)
    { 
        $trace_count = count($data);
        $traces      = array();

        for ($i = 0; $i < $trace_count; $i++)
        {                   
            if (isset($data[$i]['class']))
            {
                $function = $data[$i]['class'] . $data[$i]['type'] . $data[$i]['function'];
            }
            else
            {
                $function = $data[$i]['function'];
            }
            
            $function       .= "(";               
            $paramters      = $data[$i]['args'];
            $paramter_count = count($paramters);
            $j              = 1;
                            
            foreach ($paramters as $parameter)
            {
                $function .= "'" . $parameter . "'";
                                
                if ($paramter_count !== $j)
                {
                    $function .= ", ";
                }
                                
                $j++;
            }
                            
            $function .= ");";
            $file      = $data[$i]['file'];
            $line      = $data[$i]['line'];

            $traces[] = array ('function' => $function,
                               'file'     => $file,
                               'line'     => $line
            );
        }   
        
        return $traces;
    }
    
    public function desensitize_data(&$data, $key = null)
    {
        if (is_array($data))
        {
            array_walk_recursive($data, array($this, 'desensitize_data'));
        }
        else if (preg_match('/(password|pass|pwd|pw)/', $key))
        {
            $data = "*****";
        }
    
        return $data;
    }
}
Link to comment
Share on other sites

You can't use E_ERROR in trigger_error(). You can only use user-space errors, which are: E_USER_NOTICE, E_USER_ERROR, and E_USER_WARNING. The snippet in your first post is indeed how you capture a fatal error, but you're not triggering a fatal error. Just throw an uncaptured exception and I think that will trigger it.

Link to comment
Share on other sites

Yes it does. I'm running 5.6.7-1 and it works fine.

 

<?php

register_shutdown_function('fatal_error_catcher');

function fatal_error_catcher()
{
    $last_error = error_get_last();

    if ($last_error['type'] === E_ERROR)
    {
        echo '<pre>';
        echo print_r($last_error, true);
        echo '</pre>';
    }
}

throw new Exception('test');
Returns:

 

Array
(
    [type] => 1
    [message] => Uncaught exception 'Exception' with message 'test' in /var/www/phpfreaks/error.php:18
Stack trace:
#0 {main}
  thrown
    [file] => /var/www/phpfreaks/error.php
    [line] => 18
)
Link to comment
Share on other sites

  • 2 weeks later...

yeah that worked a charm thank you....after a bit more reading i discovered setting the register_shutdown_function() in my class seemed to have been the problem.

 

is there, or does anyone know how to use the register_shutdown_function() from a method please?

<?php

class Error_Handler
{
    protected $_handler;
    
    public function __construct()
    {
        register_shutdown_function(array($this, 'fatal_error_catcher'));
    }
    
    public function fatal_error_catcher()
    {
        $last_error = error_get_last();
                
        if ($last_error['type'] === E_ERROR)
        {
            echo "yes";
        }
    }
}

$error_handler = new Error_Handler;

throw new Exception('test');
Link to comment
Share on other sites

This thread is more than a year old. Please don't revive it unless you have something important to add.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.