PHP的错误机制也很复杂。从事PHP多年,一直没有认真总结过。现在我来补上这节课。特别说明:文章PHP版本使用错误级别5.5.32PHP。首先,您需要了解PHP中的错误是什么。从php5.5开始,一共有16个错误级别注意:尝试以下代码时请务必打开error_log:error_reporting(E_ALL);ini_set('display_errors','On');E_ERROR这个错误是致命错误,会在页面上显示FatalError。当出现这样的错误时,程序就无法继续执行。错误示例://Fatalerror:Calltounddefinedfunctionhpinfo()in/tmp/php/index.phponline5hpinfo();//E_ERROR注意,如果有Uncaught异常也会触发这个级别。//致命错误:Uncaughtexception'Exception'withmessage'testexception'in/tmp/php/index.php:5Stacktrace:#0{main}throwin/tmp/php/index.phponline5thrownew\Exception("testexception");E_WARNING这个错误只是一个警告,脚本不会终止,程序会继续,显示的错误信息是Warning。例如,包括一个不存在的文件。//Warning:include(a.php):failedtoopenstream:Nosuchfileordirectoryin/tmp/php/index.phponline7//Warning:include():Failedopening'a.php'forinclusion(include_path='.:/usr/share/pear:/usr/share/php')in/tmp/php/index.phponline7include("a.php");//E_WARNINGE_NOTICE这种错误比较轻微,提醒你这个地方不要这样写。这也是一个运行时错误。错误的代码可能在其他地方没有问题,但在当前上下文中有问题。比如$b变量不存在,我们将其赋值给另一个变量发现错误,无法解析。例如,下面的z未设置为变量。//Parseerror:syntaxerror,unexpected'='in/tmp/php/index.phponline20z=1;//E_PARSEE_STRICT这个错误是PHP5之后引入的,你的代码可以运行,但是不是PHP建议的方式。例如在函数参数中传递++符号//StrictStandards:Onlyvariableshouldbepassedbyreferencein/tmp/php/index.phponline17functionchange(&$var){$var+=10;}$var=1;change(++$var);//E_STRICTE_RECOVERABLE_ERROR这个级别其实是ERROR级别,但是预计会被捕获。如果没有被错误处理捕获到,性能和E_ERROR一样。经常会出现形参定义了类型,调用的时候却传入了错误的类型。它的错误提示在E_ERROR的致命错误前面也有一个Catachable字样。//Catchablefatalerror:Argument1passedtotestCall()mustbeaninstanceofA,instanceofBgiven,calledin/tmp/php/index.phponline37anddefinedin/tmp/php/index.phponline33classA{}classB{}functiontestCall(A$a){}$b=newB();testCall($b);E_DEPRECATED这个错误说明你使用的是老版本的函数,以后的版本可能会禁用或者不维护这个函数。例如curl的CURLOPT_POSTFIELDS使用\@FILENAME上传文件upload.php");curl_setopt($ch,CURLOPT_POSTFIELDS,array('fileupload'=>'@'."test"));E_CORE_ERROR,E_CORE_WARNING这两个错误是PHP引擎在PHP初始化过程中产生的。E_COMPILE_ERROR,E_COMPILE_WARNING这两个错误是由PHP引擎产生的,发生在编译过程中。E_USER_ERROR、E_USER_WARNING、E_USER_NOTICE、E_USER_DEPRECATED,这些错误都是用户犯的,使用trigger_error,这里相当于给用户触发各种错误类型的一个洞。这是逃避trycatch异常的好方法。trigger_error("Cannotdividebyzero",E_USER_ERROR);//E_USER_ERROR//E_USER_WARING//E_USER_NOTICE//E_USER_DEPRECATEDE_ALLE_STRICT所有传出错误和警告消息。错误控制php中有很多配置和参数可以控制错误,显示错误日志。第一步,我们需要了解的是php中有哪些错误的配置?按照php+php-fpm的模式,其实有两个配置文件会影响php的错误显示,一个是php本身的配置文件php.ini,一个是php-fpm的配置文件,php-fpm.conf。php.ini中配置error_reporting=E_ALL//上报错误级别,什么级别output可能是一个页面,也可能是stdoutdisplay_startup_errors=On//是否在页面显示启动过程的错误信息,记住上面提到的几个core类型的错误都是在启动时出现的,这个是为了控制这些错误是否显示页面。log_errors=On//是否记录错误日志log_errors_max_len=1024//错误日志的最大长度ignore_repeated_errors=Off//是否忽略重复错误track_errors=Off//是否使用全局变量$php_errormsg记录***Anerrorxmlrpc_errors=0//是否使用XML-RPC错误信息格式来记录错误xmlrpc_error_number=0//作为XML-RPCfaultCode元素的值。html_errors=On//是否将输出功能等信息改成HTML链接docref_root=http://manual/en///如果开启html_errors,这个链接的根路径是什么?fastcgi.logging=0//是否把php错误抛给fastcgi时,我们经常被问到,error_reporting和display_errors有什么区别?这两个功能是完全不同的。默认情况下,PHP会在日志和标准输出(如果是fpm模式,标准输出就是页面)。error_reporting的参数是错误级别。指示应该触发错误的级别。如果我们告诉PHP所有的错误级别都不需要触发错误,那么无论是日志还是页面都不会显示这个错误,相当于什么都没有发生。display_errors是控制是否在标准输出上显示错误信息。log_errors是控制是否在日志中记录错误信息。error_log是显示错误日志的地方。这个在php-fpm中经常被改写,所以经常会发现cli和fpm的错误日志不在同一个文件中。ignore_repeated_errors标志控制如果有重复的日志,只记录一条,比如下面的程序:error_reporting(E_ALL);ini_set('ignore_repeated_errors',1);ini_set('ignore_repeated_source',1);$a=$c;$a=$c;//E_NOTICE//Notice:Undefinedvariable:cin/tmp/php/index.phponline20本来会出现两次,现在,只会出现一次……track_errors会开启***An错误消息存储在一个变量中,这可能对日志记录很有用。但是我觉得真的没什么用。。。html_errors和doref_root是相当人性化的配置。配置完这两个参数后,如果我们返回的错误信息中有一些信息,就会变成一个链接形式。error_reporting(E_ALL);ini_set('html_errors',1);ini_set('docref_root',"https://secure.php.net/manual/zh/");include("a2.php");//E_WARNING它可以让你快速定位到我们哪里出错了。是不是很人性化~在php-fpm配置error_log=/var/log/php-fpm/error.log//php-fpm自带日志log_level=notice//php-fpm自带日志级别php_flag[display_errors]=off//覆盖php.ini中的一个配置变量,可以通过程序中的ini_set覆盖php_value[display_errors]=off//同php_flagphp_admin_value[error_log]=/tmp/www-error.log//覆盖php.iniA程序中某些配置变量不能被程序中的ini_set覆盖php_admin_flag[log_errors]=on//同php_admin_valuecatch_workers_output=yes//是否捕获fpmworker的输出request_slowlog_timeout=0//slowlogdurationslowlog=/var/log/php-fpm/www-slow.log//慢日志记录在php-fpm配置中还有一个error_log配置,经常和php.ini中的error_log配置混淆。但是他们记录的东西是不同的。php-fpm的error_log只记录了php-fpm自身的日志,比如fpm的启动、关闭等。php.ini中的error_log是记录php程序本身的错误日志。然后,在php-fpm中覆盖php.ini中的error_log配置,需要用到以下函数:php_flagphp_valuephp_admin_flagphp_admin_valueadmin的两个函数说明设置了这个变量后,就不能在代码中使用ini_set来设置这个变量了重新分配。php_flag/value仍然以php代码中的ini_set为准。slowlog由fpm记录,可以通过request_slowlog_timeout设置slowlog的时长。总结我们经常混淆的是日志问题,以及为什么某些级别的日志没有记录在日志中。最重要的是看error_log、display_errors、log_errors这三个配置,但是看配置的时候还要注意区分php.ini中的配置是什么和php-fpm中的配置是什么.ini。嗯,我想如果你了解了这些配置,基本就没有php日志不能记录的WTF问题了。
