PHP的错误处理

为什么要做错误处理

  • 页面报太多错误信息,用户会觉得你的网站特别不专业、不权威
  • 会暴露别人攻击系统的有效信息.譬如数据库信息、项目目录
  • 项目开发的过程中快速准确地定位到异常、错误

一、PHP错误级别

Parse error > Fatal Error > Waning > Notice > Deprecated

1、deprecated 最低级别的错误

使用一些过期函数的时候会出现,程序继续执行

$pattern = '[0-9]+';
$subject = 100;
//$int = preg_match($pattern,$subject);
$int = eregi($pattern,$subject);

var_dump($int);

//PHP官方指出,PHP5.3不推荐使用eregi函数,建议使用preg_match函数代替。

Deprecated 关于 性能测试 问题

$date = date('Y-m-d');
$start = microtime(true);
for ($i = 0; $i < 10000; $i++) 
{
    if ( eregi  ( "([0-9]{4})-([0-9]{1,2})-([0-9]{1,2})" ,  $date ,  $regs )) {
        //echo  " $regs[3] . $regs[2] . $regs[1] " ;
    } else {
        //echo  "Invalid date format:  $date " ;
    }
}
$end = microtime(true);
echo $end - $start.'<hr/>';

############################################################

$start = microtime(true);
for ($i = 0; $i < 10000; $i++)
{
    if ( preg_match( "/([0-9]{4})-([0-9]{1,2})-([0-9]{1,2})/" ,  $date ,  $regs )) {
    //echo  " $regs[3] . $regs[2] . $regs[1] " ;
    } else {
    //echo  "Invalid date format:  $date " ;
    }
}

$end = microtime(true);
echo $end - $start.'<hr/>';

2、notice 通知级别的错误

使用一些未定义变量、常量或者数组key没有加引号的时候出现,不影响程序继续执行

echo $a;      //打印一个未定义
echo string;  //string 会把当成常量
echo $array[name]; //name 会把当成常量

3、Warning 警告级别的错误

程序出问题,需要修改代码!也不影响程序继续执行

settype($var, 'init');
//Warning: settype(): Invalid type in

4、致命错误

【后面程序不执行】

4.1、fatal-error 致命级别的错误

程序直接报错,需要修改代码。

test('123');// 调用致命的错误。运行中
//Fatal error: Call to undefined function test()

4.2、parse error 语法解析错误

语法检查阶段报错,需要修改代码 属于高级的error错误,代码检查阶段中发现

echo 'nie ge'
//Parse error: syntax error, unexpected

5、E_USER相关的相关错误

用户自定义的错误,用户手动抛出错误。由用户自己在代码中使用PHP函数 trigger_error() 来产生的。

header('content-type:text/html;charset=utf-8');
if(!defined('JACK')){
    trigger_error('系统遇到意想不到的惊喜!'); 
}

二、错误处理

项目上线后 务必关闭所有可能出现的错误,使用抑制符@来屏蔽敏感的错误信息,利用日志程序生成运行日志的详细信息。

1、显示或关闭错误信息

  1. 修改php.ini 开启 display_errors

    display_errors = On
  2. 使用ini_set()函数 允许脚本临时覆盖PHP配置文件中的设置
    ini_set ('display_errors', 1);

2、设置报告

常量 说明
1 E_ERROR 报告导致脚本终止运行的致命错误
2 E_WARNING 报告运行时的警告类错误(脚本不会终止运行)
4 E_PARSE 报告编译时的语法解析错误
8 E_NOTICE 报告通知类错误,脚本可能会产生错误
2048 E_STRICT PHP5+,代码可以运行,但是php不建议这样写
32767 E_ALL 报告所有的可能出现的错误(不同的PHP版本,常量E_ALL的值也可能不同)
function change(&$var){
    $var += 10;
}
$var = 1;
change(++$var);
  1. php.ini配置文件里设置error_reporting级别

    ; Default Value: E_ALL & ~E_NOTICE & ~E_STRICT & ~E_DEPRECATED
    ; Development Value: E_ALL
    ; Production Value: E_ALL & ~E_DEPRECATED & ~E_STRICT  生产环境
    ; http://php.net/error-reporting
    error_reporting = E_ALL & ~E_NOTICE
  2. 使用error_reporting()函数
    error_reporting (0); //不需要报告任何错误
    error_reporting (E_ALL); //报告所有错误
    error_reporting (E_ALL & ~E_NOTICE); //除了notice以外报告所有

3、使用 die() 函数处理

  • 在设计工具类和工具函数时,die()/exit() 应该严令禁止,因为它们无权决定整个程序的生死。
  • 确实确定业务已经结束的情况下使用,其他情况,最好禁止使用
    if(!file_exists("jack.txt"))
    {
    die("文件不存在");//也可以exit,只是名字不同,效果都是一样的
    }else{
    $file=fopen("jack.txt","r");
    }

4、使用trigger_error函数触发PHP错误

trigger_error()产生一个用户级别的 error/warning/notice 信息,

$num1 = 123;
$num2 = '2a';

if( !is_numeric($num1) || !is_numeric($num2) ){
    echo trigger_error('两个值必须都是数值',E_USER_NOTICE);
}
function getCommandObject($cmd) { 

    $path = "{$cmd}.php"; 
    if (!file_exists($path)) { 
        trigger_error("Cannot find $path", E_USER_ERROR); 
    } 
    require_once $path; 
    if (!class_exists($cmd)) { 
        trigger_error("class $cmd does not exist", E_USER_ERROR); 
    } 
    $ret = new $cmd(); 
    if (!is_a($ret, 'Command')) { 
        trigger_error("$cmd is not a Command", E_USER_ERROR); 
    } 
    return $ret;
}

getCommandObject('user');

5、函数set_error_handler()

该函数是指定出现报错的时候,指向我们自定义的错误处理函数;

代码参考:

ini_set('display_errors',0);
$debug = 1;
// 自定义错误处理函数
function myErrorHandler($e_number, $e_message, $e_file, $e_line, $e_vars)
{
    global $debug;
    $message = "脚本 <strong>{$e_file}</strong> 中出现错误在第<span style=\"color:red;\">{$e_line}</span>行: $e_message";

    if ($debug) {
       echo '<div style="width:90%;padding:10px;border:1px solid green;">' . $message . '</div>';
    }
}
set_error_handler('myErrorHandler');

6、函数error_log

error_log函数:专门用于日记记录。

  • 0:通过PHP标准的错误处理机制来记录;
  • 1:邮件发生到指定的地方;
  • 3:使用指定的文件记录错误报告日志

    如果使用自己指定的文件记录错误日志,一定要确保将这个文 件存放在文档根目录之外,以减少遭到攻击的可能。并且该文件一定要让PHP脚本的执行用户(Web服务器进程所有者)具有写权限

php.ini配置:

//将会向PHP报告发生的每个错误
error_reporting  =  E_ALL;

//不显示满足上条 指令所定义规则的所有错误报告
display_errors = Off;

//开关
log_errors = On;

//设置每个日志项的最大长度
log_errors_max_len = 1024 ;

//指定产生的 错误报告写入的日志文件位置
error_log = /usr/local/errors.log;

7、register_shutdown_function函数

register_shutdown_function(function (){
    var_dump(error_get_last());
});

#demo();
if( 1 ){
    exit('hehe');
}

register_shutdown_function

捕获PHP的错误:fatal error,Parse Error等,这个方法是PHP脚本执行结束的最后调用的函数,比如脚本错误die(),exit(),异常、正常结束都会调用

通过这个函数就可以在脚本结束前判断这次执行是否有错误产生。这时就要借助于一个函数:error_get_last();这个函数可以拿到本次执行产生的所有的错误

array (size=4)
  'type' => int 1
  'message' => string 'Call to undefined function demo()' (length=33)
  'file' => string 'D:\study\error\demo.php' (length=23)
  'line' => int 14

demo.php

register_shutdown_function(function (){
    $error = error_get_last();
    var_dump($error);
    if($error!==null){
        $logInfo = date('Y-m-d H:i:s');
        $logInfo .= "文件:{$error['file']} ,在页面的第{$error['line']},报了一个{$error['type']},具体错误信息是:{$error['message']}";
        $dir = MESSAGE.DS.date('Y').DS.date('m').DS.date('d');
        var_dump(file_exists($dir));
        var_dump($dir);
        var_dump(getcwd());
        var_dump(__DIR__);
        if(!file_exists($dir)){
            mkdir($dir,0777,true);
        }
        file_put_contents($dir.DS.'error.log', $logInfo.PHP_EOL,FILE_APPEND);
    }
});
include_once 'other.php';//出现语法错误