PHP错误和异常处理详解

前言:
关于PHP的错误和异常我准备用四大块来说明,内容如下:

一、错误与异常的
  1. 区别

异常一般指非语法和编译上的错误,指不符合程序预期,业务与流程上的错误,叫异常。
错误一般指PHP本身的报错,比如语法错误、环境错误等。根据错误类型区分错误级别,并且不能被try-catch捕获。

例:如下是一个PHP代码,其中有两个错误,echo xx,没加引号和分号。


try
{
	echo xx
}catch (Execption $e)
{
	echo $e->getMessage();
}

// 结果
// Parse error: syntax error, unexpected '}', expecting ',' or ';' in E:\work\app\phptest\index.php on line 5

由此可见PHP遇见本身的错误会直接触发一个错误,而不是异常,通过异常无法自动捕获错误。

二、错误处理

在PHP中的异常机制是不完善的,有些时候必须经过处理才能抛出异常,这样相当麻烦,所以我们可以采用自定义函数来接管PHP原生的报错。

1.错误配置指令

  • 开启PHP错误
    全局:在PHP配置文件中,display_errors = on/off;
    局部:在程序中输入,ini_set(“display_error”, true/false);
    注:log_errors = On情况下,必须制定error_log文件,如果指定地址错误或没有权限则会导致display_errors = Off失效,错误正常打印出来。

  • 首先先了解下错误等级

级别 描述
E_ALL 所有错误和警告(不包括E_STRICT错误)
E_COMPILE_ERROR 致命的编译错误
E_COMPILE_WARNING 编译时警告
E_CORE_ERROR PHP启动时发生的错误
E_CORE_WARNING PHP启动时的警告
E_DEPRECATED 使用PHP将在未来版本中移除的特性
E_ERROR 致命的运行错误
E_NOTICE 运行时注意的消息
E_PARSE 编译时解析错误
E_RECOVERABLE_ERROR 几近致命的错误
E_STRICT PHP版本可移植性建议
E_USER_DEPRECATED 用户使用PHP将在未来版本中移除的特性
E_USER_ERROR 用户导致的错误
E_USER_NOTICE 用户导致的注意消息
E_USER_WARNING 用户导致的警告
E_WARNING 运行时警告
  • 这些等级的作用
    在开发阶段,希望报告的所有错误。
    在配置文件中,设置error_reporting = E_ALL & E_STRICT,表示显示所有错误。
    error_reporting = E_ERROR | E_PARSE | E_CORE_ERROR,表示只考虑致命的运行时错误、解析错误和核心错误。
    error_reporting = E_ALL & ~ E_USER_WARNING,表示不展示E_USER_WARNING错误。(~表示逻辑操作符NOT)
  • 其他配置
    display_startup_errors = On/Off ,表示显示PHP引擎初始化的所有错误。
    error_log = On/Off,表示记录错误(日志)
    log_errors = string,表示日志存放地址
    log_errors_max_len = int,表示每个日志项的最大长度,以字节为单位,默认1024字节,0表示不指定最大字节
    ignore_repeated_errors = On/Off,表示忽略PHP在同一文件同一行上发生的重复错误消息。
    ignore_repeated_source = On/Off 指令将使PHP忽略不同文件中或同一文件中不同行上发生的重复错误。
    track_errors =On/Off 指令会使PHP在变量$php_errormsg中存储最近发生的错误消息。

2.记录日志
两种方式:(以Windows为例)
第一种记录到文件,
error_log = x:/php/php_errors.log,这种方式安全性较低,被攻击者发现可以轻易的浏览目标路径等。
PHP错误和异常处理详解_第1张图片
第二种方式记录到syslog,一种系统日志工具(Linux是syslog日志工具,Windows是Event Viewer),两者大体相同。
具体操作:
error_log = syslog

echo 1/0;

点击“开始→运行”,输入eventvwr,打开事件查看器。
PHP错误和异常处理详解_第2张图片

Linux日志查看暂不举例。

手动记录到syslog函数

// define_syslog_variables(); // 初始化syslog,5.3之后不需要此函数
openlog("CHP8", LOG_PID, LOG_USER);
syslog(LOG_WARNING, "cha");
closelog();
// CHP8[11876] cha

openlog(iden,option,facility)
iden:每一项开始的表示符。
option:使用哪些日志选项,多个用|区分,比如LOG_ODELAY | LOG_PID
其中:
LOG_CONS,如果写入syslog发生错误,则将输出发送到控制台。
LOG_NDELAY,立即打开与syslog的连接
LOG_ODELAY,直到提交第一条消息打开连接,这是默认值。
LOG_PERROR,将要记录的消息同时输出到syslog和标准错误
LOG_PID,每个消息记录进程ID
facility:记录程序属于哪一类,一般包括LOG_KERN、LOG_USER、LOG_MAIL、LOG_DAEMON、LOG_AUTH、LOG_LPR、LOG_LOCALN(LOG_LOCALN最后的N是从0-7的值),指定LOG_CRON消息将发送到cron日志,一般在crontab中执行PHP使用。通常使用LOG_USER。

syslog(priority, message)
priority:日志优先级,表示严重程度。参数如下:
LOG_EMERG,严重的系统问题,可能预示崩溃。
LOG_ALERT,必须立即解决的情况,可能危害系统完整性。
LOG_CRIT,紧急错误,可能导致服务器不可用。
LOG_ERR,一般错误。
LOG_WARNING,一般警告。
LOG_NOTICE,正常但值得注意的情况。
LOG_INFO,一般信息。
LOG_DEBUG,一般与调试相关信息。
message:错误内容。
#####三、异常处理

  1. 基本异常使用
try
{
	if (1)
	{
		throw new Exception("index Exception!");
	}

}catch(Exception $e)
{
	echo $e->getFile() . $e->getLine() . $e->getMessage();
}

// E:\work\app\phptest\index.php6index Exception!

其中包括7个方法:
getCode(),返回传递给构造函数的错误代码。
getFile(),抛出异常文件名。
getLine(),抛出异常行号。
getMessage(),返回传递的消息。
getPrevious(),返回前一个异常。
getTrace(),返回一个数组,包括上下文的信息。
getTraceAsString(),同上,不过返回的是字符串。

2.扩展异常类


class MyException extends Exception
{
	function __construct($language, $errorcode)
	{
		$this->language = $language;
		$this->errorcode = $errorcode;
	}

	function getMessageMap()
	{
		$errors = [
			'en' => ['file error', 'name error'],
			'ch' => ['文件错误', '名字错误'],
		];

		return $errors[$this->language][$this->errorcode];
	}
}

try
{
	if (1)
	{
		throw new MyException("ch", 1);
	}

}catch(MyException $e)
{
	echo $e->getMessageMap();
}
// 结果
// 名字错误

但是这样的异常处理其实意义不大。

四、错误异常联用
  1. 接管PHP原生异常
    用set_error_handler(error_function, error_type)函数自定义错误处理函数。

// 第一种
/*class MyErrorClass{
	// 必须是静态
	public static function MyError($number, $message, $file, $line)
	{
		print_r(['code' => $number, 'message' => $message, 'file' => $file, 'line' => $line]);
	} 
}

set_error_handler(['MyErrorClass', 'MyError']);*/

// 第二种
function MyError($number, $message, $file, $line)
{
	print_r(['code' => $number, 'message' => $message, 'file' => $file, 'line' => $line]);
}

set_error_handler('myError');


try {
    $a = 1 / 0;
} catch (Exception $e) 
{
    echo "0不能做被除数";
}

// 结果
// Array ( [code] => 2 [message] => Division by zero [file] => E:\work\app\phptest\index.php [line] => 23 )

由此可知,自定方法截取了错误,此时我们可以操作错误抛出异常。
注意三点:

  • 若用该方法,则error_reporting()不能再使用。
  • 此方法不能处理E_ERROR、 E_PARSE、 E_CORE_ERROR、 E_CORE_WARNING、 E_COMPILE_ERROR、 E_COMPILE_WARNING级别错误,该函数只能捕获系统产生的一些Warning、Notice级别的错误。
  • 在报错前需要先注册本函数。

2.执行结束返回错误数据
register_shutdown_function(exception_function)可捕获Fatal Error、Parse Error等错误。不如脚本错误、die、exit、异常等结束都会调用。通过它可以在脚本结束前发现执行是否有误。利用error_get_last()查看错误。

echo aaa; 
register_shutdown_function('shutdown');
function shutdown()
{
    // error_get_last()获取错误数组
    if ($error = error_get_last()) 
    {
        echo "
";
        print_r($error);
    }
}
/*

Warning: Use of undefined constant aaa - assumed 'aaa' (this will throw an Error in a future version of PHP) in E:\work\app\phptest\index.php on line 8
aaa
Array
(
    [type] => 2
    [message] => Use of undefined constant aaa - assumed 'aaa' (this will throw an Error in a future version of PHP)
    [file] => E:\work\app\phptest\index.php
    [line] => 8
)
*/
  1. 用户自定义异常处理
    set_exception_handler(exception_function)

function myException($exception) 
{
    echo "异常:" , $exception->getMessage();
}
set_exception_handler('myException');
throw new Exception('aa');
// 异常:aa

完整代码


class MyException extends Exception
{
    function __construct($language, $errorcode)
    {
        $this->language = $language;
        $this->errorcode = $errorcode;
    }

    function getMessageMap()
    {
        $errors = [
            'en' => ['file error', 'name error'],
            'ch' => ['文件错误', '名字错误'],
        ];

        // 记录日志
        openlog("PHP7.2", LOG_PID, LOG_USER);
        syslog(LOG_CRIT, $errors[$this->language][$this->errorcode]);

        return $errors[$this->language][$this->errorcode];
    }
}

function MyError($number, $message, $file, $line)
{
    $errorArray = ['code' => $number, 'message' => $message, 'file' => $file, 'line' => $line];

    // code==2抛出异常
	if ($errorArray['code'] == 2)
	{
		throw new MyException('ch', 1);
	}
}

set_error_handler('myError');

try {
    $a = 1 / 0;

} catch (MyException $e) 
{
    echo $e->getMessageMap();
}

你可能感兴趣的:(PHP)