Classes

File core/error/ErrorHandler.class.php

File core/error/ErrorHandler.class.php

  1:   2:   3:   4:   5:   6:   7:   8:   9:  10:  11:  12:  13:  14:  15:  16:  17:  18:  19:  20:  21:  22:  23:  24:  25:  26:  27:  28:  29:  30:  31:  32:  33:  34:  35:  36:  37:  38:  39:  40:  41:  42:  43:  44:  45:  46:  47:  48:  49:  50:  51:  52:  53:  54:  55:  56:  57:  58:  59:  60:  61:  62:  63:  64:  65:  66:  67:  68:  69:  70:  71:  72:  73:  74:  75:  76:  77:  78:  79:  80:  81:  82:  83:  84:  85:  86:  87:  88:  89:  90:  91:  92:  93:  94:  95:  96:  97:  98:  99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128: 129: 130: 131: 132: 133: 134: 135: 136: 137: 138: 139: 140: 141: 142: 143: 144: 145: 146: 147: 148: 149: 150: 151: 152: 153: 154: 155: 156: 157: 158: 159: 160: 161: 162: 163: 164: 165: 166: 167: 168: 169: 170: 171: 172: 173: 174: 175: 176: 177: 178: 179: 180: 181: 182: 183: 184: 185: 186: 187: 188: 189: 190: 191: 192: 193: 194: 195: 196: 197: 198: 199: 200: 201: 202: 203: 204: 205: 206: 207: 208: 209: 210: 211: 212: 213: 214: 215: 216: 217: 218: 219: 220: 221: 222: 223: 224: 225: 226: 227: 228: 229: 230: 231: 232: 233: 234: 235: 236: 237: 
<?php
/**
 * @package     Core
 * @subpackage  Error
 * @copyright   &copy; 2005-2019 PHPBoost
 * @license     https://www.gnu.org/licenses/gpl-3.0.html GNU/GPL-3.0
 * @author      Benoit SAUTEL <ben.popeye@phpboost.com>
 * @version     PHPBoost 5.2 - last update: 2018 01 31
 * @since       PHPBoost 3.0 - 2009 09 30
 * @contributor Loic ROUCHON <horn@phpboost.com>
 * @contributor Arnaud GENET <elenwii@phpboost.com>
 * @contributor mipel <mipel@phpboost.com>
 * @contributor Julien BRISWALTER <j1.seth@phpboost.com>
*/

class ErrorHandler
{
    const FATAL_MESSAGE = 'Sorry, we encountered a problem and we cannot complete your request...';

    /**
     * @var int the maximum size of the error log file in bytes
     */
    private static $LOG_FILE_MAX_SIZE = 1048576;

    protected $errno;
    protected $errfile;
    protected $errline;
    protected $errdesc;
    protected $errclass;
    protected $fatal;
    protected $stacktrace;
    protected $exception;

    /**
     * log the error and displays it in debug mode
     * @param unknown_type $errno contains the level of the error raised, as an integer
     * @param unknown_type $errstr contains the error message, as a string
     * @param unknown_type $errfile the filename that the error was raised in, as a string
     * @param unknown_type $errline the line number the error was raised at, as an integer
     * @return bool always true because we don't want the php default error handler to process the
     * error again
     */
    public function handle($errno, $errstr, $errfile, $errline)
    {
        if ($this->needs_to_be_processed($errno))
        {
            $this->prepare($errno, $errstr, $errfile, $errline);
            $this->process();
            $this->display();
            $this->log();
            if ($this->fatal || Debug::is_strict_mode_enabled())
            {
                exit;
            }
        }
        return true;
    }

    private function prepare($errno, $errstr, $errfile, $errline)
    {
        $this->exception  = new Exception($errstr);
        $this->errno      = $errno;
        $this->errfile    = $errfile;
        $this->errline    = $errline;
        $this->stacktrace = '';
        $this->errdesc    = '';
        $this->errclass   = '';
        $this->fatal      = false;

    }

    /**
     * @return boolean true if the error is not thrown by a functionprefixed with an @ and if the
     * errno is in the ERROR_REPORTING level
     */
    private function needs_to_be_processed($errno)
    {
        return error_reporting() != 0 && ($errno & ERROR_REPORTING);
    }

    private function process()
    {
        switch ($this->errno)
        {
            case E_USER_NOTICE:
            case E_NOTICE:
                $this->errdesc = 'Notice';
                $this->errclass =  'notice';
                break;
                //Warning utilisateur.
            case E_USER_WARNING:
            case E_WARNING:
                $this->errdesc = 'Warning';
                $this->errclass =  'warning';
                break;
                //Strict standards
            case E_STRICT:
                $this->errdesc = 'Strict Standards';
                $this->errclass =  'notice';
                break;
                //Erreur fatale.
            case E_USER_ERROR:
            case E_ERROR:
            case E_RECOVERABLE_ERROR:
                $this->fatal = true;
                $this->errdesc = 'Fatal Error';
                $this->errclass =  'error';
                break;
            default:
                $this->errdesc = 'Unknown Error';
                $this->errclass =  'question';
                break;
        }
    }

    private function display()
    {
        AppContext::get_response()->set_status_code(503);

        if ($this->fatal)
        {
            AppContext::get_response()->clean_output();
            if (Debug::is_debug_mode_enabled())
            {
                Debug::fatal($this->exception);
            }
            else
            {
                Environment::init_output_bufferization();
                echo self::FATAL_MESSAGE;
                Environment::destroy();
                exit;
            }
        }
        elseif (Debug::is_debug_mode_enabled())
        {
            $this->display_debug();
        }
    }

    protected function get_stackstrace_as_string($start_trace_index) {
        $stack = '[0] ' . Path::get_path_from_root($this->errfile) . ':' . $this->errline;
        if (count($this->exception->getTrace()) > 2)
        {
            $stack .= (Debug::is_output_html() ? '<br />' : "\n");
            $stack .= Debug::get_stacktrace_as_string($start_trace_index);
        }
        return $stack;
    }

    protected function display_debug()
    {
        echo '<div class="message-helper ' . $this->errclass . ' error-handler">
                <strong>' . $this->errdesc . ' : </strong>' . $this->exception->getMessage() . '<br /><br /><br />
                <em>' . $this->get_stackstrace_as_string(6) . '</em>
              </div>';
    }

    protected function display_fatal()
    {
        $this->display_debug();
    }

    private function log()
    {
        self::add_error_in_log($this->exception->getMessage(), $this->get_stackstrace_as_string(4), $this->errno);
    }

    public static function add_error_in_log($error_msg, $error_stacktrace, $errno = 0)
    {
        $error_log_file = PATH_TO_ROOT . '/cache/error.log';
        self::clear_error_log_file($error_log_file);
        self::add_error_in_log_file($error_log_file, $error_msg, $error_stacktrace, $errno);
    }

    private static function clear_error_log_file($log_file)
    {
        if (file_exists($log_file) && filesize($log_file) > self::$LOG_FILE_MAX_SIZE)
        {
            $handle = @fopen($log_file, 'w+');
            @ftruncate($handle, 0);
            @fclose($handle);
        }
    }

    private static function add_error_in_log_file($log_file, $error_msg, $error_stacktrace, $errno = 0)
    {
        $handle = @fopen($log_file, 'a+');
        $write = @fwrite($handle, self::compute_error_log_string($error_msg, $error_stacktrace, $errno));
        $close = @fclose($handle);

        if ($handle === false || $write === false || $close === false)
        {
            echo '<span id="message_handler">Can\'t write error to log file</span>';
        }
    }

    private static function compute_error_log_string($error_msg, $error_stacktrace, $errno = 0)
    {
        return Date::to_format(time(), 'Y-m-d H:i:s', Timezone::SERVER_TIMEZONE) . "\n" .
        $errno . "\n" .
        self::clean_error_string($error_msg) . "\n" .
        self::clean_error_string($error_stacktrace) . "\n";
    }

    private static function clean_error_string($message)
    {
        return preg_replace("`(\n)+`u", '<br />', preg_replace("`\r|\n|\t`u", "\n", $message));
    }

    /**
     * Get Error type
     */
    public static function get_errno_class($errno)
    {
        switch ($errno)
        {
                //Notice utilisateur.
            case E_USER_NOTICE:
            case E_NOTICE:
                return 'notice';
                //Warning utilisateur.
            case E_USER_WARNING:
            case E_WARNING:
                return 'warning';
                //Erreur fatale.
            case E_USER_ERROR:
            case E_ERROR:
            case E_RECOVERABLE_ERROR:
                return 'error';
            default: //Erreur inconnue.
                return 'question';
         }
    }
}
?>