PHP错误、异常汇总

Error

Error级别

Fatal Error:致命错误(脚本终止运行)

E_ERROR          致命的运行时的致命错误,终止程序执行
E_CORE_ERROR PHP 启动时的致命错误
E_COMPILE_ERROR  PHP编译时的致命错误
E_USER_ERROR     用户产生的致命错误

Parse Error:编译时的解析错误(脚本终止运行)

Parse Error  编译时的语法解析错误

Warning Error:警告错误(仅给出提示信息,但是脚本不会终止运行。)

E_WARNING          运行时警告 (非致命错误)。
E_CORE_WARNING     PHP初始化启动过程中发生的警告 (非致命错误) 。
E_COMPILE_WARNING  编译警告
E_USER_WARNING     用户产生的警告信息

Notice Error:通知错误(仅给出通知信息,但是脚本不会终止运行。)

E_NOTICE       运行时通知。表示脚本遇到可能会表现为错误的情况.
E_USER_NOTICE  用户产生的通知信息。

set_error_handler()捕获错误【有局限】

函数说明

set_error_handler($callback);//设置一个用户的函数(error_handler)来处理脚本中出现的错误。

函数的局限性

以下级别的错误不能由用户定义的函数来处理:
E_ERROR、 E_PARSE、 E_CORE_ERROR、 E_CORE_WARNING、 E_COMPILE_ERROR、 E_COMPILE_WARNING,和在 调用 set_error_handler() 函数所在文件中产生的大多数 E_STRICT。

也就是:set_error_handler($callback)只能捕获系统产生的一些Warning、Notice级别的Error。

使用方法

<?php
set_error_handler("error_handler");
function error_handler($errno,$errstr,$errfile,$errline){
    $str=<<<EOF
         "errno":$errno
         "errstr":$errstr
         "errfile":$errfile
         "errline":$errline
EOF;
//获取到错误可以自己处理,比如记Log、报警等等
    echo $str;
}
echo $test;//$test未定义,会报一个notice级别的错误

输出结果:

"errno":8
"errstr":Undefined variable: test
"errfile":/Users/shuchao/Desktop/handler.php
"errline":13

如何捕获PHP的Fatal Error、Parse Error等

需求描述

获取PHP的fatal error,比如记录到Log里面,利于我们分析线上问题,可以做线上服务的监控。

两个函数

register_shutdown_function()
register_shutdown_function($callback)

register_shutdown_function(),就把你要注册进去的function放进【假装是队列吧】,等到脚本正常退出或显示调用exit时,再把注册进去的function拉出来执行.

register_shutdown_function()调用的3种情况:

  • 脚本正常退出时;

  • 在脚本运行(run-time not parse-time)出错退出时;

  • 用户调用exit方法退出时。

error_get_last()
error_get_last();//函数获取最后发生的错误。

该函数以数组的形式返回最后发生的错误。

返回的数组包含 4 个键和值:

[type] - 错误类型
[message] - 错误消息
[file] - 发生错误所在的文件
[line] - 发生错误所在的行

使用方法

<?php
register_shutdown_function( "fatal_handler" );
define('E_FATAL',  E_ERROR | E_USER_ERROR |  E_CORE_ERROR | 
        E_COMPILE_ERROR | E_RECOVERABLE_ERROR| E_PARSE );
function fatal_handler() {
  if( $error = error_get_last()) {
    $errno   = $error["type"];
    $errfile = $error["file"];
    $errline = $error["line"];
    $errstr  = $error["message"];
    $str=<<<EOF
         "errno":$errno
         "errstr":$errstr
         "errfile":$errfile
         "errline":$errline
EOF;
//获取到错误可以自己处理,比如记Log、报警等等
    echo $str;
  }
}

强烈注意

在parse-time出错的时候,是不会调用register_shutdown_function()函数的。只有在run-time出错的时候,才会调用register_shutdown_function()。

下面我们举例说明:

NO.1
error_handler.php
<?php
register_shutdown_function("error_handler");
function error_handler(){
    echo "Yeah,it's worked!";
}
function test(){}
function test(){}

执行结果如下:

Fatal error: Cannot redeclare test() (previously declared in /Users/shuchao/Desktop/error_handler.php:6) in /Users/shuchao/Desktop/error_handler.php on line 7
原因分析

在执行error_handler.php的时候,由于重复定义了两个函数test(),在php的parse-time就出错了(不是run-time),所以不能回调register_shutdown_function()中的函数。

NO.2
error_handler.php
<?php
register_shutdown_function("error_handler");
function error_handler(){
    echo "Yeah,it's worked!";
}
if(true){
   function test(){}
}
function test(){}

执行结果如下:

Fatal error: Cannot redeclare test() (previously declared in /Users/shuchao/Desktop/error_handler.php:9) in /Users/shuchao/Desktop/error_handler.php on line 7
Yeah,it's worked!%
原因分析

我们看到,上面回调了register_shutdown_function().
因为我们加了一个if()判断,if()里面的test()方法,相当于一个闭包,与外面的test()名称不冲突。
也就是,上面的代码在parse-time没有出错,而是在run-time的时候出错了,所以我们能够获取到fatal error。

NO.3
error_handler.php
<?php
register_shutdown_function("error_handler");
function error_handler(){
    echo "Yeah,it's worked!";
}
test_error.php
<?php
include './error_handler.php';
function test(){}
function test(){}

执行 test_error.php的结果如下

Fatal error: Cannot redeclare test() (previously declared in /Users/shuchao/Desktop/test_error.php:3) in /Users/shuchao/Desktop/test_error.php on line 4
原因分析

当我们在运行test_error.php的时候,因为redeclare了两个test()方法,所以php的语法解析器在parse-time的时候就出错了。 所以不能回调register_shutdown_function()中的方法,不能catch住这个fatal error。

NO.4
error_handler.php
<?php
register_shutdown_function("error_handler");
function error_handler(){
    echo "Yeah,it's worked!";
}
test_error.php
<?php
function test(){}
function test(){}
include_all.php
require './error_handler.php';
require './test_error.php';

执行 include_all.php的结果如下

Fatal error: Cannot redeclare test() (previously declared in /Users/shuchao/Desktop/include_all.php:2) in /Users/shuchao/Desktop/include_all.php on line 3
Yeah,it's worked!%
结果分析

上面我们捕获了fatal_error.
因为在运行include_all.php的时候,include_all.php本身语法并没有出错,也就是在parse-time的时候并没有出错,而是include的文件出错了,也就是在run-time的时候出错了,这个时候是能回调register_shutdown_function()中的函数的。

强烈建议:如果我们要使用register_shutdown_function进行错误捕捉,使用NO.4,最后一种方法,可以确保错误都能捕捉到。

更优美的写法·获取所有错误

set_error_handler()与register_shutdown_function()、error_get_last()的结合使用
<?php
register_shutdown_function( "fatal_handler" );
set_error_handler("error_handler");

define('E_FATAL',  E_ERROR | E_USER_ERROR |  E_CORE_ERROR | 
        E_COMPILE_ERROR | E_RECOVERABLE_ERROR| E_PARSE );

//获取fatal error
function fatal_handler() {
    $error = error_get_last();
    if($error && ($error["type"]===($error["type"] & E_FATAL))) {
        $errno   = $error["type"];
        $errfile = $error["file"];
        $errline = $error["line"];
        $errstr  = $error["message"];
        error_handler($errno,$errstr,$errfile,$errline);
  }
}
//获取所有的error
function error_handler($errno,$errstr,$errfile,$errline){
    $str=<<<EOF
         "errno":$errno
         "errstr":$errstr
         "errfile":$errfile
         "errline":$errline
EOF;
//获取到错误可以自己处理,比如记Log、报警等等
    echo $str;
}

Exception

Exception与Error的区别

Exception

当异常抛出的时候,我们是想要去捕获他,并去做处理的。
所以异常经常被当做程序的控制流程使用。

Error

Error是不可恢复的,是在开发过程中要去解决的。

使用Exception的例子

我想执行insert语句插入一条数据,可能插入失败(比如ID重复),注意是可能失败,所以这是一个可能的情况,也就是异常情况。
我们就可以使用异常来处理这个问题

try {
  $row->insert();
  $inserted = true;
} catch (Exception $e) {
  echo "There was an error inserting the row - ".$e->getMessage();
  $inserted = false;
}

echo "Some more stuff";

如何catch一个未捕获的Exception

场景描述

假设程序中的有些地方直接throw了异常,没有进行catch。
我们现在想要不管在程序的任何一个地方throw异常,即便在throw的地方没有被catch,我们也要能catch住,如何做到呢?

一个函数:set_exception_handler()

//设置默认的异常处理程序,用于没有用 try/catch 块来捕获的异常。 在 exception_handler 调用后异常会中止。
set_exception_handler()

使用示例

1、exception_handler.php
<?php
set_exception_handler("my_exception");
function my_exception($exception){
    echo $exception->getMessage();
}
2、test_exception.php
<?php
require "./exception_handler.php";
throw new Exception("I am Exception");

现在我们运行 test_exception.php,结果如下:

I am Exception //证明我们throw的Exception被捕获了

更多精彩,请关注公众号“聊聊代码”,让我们一起聊聊“左手代码右手诗”的事儿。
图片描述


畅想代码
代码是人与计算机沟通的桥梁,畅想代码!
1.3k 声望
175 粉丝
0 条评论
推荐阅读
设计模式系列·无从下手的困惑(一)
小二的困惑 最近,小二跟着C哥学了不少设计模式,写代码的功夫可谓更上一层楼。 但是当小二单独面对新的项目时,想用学过的设计模式来解决问题,往往无从下手。 这样尴尬的处境令小二很是苦恼。于是,小二决定去...

zilu1阅读 1.9k

怎样用 PHP 来实现枚举?
在数学和计算机科学理论中,一个集的枚举是列出某些有穷序列集的所有成员的程序,或者是一种特定类型对象的计数。这两种类型经常(但不总是)重叠。枚举是一个被命名的整型常数的集合,枚举在日常生活中很常见,...

唯一丶25阅读 6.4k评论 4

PHP转Go实践:xjson解析神器「开源工具集」
我和劲仔都是PHP转Go,身边越来越多做PHP的朋友也逐渐在用Go进行重构,重构过程中,会发现php的json解析操作(系列化与反序列化)是真的香,弱类型语言的各种隐式类型转换,很大程度的减低了程序的复杂度。

王中阳Go10阅读 1.7k评论 2

封面图
图片防盗链破解 解决图片防盗链问题 反向代理
当客户端(浏览器)向服务器请求内容的时候,会提交一个header,这个header中包含了如:浏览器信息、cookie等内容,那么有一个叫referer的东东,也包含在这里面。

TANKING7阅读 11.3k评论 5

Git操作不规范,战友提刀来相见!
年终奖都没了,还要扣我绩效,门都没有,哈哈。这波骚Git操作我也是第一次用,担心闪了腰,所以不仅做了备份,也做了笔记,分享给大家。问题描述小A和我在同时开发一个功能模块,他在优化之前的代码逻辑,我在开...

王中阳Go5阅读 2.2k评论 2

封面图
微信公众号开发:自动回复文本/图片/图文消息/关键词回复/上传素材/自定义菜单
对接流程1、申请微信公众号测试账号URL:[链接]2、登录,配置开发者服务器URL和Token开发者服务器配置代码:config.php {代码...} URL是config.php在你服务器的URLToken是上面代码自己设置的Token搞定之后,就能完...

TANKING2阅读 10.1k

Ajax实现搜索联想 搜索关键词提醒 无刷新搜索
通过javascript监听搜索框的内容,调用后端即可。(1)javascript监听搜索框的内容(2)把搜索框的关键词传给后端进行搜索(3)搜索到结果,遍历到页面

TANKING1阅读 4.4k

1.3k 声望
175 粉丝
宣传栏