views:

46

answers:

2

I am about to start implementing error handling into my framework and looking for some advice on how to build it.

Firstly let me explain how my framework is currently built up:

Im separating the framework startup from the application startup, so any errors caused within the application startup should be handled specifically by a class dedicated to that.

My idea is to have a class called Core_Error_exception witch will set the error reporting to E_ALL as my framework will be strict on errors for PHP 5.3, then as my as my application load I will run a shutdown function function within that class to restore all default values changed.

What im looking to do is capture all errors that are E_*_NOTICE and not E_*_ERROR and then before the application starts I tell the class to stop capturing errors as the Application_Error_Exception will be watching out for errors.

So i will need a way to track all errors including exceptions and triggers and then before the application initializes show a framework debug page.

The kind of class I was looking for is like so:

class Core_Error_Exception
{
    var $previus_error_level,$captured_contents;

    private $stack_trace = array();

    public function Core_Error_Exception()
    {
        $this->previus_error_level = error_reporting(-1);
        set_error_handler(array($this,'_collect_error'));
        set_exception_handler(array($this,'_collect_error'));
        ob_start(array($this,'_capture'));
    }

    public function _collect_error($errno, $errstr, $errfile, $errline, $context)
    {
        $this->stack_trace[] = array(
            array('name' => 'Error ID:',    'value' => $errno),
            array('name' => 'Error String:','value' => $errstr),
            array('name' => 'Error File:',  'value' => $errfile),
            array('name' => 'Error Line:',  'value' => $errline),
            array('name' => 'Context PRE:', 'value' => $context)
        );
        var_dump($this->stack_trace);
    }

    /*
     * _capture is used to capture pre_bufferd content.
     */
    public function _capture($content,$bitfeild)
    {
        if($bitfeild & PHP_OUTPUT_HANDLER_START)
        {
            $this->captured_contents = $content;
        }

        if($bitfeild & PHP_OUTPUT_HANDLER_CONT)
        {
            $this->captured_contents .= $content;
        }

        if($bitfeild & PHP_OUTPUT_HANDLER_END)
        {
            $this->captured_contents .= $content;
        }
        return false;
    }
}

So what im looking to do is to be able to construct this class in a fall-proof way so that any notice errors that may have been triggered will be placed into an array, if an E_ERROR notice is called then this automatically run the shut-down at that point, to prevent more errors being caused.

I will be using a small html template handler where I can pass into that context sets of errors, so Notice errors and a single E_*_ERROR if applicable.

Whats the best way to build this class as in the past I have had some difficulty in doing Error Tracking / Reporting.

Updated: with current class

If errors are triggered such as trigger_error('test',XXX); I want to be able to track all errors until the application launches or an E_USER_ERROR has been triggered.

Sometimes I struggle to fully grasp PHP's error system, and so on as sometimes i get confused in how to build it so its fall-proof.

+1  A: 

I'm not quite sure about what you're doing, but the most straightforward way would be to use nested try blocks, along the lines of

in Class Application:

    function run() {
        try {
            --do stuff
        } catch(AppException $e) {
            -- handle application-level exception
        }
        -- all other exceptions fall through


in Class Core:

    try {
        $core->init();
        $application->run(); <-- calls the above function
        $core->done();
    } catch(Exception $e) {
        --last chance exception handler
        --process exceptions the Application was unable to handle on its own
    }

To be able to catch php built-in errors or trigger_error events this way, you also should always install an errors-to-exceptions handler.

stereofrog
Thumbs up, thats the most robust way to do that.
Steffen Müller
but this is only for people who understand Exceptions, a lot of users do not so I am also tracking errors that may are not captured, or even triggered by `trigger_error`
RobertPitt
@Robert, good point, see update.
stereofrog
ive updated my my code to see if you can get a better understanding of what im trying to accomplish, thanks for your responses so far.
RobertPitt
A: 

What you have looks pretty solid. You would have to add some logic so when the error is thrown that shuts down your script you flush output in the ob buffer and send all the collected data to your error HTML file to display the trace. Something like:

public function _collect_error($errno, $errstr, $errfile, $errline, $context)
{
    $this->stack_trace[] = array(
        array('name' => 'Error ID:',    'value' => $errno),
        array('name' => 'Error String:','value' => $errstr),
        array('name' => 'Error File:',  'value' => $errfile),
        array('name' => 'Error Line:',  'value' => $errline),
        array('name' => 'Context PRE:', 'value' => $context)
    );

    if($errno == E_USER_ERROR) {
        ob_clean();
        // Pass collected data to HTML template
        // Display HTML
        exit();
    }

    var_dump($this->stack_trace);
}

I'm not 100% certain you can gracefully recover from a fatal error, but from what you are saying you are just looking for specific non-fatal errors to shut down processing.

Also what is your intent with capturing buffered content? I would assume it's either displayed if the error your are looking for is never hit or its thrown out and an error screen is shown (why I added ob_clean in the error function)?

methodin
ob_clean is just to secure errors such as whit space and pre sent text from libraries un till the application start up is initiated.
RobertPitt
Correct. Meant ob_end_clean.
methodin
yea just a small oversight, think ove clocked it: http://pastebin.com/2kWyDcNJ and just execute the the start(); before loading all core, adn stop(); before the application kicks in. works great so far :)
RobertPitt