블로그가 오랫동안 업데이트되지 않아서 정말 부끄럽습니다. 이 기간 동안 새로운 내용을 읽은 적이 없습니다. 프로젝트에서 발생한 PHP 예외 처리 문제를 기반으로 이전에 모호했던 개념을 재구성하여 모든 사람이 PHP 예외 처리를 이해하는 데 도움이 되기를 바랍니다.
특별한 지침이 없다는 점에 유의하세요. 이 예는 PHP 버전 < 7
PHP 예외 처리에 관해서는 누구나 먼저 생각할 것입니다. try-catch, 그럼 먼저 프로그램을 살펴보겠습니다. test.php 파일과 다음 내용이 포함된 간단한 PHP 프로그램이 있고 명령줄 실행이 있습니다: php test.php
<? = 0 1/ ( -> ?><p><span style="font-size: 14px">내 질문은: 이 프로그램이 0으로 나누기 오류 메시지를 올바르게 캡처할 수 있습니까? </span><br><span style="font-size: 14px"> 그렇다고 답했다면 이 글을 읽어보세요! 당신은 뭔가를 배울 수 있어야합니다. </span></p> <p><span style="font-size: 15px; font-family: Microsoft YaHei">이 문서는 예외 처리에 대한 나의 이해를 소개하기 위해 5개 부분으로 나누어져 있습니다. </span></p> <p style="margin-left: 30px"><span style="color: #008000"><span style="font-size: 15px; color: #008000"><span style='font-family: "DejaVu Sans Mono"; color: #000000'><span style="font-family: Microsoft YaHei"> 1. 예외 및 오류 개요 </span></span></span></span></p> <p style="margin-left: 30px"><span style="color: #008000"><span style="font-size: 15px; color: #008000"><span style='font-family: "DejaVu Sans Mono"; color: #000000'><span style="font-family: Microsoft YaHei"><span style='font-family: "DejaVu Sans Mono"; color: #000000'><span style="font-family: Microsoft YaHei"> 2. 오류 수준 </span> 3. PHP 예외 처리의 블랙 기술 </span></span></span></span></span></p> <p style="margin-left: 30px"><span style="color: #008000">4 영리하게 오류 및 예외 잡기<span style="font-size: 15px; color: #008000"><span style='font-family: "DejaVu Sans Mono"; color: #000000'><span style="font-family: Microsoft YaHei"><span style='font-family: "DejaVu Sans Mono"; color: #000000'><span style="font-family: Microsoft YaHei"><span style='font-family: "DejaVu Sans Mono"; color: #000000'><span style="font-family: Microsoft YaHei"></span></span></span></span></span></span></span></span></p> <p style="margin-left: 30px"> <span style="color: #008000"><span style="font-size: 15px; color: #008000"><span style='font-family: "DejaVu Sans Mono"; color: #000000'><span style="font-family: Microsoft YaHei"><span style='font-family: "DejaVu Sans Mono"; color: #000000'><span style="font-family: Microsoft YaHei"><span style='font-family: "DejaVu Sans Mono"; color: #000000'><span style="font-family: Microsoft YaHei"><span style='font-family: "DejaVu Sans Mono"; color: #000000'> 5 . 사용자 정의 예외 처리 및 예외 중첩<span style="font-family: Microsoft YaHei"> </span></span></span></span></span></span></span></span></span></span></p> <p style="margin-left: 30px"><span style="color: #008000; font-size: 15px"><span style="color: #008000"><span style='font-family: "DejaVu Sans Mono"; color: #000000'><span style="font-family: Microsoft YaHei"><span style='font-family: "DejaVu Sans Mono"; color: #000000'><span style="font-family: Microsoft YaHei"><span style='font-family: "DejaVu Sans Mono"; color: #000000'><span style="font-family: Microsoft YaHei"><span style='font-family: "DejaVu Sans Mono"; color: #000000'><span style="font-family: Microsoft YaHei"><span style='font-family: "DejaVu Sans Mono"; color: #000000'><span style="font-family: Microsoft YaHei"><span style='font-family: "DejaVu Sans Mono"; color: #000000'><span style="font-family: Microsoft YaHei"></span> 6. PHP7</span></span></span></span></span></span></span></span></span>의 예외 처리 </span></span></span> </span></p> <p style="margin-left: 30px"><span style="color: #008000"><span style="font-size: 15px; color: #008000"><span style='font-family: "DejaVu Sans Mono"; color: #000000'>1. 예외 및 오류 개요<span style="font-family: Microsoft YaHei"><span style='font-family: "DejaVu Sans Mono"; color: #000000'> <span style="font-family: Microsoft YaHei"> PHP에서 예외란 무엇입니까? <span style='font-family: "DejaVu Sans Mono"; color: #000000'> 프로그램 실행 중 예상치 못한 상황이 발생할 때 허용됩니다(원하지 않는 경우). 발생해야 하지만 계속 발생하는 오류는 컴파일이나 구문 오류가 아닌 논리 및 비즈니스 프로세스 오류입니다. <span style="font-family: Microsoft YaHei"><span style='font-family: "DejaVu Sans Mono"; color: #000000'> PHP의 오류는 무엇입니까? <span style="font-family: Microsoft YaHei"> PHP 스크립트 자체의 문제입니다. 대부분의 경우 잘못된 구문 및 서버 환경으로 인해 컴파일러가 검사를 통과하지 못하거나 심지어 실행에 실패하기도 합니다. 경고와 알림은 모두 오류이지만 수준이 다르며 오류는 try-catch로 잡을 수 없습니다. <span style='font-family: "DejaVu Sans Mono"; color: #000000'><span style="font-family: Microsoft YaHei"></span><p><span style='font-family: "DejaVu Sans Mono"; color: #000000'><span style="font-family: Microsoft YaHei; font-size: 14px"> 上面的说法是有前提条件的:<br> 在PHP中,因为在其他语言中就不能这样下结论了,也就是说异常和错误的说法在不同的语言有不同的说法。在PHP中任何自身的错误或者是非正常的代码都会当做错误对待,并不会以异常的形式抛出,但是也有一些情况会当做异常和错误同时抛出(据说是,我没有找到合适的例子)。也就是说,你想在数据库连接失败的时候自动捕获异常是行不通的,因为这就不是异常,是错误。但是在java中就不一样了,他会把很多和预期不一致的行为当做异常来进行捕获。</span></span></p> <p><span style='font-family: "DejaVu Sans Mono"; color: #000000'><span style="font-family: Microsoft YaHei; font-size: 14px"> PHP异常处理很鸡肋?<br> 在上面的分析中我们可以看出,PHP并不能主动的抛出异常,但是你可以手动抛出异常,这就很无语了,如果你知道哪里会出问题,你添加if else解决不就行了吗,为啥还要手动抛出异常,既然能手动抛出就证明这个不是异常,而是意料之中。以我的理解,这就是PHP异常处理鸡肋的地方(不一定对啊)。所以PHP的异常机制不是那么的完美,但是使用过框架的同学都知道有这个情况:你在框架中直接写开头那段php“自动”捕获异常的代码是可以的,这是为什么?看过源码的同学都知道框架中都会涉及三个函数:register_shutdown_function,set_error_handler,set_exception_handler后面我会重点讲解着三个黑科技,通过这几个函数我们可以实现PHP假自动捕获异常和错误。<br></span></span></p> <p><span style='font-family: "DejaVu Sans Mono"; color: #000000'><span style="font-family: Microsoft YaHei; font-size: 14px"><strong><span style="font-size: 18px">二、ERROR的级别</span></strong><br> 只有熟悉错误级别才能对错误捕捉有更好的认识。 ERROR有不同的错误级别,我之前的一篇文章中有写到:<br> 下面我再总结性的给出这几类错误级别:<br></span></span></p> <div class="cnblogs_code"><pre class="brush:php;toolbar:false"> 1 Fatal Error:致命错误(脚本终止运行) 2 E_ERROR // 致命的运行错误,错误无法恢复,暂停执行脚本 3 E_CORE_ERROR // PHP启动时初始化过程中的致命错误 4 E_COMPILE_ERROR // 编译时致命性错,就像由Zend脚本引擎生成了一个E_ERROR 5 E_USER_ERROR // 自定义错误消息。像用PHP函数trigger_error(错误类型设置为:E_USER_ERROR) 6 7 Parse Error:编译时解析错误,语法错误(脚本终止运行) 8 E_PARSE //编译时的语法解析错误 9 10 Warning Error:警告错误(仅给出提示信息,脚本不终止运行)11 E_WARNING // 运行时警告 (非致命错误)。12 E_CORE_WARNING // PHP初始化启动过程中发生的警告 (非致命错误) 。13 E_COMPILE_WARNING // 编译警告14 E_USER_WARNING // 用户产生的警告信息15 16 Notice Error:通知错误(仅给出通知信息,脚本不终止运行)17 E_NOTICE // 运行时通知。表示脚本遇到可能会表现为错误的情况.18 E_USER_NOTICE // 用户产生的通知信息。
由此可知有5类是产生ERROR级别的错误,这种错误直接导致PHP程序退出。
可以定义成:
1 ERROR = E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR | E_USER_ERROR | E_PARSE
三、PHP异常处理中的黑科技
前面提到框架中是可以捕获所有的错误和异常的,之所以能实现应该是使用了黑科技,哈哈!其实也不是什么黑科技,主要是三个重要的函数:
1:set_error_handler()
看到这个名字估计就知道什么意思了,这个函数用于捕获错误,设置一个用户自定义的错误处理函数。
1 <?php2 set_error_handler('zyferror');3 function zyferror($type, $message, $file, $line)4 {5 var_dump('<b>set_error_handler: ' . $type . ':' . $message . ' in ' . $file . ' on ' . $line . ' line .</b><br />');6 }7 ?>
当程序出现错误的时候自动调用此方法,不过需要注意一下两点:第一,如果存在该方法,相应的error_reporting()就不能在使用了。所有的错误都会交给自定义的函数处理。第二,此方法不能处理以下级别的错误:E_ERROR、 E_PARSE、 E_CORE_ERROR、 E_CORE_WARNING、 E_COMPILE_ERROR、 E_COMPILE_WARNING,set_error_handler() 函数所在文件中产生的E_STRICT,该函数只能捕获系统产生的一些Warning、Notice级别的错误。
并且他有多种调用的方法:
1 <?php2 // 直接传函数名 NonClassFunction3 set_error_handler('function_name'); 4 5 // 传 class_name && function_name6 set_error_handler(array('class_name', 'function_name')); 7 ?>
2:register_shutdown_function()
捕获PHP的错误:Fatal Error、Parse Error等,这个方法是PHP脚本执行结束前最后一个调用的函数,比如脚本错误、die()、exit、异常、正常结束都会调用,多么牛逼的一个函数啊!通过这个函数就可以在脚本结束前判断这次执行是否有错误产生,这时就要借助于一个函数:error_get_last();这个函数可以拿到本次执行产生的所有错误。error_get_last();返回的信息:
[type] - 错误类型
[message] - 错误消息
[file] - 发生错误所在的文件
[line] - 发生错误所在的行
1 <?php2 register_shutdown_function('zyfshutdownfunc');3 function zyfshutdownfunc()4 {5 if ($error = error_get_last()) {6 var_dump('<b>register_shutdown_function: Type:' . $error['type'] . ' Msg: ' . $error['message'] . ' in ' . $error['file'] . ' on line ' . $error['line'] . '</b>');7 }8 }9 ?>
通过这种方法就可以巧妙的打印出程序结束前所有的错误信息。但是我在测试的时候我发现并不是所有的错误终止后都会调用这个函数,可以看下面的一个测试文件,内容是:
1 <?php 2 register_shutdown_function('zyfshutdownfunc'); 3 function zyfshutdownfunc() 4 { 5 if ($error = error_get_last()) { 6 var_dump('<b>register_shutdown_function: Type:' . $error['type'] . ' Msg: ' . $error['message'] . ' in ' . $error['file'] . ' on line ' . $error['line'] . '</b>'); 7 } 8 } 9 var_dump(23+-+); //此处语法错误10 ?>
自己可以试一下,你可以看到根本就不会触发zyfshutdownfunc()函数,其实这是一个语法错误,直接报了一个:
1 <?php2 Parse error: syntax error, unexpected ')' in /www/mytest/exception/try-catch.php on line 713 ?>
由此引出一个奇葩的问题:问什么不能触发,为什么框架中是可以的?其实原因很简单,只在parse-time出错时是不会调用本函数的。只有在run-time出错的时候,才会调用本函数,我的理解是语法检查器前没有执行register_shutdown_function()去把需要注册的函数放到调用的堆栈中,所以就根本不会运行。那框架中为什么任何错误都能进入到register_shutdown_function()中呢,其实在框架中一般会有统一的入口index.php,然后每个类库文件都会通过include ** 的方式加载到index.php中,相当与所有的程序都会在index.php中聚集,同样,你写的具有语法错误的文件也会被引入到入口文件中,这样的话,调用框架,执行index.php,index.php本身并没有语法错误,也就不会产生parse-time错误,而是 include 文件出错了,是run-time的时候出错了,所以框架执行完之后就会触发register_shutdown_function();
所以现在可是试一下这个写法,这样就会触发zyfshutdownfunc()回调了:
1 a.php文件 2 <?php 3 // 模拟语法错误 4 var_dump(23+-+); 5 ?> 6 7 b.php文件 8 <?php 9 register_shutdown_function('zyfshutdownfunc');10 function zyfshutdownfunc()11 {12 if ($error = error_get_last()) {13 var_dump('<b>register_shutdown_function: Type:' . $error['type'] . ' Msg: ' . $error['message'] . ' in ' . $error['file'] . ' on line ' . $error['line'] . '</b>');14 }15 }16 require 'a.php';17 ?>
3:set_exception_handler()
设置默认的异常处理程序,用在没有用try/catch块来捕获的异常,也就是说不管你抛出的异常有没有人捕获,如果没有人捕获就会进入到该方法中,并且在回调函数调用后异常会中止。看一下用法:
1 <?php2 set_exception_handler('zyfexception');3 function zyfexception($exception)4 {5 var_dump("<b>set_exception_handler: Exception: " . $exception->getMessage() . '</b>');6 }7 throw new Exception("zyf exception");8 ?>
四、巧妙的捕获错误和异常
1:把错误以异常的形式抛出(不能完全抛出)
由上面的讲解我们知道,php中的错误是不能以异常的像是捕获的,但是我们需要让他们抛出,已达到扩展 try-catch的影响范围,我们前面讲到过set_error_handler() 方法,他是干嘛用的,他是捕获错误的,所以我们就可以借助他来吧错误捕获,然后再以异常的形式抛出,ok,试试下面的写法:
1 <?php 2 set_error_handler('zyferror'); 3 function zyferror($type, $message, $file, $line) 4 { 5 throw new \Exception($message . 'zyf错误当做异常'); 6 } 7 8 $num = 0; 9 try {10 echo 1/$num;11 12 } catch (Exception $e){13 echo $e->getMessage();14 }15 ?>
好了,试一下,会打印出:
1 Division by zero zyf123
流程:本来是除0错误,然后触发set_error_handler(),在set_error_handler()中相当与杀了个回马枪,再把错误信息以异常的形式抛出来,这样就可以实现错误以异常的形式抛出。大家要注意:这样做是有缺点的,会受到set_error_handler()函数捕获级别的限制。
2:捕获所有的错误
由set_error_handler()可知,他能够捕获一部分错误,不能捕获系统级E_ERROR、E_PARSE等错误,但是这部分可以由register_shutdown_function()捕获。所以两者结合能出现很好的功能。
看下面的程序:
a. <? (23+-+ ?> b. <? (0 ('zyfshutdownfunc' ( = ('<b>register_shutdown_function: Type:' . ['type'] . ' Msg: ' . ['message'] . ' in ' . ['file'] . ' on line ' . ['line'] . '</b>' ('zyferror' zyferror(, , , ('<b>set_error_handler: ' . . ':' . . ' in ' . . ' on ' . . ' line .</b><br />' 'a.php' ?>
到此就可以解释开头的那个程序了吧,test.php 如果是单文件执行是不能捕获到错误的,如果你在框架中执行就是可以的,当然你按照我上面介绍的来扩展也是可以的。
五、自定义异常处理和异常嵌套
1:自定义异常处理
在复杂的系统中,我们往往需要自己捕获我们需要特殊处理的异常,这些异常可能是特殊情况下抛出的。所以我们就自己定义一个异常捕获类,该类必须是 exception 类的一个扩展,该类继承了 PHP 的 exception 类的所有属性,并且我们可以添加自定义的函数,使用的时候其实和之前的一样,大致写法如下:
1 <?php 2 class zyfException extends Exception 3 { 4 public function errorzyfMessage() 5 { 6 return 'Error line ' . $this->getLine().' in ' . $this->getFile() 7 .': <b>' . $this->getMessage() . '</b> Must in (0 - 60)'; 8 } 9 }10 11 $age = 10;12 try {13 $age = intval($age);14 if($age > 60) {15 throw new zyfException($age);16 }17 18 } catch (zyfException $e) {19 echo $e->errorzyfMessage();20 21 }22 ?>
2:异常嵌套
异常嵌套是比较常见的写法,在自定义的异常处理中,try 块中可以定义多个异常捕获,然后分层传递异常,理解和冒泡差不多,看下面的实现:
1 <?php 2 $age = 10; 3 try { 4 $age = intval($age); 5 if($age > 60) { 6 throw new zyfException($age); 7 } 8 9 if ($age <= 0) {10 throw new Exception($age . ' must > 0');11 }12 13 } catch (zyfException $e) {14 echo $e->errorzyfMessage();15 16 } catch(Exception $e) {17 echo $e->getMessage();18 }19 ?>
当然也可以在catch中再抛出异常给上层:
1 <?php 2 $age = 100; 3 try { 4 try { 5 $age = intval($age); 6 if($age > 60) { 7 throw new Exception($age); 8 } 9 10 } catch (Exception $e) {11 throw new zyfException($age);12 13 }14 15 } catch (zyfException $e) {16 echo $e->errorzyfMessage();17 }18 ?>
六、PHP7中的异常处理
现在写PHP必须考虑版本情况,上面的写法在PHP7中大部分都能实现,但是也会有不同点,在PHP7更新中有一条:更多的Error变为可捕获的Exception,现在的PHP7实现了一个全局的throwable接口,原来老的Exception和其中一部分Error实现了这个接口(interface),PHP7中更多的Error变为可捕获的Exception返回给捕捉器,这样其实和前面提到的扩展try-catch影响范围一样,但是如果不捕获则还是按照Error对待,看下面两个:
1 <?php 2 try { 3 test(); 4 5 } catch(Throwable $e) { 6 echo $e->getMessage() . ' zyf'; 7 } 8 9 try {10 test();11 12 } catch(Error $e) {13 echo $e->getMessage() . ' zyf';14 }15 ?>
因为PHP7实现了throwable接口,那么就可以使用第一个这种方式来捕获异常。又因为部分Error实现了接口,并且更多的Error变为可捕获的Exception,那么就可以使用第二种方式来捕获异常。下面是在网上找的PHP7的异常层次树:
Throwable
Exception 异常
...
Error 错误
ArithmeticError 算数错误
DivisionByZeroError 除数为0的错误
AssertionError 声明错误
ParseError 解析错误
TypeError 类型错误
就写到这吧,写得手疼,关于错误和异常处理的大致就写这么多,有什么错误请在评论中给出,多谢大家
위 내용은 PHP의 오류 및 예외 처리 예의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!