Drupal在配置阶段的最开始就设置了自己的错误处理器和异常处理器:

function _drupal_bootstrap_configuration() {
set_error_handler('_drupal_error_handler');
set_exception_handler('_drupal_exception_handler');
// ... ...
}

先来看看错误处理器_drupal_error_handler()是如何做的?

function _drupal_error_handler($error_level, $message, $filename, $line, $context) {
require_once DRUPAL_ROOT . '/includes/errors.inc';
_drupal_error_handler_real($error_level, $message, $filename, $line, $context);
}

调用了_drupal_error_handler_real()函数,该函数与当前error_reporting()比较,判断当前错误是否需要被处理:

function _drupal_error_handler_real($error_level, $message, $filename, $line, $context) {
if ($error_level & error_reporting()) {
$types = drupal_error_levels();
list($severity_msg, $severity_level) = $types[$error_level];
$caller = _drupal_get_last_caller(debug_backtrace()); if (!function_exists('filter_xss_admin')) {
require_once DRUPAL_ROOT . '/includes/common.inc';
} // We treat recoverable errors as fatal.
_drupal_log_error(array(
'%type' => isset($types[$error_level]) ? $severity_msg : 'Unknown error',
// The standard PHP error handler considers that the error messages
// are HTML. We mimick this behavior here.
'!message' => filter_xss_admin($message),
'%function' => $caller['function'],
'%file' => $caller['file'],
'%line' => $caller['line'],
'severity_level' => $severity_level,
), $error_level == E_RECOVERABLE_ERROR);
}
}

重点是_drupal_log_error()函数,该函数具体地处理错误逻辑:

function _drupal_log_error($error, $fatal = FALSE) {
// Initialize a maintenance theme if the bootstrap was not complete.
// Do it early because drupal_set_message() triggers a drupal_theme_initialize().
if ($fatal && (drupal_get_bootstrap_phase() != DRUPAL_BOOTSTRAP_FULL)) {
unset($GLOBALS['theme']);
if (!defined('MAINTENANCE_MODE')) {
define('MAINTENANCE_MODE', 'error');
}
drupal_maintenance_theme();
} // When running inside the testing framework, we relay the errors
// to the tested site by the way of HTTP headers.
$test_info = &$GLOBALS['drupal_test_info'];
if (!empty($test_info['in_child_site']) && !headers_sent() && (!defined('SIMPLETEST_COLLECT_ERRORS') || SIMPLETEST_COLLECT_ERRORS)) {
// $number does not use drupal_static as it should not be reset
// as it uniquely identifies each PHP error.
static $number = 0;
$assertion = array(
$error['!message'],
$error['%type'],
array(
'function' => $error['%function'],
'file' => $error['%file'],
'line' => $error['%line'],
),
);
header('X-Drupal-Assertion-' . $number . ': ' . rawurlencode(serialize($assertion)));
$number++;
} watchdog('php', '%type: !message in %function (line %line of %file).', $error, $error['severity_level']); if ($fatal) {
drupal_add_http_header('Status', '500 Service unavailable (with message)');
} if (drupal_is_cli()) {
if ($fatal) {
// When called from CLI, simply output a plain text message.
print html_entity_decode(strip_tags(t('%type: !message in %function (line %line of %file).', $error))). "\n";
exit;
}
} if (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest') {
if ($fatal) {
if (error_displayable($error)) {
// When called from JavaScript, simply output the error message.
print t('%type: !message in %function (line %line of %file).', $error);
}
exit;
}
}
else {
// Display the message if the current error reporting level allows this type
// of message to be displayed, and unconditionnaly in update.php.
if (error_displayable($error)) {
$class = 'error'; // If error type is 'User notice' then treat it as debug information
// instead of an error message, see dd().
if ($error['%type'] == 'User notice') {
$error['%type'] = 'Debug';
$class = 'status';
} drupal_set_message(t('%type: !message in %function (line %line of %file).', $error), $class);
} if ($fatal) {
drupal_set_title(t('Error'));
// We fallback to a maintenance page at this point, because the page generation
// itself can generate errors.
print theme('maintenance_page', array('content' => t('The website encountered an unexpected error. Please try again later.')));
exit;
}
}
}

有两点需要先说一下:
1. 什么是严重错误$fatal?$error_level == E_RECOVERABLE_ERROR,还有异常也是严重错误。
2. 哪些错误可以显示error_displayable($error)==TRUE?这是在Drupal系统配置中设置的:

在_drupal_log_error()函数中,首先检查是否是在启动过程中发生的严重地不可恢复错误,如果是不可恢复的错误,则切换到维护显示主题:

if ($fatal && (drupal_get_bootstrap_phase() != DRUPAL_BOOTSTRAP_FULL)) {
unset($GLOBALS['theme']);
if (!defined('MAINTENANCE_MODE')) {
define('MAINTENANCE_MODE', 'error');
}
drupal_maintenance_theme();
}

检查是否运行在SimpleTest框架,如果是,则需要为SimpleTest返回一个包含错误信息的HTTP头:

$test_info = &$GLOBALS['drupal_test_info'];
if (!empty($test_info['in_child_site']) && !headers_sent() && (!defined('SIMPLETEST_COLLECT_ERRORS') || SIMPLETEST_COLLECT_ERRORS)) {
static $number = 0;
$assertion = array(
$error['!message'],
$error['%type'],
array(
'function' => $error['%function'],
'file' => $error['%file'],
'line' => $error['%line'],
),
);
header('X-Drupal-Assertion-' . $number . ': ' . rawurlencode(serialize($assertion)));
$number++;
}

然后调用watchdog()记录错误信息。watchdog()使用了钩子,这样允许开发人员自定义模块将错误信息记录到各种不同的地方。默认的Drupal使用dblog模块将错误信息保存到后台数据库watchdog表中。

watchdog('php', '%type: !message in %function (line %line of %file).', $error, $error['severity_level']);

function watchdog($type, $message, $variables = array(), $severity = WATCHDOG_NOTICE, $link = NULL) {
global $user, $base_root; static $in_error_state = FALSE; // It is possible that the error handling will itself trigger an error. In that case, we could
// end up in an infinite loop. To avoid that, we implement a simple static semaphore.
if (!$in_error_state && function_exists('module_implements')) {
$in_error_state = TRUE; // The user object may not exist in all conditions, so 0 is substituted if needed.
$user_uid = isset($user->uid) ? $user->uid : 0; // Prepare the fields to be logged
$log_entry = array(
'type' => $type,
'message' => $message,
'variables' => $variables,
'severity' => $severity,
'link' => $link,
'user' => $user,
'uid' => $user_uid,
'request_uri' => $base_root . request_uri(),
'referer' => isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '',
'ip' => ip_address(),
// Request time isn't accurate for long processes, use time() instead.
'timestamp' => time(),
); // Call the logging hooks to log/process the message
foreach (module_implements('watchdog') as $module) {
module_invoke($module, 'watchdog', $log_entry);
} // It is critical that the semaphore is only cleared here, in the parent
// watchdog() call (not outside the loop), to prevent recursive execution.
$in_error_state = FALSE;
}
}

// dblog模块
function dblog_watchdog(array $log_entry) {
Database::getConnection('default', 'default')->insert('watchdog')
->fields(array(
'uid' => $log_entry['uid'],
'type' => substr($log_entry['type'], 0, 64),
'message' => $log_entry['message'],
'variables' => serialize($log_entry['variables']),
'severity' => $log_entry['severity'],
'link' => substr($log_entry['link'], 0, 255),
'location' => $log_entry['request_uri'],
'referer' => $log_entry['referer'],
'hostname' => substr($log_entry['ip'], 0, 128),
'timestamp' => $log_entry['timestamp'],
))
->execute();
}

如果是严重错误,Drupal会返回500状态码:

if ($fatal) {
drupal_add_http_header('Status', '500 Service unavailable (with message)');
}

检查是否是AJAX请求。$_SERVER['HTTP_X_REQUESTED_WITH']等于XMLHttpRequest是AJAX请求。如果是AJAX请求,且错误允许显示(error_displayable($error)为TRUE),则显示错误信息,终止请求。

if (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest') {
if ($fatal) {
if (error_displayable($error)) {
// When called from JavaScript, simply output the error message.
print t('%type: !message in %function (line %line of %file).', $error);
}
exit;
}
}

如果不是AJAX请求,且错误允许显示,则加入一条FLASH信息,这条信息会显示在下一次请求的顶部。

if (error_displayable($error)) {
$class = 'error'; // If error type is 'User notice' then treat it as debug information
// instead of an error message, see dd().
if ($error['%type'] == 'User notice') {
$error['%type'] = 'Debug';
$class = 'status';
} drupal_set_message(t('%type: !message in %function (line %line of %file).', $error), $class);
}

最后,对于非AJAX的严重错误,直接输出信息,终止请求。

if ($fatal) {
drupal_set_title(t('Error'));
// We fallback to a maintenance page at this point, because the page generation
// itself can generate errors.
print theme('maintenance_page', array('content' => t('The website encountered an unexpected error. Please try again later.')));
exit;
}

异常处理器和错误处理器实质上是相同的,内部都是通过_drupal_log_error()来处理的:

function _drupal_exception_handler($exception) {
require_once DRUPAL_ROOT . '/includes/errors.inc'; try {
// Log the message to the watchdog and return an error page to the user.
_drupal_log_error(_drupal_decode_exception($exception), TRUE);
}
catch (Exception $exception2) {
// Another uncaught exception was thrown while handling the first one.
// If we are displaying errors, then do so with no possibility of a further uncaught exception being thrown.
if (error_displayable()) {
print '<h1>Additional uncaught exception thrown while handling exception.</h1>';
print '<h2>Original</h2><p>' . _drupal_render_exception_safe($exception) . '</p>';
print '<h2>Additional</h2><p>' . _drupal_render_exception_safe($exception2) . '</p><hr />';
}
}
}

Drupal的错误和异常处理的更多相关文章

  1. PHP错误以及异常处理

    以前一直觉得php的异常处理没有什么,现在才发现这个还真是门学问,于是狠下心来好好研究了一下,写一篇文章,也作备忘吧. 1. php错误 无论是什么语言编程,都会有如下三种错误,当然php也不例外. ...

  2. Yii中的错误及异常处理

    Yii中的错误及异常处理 Yii已经默认已经在CApplication上实现了异常和错误的接管,这是通过php的set_exception_handler, set_error_handler实现的. ...

  3. 再谈PHP错误与异常处理

    博客好久没有更新了,实在惭愧,最近在忙人生大事,哈哈!这段时间没有看什么新的东西,结合项目中遇到的PHP异常处理问题,我又重新梳理了之前模糊的概念,希望对大家理解PHP异常处理有所帮助. 请一定要注意 ...

  4. Golang错误和异常处理的正确姿势

    Golang错误和异常处理的正确姿势 错误和异常是两个不同的概念,非常容易混淆.很多程序员习惯将一切非正常情况都看做错误,而不区分错误和异常,即使程序中可能有异常抛出,也将异常及时捕获并转换成错误.从 ...

  5. 【PHP】解析PHP中的错误和异常处理

    目录结构: contents structure [-] 错误级别 自定义处理器 设置异常日志 自定义异常类 在这篇文章中,笔者将会阐述PHP中的异常处理,希望能够对你有所帮助. 1.错误级别 PHP ...

  6. ThinkPHP5.0源码学习之注册错误和异常处理机制

    在base.php文件中,用一句代码\think\Error::register();实现错误和异常处理机制的注册. // 注册错误和异常处理机制 \think\Error::register(); ...

  7. 2018/05/02 PHP 之错误与异常处理

    在学习中,越学习越觉得自己基础薄弱. 在平常工作中,对于某些错误处理感觉不知道怎么下手,于是决定重新再整理一下. 强烈推荐这篇文章,真的感觉学习到了很多. 部分引用::再谈PHP错误与异常处理 -- ...

  8. PHP 注册错误和异常处理机制

    注册错误和异常处理机制有三个PHP函数需要学习 1. register_shutdown_function('Bootstrap\Library\Frame::fatalError'); 2. set ...

  9. PHP常用功能块_错误和异常处理 — php(32)

    一.错误和异常处理 1.1 错误类型和基本的调试方法PHP程序的错误发生一般归属于下列三个领域: 语法错误:语法错误最常见,并且也容易修复.如:代码中遗漏一个分号.这类错误会阻止脚本的执行. 运行时错 ...

随机推荐

  1. Socket读取页面

    http://www.knowsky.com/363189.html http://hi.baidu.com/myyers/item/f90fa3f57d89e1d243c36a34 http://h ...

  2. [TCO2013]LitPanels

    题意:一个$n\times m$的无色网格,你可以在其中选择两个$x\times y$的子矩形并在其中将其中任意的格子涂上颜色,问最终能得到多少种不同的网格 做这题会用到一个概念叫包围盒(boundi ...

  3. 基于tiny4412的Linux内核移植 -- eMMC驱动移植(六)

    作者信息 作者: 彭东林 邮箱:pengdonglin137@163.com QQ:405728433 平台简介 开发板:tiny4412ADK + S700 + 4GB Flash 要移植的内核版本 ...

  4. this和$(this)的关系

    环境关键字this引用的是DOM元素 $(this)是jQuery对象 下面点击按钮分别alert一下 alert(this); alert($(this)); 获取DOM对象的属性id,可以 $(t ...

  5. NSIS安装包制作

    6)闪屏界面,可以在源代码中加入下列代码即可: A.普通效果 以下是代码片段: Function .onInit SetOutPath $TEMP File /oname=spltmp.bmp &qu ...

  6. easyui时间控件设置为可清空——jquery-easyui-1.3.3(这个版本还没有buttons,网上的好多博文都是1.3.5之后的版本)

    效果图: 更改的源码jquery.easyui.min.js 11358行: var _858=$("<div class=\"datebox-button\"&g ...

  7. Android 花钱 划动手指每天一元钱

    花钱(英文ColorMoney)是由上海花动传媒开发的一款免费的应用程序,现支持Android操作系统.安装花钱app后,用户将获得全新的手机锁屏背景图,其中包含各种有趣.唯美的壁纸类图片,亦包含应用 ...

  8. Oracle错误代码ORA-01653,表空间容量无法扩展

    业务模块在进行增操作时,报错“Caused by: java.sql.BatchUpdateException: ORA-01653: 表 JAZZ_V3.T_MZ_BK 无法通过 128 (在表空间 ...

  9. [Git] git revert ( revert commit 和 revert merge)

    转载自:http://blog.csdn.net/qinjienj/article/details/7621887 我们难免会因为种种原因执行一些错误的commit / push,git提供了reve ...

  10. Traefik访问master节点不通的问题定位

    问题 部署traefik到客户节点的对外访问节点后,发现日志里面报错 类似于 E0122 :: reflector.go:] k8s.io/dns/vendor/k8s.io/client-go/to ...