/** * Handle an error and report it to the Core system log. * * @param $errno * @param $errstr * @param $errfile * @param $errline * @param null $errcontext */ function error_handler($errno, $errstr, $errfile, $errline, $errcontext = null){ $type = null; $fatal = false; $code = null; $class = ''; // The exception to this is when error_reporting is explictly set to 0. // This happens when a function is called with the "@" error suppressor. // In this event, I still want to log the error, but simply do not display it on the screen. // Damn f*****g "@" operator..... $suppressed = (error_reporting() === 0); switch($errno){ case E_ERROR: case E_USER_ERROR: $fatal = true; $type = 'error'; $class = 'error'; $code = 'PHP Error'; break; case E_WARNING: case E_USER_WARNING: $type = 'error'; $class = 'warning'; $code = 'PHP Warning'; break; case E_NOTICE: case E_USER_NOTICE: $type = 'info'; $class = 'info'; $code = 'PHP Notice'; break; case E_DEPRECATED: case E_USER_DEPRECATED: $type = 'info'; $class = 'deprecated'; $code = 'PHP Deprecated Notice'; break; case E_STRICT: $type = 'info'; $class = 'warning'; $code = 'PHP Strict Warning'; $suppressed = true; break; default: $type = 'info'; $class = 'unknown'; $code = 'Unknown PHP Error [' . $errno . ']'; break; } if($suppressed){ // Ignore suppressed errors when on production. // This is required because PHP < 7.0 has some functions that can only be called with the '@' operator. // Such as LDAP binding or many things in Smarty. if(!DEVELOPMENT_MODE){ return; } $code .= ' @SUPPRESSED'; } // All errors/warnings/notices get logged! if($errfile && strpos($errfile, ROOT_PDIR) === 0){ $details = '[src: ' . '/' . substr($errfile, strlen(ROOT_PDIR)) . ':' . $errline . '] '; } elseif($errfile){ $details = '[src: ' . $errfile . ':' . $errline . '] '; } else{ $details = ''; } try{ if(!\Core::GetComponent()){ // SQUAK! Core isn't even loaded yet! return; } // Allow external systems to hook into this event. \HookHandler::DispatchHook('/core/error_handler', $code, $errstr); $log = \SystemLogModel::Factory(); $log->setFromArray([ 'type' => $type, 'code' => $code, 'message' => $details . $errstr ]); $log->save(); } catch(\Exception $e){ // meh, try a traditional log. try{ if(class_exists('Core\\Utilities\\Logger\\LogFile')){ $log = new LogFile($type); $log->write($details . $errstr, $code); } else{ error_log($details . $errstr); } } catch(\Exception $e){ // Really meh now! } } // Display all errors when in development mode. if(DEVELOPMENT_MODE && !$suppressed){ // The correct way to handle output is via EXEC_MODE. // HOWEVER, since the unit tests emulate a WEB mode so that the scripts behave as they would in the web browser, // this is not a reliable test here. if(isset($_SERVER['TERM']) || isset($_SERVER['SHELL'])){ print_error_as_text($class, $code, $errstr); } elseif(EXEC_MODE == 'WEB'){ print_error_as_html($class, $code, $errstr); } else{ print_error_as_text($class, $code, $errstr); } } // If it's a fatal error and it's not in development mode, simply display a friendly error page instead. if($fatal){ if(EXEC_MODE == 'WEB'){ require(ROOT_PDIR . 'core/templates/halt_pages/fatal_error.inc.html'); } exit(); } }