本文操作环境:windows7系统、php7.1版、dell g3电脑。
如何解决php 500错误问题?
php与500错误
php开发过程中经常会遇到返回500错误的情况,而且body体中也没有任何调试(可用)内容。这个时候你就需要慢慢调试了(打断点,开调试模式等),但如果是现网,这个错误就比较让人抓狂了,既不好打断点也不能开调试模式。但既然是错误,总是会有处理方法,下面就一步步分析500的成因及处理方案。
0x01、500错误
500错误,也叫internal server error(内部服务错误),表示服务因未知错误导致无法处理请求。在php站点中一般是由php返回,也就是说,500错误一般都是php脚本的错误。
php-fpm抓包500
从上图中可以看出(nginx+php-fpm架构),在php调用一个不存在的类时,脚本发生错误并返回500给nginx(并且将错误信息也做了返回,只不过是卸载stderr中)。
0x02、哪些错误异常会导致500
那么哪类错误会导致500错误呢,php所有的错误级别可以在php的官方文文档(http://php.net/manual/zh/errorfunc.constants.php)中查询到,而这其中错误级别为e_error、e_parse、e_recoverable_error、e_user_error以及未捕获的异常等都会导致500错误。
e_error级别错误导致的500
0x03、什么情况下错误不会返回500
上面说了,这个是php脚本的错误导致的,但是php脚本有了错误或异常一定会导致500吗?显然不是,即使在脚本有致命错误的情况下,依旧可以返回200。
display_errors配置选项
在基于python、nodejs等的web应用中,默认情况下,如果出现异常信息会被打印到控制台(stderr/stdout)中。而在基于php-fpm架构的php中没有控制台可以打印,它的stderr和stdout被置为fastcgi中对应的strderr和stdout。如果将错误重定向到stdout中,错误会直接输出到响应中,并且状态码也会置为200。这个也是display_errors选项所实现的能力。
display_errors选项的配置需要通过ini_set来实现,php文档中关于display_errors的配置表明该值为字符串类型,实际使用中数字和布尔类型也可以打开或关闭该配置。
error_reporting配置
display_errors控制了php脚本发生错误时是否显示错误详情以及是否返回错误状态码,而error_reporting项则用来控制哪级别的错误可以被直接打印出来。
error_reporting的设置项可以通过error_reporting(e_all)或ini_set('error_reporting', e_all)来配置,函数参数的详情可以参考php文档。
需要注意的是,php本身是有错误日志的(error_log和log_errors两个配置项目),若发生错误,php会将改错误写入错误日志中,而哪些错误需要被写入是受error_reporting项的控制的。
在错误级别不匹配的情况下不显示错误详情
0x04、现网如何合理处理500
500错误发生已经说明php脚本无法正常运行了,这时候能做的只是捕获异常并记录异常到日志,以方便日后的调试和现网bug的处理。
php自带错误日志
php本身已经带了错误日志的记录,可以在php.ini中将log_errors项设置为on,并配合error_log配置项来指定错误日志的存放路径。
错误日志记录开关
日志路径设置
该错误日志的的写入不受display_errors的配置的控制。也就是说不管display_errors是否开启,错误都会记录到日志中。但是却受error_reporting配置的控制,如果当前错误级别跟error_reporting中的错误级别不匹配的话,错误不会写入日志中。即如果错误级别是e_error,但是设置却为error_reporting(e_notice),那么日志中不会出现e_error的出错信息。
php错误日志记录各种类型的错误
错误级别不匹配导致的日志不写入
捕获错误异常记录
php提供了set_error_handler、register_shutdown_function、set_exception_handler、error_get_last等相关的错误处理函数。可以通过函数将捕获到的错误信息写入指定日志来实现错误的记录。
函数的使用详情可以参考http://km.oa.com/group/19368/articles/show/302491,这里提供一个模版:
$previoushandler = set_exception_handler(function(exception $ex) use (&$previoushandler) { call_user_func('exceptionhandler', $ex, $previoushandler);});set_error_handler('errorhandler');register_shutdown_function('fatalerrorhandler');function exceptionhandler(exception $ex, $previoushandler){ $info = array( $ex->getfile(), $ex->getline(), $ex->getcode(), $ex->getmessage() ); // 记录日志 logphperror($info); if (isset($previoushandler) && is_callable($previoushandler)) { call_user_func($previoushandler, $ex); }}/** * 框架错误处理函数 * @param $errno * @param $errstr * @param $errfile * @param $errline * @return bool */function errorhandler($errno = 0, $errstr = '', $errfile = '', $errline = 0){ switch ($errno) { case e_warning: $errname = 'e_warning'; break; case e_notice: $errname = 'e_notice'; break; case e_strict: $errname = 'e_strict'; break; case e_recoverable_error: $errname = 'e_recoverable_error'; break; case e_deprecated: $errname = 'e_deprecated'; break; case e_user_error: $errname = 'e_user_error'; break; case e_user_warning: $errname = 'e_user_warning'; break; case e_user_notice: $errname = 'e_user_notice'; break; case e_user_deprecated: $errname = 'e_user_deprecated'; break; default: restore_error_handler(); return false; } // 记录日志 $info = array( $errfile, $errline, $errname, $errstr ); logphperror($info); restore_error_handler(); return false;}/** * fatal error错误处理 */function fatalerrorhandler(){ if (($e = error_get_last()) && $e['type'] === e_error) { $info = array( $e['file'], $e['line'], 'e_error', $e['message'] ); // 记录日志 logphperror($info); }}
0x05 总结
总结起来,error_reporting是用于控制向浏览器或php错误日志输出错误信息级别的函数或配置,而display_errors则是控制是否向浏览器输出错误和告警信息。
由于php的错误日志是全局的,而且受到error_reporting的控制,因此推荐在业务中实现自己的错误(异常)捕获记录逻辑。
推荐学习:《php视频教程》
以上就是如何解决php 500错误问题的详细内容。