mirror of
https://github.com/EGroupware/egroupware.git
synced 2024-12-01 04:13:28 +01:00
199 lines
6.9 KiB
PHP
Executable File
199 lines
6.9 KiB
PHP
Executable File
<?php
|
|
/**
|
|
* EGroupware exception handler and friends
|
|
*
|
|
* Usually loaded via header.inc.php or api/src/loader/common.php
|
|
*
|
|
* @link http://www.egroupware.org
|
|
* @author Ralf Becker <RalfBecker-AT-outdoor-training.de>
|
|
* @package api
|
|
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
|
|
* @version $Id$
|
|
*/
|
|
|
|
use EGroupware\Api;
|
|
|
|
/**
|
|
* Translate message only if translation object is already loaded
|
|
*
|
|
* This function is usefull for exception handlers or early stages of the initialisation of the egw object,
|
|
* as calling lang would try to load the translations, evtl. cause more errors, eg. because there's no db-connection.
|
|
*
|
|
* @param string $key message in englich with %1, %2, ... placeholders
|
|
* @param string $vars =null multiple values to replace the placeholders
|
|
* @return string translated message with placeholders replaced
|
|
*/
|
|
function try_lang($key,$vars=null)
|
|
{
|
|
static $varnames = array('%1','%2','%3','%4');
|
|
|
|
if(!is_array($vars))
|
|
{
|
|
$vars = func_get_args();
|
|
array_shift($vars); // remove $key
|
|
}
|
|
return class_exists('EGroupware\Api\Translations',false) ? Api\Translation::translate($key,$vars) : str_replace($varnames,$vars,$key);
|
|
}
|
|
|
|
/**
|
|
* Clasify exception for a headline and log it to error_log, if not running as cli
|
|
*
|
|
* @param Exception|Error $e
|
|
* @param string &$headline
|
|
*/
|
|
function _egw_log_exception($e,&$headline=null)
|
|
{
|
|
$trace = explode("\n", $e->getTraceAsString());
|
|
if ($e instanceof Api\Exception\NoPermission)
|
|
{
|
|
$headline = try_lang('Permission denied!');
|
|
}
|
|
elseif ($e instanceof Api\Db\Exception)
|
|
{
|
|
$headline = try_lang('Database error');
|
|
}
|
|
elseif ($e instanceof Api\Exception\WrongUserinput)
|
|
{
|
|
$headline = ''; // message contains the whole message, it's usually no real error but some input validation
|
|
}
|
|
elseif ($e instanceof egw_exception_warning)
|
|
{
|
|
$headline = 'PHP Warning';
|
|
array_shift($trace);
|
|
}
|
|
else
|
|
{
|
|
$headline = try_lang('An error happened');
|
|
}
|
|
// log exception to error log, if not running as cli,
|
|
// which outputs the error_log to stderr and therefore output it twice to the user
|
|
if(isset($_SERVER['HTTP_HOST']) || $GLOBALS['egw_info']['flags']['no_exception_handler'] !== 'cli')
|
|
{
|
|
error_log($headline.($e instanceof egw_exception_warning ? ': ' : ' ('.get_class($e).'): ').$e->getMessage());
|
|
foreach($trace as $line)
|
|
{
|
|
error_log($line);
|
|
}
|
|
error_log('# Instance='.$GLOBALS['egw_info']['user']['domain'].', User='.$GLOBALS['egw_info']['user']['account_lid'].
|
|
', Request='.$_SERVER['REQUEST_METHOD'].' '.($_SERVER['HTTPS']?'https://':'http://').$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'].
|
|
', User-agent='.$_SERVER['HTTP_USER_AGENT']);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Fail a little bit more gracefully then an uncought exception
|
|
*
|
|
* Does NOT return
|
|
*
|
|
* @param Exception|Error $e
|
|
*/
|
|
function egw_exception_handler($e)
|
|
{
|
|
// handle redirects without logging
|
|
if ($e instanceof Api\Exception\Redirect)
|
|
{
|
|
Api\Egw::redirect($e->url, $e->app);
|
|
}
|
|
// logging all exceptions to the error_log (if not cli) and get headline
|
|
$headline = null;
|
|
_egw_log_exception($e,$headline);
|
|
|
|
// exception handler for cli (command line interface) clients, no html, no logging
|
|
if(!isset($_SERVER['HTTP_HOST']) || $GLOBALS['egw_info']['flags']['no_exception_handler'] == 'cli')
|
|
{
|
|
echo ($headline ? $headline.': ' : '').$e->getMessage()."\n";
|
|
if ($GLOBALS['egw_info']['server']['exception_show_trace'])
|
|
{
|
|
echo $e->getTraceAsString()."\n";
|
|
}
|
|
exit($e->getCode() ? $e->getCode() : 9999); // allways give a non-zero exit code
|
|
}
|
|
// regular GUI exception
|
|
if (!isset($GLOBALS['egw_info']['flags']['no_exception_handler']))
|
|
{
|
|
header('HTTP/1.1 500 '.$headline);
|
|
$message = '<h3>'.Api\Html::htmlspecialchars($headline)."</h3>\n".
|
|
'<pre><b>'.Api\Html::htmlspecialchars($e->getMessage())."</b>\n\n";
|
|
|
|
// only show trace (incl. function arguments) if explicitly enabled, eg. on a development system
|
|
if ($GLOBALS['egw_info']['server']['exception_show_trace'])
|
|
{
|
|
$message .= Api\Html::htmlspecialchars($e->getTraceAsString());
|
|
}
|
|
$message .= "</pre>\n";
|
|
if (is_a($e, 'EGroupware\Api\Db\Exception\Setup'))
|
|
{
|
|
$setup_dir = str_replace(array('home/index.php','index.php'),'setup/',$_SERVER['PHP_SELF']);
|
|
$message .= '<a href="'.$setup_dir.'">Run setup to install or configure EGroupware.</a>';
|
|
}
|
|
elseif (is_object($GLOBALS['egw']) && isset($GLOBALS['egw']->session) && method_exists($GLOBALS['egw'],'link'))
|
|
{
|
|
$message .= '<p><a href="'.$GLOBALS['egw']->link('/index.php').'">'.try_lang('Click here to resume your eGroupWare Session.').'</a></p>';
|
|
}
|
|
if (is_object($GLOBALS['egw']) && isset($GLOBALS['egw']->framework))
|
|
{
|
|
$GLOBALS['egw']->framework->render($message,$headline);
|
|
}
|
|
else
|
|
{
|
|
echo "<html>\n<head>\n<title>".Api\Html::htmlspecialchars($headline)."</title>\n</head>\n<body>\n$message\n</body>\n</html>\n";
|
|
}
|
|
}
|
|
// exception handler sending message back to the client as basic auth message
|
|
elseif($GLOBALS['egw_info']['flags']['no_exception_handler'] == 'basic_auth')
|
|
{
|
|
$error = str_replace(array("\r", "\n"), array('', ' | '), $e->getMessage());
|
|
header('WWW-Authenticate: Basic realm="'.$headline.' '.$error.'"');
|
|
header('HTTP/1.1 401 Unauthorized');
|
|
header('X-WebDAV-Status: 401 Unauthorized', true);
|
|
}
|
|
exit;
|
|
}
|
|
|
|
if (!isset($GLOBALS['egw_info']['flags']['no_exception_handler']) || $GLOBALS['egw_info']['flags']['no_exception_handler'] !== true)
|
|
{
|
|
set_exception_handler('egw_exception_handler');
|
|
}
|
|
|
|
/**
|
|
* Fail a little bit more gracefully then a catchable fatal error, by throwing an exception
|
|
*
|
|
* @param int $errno level of the error raised: E_* constants
|
|
* @param string $errstr error message
|
|
* @param string $errfile filename that the error was raised in
|
|
* @param int $errline line number the error was raised at
|
|
* @link http://www.php.net/manual/en/function.set-error-handler.php
|
|
* @throws ErrorException
|
|
*/
|
|
function egw_error_handler ($errno, $errstr, $errfile, $errline)
|
|
{
|
|
switch ($errno)
|
|
{
|
|
case E_RECOVERABLE_ERROR:
|
|
case E_USER_ERROR:
|
|
throw new ErrorException($errstr, $errno, 0, $errfile, $errline);
|
|
|
|
case E_WARNING:
|
|
case E_USER_WARNING:
|
|
// skip message for warnings supressed via @-error-control-operator (eg. @is_dir($path))
|
|
// can be commented out to get suppressed warnings too!
|
|
if (error_reporting() &&
|
|
// silence "Declaration of $class::$method should be compatible with $parent::$method" warning
|
|
!(substr($errstr, 0, 15) === 'Declaration of ' &&
|
|
strpos($errstr, ' should be compatible with ') !== false))
|
|
{
|
|
_egw_log_exception(new egw_exception_warning($errstr.' in '.$errfile.' on line '.$errline));
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Used internally to trace warnings
|
|
*/
|
|
class egw_exception_warning extends Exception {}
|
|
|
|
// install our error-handler only for catchable fatal errors and warnings
|
|
// following error types cannot be handled with a user defined function: E_ERROR, E_PARSE, E_CORE_ERROR, E_CORE_WARNING, E_COMPILE_ERROR, E_COMPILE_WARNING
|
|
set_error_handler('egw_error_handler', E_RECOVERABLE_ERROR|E_USER_ERROR|E_WARNING|E_USER_WARNING);
|