此内容是对文件 zblogphp1.5.1\zb_system\function\c_system_debug.php 的代码分析,建议打开此文件对照阅读。
该文件包含以下6个函数以及一个类,如下:
function Debug_PrintGlobals 显示全局变量
function Debug_PrintIncludefiles 打印全局Include文件
function Debug_PrintConstants 打印全局自定义常量
function Debug_Error_Handler 错误调度提示
function Debug_Exception_Handler 异常处理
function Debug_Shutdown_Handler 当机错误处理
class ZBlogException 异常处理类
首先讲解异常处理类,位于该文件第238行处。
类的开头定义私有静态变量 $_zbe = null,$_isdisable = null (类实例和是否禁用调度标识)
定义公共静态变量
$isdisable = false 是否禁用调度
$isstrict = false 是否严格调度模式
$iswarning = true 是否警告调度模式
$error_id = 0 错误ID
$error_file = null 错误文件
$error_line = null 错误行
$islogerror = false 是否写入错误日志
定义公共变量 $type(错误类型/错误级别,如下17中错误类型,) $message(错误信息) $messagefull(完整的错误信息,会输出是什么地方出错的) $file(错误文件) $line(错误行) 和数组 $errarray = array()(错误说明数组)
构造函数 __construct() 会在实例化该类时自动调用初始化错误数组列表
$this->errarray 详细信息如下:
0:UNKNOWN 未知错误
1:E_ERROR 运行时致命错误
2:E_WARNING 运行时警告
4:E_PARSE 编译时语法解析错误
8:E_NOTICE 运行时通知
16:E_CORE_ERROR 初始化启动过程中发生的致命错误
32:E_CORE_WARNING 初始化启动过程中发生的警告
64:E_COMPILE_ERROR 致命编译时错误
128:E_COMPILE_WARNING 编译时警告
256:E_USER_ERROR 用户产生的错误信息
512:E_USER_WARNING 用户产生的警告信息
1024:E_USER_NOTICE 用户产生的通知信息
2048:E_STRICT 启用PHP对代码的修改建议,以确保代码具有最佳的互操作性和向前兼容性
4096:E_RECOVERABLE_ERROR 可被捕捉的致命错误
8192:E_DEPRECATED 运行时通知
16384:E_USER_DEPRECATED 用户产生的警告信息
30719:E_ALL E_STRICT除外的所有错误和警告信息
类函数 __get($name) 获取参数,(暂时没有找到更多信息,从代码看是返回$this->errarray的错误级别)
该函数首先判断参数是否为 typeName 字符串,如果不是则不执行,然后根据错误级别来返回错误级别的字符串说明。
类函数 GetInstance() 获得单一实例,实例化异常处理类
根据 $_zbe 是否定义来进行实例,外部使用 ZBlogException::GetInstance() 进行实例化该类。
静态类函数 SetErrorHook() 设置错误处理函数,该函数执行了下面3个处理
设置错误处理函数由 Debug_Error_Handler 函数处理
设置异常处理函数由 Debug_Exception_Handler 函数处理
注册一个函数,这个函数会在PHP执行完成后执行,这里这个函数是 Debug_Shutdown_Handler
静态类函数 ClearErrorHook() 清除注册的错误处理函数,这个函数主要执行下面的3个操作
create_function创建匿名函数,参数为空。返回值设置为 false 后,则因为这个函数不处理任何事情且返回false,这会让标准的错误处理程序继续调用。起到清除之前设置的处理函数。下面两个同理。
静态类函数 EnableErrorHook() 启用错误调度
该函数设置 $isdisable 变量为 false,启用错误处理标记
静态类函数 DisableErrorHook() 禁止错误调度
该函数设置 $isdisable 变量为 true,关闭错误处理标记
静态类函数 SuspendErrorHook() 暂停错误调度
静态类函数 ResumeErrorHook() 恢复错误调度
两个函数都是首先判断 self::$_isdisable 的私有标记与 null 的全比较
暂停错误调度函数是不全等跳出,恢复错误调度函数是全等跳出
然后设置 $isdisable 和 $_isdisable 的状态
理解:启用调度后,$isdisable = false,$_isdisable = null,这时执行恢复调度会直接return,只能执行暂停调度
执行暂停调度后,$_isdisable会更改为false或者true,$isdisable设置为true禁止调度!
这时因为$_isdisable不等于null,所以此时只能执行恢复调度函数而无法再次执行暂停调度。
执行恢复调度会再次设置$_isdisable为null,$isdisable设置为调度状态。
总结:调度状态只能先暂停,才能执行恢复。反之不行。
以下4个函数分别设置调度状态级别
静态类函数 DisableStrict() 禁用严格模式
静态类函数 EnableStrict() 打开严格模式
静态类函数 DisableWarning() 禁用警告
静态类函数 EnableWarning() 打开警告
静态类函数 Trace($s) 日志跟踪
调用位于 c_system_common.php 文件中的 Logs 函数 进行记录日志操作,内容为参数。Logs函数不在此处展开。
类函数 ParseError($type, $message, $file, $line) 解析错误信息
参数为:错误级别,内容,文件,行。
该函数只做了一件事情就是对 $type,$message,$messagefull,$file,$line进行了赋值操作。
$messagefull变量额外添加字符串 ‘(set_error_handler)’。说明是错误调度捕捉的错误,这个函数只会在错误调度函数执行,而错误调度函数是被SetErrorHook() 函数设置的错误处理函数。
同理
类函数 ParseShutdown($error) 解析当机错误处理,参数是一个数组
类函数 ParseException($exception) 解析异常处理,参数有 异常处理函数 Debug_Exception_Handler 获取并传递,$exception 是PHP抛出的异常对象,在PHP7以上是Throwable。这里只讨论exception
$exception对象包含属性
string $message 异常消息内容
int $code 异常代码
string $file 抛出异常的文件名
int $line 抛出异常在该文件中的行号
$exception对象包含方法
string getMessage() 获取异常消息内容
Throwable getPrevious() 返回异常链中的前一个异常
int getCode() 获取异常代码
string getFile() 创建异常时的程序文件名称
int getLine() 获取创建的异常所在文件中的行号
array getTrace() 获取异常追踪信息
string getTraceAsString() 获取字符串类型的异常追踪信息
string __toString() 将异常对象转换为字符串
void __clone() 异常克隆
这里使用了
$exception->getMessage() 异常消息内容
$exception->getCode() 异常代码
$exception->getFile() 异常时的程序文件名称
$exception->getLine() 异常所在文件中的行
然后判断 异常类的 $error_file 是否有值(是否不等于null),如果是则这个值赋值给 $this->file
同样判断 self::$error_line 不全等于 null,将值赋值给 $this->line。
类函数 Display() 输出错误信息
使用函数 headers_sent() 判断 HTTP 标头是否已被发送以及在哪里被发送,如果没有,执行位于 c_system_common.php文件的 Http500() 函数。清空(擦掉)输出缓冲区。
$error = $this 将类本身赋值到 $error。
执行 挂载在 Filter_Plugin_Debug_Display 插件中的函数
加载 zblogphp1.5.1\zb_system\defend\error.php 页面,显示错误信息页面
执行 RunTime() 函数显示页面执行时间
//``flush()`` and ``exit($errorCode)`` is for HHVM
//https://github.com/zblogcn/zblogphp/issues/32
flush() 函数刷新输出缓冲
exit(1) 退出
类函数 get_code($file, $line) 获取出错代码信息
参数:错误的文件名和行号
如果 $file 值是 Unknown,返回空数组
如果 指定的$file文件名不可读,返回空数组
array_slice函数根据条件从数组中取一段值
file($file) 把整个文件读入一个数组,文件中的每一行都是数组的某一个元素
max(0, $line - 5) 函数返回两个数的最大值,这里表示如果参数行小于5,则返回0
array_slice 第三个参数表示需要获取的行数
array_slice 第四个参数表示是否重新排序并重置数组的数字索引。这里用TRUE来阻止这个行为
所以 $aFile = array_slice(file($file), max(0, $line - 5), 10, true);
的结果为获得出错文件以错误所在行前5行开始起的10行的内容数组。
然后 foreach 将每一行的字符串使用 htmlspecialchars 将特殊字符转换为 HTML 实体
源代码为:
foreach ($aFile as &$sData) {
//&$ = ByRef
$sData = htmlspecialchars($sData);
}
最后 return $aFile;,总之我看不懂 $aFile as &$sData 这个是什么意思,是将 $aFile 每个元素追加到 $sData?
类函数 possible_causes_of_the_error() 得到可能的错误原因
global $lang 全局公共语言数组
global $bloghost 当前网站地址
$result = '' 返回结果
如果 ZBlogException::$error_id 错误ID不等于0,表示是Z-BlogPHP自身抛出的错误
然后在判断 $lang['error_reasons'][ZBlogException::$error_id] 指定的ID值是否定义,如果定义,将对应结果(什么系统的哪哪哪错了,环境啊,权限啊什么的)赋值给 $result
否则将默认错误(空)赋值给$result
然后$lowerErrorReason = strtolower($this->message); strtolower函数将所有字符转换为小写并赋值$this->message是错误信息。上面说过。
foreach ($lang['error_reasons']['other'] as $key => $value) 判断语言包中其他数组中的各个数组元素,看看$lowerErrorReason的值是不是和这里的错误字符串一样,如果一样,将对应值赋值到$result
$errorId = urlencode(ZBlogException::$error_id); 得到错误ID URL编码后赋值
$errorMessage = urlencode($this->message); 得到错误信息URL编码后赋值
$moreHelp = $lang['offical_urls']['more_help']; 获得语言包中的该值,是一个在线帮助链接
$moreHelp = str_replace('{%id%}', $errorId, $moreHelp); 将该链接的对应字符替换为实际ID值
$moreHelp = str_replace('{%message%}', $errorMessage, $moreHelp); 同上
$result .= $lang['error_reasons']['end']; 拼接语言包中的对应内容
$result = str_replace('{%bloghost%}', $bloghost, $result); 继续替换
$result = str_replace('{%morehelp%}', $moreHelp, $result); 继续替换
返回结果。
这个函数的执行结果是:
第一段,哪里错了,哪里权限有问题。
第二段,如果你是访客,请联系站长,如果你是站长,什么什么。
异常类代码解读完毕,讲解这个文件中的几个函数,这几个函数可以在任意文件中使用。
函数 Debug_PrintGlobals() 显示全局变量
说明:使用 foreach 函数来获取 $GLOBALS 的所有内容,返回一个数组吧。
函数 Debug_PrintIncludefiles() 打印全局include文件
说明:使用 foreach 函数通过 get_included_files() 来获取数据,返回一个数组
函数 Debug_PrintConstants() 打印全局自定义常量
说明:函数 get_defined_constants 获得所有常量的关联数组,键是常量名,值是常量值
这里只获取 user的子健内容,所以使用
if (isset($a['user'])) {
$a = $a['user'];
}
获得,,返回一个数组
上面这三个函数都是只应用在了查看 phpinfo 信息的页面里。
函数 Debug_Error_Handler($errno, $errstr, $errfile, $errline) 错误调度提示
改函数4个参数分别是错误级别,错误信息,错误文件名,错误行
1.首先判断错误调度是否关闭,如果是则直接return。 ZBlogException::$isdisable == true
2.然后 执行挂载到 Filter_Plugin_Debug_Handler 钩子上的插件函数
3.然后对 $_SERVER['_error_count'] 的服务器指示错误次数变量值加1
4.判断是否写入错误日志,如果写入的话调用 Logs 函数执行写入操作 ZBlogException::$islogerror == true
var_export的第二个参数设置为true将数组的各个元素值以字符串拼接后写入文件,Logs函数第二个参数true表示这个一个error日志。
5.判断给定的文件名是否可读,如果可以读取的话,获取错误行的前一行信息
reset 将数组内部指针倒回到第一个单元并返回第一个数组单元的值
判断该行有没有 @ 符号,如果没有则 return (这里为什么要return?)
6.如果关闭警告,则错误级别是警告级别的话直接return。
7.如果关闭严格模式,则对 E_STRICT E_NOTICE E_USER_NOTICE 级别(建议,运行时通知,用户产生的通知信息)直接 return。
//屏蔽屏蔽系统的错误,防ZBP报系统的错误,不过也有可能导致ZBP内的DEPRECATED错误也被屏蔽了
8.如果错误级别是 E_CORE_WARNING (警告性)直接返回return
9.如果错误级别是 E_COMPILE_WARNING (警告性)直接返回return
10.如果常量 E_DEPRECATED 已经定义且错误级别是 E_DEPRECATED (运行时通知)直接返回return
11.如果常量 E_USER_DEPRECATED 已经定义且错误级别是 E_USER_DEPRECATED (用户产生的警告信息)直接返回return
下面的就是 用户产生的错误信息 和 可被扑捉的致命错误 了
首先 $zbe = ZBlogException::GetInstance(); 获得唯一异常处理类实例
然后 $zbe->ParseError($errno, $errstr, $errfile, $errline); 解析错误信息
最后 $zbe->Display(); 输出错误信息
函数 Debug_Exception_Handler($exception) 异常处理
1.首先判断错误调度是否关闭,如果是则直接return。 ZBlogException::$isdisable == true
2.然后 执行挂载到 Filter_Plugin_Debug_Handler 钩子上的插件函数
3.然后对 $_SERVER['_error_count'] 的服务器指示错误次数变量值加1
4.判断是否写入错误日志,如果写入的话调用 Logs 函数执行写入操作 ZBlogException::$islogerror == true
var_export的第二个参数设置为true将数组的各个元素值以字符串拼接后写入文件,Logs函数第二个参数true表示这个一个error日志。
5.$zbe = ZBlogException::GetInstance(); 获得唯一异常处理类实例
6.$zbe->ParseException($exception); 解析异常处理
7.$zbe->Display(); 输出错误信息
函数 Debug_Shutdown_Handler() 当机错误处理
首先使用 error_get_last函数获取最后一个发生的错误的信息,如果没有,返回NULL,不执行该函数。
如果获取到了错误信息
1.判断错误调度是否关闭,如果是则直接return。
2.然后 执行挂载到 Filter_Plugin_Debug_Handler 钩子上的插件函数
3.然后对 $_SERVER['_error_count'] 的服务器指示错误次数变量值加1
4.判断是否写入错误日志,如果写入的话调用 Logs 函数执行写入操作 ZBlogException::$islogerror == true
var_export的第二个参数设置为true将数组的各个元素值以字符串拼接后写入文件,Logs函数第二个参数true表示这个一个error日志。
5.如果关闭警告,则错误级别是警告级别的话直接return。
6.如果关闭严格模式,则对 E_STRICT E_NOTICE E_USER_NOTICE 级别(建议,运行时通知,用户产生的通知信息)直接 return。
7.如果错误级别是 E_CORE_WARNING (警告性)直接返回return
8.如果错误级别是 E_COMPILE_WARNING (警告性)直接返回return
9.如果常量 E_DEPRECATED 已经定义且错误级别是 E_DEPRECATED (运行时通知)直接返回return
10.如果常量 E_USER_DEPRECATED 已经定义且错误级别是 E_USER_DEPRECATED (用户产生的警告信息)直接返回return
最后
$zbe = ZBlogException::GetInstance(); 获得唯一异常处理类实例
$zbe->ParseShutdown($error); 解析当机错误处理
$zbe->Display(); 输出错误信息
全文完 2017年4月24日