一、PHP的错误和异常处理
1、异常与错误的概述
PHP中什么是异常:
程序在运行中出现不符合预期的情况,允许发生(你也不想让他出现不正常的情况)但他是一种不正常的情况,按照我们的正常逻辑本不该出的错误,但仍然会出现的错误,属于逻辑和业务流程的错误,而不是编译或者语法上的错误。
PHP中什么是错误:
属于php脚本自身的问题,大部分情况是由错误的语法,服务器环境导致,使得编译器无法通过检查,甚至无法运行的情况。warning、notice都是错误,只是他们的级别不同而已,并且错误是不能被try-catch捕获的。
2、ERROR的级别
只有熟悉错误级别才能对错误捕捉有更好的认识。 ERROR有不同的错误级别:
Fatal Error:致命错误(脚本终止运行) E_ERROR // 致命的运行错误,错误无法恢复,暂停执行脚本 E_CORE_ERROR // PHP启动时初始化过程中的致命错误 E_COMPILE_ERROR // 编译时致命性错,就像由Zend脚本引擎生成了一个E_ERROR E_USER_ERROR // 自定义错误消息。像用PHP函数trigger_error(错误类型设置为:E_USER_ERROR) Parse Error:编译时解析错误,语法错误(脚本终止运行) E_PARSE //编译时的语法解析错误 Warning Error:警告错误(仅给出提示信息,脚本不终止运行) E_WARNING // 运行时警告 (非致命错误)。 E_CORE_WARNING // PHP初始化启动过程中发生的警告 (非致命错误) 。 E_COMPILE_WARNING // 编译警告 E_USER_WARNING // 用户产生的警告信息 Notice Error:通知错误(仅给出通知信息,脚本不终止运行) E_NOTICE // 运行时通知。表示脚本遇到可能会表现为错误的情况. E_USER_NOTICE // 用户产生的通知信息。
由此可知有5类是产生ERROR级别的错误,这种错误直接导致PHP程序退出。可以定义成: ERROR = E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR | E_USER_ERROR | E_PARSE
如果希望在PHP脚本中,遇到以上某个错误时,报告错误消息,必须在配置文件php.ini中将display_errors指令的值设置为On,来开启PHP输出错误报告的功能。
在PHP脚本中调用ini_set()函数,强制在该脚本中启动display_errors指令。并通过error_reporting()函数设置错误级别为E_ALL,报告所有错误警告和注意。
3、自定义错误处理
1:set_error_handler()
看到这个名字估计就知道什么意思了,这个函数用于捕获错误,设置一个用户自定义的错误处理函数。
<?php set_error_handler('zyferror');//使用此句把错误的处理交给用户自定义的函数zyferror();处理,error_reporting()函数将会失效。 function zyferror($type, $message, $file, $line) { var_dump('<b>set_error_handler: ' . $type . ':' . $message . ' in ' . $file . ' on ' . $line . ' line .</b><br />'); } ?>
当程序出现错误的时候自动调用此方法,不过需要注意一下两点:第一,如果存在该方法,相应的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级别的错误。
并且他有多种调用的方法:
<?php // 直接传函数名 NonClassFunction set_error_handler('function_name'); // 传 class_name && function_name set_error_handler(array('class_name', 'function_name')); ?>
2:register_shutdown_function()
捕获PHP的错误:Fatal Error、Parse Error等,这个方法是PHP脚本执行结束前最后一个调用的函数,比如脚本错误、die()、exit、异常、正常结束都会调用,多么牛逼的一个函数啊!通过这个函数就可以在脚本结束前判断这次执行是否有错误产生,这时就要借助于一个函数:error_get_last();这个函数可以拿到本次执行产生的所有错误。error_get_last();返回的信息:
[type] - 错误类型
[message] - 错误消息
[file] - 发生错误所在的文件
[line] - 发生错误所在的行
<?php register_shutdown_function('zyfshutdownfunc');//在退出脚本前,调用并执行在该函数中注册的函数。 function zyfshutdownfunc() { if ($error = error_get_last()) //通过error_get_last()函数获取错误信息,赋值给$error数组,通过下面语句打印输出错误信息
{ var_dump('<b>register_shutdown_function: Type:' . $error['type'] . ' Msg: ' . $error['message'] . ' in ' . $error['file'] . ' on line ' . $error['line'] . '</b>'); } }
var_dump(23+-+); //此处语法错误
echo "test";//函数会在输出此句后,打印出上面一句的错误
?>
通过这种方法就可以巧妙的打印出程序结束前所有的错误信息。
3:set_exception_handler()
设置默认的异常处理程序,用在没有用try/catch块来捕获的异常,也就是说不管你抛出的异常有没有人捕获,如果没有人捕获就会进入到该方法中,并且在回调函数调用后异常会中止。看一下用法:
1 <?php 2 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 ?>
以上内容来自: http://www.cnblogs.com/zyf-zhaoyafei/p/6928149.html。
4、错误日志
对于PHP开发者来说,一旦某个产品投入使用,应该立即将display_errors选项关闭,以免因为这些错误所透露的路径、数据库连接、数据表等信息而遭到黑客攻击。但是,任何一个产品在投入使用后,都难免会有错误出现,那么如何记录一些对开发者有用的错误报告呢?我们可以在单独的文本文件中将错误报告作为日志记录。错误日志的记录,可以帮助开发人员或者管理人员查看系统是否存在问题。
如果需要将程序中的错误报告写入错误日志中,只要在PHP的配置文件中,将配置指令log_errors开启即可。错误报告默认就会记录到Web服务器的日志文件里,例如记录到Apache服务器的错误日志文件error.log中。当然也可以记录错误日志到指定的文件中或发送给系统syslog,分别介绍如下:
1.使用指定的文件记录错误报告日志
如果使用自己指定的文件记录错误日志,一定要确保将这个文件存放在文档根目录之外,以减少遭到攻击的可能。并且该文件一定要让PHP脚本的执行用户(Web服务器进程所有者)具有写权限。假设在Linux操作系统中,将/usr/local/目录下的error.log文件作为错误日志文件,并设置Web服务器进程用户具有写的权限。然后在PHP的配置文件中,将error_log指令的值设置为这个错误日志文件的绝对路径。需要将php.ini中的配置指令做如下修改:
error_reporting = E_ALL ;将会向PHP报告发生的每个错误 display_errors = Off ;不显示满足上条指令所定义规则的所有错误报告 log_errors = On ;决定日志语句记录的位置 log_errors_max_len = 1024 ;设置每个日志项的最大长度 error_log = /usr/local/error.log ;指定产生的错误报告写入的日志文件位置 |
PHP的配置文件按上面的方式设置完成以后,并重新启动Web服务器。这样,在执行PHP的任何脚本文件时,所产生的所有错误报告都不会在浏览器中显示,而会记录在自己指定的错误日志/usr/local/error.log中。此外,不仅可以记录满足error_reporting所定义规则的所有错误,而且还可以使用PHP中的error_log()函数,送出一个用户自定义的错误信息。该函数的原型如下所示:
bool error_log ( string message [, int message_type [, string destination [, string extra_headers]]] ) |
此函数会送出错误信息到Web服务器的错误日志文件、某个TCP服务器或到指定文件中。该函数执行成功则返回TRUE,失败则返回FALSE。第一个参数message 是必选项,即为要送出的错误信息。如果仅使用这一个参数,会按配置文件php.ini中所设置的位置处发送消息。第二个参数message_type为整数值:0表示送到操作系统的日志中;1则使用PHP的Mail()函数,发送信息到某E-mail处,第四个参数extra_headers亦会用到;2则将错误信息送到TCP 服务器中,此时第三个参数destination表示目的地IP及Port;3则将信息存到文件destination中。
5、 异常处理
异常(Exception)处理用于在指定的错误发生时改变脚本的正常流程,是PHP 5中的一个新的重要特性。异常处理是一种可扩展、易维护的错误处理统一机制,并提供了一种新的面向对象的错误处理方式。
1.异常处理实现
异常处理和编写程序的流程控制相似,所以也可以通过异常处理实现一种另类的条件选择结构。异常就是在程序运行过程中出现的一些意料之外的事件,如果不对此事件进行处理,则程序在执行时遇到异常将崩溃。处理异常需要在PHP脚本中使用以下语句:
try { //所有需要进行异常处理的代码都必须放入这个代码块内 … … //在这里可以使用throw语句抛出一个异常对象 }catch(ex1) { //使用该代码块捕获一个异常,并进行处理 … … //处理发生的异常,也可再次抛出异常 }
在PHP代码中所产生的异常可以被throw语句抛出并被catch语句捕获。需要进行异常处理的代码都必须放入try代码块内,以便捕获可能存在的异常。每一个try至少要有一个与之对应的catch,也不能出现单独的catch,另外try和cache之间也不能有任何的代码出现。一个异常处理的简单实例如下所示:
<?php try { $error = 'Always throw this error'; throw new Exception($error); //创建一个异常对象,通过throw语句抛出 echo 'Never executed'; //从这里开始,try代码块内的代码将不会再被执行 } catch (Exception $e) { echo 'Caught exception: ', $e->getMessage(), " "; //输出捕获的异常消息 } echo 'Hello World'; //程序没有崩溃继续向下执行 ?>
在上面的代码中,如果try代码块中出现某些错误,我们就可以执行一个抛出异常的操作。在某些编程语言中,例如Java,在出现异常时将自动抛出异常。而在PHP中,异常必须手动抛出。throw关键字将触发异常处理机制,它是一个语言结构,而不是一个函数,但必须给它传递一个对象作为值。在最简单的情况下,可以实例化一个内置的Exception类,就像以上代码所示那样。如果在try语句中有异常对象被抛出,该代码块不会再继续向下执行,而直接跳转到catch中执行。并传递给catch代码块一个对象,也可以理解为被catch代码块捕获的对象,其实就是导致异常被throw语句抛出的对象。在catch代码块中可以简单的输出一些异常的原因,也可以是try代码块中任务的另一个版本解决方案,此外,也可以在这个catch代码块中产生新的异常。最重要的是,在异常处理之后,程序不会崩溃,而会继续执行。
6、捕获多个异常
在try代码块之后,必须至少给出一个catch代码块,也可以将多个catch代码块与一个try代码块进行关联。如果每个catch代码块可以捕获一个不同类型的异常,那么使用多个catch就可以捕获不同的类所产生的异常。当产生一个异常时,PHP将查询一个匹配的catch代码块。如果有多个catch代码块,传递给每一个catch代码块的对象必须具有不同的类型,这样PHP可以找到需要进入哪一个catch代码块。当try代码块不再抛出异常或者找不到catch能匹配所抛出的异常时,PHP代码就会在跳转到最后一个 catch 的后面继续执行。多个异常的捕获的示例如下:
内置的异常处理类(Exception)
<?php /* 自定义的一个异常处理类,但必须是扩展内异常处理类的子类 */ class MyException extends Exception{ //重定义构造器使第一个参数 message 变为必须被指定的属性 public function __construct($message, $code=0){ //可以在这里定义一些自己的代码 //建议同时调用 parent::construct()来检查所有的变量是否已被赋值 parent::__construct($message, $code); } //重写父类中继承过来的方法,自定义字符串输出的样式 public function __toString() { return __CLASS__.":[".$this->code."]:".$this->message."<br>"; } //为这个异常自定义一个处理方法 public function customFunction() { echo "按自定义的方法处理出现的这个类型的异常"; } } /* 创建一个用于测试自定义扩展的异常类MyException */ class TestException { public $var; //一个成员属性,用来判断对象是否创建成功被初始化 function __construct($value=0) { //通过构造方法的传值决定抛出的异常 switch($value){ //对传入的值进行选择性的判断 case 1: //如果传入的参数值为1,则抛出自定义的异常对象 throw new MyException("传入的值“1” 是一个无效的参数", 5); break; case 2: //如果传入的参数值为2,则抛出PHP内置的异常对象 throw new Exception("传入的值“2”不允许作为一个参数", 6); break; default: //如果传入的参数值合法,则不抛出异常创建对象成功 $this->var=$value; //为对象中的成员属性赋值 break; } } } //示例1,在没有异常时,程序正常执行,try中的代码全部执行并不会执行任何catch区块 try{ $testObj=new TestException(); //使用默认参数创建异常的测试类对象 echo "***********<br>"; //没有抛出异常这条语句就会正常执行 }catch(MyException $e){ //捕获用户自定义的异常区块 echo "捕获自定义的异常:$e <br>"; //按自定义的方式输出异常消息 $e->customFunction(); //可以调用自定义的异常处理方法 }catch(Exception $e) { //捕获PHP内置的异常处理类的对象 echo "捕获默认的异常:".$e->getMessage()."<br>"; //输出异常消息 } var_dump($testObj); //判断对象是否创建成功,如果没有任何异常,则创建成功 //示例2,抛出自定义的异常,并通过自定义的异常处理类捕获这个异常并处理 try{ $testObj1=new TestException(1); //传入参数1时,创建测试类对象抛出自定义异常 echo "
以上部分内容来自:http://www.cnblogs.com/laojie4321/p/4187620.html。