1<?php 2/** 3 * Provides methods used to handle error reporting. 4 * 5 * Copyright 2012-2017 Horde LLC (http://www.horde.org/) 6 * 7 * See the enclosed file COPYING for license information (LGPL). If you 8 * did not receive this file, see http://www.horde.org/licenses/lgpl21. 9 * 10 * @author Michael Slusarz <slusarz@horde.org> 11 * @category Horde 12 * @license http://www.horde.org/licenses/lgpl21 LGPL-2.1 13 * @package Core 14 */ 15class Horde_ErrorHandler 16{ 17 /** 18 * Aborts with a fatal error, displaying debug information to the user. 19 * 20 * @param mixed $error Either a string or an object with a getMessage() 21 * method (e.g. PEAR_Error, Exception). 22 */ 23 public static function fatal($error) 24 { 25 global $registry; 26 27 if (is_object($error)) { 28 switch (get_class($error)) { 29 case 'Horde_Exception_AuthenticationFailure': 30 $auth_app = !$registry->clearAuthApp($error->application); 31 32 if ($auth_app && 33 $registry->isAuthenticated(array('app' => $error->application, 'notransparent' => true))) { 34 break; 35 } 36 37 try { 38 Horde::log($error, 'NOTICE'); 39 } catch (Exception $e) {} 40 41 if (Horde_Cli::runningFromCLI()) { 42 $cli = new Horde_Cli(); 43 $cli->fatal($error); 44 } 45 46 $params = array(); 47 48 if ($registry->getAuth()) { 49 $params['app'] = $error->application; 50 } 51 52 switch ($error->getCode()) { 53 case Horde_Auth::REASON_MESSAGE: 54 $params['msg'] = $error->getMessage(); 55 $params['reason'] = $error->getCode(); 56 break; 57 } 58 59 $logout_url = $registry->getLogoutUrl($params); 60 61 /* Clear authentication here. Otherwise, there might be 62 * issues on the login page since we would otherwise need 63 * to do session token checking (which might not be 64 * available, so logout won't happen, etc...) */ 65 if ($auth_app && array_key_exists('app', $params)) { 66 $registry->clearAuth(); 67 } 68 69 $logout_url->redirect(); 70 } 71 } 72 73 try { 74 Horde::log($error, 'EMERG'); 75 } catch (Exception $e) {} 76 77 try { 78 $cli = Horde_Cli::runningFromCLI(); 79 } catch (Exception $e) { 80 die($e); 81 } 82 83 if ($cli) { 84 $cli = new Horde_Cli(); 85 $cli->fatal($error); 86 } 87 88 if (!headers_sent()) { 89 header('Content-type: text/html; charset=UTF-8'); 90 } 91 echo <<< HTML 92<html> 93<head><title>Horde :: Fatal Error</title></head> 94<body style="background:#fff; color:#000"> 95HTML; 96 97 ob_start(); 98 try { 99 $admin = (isset($registry) && $registry->isAdmin()); 100 101 echo '<h1>' . Horde_Core_Translation::t("A fatal error has occurred") . '</h1>'; 102 103 if (is_object($error) && method_exists($error, 'getMessage')) { 104 echo '<h3>' . htmlspecialchars($error->getMessage()) . '</h3>'; 105 } elseif (is_string($error)) { 106 echo '<h3>' . htmlspecialchars($error) . '</h3>'; 107 } 108 109 if ($admin) { 110 if ($error instanceof Throwable || 111 $error instanceof Exception) { 112 $trace = $error; 113 $file = $error->getFile(); 114 $line = $error->getLine(); 115 } else { 116 $trace = debug_backtrace(); 117 $calling = array_shift($trace); 118 $file = $calling['file']; 119 $line = $calling['line']; 120 } 121 printf(Horde_Core_Translation::t("in %s:%d"), $file, $line); 122 echo '<div id="backtrace"><pre>' . 123 strval(new Horde_Support_Backtrace($trace)) . 124 '</pre></div>'; 125 if (is_object($error)) { 126 echo '<h3>' . Horde_Core_Translation::t("Details") . '</h3>'; 127 echo '<h4>' . Horde_Core_Translation::t("The full error message is logged in Horde's log file, and is shown below only to administrators. Non-administrative users will not see error details.") . '</h4>'; 128 ob_flush(); 129 flush(); 130 echo '<div id="details"><pre>' . htmlspecialchars(print_r($error, true)) . '</pre></div>'; 131 } 132 } else { 133 echo '<h3>' . Horde_Core_Translation::t("Details have been logged for the administrator.") . '</h3>'; 134 } 135 } catch (Exception $e) { 136 die($e); 137 } 138 139 ob_end_flush(); 140 echo '</body></html>'; 141 exit(1); 142 } 143 144 /** 145 * PHP legacy error handling (non-Exceptions). 146 * 147 * @param integer $errno See set_error_handler(). 148 * @param string $errstr See set_error_handler(). 149 * @param string $errfile See set_error_handler(). 150 * @param integer $errline See set_error_handler(). 151 * @param array $errcontext See set_error_handler(). 152 */ 153 public static function errorHandler($errno, $errstr, $errfile, $errline, 154 $errcontext) 155 { 156 $er = error_reporting(); 157 158 // Calls prefixed with '@'. 159 if ($er == 0) { 160 // Must return false to populate $php_errormsg (as of PHP 5.2). 161 return false; 162 } 163 164 if (!($er & $errno) || !class_exists('Horde_Log')) { 165 return; 166 } 167 168 $options = array(); 169 170 try { 171 switch ($errno) { 172 case E_WARNING: 173 case E_USER_WARNING: 174 case E_RECOVERABLE_ERROR: 175 $priority = Horde_Log::WARN; 176 break; 177 178 case E_NOTICE: 179 case E_USER_NOTICE: 180 $priority = Horde_Log::NOTICE; 181 break; 182 183 case E_STRICT: 184 $options['notracelog'] = true; 185 $priority = Horde_Log::DEBUG; 186 break; 187 188 default: 189 $priority = Horde_Log::DEBUG; 190 break; 191 } 192 193 Horde::log(new ErrorException('PHP ERROR: ' . $errstr, 0, $errno, $errfile, $errline), $priority, $options); 194 } catch (Exception $e) {} 195 } 196 197 /** 198 * Catch fatal errors. 199 */ 200 public static function catchFatalError() 201 { 202 $error = error_get_last(); 203 if ($error['type'] == E_ERROR) { 204 self::fatal(new ErrorException( 205 $error['message'], 206 0, 207 $error['type'], 208 $error['file'], 209 $error['line'] 210 )); 211 } 212 } 213 214} 215