在日常的PHP开发中,我们经常会遇到各种各样的PHP错误,有常见的语法错误,数据库错误,也有各种很难调试的奇怪问题。

一、PHP错误等级

在PHP中预定义了一些PHP错误常量,在Core.php文件中可以查看定义。这里是一份完整的列表:Predefined Constants

这些变量不仅可以在PHP文件中使用,也可以用在php.ini配置文件中。譬如在PHP中打开错误提示,可以使用下面的代码:

ini_set('display_errors','on'); 
error_reporting(E_ALL & ~E_NOTICE); 

这两句PHP代码对应的php.ini配置是:

display_errors = On 
error_reporting = E_ALL & ~E_NOTICE 

一般在生产环境不应该将错误信息打开,这样容易暴露服务器上的一些敏感信息,如网站路径,php文件函数,数据库连接等等。可以参考CodeIgniter的做法,在CI的入口文件index.php中有这样的错误处理代码:

/*
 *---------------------------------------------------------------
 * ERROR REPORTING
 *---------------------------------------------------------------
 *
 * Different environments will require different levels of error reporting.
 * By default development will show errors but testing and live will hide them.
 */

if (defined('ENVIRONMENT'))
{
    switch (ENVIRONMENT)
    {
        case 'development':
            error_reporting(E_ALL);
        break;
    
        case 'testing':
        case 'production':
            error_reporting(0);
        break;

        default:
            exit('The application environment is not set correctly.');
    }
}

在开发环境,需要打开错误提示,可以在这段代码的上面 define('ENVIRONMENT', 'development'),或者测试和生产环境定义testingproduction就可以了。

二、PHP异常处理

PHP也和其他编程语言一样,支持异常的处理,基本的语法如下:

try
{
    //可能出现错误或异常的代码
} 
catch(Exception $e)
{
    //对异常进行处理
    die( 'Exception: ' .$e->getMessage() );
}

当一个异常被抛出时,其后的代码将不会继续执行,PHP 会尝试查找匹配的 catch 代码块。如果一个异常没有被捕获,而且又没用使用set_exception_handler()作相应的处理的话,那么 PHP 将会产生一个严重的错误,并且输出未能捕获异常 "Uncaught Exception ..." 的提示信息。

三、顶层异常处理器 (Top Level Exception Handler)

使用set_error_handler可以自定义PHP的错误处理函数。下面是一个简单的例子:

<?php
//error handler function
function customError($errno, $errstr, $errfile, $errline)
{
    echo "<b>Custom error:</b> [$errno] $errstr<br />";
    echo " Error on line $errline in $errfile<br />";
    die();
}

//set error handler
set_error_handler("customError");

//trigger error
trigger_error("A custom error has been triggered");
?>

要注意的是,set_error_handler例程只能处理用户级错误,如变量未定义或通过trigger_error引发的错误等。如果需要处理其他错误譬如E_ERROR, E_PARSE, E_CORE_ERROR,一种解决方法是使用register_shutdown_function函数,这个函数可以让用户自定义PHP程序执行完成后执行的函数。如果PHP程序是出现错误导致的退出,可以通过error_get_last函数获取最后一次发生的错误。在CodeIgniter.php文件中可以看到使用set_error_handler自定义了错误提示信息,当CodeIgniter出错时页面上可以看到自定义的显示,该代码位于Common.php中的_exception_handlerExceptions.php中的show_php_error。我们修改CodeIgniter的代码,让其支持register_shutdown_function
首先在CodeIgniter.php中添加如下代码:

set_error_handler('_exception_handler');
register_shutdown_function('_shutdown_handler');

然后在Common.php中添加_shutdown_handler

/**
 * Shutdown Handler
 *
 * The following error types cannot be handled with a user defined function: 
 *         E_ERROR, E_PARSE, E_CORE_ERROR, E_CORE_WARNING, E_COMPILE_ERROR, E_COMPILE_WARNING, 
 *         and most of E_STRICT raised in the file where set_error_handler() is called.
 * So, we use shutdown function to work around this situation.
 *
 * @access    private
 * @return    void
 */
if ( ! function_exists('_shutdown_handler'))
{
    function _shutdown_handler()
    {
        $lasterror = error_get_last();
        switch ($lasterror['type'])
        {
            case E_ERROR:
            case E_CORE_ERROR:
            case E_COMPILE_ERROR:
            case E_USER_ERROR:
            case E_RECOVERABLE_ERROR:
            case E_CORE_WARNING:
            case E_COMPILE_WARNING:
            case E_PARSE:
                $severity = $lasterror['type'];
                $message = $lasterror['message'];
                $filepath = $lasterror['file'];
                $line = $lasterror['line'];
                //header('Location: /ci/index.php/error');
        }
        
        $_error =& load_class('Exceptions', 'core');

        // Should we display the error? We'll get the current error_reporting
        // level and add its bits with the severity bits to find out.
        
        if (($severity & error_reporting()) == $severity)
        {
            $_error->show_php_error($severity, $message, $filepath, $line);
        }
        
        // Should we log the error?  No?  We're done...
        if (config_item('log_threshold') == 0)
        {
            return;
        }
        
        $_error->log_exception();
    }
}

注意:shutdown_handler可以捕获到E_PARSE,但是只能捕获到include的PHP文件里的语法错误。

四、PHP错误实例

熟悉常见的错误代码和返回信息可以快速诊断出代码中出现的问题。下面列出一些常见的PHP错误,及提示信息。

1. E_ERROR = 1

$b = undefined_func();  // 函数未定义 Fatal error: Call to undefined function func()
$a = new C(); // 类未定义 Fatal error: Class 'C' not found in
$a->undefined_func(); // 类的成员函数未定义 Fatal error: Call to undefined method C::undefined_func()
$a = 0;
for (;;) {  // 死循环 Fatal error: Maximum execution time of 300 seconds exceeded
    $a ++;
}

2. E_WARNING = 2

$a = 1;
$b = 0;
$c = $a / $b;  // 被零除 Warning: Division by zero

3. E_PARSE = 4

$b = 1  // 语法错误,少个逗号 Parse error: syntax error, unexpected $end
if (true)
{
    if (true)
    {
        if (false)
        {  // 语法错误,缺右括号
    }
}

4. E_NOTICE = 8

$b = $undefined_var;  // 变量未定义 Notice:Undefined variable: undefined_var

5. E_USER_ERROR/E_USER_WARNING/E_USER_NOTICE

trigger_error("This is an error.", E_USER_ERROR);  // User error

参考

  1. PHP异常处理详解
  2. PHP Error Handling
  3. PHP set_error_handler() 函数
  4. register_shutdown_function
  5. register_shutdown_function 函数详解
扫描二维码,在手机上阅读!