<?php

namespace SYSTEM\LOG;

class LOG {
    
    //Singleton
    private $handlers = array();    
    private static $instance = null;    
    public static function getInstance(){
        if (null === self::$instance) {
            self::$instance = new self;}
        return self::$instance;        
    }
    private function __construct(){}
    private function __clone(){}
    
    //$handler = string with classname
    public function registerHandler($handler){       
        if( !class_exists($handler) ||
            !\method_exists($handler,'MASK') ||
            !\method_exists($handler,'CALL')){            
             die("You registered an invalid Errorhandler!");}        
        $this->handlers[] = $handler;
        
        set_error_handler('\SYSTEM\LOG\LOG::__error_handler');
        set_exception_handler('\SYSTEM\LOG\LOG::__exception_handler');        
    }
    
    private function call_handlers(\Exception $E, $errno, $thrown = false){        
        foreach($this->handlers as $handler){                        
            if( ((call_user_func(array($handler,'MASK')) & $errno)) &&
                call_user_func_array(array($handler,'CALL'),array($E,$errno, $thrown))){
                return true;
                }            
        }        
        return false;
    }
    
//Static          
    //Exceptions      
    public static function __exception_handler(\Exception $E, $thrown = true){
        if($E instanceof \SYSTEM\LOG\ERROR)     { return self::LOG_EXCEPTION($E, E_USER_ERROR, $thrown);}        //Dies
        if($E instanceof \SYSTEM\LOG\WARNING)   { return self::LOG_EXCEPTION($E, E_USER_WARNING, $thrown);}      //Continues
        if($E instanceof \SYSTEM\LOG\INFO)      { return self::LOG_EXCEPTION($E, E_NOTICE, $thrown);}            //Continues
        if($E instanceof \SYSTEM\LOG\DEPRECATED){ return self::LOG_EXCEPTION($E, E_USER_DEPRECATED, $thrown);}   //Continues
        self::LOG_EXCEPTION($E, E_USER_ERROR, $thrown);                                                          //Dies
    }    
    
    //Triggers error, if not handled by registered Handlers(double check -> but only this way we can keep the original Exception class)     
    protected static function LOG_EXCEPTION(\Exception $E,$errno = E_USER_ERROR, $thrown = false){
        self::__error_handler_e($E, $errno, $thrown);}
            
    //Errors
    public static function __error_handler($errno, $errstr, $errfile, $errline){                
        $e = new \SYSTEM\LOG\ERROR($errstr,$errno);        
        return self::__error_handler_e($e ,$errno, true);}
    public static function __error_handler_e(\Exception $E, $errno, $thrown){
        switch ($errno) {
            //Fatal run-time errors. These indicate errors that can not be recovered from, such as a memory allocation problem. Execution of the script is halted.
            case E_ERROR:               return self::_DIE($E,$errno,true);
            //Run-time warnings (non-fatal errors). Execution of the script is not halted.  
            case E_WARNING:             return self::_CONTINUE($E,$errno,true);
            //Compile-time parse errors. Parse errors should only be generated by the parser.
            case E_PARSE:               return self::_DIE($E,$errno,true);
            //Run-time notices. Indicate that the script encountered something that could indicate an error, but could also happen in the normal course of running a script.               
            case E_NOTICE:              return self::_CONTINUE($E,$errno,false | $thrown);
            //Fatal errors that occur during PHP's initial startup. This is like an E_ERROR, except it is generated by the core of PHP.
            case E_CORE_ERROR:          return self::_DIE($E,$errno,true);
            //Warnings (non-fatal errors) that occur during PHP's initial startup. This is like an E_WARNING, except it is generated by the core of PHP.     
            case E_CORE_WARNING:        return self::_CONTINUE($E,$errno,true);
            //Fatal compile-time errors. This is like an E_ERROR, except it is generated by the Zend Scripting Engine.     
            case E_COMPILE_ERROR:       return self::_DIE($E,$errno,true);
            //Compile-time warnings (non-fatal errors). This is like an E_WARNING, except it is generated by the Zend Scripting Engine.     
            case E_COMPILE_WARNING:     return self::_CONTINUE($E,$errno,true);
            //User-generated error message. This is like an E_ERROR, except it is generated in PHP code by using the PHP function trigger_error().     
            case E_USER_ERROR:          return self::_DIE($E,$errno,false | $thrown);
            //User-generated warning message. This is like an E_WARNING, except it is generated in PHP code by using the PHP function trigger_error().        
            case E_USER_WARNING:        return self::_CONTINUE($E,$errno,false | $thrown);
            //Enable to have PHP suggest changes to your code which will ensure the best interoperability and forward compatibility of your code.    Since PHP 5 but not included in E_ALL until PHP 5.4.0                                                                                        rated in PHP code by using the PHP function trigger_error().     
            case E_STRICT:              return self::_CONTINUE($E,$errno,true);
            //Catchable fatal error. It indicates that a probably dangerous error occurred, but did not leave the Engine in an unstable state. If the error is not caught by a user defined handle (see also set_error_handler()), the application aborts as it was an E_ERROR.    Since PHP 5.2.0
            case E_RECOVERABLE_ERROR:   return self::_CONTINUE($E,$errno,true);
            //Run-time notices. Enable this to receive warnings about code that will not work in future versions.    Since PHP 5.3.0
            case E_DEPRECATED:          return self::_CONTINUE($E,$errno,true);
            //User-generated warning message. This is like an E_DEPRECATED, except it is generated in PHP code by using the PHP function trigger_error().    Since PHP 5.3.0
            case E_USER_DEPRECATED:     return self::_CONTINUE($E,$errno,false | $thrown);
            default:                    return false; //Not handled Error use std php handler
        }
    }
            
    private static function _DIE(\Exception $E, $errno, $thrown = false){
        if(self::getInstance()->call_handlers($E, $errno, $thrown)){die();}}
    private static function _CONTINUE(\Exception $E, $errno, $thrown = false){
        return self::getInstance()->call_handlers($E, $errno, $thrown);}
}