任何程序员在开发时均可能遇到过一些失误,或其余缘由形成错误的发生。固然,用户若是不肯意或不遵循应用程序的约束,也会在使用时引发一些错误发生。PHP程序的错误发生通常归属于下列三个领域。php
Ø 语法错误html
语法错误最多见,而且最容易修复。例如,遗漏了一个分号,就会显示错误信息。这类错误会阻止脚本执行。一般发生在程序开发时,能够经过错误报告进行修复,再从新运行。程序员
Ø 运行时错误web
这种错误通常不会阻止PHP脚本的运行,可是会阻止脚本作但愿它所作的任何事情。例如,在调用header()函数前若是有字符输出,PHP一般会显示一条错误消息,虽然PHP脚本继续运行,但header()函数并无执行成功。数据库
Ø 逻辑错误编程
这种错误其实是最麻烦的,不但不会阻止PHP脚本的执行,也不会显示出错误消息。例如,在if语句中判断两个变量的值是否相等,若是错把比较运行符号“==”写成赋值运行符号“=”就是一种逻辑错误,很难会被发现。数组
一个异常则是在一个程序执行过程当中出现的一个例外,或是一个事件,它中断了正常指令的运行,跳转到其余程序模块继续执行。因此异常处理常常被当作程序的控制流程使用。不管是错误仍是异常,应用程序都必须可以以妥善的方式处理,并作出相应的反应,但愿不要丢失数据或者致使程序崩溃。浏览器
运行PHP脚本时,PHP解析器会尽其所能地报告它遇到的问题。在PHP中错误报告的处理行为,都是经过PHP的配置文件php.ini中有关的配置指令肯定的。另外PHP的错误报告有不少种级别,能够根据不一样的错误报告级别提供对应的调试方法。一旦把PHP设置成呈现出发生了哪些错误,你可能想调整错误报告的级别。在表10-1中列出了PHP中大多数的错误报告级别。安全
表10-1 PHP的错误报告级别服务器
<DIV align=center>
级别常量 |
错误报告描述 |
E_ERROR |
致命的运行时错误(它会阻止脚本的执行) |
E_WARNING |
运行时警告(非致命的错误) |
E_PARSE |
从语法中解析错误 |
E_NOTICE |
运行时注意消息(多是或者可能不是一个问题) |
E_CORE_ERROR |
相似E_ERROR,但不包括PHP核心形成的错误 |
E_CORE_WARNING |
相似E_WARNING,但不包括PHP核心错误警告 |
E_COMPILE_ERROR |
致命的编译时错误 |
E_COMPILE_WARNING |
致命的编译时警告 |
E_USER_ERROR |
用户致使的错误消息 |
E_USER_WARNING |
用户致使的警告 |
E_USER_NOTICE |
用户致使的注意消息 |
E_ALL |
全部的错误、警告和注意 |
E_STRICT |
关于PHP版本移植的兼容性和互操做性建议 |
</DIV>
若是用户但愿在PHP脚本中,遇到上表中的某个级别的错误时,将错误消息报告给用户。则必须在配置文件php.ini中,将display_errors指令的值设置为On,开启PHP输出错误报告的功能。也能够在PHP脚本中调用ini_set()函数,动态设置配置文件php.ini中的某个指令。若是display_errors被启用,就会显示知足已设置的错误级别的全部错误。当用户在访问时,看到显示的这些消息不只会感到迷惑,并且还可能会过多地泄露有关服务器的信息,使服务器变得很不安全。因此在项目开发或测试期间启用此指令,能够根据不一样的错误报告更好的调试程序。出于安全性和美感的目的,让公众用户查看PHP的详细出错消息通常是不明智的,因此在网站投入使用时要将其禁用。
当你正在开发站点时,你将但愿PHP报告特定类型的错误,能够经过调整错误报告的级别实现,能够经过如下两种方法设置错误报告级别。
Ø 能够经过在配置文件php.ini中,修改配置指令error_reporting的值,修改为功后从新启动Web服务器,则每一个PHP脚本均可以按调整后的错误级别输出错误报告。下面是修改php.ini配置文件的示例,列出几种为error_reporting指令设置不一样级别值的方式,能够把位运算符[&(与)、|(或)、~(非)]和错误级别常量一块儿使用。以下所示:
<DIV align=center>
; 能够抛出任何非注意的错误,默认值 error_reporting = E_ALL & ~E_NOTICE ; 只考虑致命的运行时错误、解析错误和核心错误 ; error_reporting = E_ERROR | E_PARSE | E_CORE_ERROR ; 报告除用户致使的错误以外的全部错误 ; error_reporting = E_ALL & ~(E_USER_ERROR | E_USER_WARNING | E_USER_NOTICE) |
</DIV>
Ø 或者能够在PHP脚本中使用error_reporting()函数,基于各个脚原本调整这种行为。这个函数用于肯定PHP应该在特定的页面内报告哪些类型的错误。该函数获取一个数字或上表中错误级别常量做为参数。以下所示:
<DIV align=center>
error_reporting(0); //设置为0会彻底关闭错误报告 error_reporting (E_ALL); //将会向PHP报告发生的每一个错误 error_reporting (E_ALL & ~E_NOTICE); //能够抛出任何非注意的错误报告 |
</DIV>
在下面的示例中,咱们在PHP脚本中分别建立出一个“注意”、一个“警告”和一个致命“错误”。并经过设置不一样的错误级别,限制程序输出没有被容许的错误报告。建立一个名为error.php的脚本文件,代码以下所示:
<DIV align=center>
<html> <head><title>测试错误报告</title></head> <body> <h2>测试错误报告</h2> <?php /*开启php.ini中的display_errors指令,只有该指令开启若是有错误报告才能输出*/ ini_set('display_errors',1); /*经过error_reporting()函数设置在本脚本中,输出全部级别的错误报告*/ error_reporting(E_ALL); /*“注意(notice)”的报告,不会阻止脚本的执行,而且可能不必定是一个问题 */ getType($var); //调用函数时提供的参数变量没有在以前声明 /*“警告(warning)”的报告,指示一个问题,可是不会阻止脚本的执行 */ getType(); //调用函数时没有提供必要的参数 /*“错误(error)”的报告,它会终止程序,脚本不会再向下执行 */ get_Type(); //调用一个没有被定义的函数 ?> </body> </html> |
</DIV>
在上面的脚本中,为了确保配置文件中的display_errors指令开启,经过ini_set()函数强制在该脚本执行中启动,并经过error_repoting()函数设置错误级别为E_ALL,报告全部错误、警告和注意。并在脚本中分别建立出注意、警告和错误,PHP脚本只有在遇到错误时才会终止运行,输出的错误报告结果如图10-1所示。
图10-1 输出错误报告结果的演示
“注意”和“警告”的错误报告并不会终止程序运行。若是在上面的输出结果中,不但愿有注意和警告的报告输出,就能够在脚本error.php中修改error_repoting()函数,修改的代码以下所示:
<DIV align=center>
error_reporting(E_ALL&~(E_WARNING | E_NOTICE)); //报告除注意和警告以外的全部错误 |
</DIV>
脚本error.php被修改之后并从新运行,在输出的结果中就只剩下一条错误报告了,如图10-2所示。
图10-2 屏蔽“注意”和“警告”后的输出结果
除了使用error_reporting和display_error两个配置指令能够修改错误报告行为之外,还有许多配置指令能够肯定PHP的错误报告行为。其余的一些重要的指令如表10-2所示。
表10-2 肯定PHP错误报告行为的配置指令
<DIV align=center>
配置指令 |
描 述 |
默 认 值 |
display_startup_errors |
是否显示PHP引擎在初始化时遇到的全部错误 |
Off |
log_errors |
肯定日志语句记录的位置 |
Off |
error_log |
设置错误能够发送到syslog中 |
Null |
log_errors_max_len |
每一个日志项的最大长度,以字节为单位,设置0表示指定最大长度 |
1024 |
ignore_repeated_errors |
是否忽略同一文件、同一行发生的重复错误消息 |
Off |
ignore_repeated_source |
忽略不一样文件中或同一文件中不一样行上发生的重复错误 |
Off |
track_errors |
启动该指令会使PHP在$php_errormsg中存储最近发生的错误信息 |
Off |
</DIV>
对于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中的配置指令作以下修改:
<DIV align=center>
error_reporting = E_ALL ;将会向PHP报告发生的每一个错误 display_errors = Off ;不显示知足上条指令所定义规则的全部错误报告 log_errors = On ;决定日志语句记录的位置 log_errors_max_len = 1024 ;设置每一个日志项的最大长度 error_log = /usr/local/error.log ;指定产生的错误报告写入的日志文件位置 |
</DIV>
PHP的配置文件按上面的方式设置完成之后,并从新启动Web服务器。这样,在执行PHP的任何脚本文件时,所产生的全部错误报告都不会在浏览器中显示,而会记录在本身指定的错误日志/usr/local/error.log中。此外,不只能够记录知足error_reporting所定义规则的全部错误,并且还可使用PHP中的error_log()函数,送出一个用户自定义的错误信息。该函数的原型以下所示:
<DIV align=center>
bool error_log ( string message [, int message_type [, string destination [, string extra_headers]]] ) |
</DIV>
此函数会送出错误信息到Web服务器的错误日志文件、某个TCP服务器或到指定文件中。该函数执行成功则返回TRUE,失败则返回FALSE。第一个参数message 是必选项,即为要送出的错误信息。若是仅使用这一个参数,会按配置文件php.ini中所设置的位置处发送消息。第二个参数message_type为整数值:0表示送到操做系统的日志中;1则使用PHP的Mail()函数,发送信息到某E-mail处,第四个参数extra_headers亦会用到;2则将错误信息送到TCP 服务器中,此时第三个参数destination表示目的地IP及Port;3则将信息存到文件destination中。若是以登入Oracle数据库出现问题的处理为例,该函数的使用以下所示:
<DIV align=center>
<?php if(!Ora_Logon($username, $password)){ error_log("Oracle数据库不可用!", 0); //将错误消息写入到操做系统日志中 } if(!($foo=allocate_new_foo()){ error_log("出现大麻烦了!", 1, "webmaster@www.mydomain.com"); //发送到管理员邮箱中 } error_log("搞砸了!", 2, "localhost:5000"); //发送到本机对应5000端口的服务器中 error_log("搞砸了!", 3, "/usr/local/errors.log"); //发送到指定的文件中 ?> |
</DIV>
2.错误信息记录到操做系统的日志里
错误报告也能够被记录到操做系统日志里,但不一样的操做系统之间的日志管理有点区别。在Linux上错误语句将送往syslog,而在Windows上错误将发送到事件日志里。若是你不熟悉syslog,起码要知道它是基于UNIX的日志工具,它提供了一个API来记录与系统和应用程序执行有关的消息。Windows事件日志实际上与UNIX的syslog相同,这些日志一般能够经过事件查看器来查看。若是但愿将错误报告写到操做系统的日志里,能够在配置文件中将error_log指令的值设置为syslog。具体须要在php.ini中修改的配置指令以下所示:
<DIV align=center>
error_reporting = E_ALL ;将会向PHP报告发生的每一个错误 display_errors = Off ;不显示知足上条指令所定义规则的全部错误报告 log_errors = On ;决定日志语句记录的位置 log_errors_max_len = 1024 ;设置每一个日志项的最大长度 error_log = syslog ;指定产生的错误报告写入操做系统的日志里 |
</DIV>
除了通常的错误输出以外,PHP还容许向系统syslog中发送定制的消息。虽然经过前面介绍的error_log()函数,也能够向syslog中发送定制的消息,但在PHP中为这个特性提供了须要一块儿使用的4个专用函数。分别介绍以下:
Ø define_syslog_variables()
在使用openlog()、syslog及closelog()三个函数以前必须先调用该函数。由于在调用该函数时,它会根据如今的系统环境为下面三个函数初使用化一些必需的常量。
Ø openlog()
打开一个和当前系统中日志器的链接,为向系统插入日志消息作好准备。并将提供的第一个字符串参数插入到每一个日志消息中,该函数还须要指定两个将在日志上下文使用的参数,能够参考官方文档使用。
Ø syslog()
该函数向系统日志中发送一个定制消息。须要两个必选参数,第一个参数经过指定一个常量定制消息的优先级。例如LOG_WARNING表示通常的警告,LOG_EMERG表示严重地能够预示着系统崩溃的问题,一些其余的表示严重程度的常量能够参考官方文档使用。第二个参数则是向系统日志中发送的定制消息,须要提供一个消息字符串,也能够是PHP引擎在运行时提供的错误字符串。
Ø closelog()
该函数在向系统日志中发送完成定制消息之后调用,关闭由openlog()函数打开的日志链接。
若是在配置文件中,已经开启向syslog发送定制消息的指令,就可使用前面介绍的四个函数发送一个警告消息到系统日志中,并经过系统中的syslog解析工具,查看和分析由PHP程序发送的定制消息,以下所示:
<DIV align=center>
<?php define_syslog_variables(); openlog("PHP5", LOG_PID , LOG_USER); syslog(LOG_WARNING, "警告报告向syslog中发送的演示,警告时间:".date("Y/m/d H:i:s")); closelog(); ?> |
</DIV>
以Windows系统为例,经过右击“个人电脑”选择管理选项,而后到系统工具菜单中,选择事件查看器,再找到应用程序选项,就能够看到咱们本身定制的警告消息了。上面这段代码将在系统的syslog文件中,生成相似下面的一条信息,是事件的一部分:
<DIV align=center>
PHP5[3084], 警告报告向syslog中发送的演示,警告时间:2009/03/26 04:09:11. |
</DIV>
使用指定的文件仍是使用syslog记录错误日志,取决于你所在的Web服务器环境。若是你能够控制Web服务器,使用syslog是最理想的,由于你能利用syslog的解析工具来查看和分析日志。但若是你的网站在共享服务器的虚拟主机中运行,就只有使用单独的文本文件记录错误日志了。
异常(Exception)处理用于在指定的错误发生时改变脚本的正常流程,是PHP 5中的一个新的重要特性。异常处理是一种可扩展、易维护的错误处理统一机制,并提供了一种新的面向对象的错误处理方式。在Java、C#及Python等
语言中很早就提供了这种异常处理机制,若是你对哪种语言中的异常处理熟悉,那对PHP中提供的异常处理机制也不会陌生。
1.异常处理实现
异常处理和编写程序的流程控制类似,因此也能够经过异常处理实现一种另类的条件选择结构。异常就是在程序运行过程当中出现的一些意料以外的事件,若是不对此事件进行处理,则程序在执行时遇到异常将崩溃。处理异常须要在PHP脚本中使用如下语句:
<DIV align=center>
try { //全部须要进行异常处理的代码都必须放入这个代码块内 … … //在这里可使用throw语句抛出一个异常对象 }catch(ex1) { //使用该代码块捕获一个异常,并进行处理 … … //处理发生的异常,也可再次抛出异常 } |
</DIV>
在PHP代码中所产生的异常能够被throw语句抛出并被catch语句捕获。须要进行异常处理的代码都必须放入try代码块内,以便捕获可能存在的异常。每个try至少要有一个与之对应的catch,也不能出现单独的catch,另外try和cache之间也不能有任何的代码出现。一个异常处理的简单实例以下所示:
<DIV align=center>
<?php try { $error = 'Always throw this error'; throw new Exception($error); //建立一个异常对象,经过throw语句抛出 echo 'Never executed'; //从这里开始,try代码块内的代码将不会再被执行 } catch (Exception $e) { echo 'Caught exception: ', $e->getMessage(), "\n"; //输出捕获的异常消息 } echo 'Hello World'; //程序没有崩溃继续向下执行 ?> |
</DIV>
在上面的代码中,若是try代码块中出现某些错误,咱们就能够执行一个抛出异常的操做。在某些编程语言中,例如Java,在出现异常时将自动抛出异常。而在PHP中,异常必须手动抛出。throw关键字将触发异常处理机制,它是一个语言结构,而不是一个函数,但必须给它传递一个对象做为值。在最简单的状况下,能够实例化一个内置的Exception类,就像以上代码所示那样。若是在try语句中有异常对象被抛出,该代码块不会再继续向下执行,而直接跳转到catch中执行。并传递给catch代码块一个对象,也能够理解为被catch代码块捕获的对象,其实就是致使异常被throw语句抛出的对象。在catch代码块中能够简单的输出一些异常的缘由,也能够是try代码块中任务的另外一个版本解决方案,此外,也能够在这个catch代码块中产生新的异常。最重要的是,在异常处理以后,程序不会崩溃,而会继续执行。
2.扩展PHP内置的异常处理类
在try代码块中,须要使用throw语句抛出一个异常对象,才能跳转到catch代码块中执行,并在catch代码块中捕获并使用这个异常类的对象。虽然在PHP中提供的内置异常处理类Exception,已经具备很是不错的特性,但在某些状况下,可能还要扩展这个类来获得更多的功能。因此用户能够用自定义的异常处理类来扩展PHP内置的异常处理类。如下的代码说明了在内置的异常处理类中,哪些属性和方法在子类中是可访问和可继承的:
内置的异常处理类(Exception)
<DIV align=center>
<?php class Exception { protected $message = 'Unknown exception'; //异常信息 protected $code = 0; //用户自定义异常代码 protected $file; //发生异常的文件名 protected $line; //发生异常的代码行号
function __construct($message = null, $code = 0){} //构造方法
final function getMessage(){} //返回异常信息 final function getCode(){} //返回异常代码 final function getFile(){} //返回发生异常的文件名 final function getLine(){} //返回发生异常的代码行号 final function getTrace(){} //backtrace() 数组 final function getTraceAsString(){} //已格成化成字符串的 getTrace() 信息
/* 可重载的方法 */ function __toString(){} //可输出的字符串 } ?> |
</DIV>
上面这段代码只为说明内置异常处理类Exception的结构,它并非一段有实际意义的可用代码。若是使用自定义的类做为异常处理类,则必须是扩展内置异常处理类Exception的子类,非Exception类的子类是不能做为异常处理类使用的。若是在扩展内置处理类Exception时从新定义构造函数的话,建议同时调用parent::construct()来检查全部的变量是否已被赋值。当对象要输出字符串的时候,能够重载__toString()并自定义输出的样式。能够在自定义的子类中,直接使用内置异常处理Exception类中的全部成员属性,但不能从新改写从该父类中继承过来的成员方法,由于该类的大多数公有方法都是final的。
建立自定义的异常处理程序很是简单,和传统类的声明方式相同,但该类必须是内置异常处理类Exception的一个扩展。当PHP中发生异常时,可调用自定义异常类中的方法进行处理。建立一个自定义的MyException类,继承了内置异常处理类Exception中的全部属性,并向其添加了自定义的方法。代码及应用以下所示:
扩展PHP内置的异常处理类的应用
<DIV align=center>
<?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 "按自定义的方法处理出现的这个类型的异常<br>"; } }
try { //使用自定义的异常类捕获一个异常,并处理异常 $error = '容许抛出这个错误'; throw new MyException($error); //建立一个自定义的异常类对象,经过throw语句抛出 echo 'Never executed'; //从这里开始,try代码块内的代码将不会再被执行 } catch (MyException $e) { //捕获自定义的异常对象 echo '捕获异常: '.$e; //输出捕获的异常消息 $e->customFunction(); //经过自定义的异常对象中的方法处理异常 } echo '你好呀'; //程序没有崩溃继续向下执行 ?> |
</DIV>
在自定义的MyException类中,使用父类中的构造方法检查全部的变量是否已被赋值。并且重载了父类中的__toString()方法,输出本身定制捕获的异常消息。自定义和内置的异常处理类,在使用上没有多大区别,只不过在自定义的异常处理类中,能够调用为具体的异常专门编写的处理方法。
3.捕获多个异常
在try代码块以后,必须至少给出一个catch代码块,也能够将多个catch代码块与一个try代码块进行关联。若是每一个catch代码块能够捕获一个不一样类型的异常,那么使用多个catch就能够捕获不一样的类所产生的异常。当产生一个异常时,PHP将查询一个匹配的catch代码块。若是有多个catch代码块,传递给每个catch代码块的对象必须具备不一样的类型,这样PHP能够找到须要进入哪个catch代码块。当try代码块再也不抛出异常或者找不到catch能匹配所抛出的异常时,PHP代码就会在跳转到最后一个 catch 的后面继续执行。多个异常的捕获的示例以下:
内置的异常处理类(Exception)
<DIV align=center>
<?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 " |