php中的错误和异常处理

简介

php程序在运行过程中,会在出现错误的时候抛出异常。异常可能来自php内部,也可以由用户自行抛出。为此,php提供了Throwable接口,这个接口就是所有可抛出异常的公共接口。与很多编程语言类似,php也是通过throw抛出异常。能够通过throw进行抛出的对象都必须实现一个php的预定义接口——Throwable,此接口提供了以下方法:


abstract public string getMessage ( void ) 
- 获取消息

abstract public int getCode ( void ) 
- 获取出错代码

abstract public string getFile ( void )
- 获取抛出此异常的的文件名

abstract public int getLine ( void ) 
- 异常对象在哪一行被实例化

abstract public array getTrace ( void ) 
- 获取堆栈追踪

abstract public string getTraceAsString ( void ) 
- 获取堆栈追踪(字符串)

abstract public Throwable getPrevious ( void ) 
- 返回之前的Throwable

abstract public string __toString ( void )

- 异常对象的字符串表示


注意,用户代码中不应该直接使用此接口。php提供了Exception类,可作为用户异常的基类。而对于内部错误,php更是提供了大量的预定义类,包括:
Throwable - 接口
  Error - 内部错误的基类 
    ArithmeticError - 数学运算错误
      DivisionByZeroError - 除0错误
    AssertionError - 执行 assert()时断言失败
    ParseError - 当解析PHP代码时出现错误时,会抛出ParseError,比如调用eval()时。
    TypeError - 参数类型错误,包括函数参数类型错误、函数返回值类型错误
      ArgumentCountError - 实参数量少于形参数量

注意,Exception和Error是属于同一级别,只不过是提供给用户使用的。Error类和Excepton类都使用如下构造函数:
__construct ([ string $message = "" [, int $code = 0 [, Throwable $previous = NULL ]]] )


配置(php.ini文件)

在php.ini配置文件中,有几个选项可以配置php的错误处理方式:
error_reporting 
设置错误报告的级别

display_errors
该选项设置是否将错误信息作为输出的一部分显示到屏幕,或者对用户隐藏而不显示。

display_startup_errors
即使 display_errors 设置为开启, PHP 启动过程中的错误信息也不会被显示。

log_errors
设置是否将脚本运行的错误信息记录到服务器错误日志或者error_log之中。

log_errors_max_len
设置 log_errors 的最大字节数

ignore_repeated_errors
不记录重复的信息

ignore_repeated_source
忽略重复消息时,也忽略消息的来源。

report_memleaks
如果这个参数设置为Off,则内存泄露信息不会显示 (在 stdout 或者日志中)。

track_errors
如果开启,最后的一个错误将永远存在于变量 $php_errormsg 中。

html_errors
在错误信息中关闭HTML标签。

xmlrpc_errors
关闭正常的错误报告,并将错误的格式设置为XML-RPC错误信息的格式。

xmlrpc_error_number
用作 XML-RPC faultCode 元素的值。

docref_root
新的错误信息格式包含了对应的参考页面,该页面对错误进行具体描述,或者描述了导致该错误发生的函数。

error_prepend_string
错误信息之前输出的内容。 

error_append_string
错误信息之后输出的内容。 

error_log
设置脚本错误将被记录到的文件。


配置(执行函数)

除了可以在php.ini配置错误处理方式以外,程序也可以通过一些函数来进行配置。


error_reporting()
int error_reporting ([ int $level ] )
设置应该报告何种 PHP 错误
error_reporting() 函数能够在运行时设置 error_reporting 指令。 PHP 有诸多错误级别,使用该函数可以设置在脚本运行时的级别。 如果没有设置可选参数 level, error_reporting() 仅会返回当前的错误报告级别。 


异常处理(捕获)

捕获异常的基本语法结构为:

try                  { // 可能抛出异常的业务代码 }
catch (Exception $e) { // 处理捕获到的异常 } 
catch (Exception $e) { // 处理捕获到的异常 } 
catch (Exception $e) { // 处理捕获到的异常 } 
...
finally              { // 总是会执行的代码 }

注意,catch块和finally块至少必须存在一个,而且抛出的对象必须是Exception类(或子类)的一个实例。也就是说,Error类型的错误并不能直接通过try来捕获。
可以使用多个catch块来捕捉不同类别的异常。 也可以在catch块中抛出(或重新抛出)异常。如果异常未能在catch捕获,还可以通过set_exception_handler()注册异常处理函数进行处理
无论是否抛出异常,finally块中的代码将始终在try和catch块之后执行。

finally块并不是用于“捕获所有异常”的,没有被catch捕获的异常就会是未捕获的异常。

try {
  throw new Exception("exception");
}
finally {
  echo "finally\n";
}
// 未处理异常~

对于未处理的异常,也可以由上层try进行捕获。

try {
  try {
    throw new Exception("Oh no~");
  }
  finally {
    echo "Zzz~\n";
  }
}
catch (Exception $e) {
  echo "catch: ".$e->getMessage()."\n";
}
finally {
  echo "finally\n";
}
/*
Zzz~
catch: Oh no~
finally
*/

catch中也可以抛出异常

try {
  try {
    throw new Exception("Oh no~");
  }
  catch(Exception $e) {
    echo "catch 01: " . $e->getMessage() . "\n";
    throw $e;
  }
}
catch (Exception $e) {
  echo "catch 02: ".$e->getMessage()."\n";
}
/*
catch 01: Oh no~
catch 02: Oh no~
*/

如果catch和finally都抛出异常,则catch中的异常会被嵌套

try {
  try {
    throw new Exception("Oh no~");
  }
  catch(Exception $e) {
    echo "catch 01: " . $e->getMessage() . "\n";
    throw $e;
  }
  finally {
    throw new Exception("Oh finally~");
  }
}
catch (Exception $e) {
  echo "catch 02: ".$e->getMessage()."\n";
}
/*
catch 01: Oh no~
catch 02: Oh finally~
*/

<?php

function getException(Throwable $ex)
{
  echo $ex->getMessage()."\n";
  echo $ex->getCode()."\n";
  echo $ex->getFile()."\n";
  echo $ex->getLine()."\n";
  echo $ex->getTraceAsString()."\n";

  var_dump($ex->getPrevious());
  var_dump($ex->getTrace());
}
set_exception_handler("getException");

function f1() {
  try {
    throw new Exception("hello exception 0");
  }
  finally {
    throw new Exception("hello exception 1");
  }
}
function f2() {
  try {
    f1();
  }
  finally {
    throw new Exception("hello exception 2");
  }
}
function f3() {
  f2();
}

f3();
/*
hello exception 2                                   // $ex->getMessage()
0                                                   // $ex->getCode()
F:\server\www\other.php                             // $ex->getFile()
29                                                  // $ex->getLine()
#0 F:\server\www\other.php(33): f2()                // $ex->getTraceAsString()
#1 F:\server\www\other.php(36): f3()
#2 {main}
object(Exception)#2 (7) {                           // $ex->getPrevious()
  ["message":protected]=>
  string(17) "hello exception 1"
  ["string":"Exception":private]=>
  string(0) ""
  ["code":protected]=>
  int(0)
  ["file":protected]=>
  string(23) "F:\server\www\other.php"
  ["line":protected]=>
  int(21)
  ["trace":"Exception":private]=>
  array(3) {
    [0]=>
    array(4) {
      ["file"]=>
      string(23) "F:\server\www\other.php"
      ["line"]=>
      int(26)
      ["function"]=>
      string(2) "f1"
      ["args"]=>
      array(0) {
      }
    }
    [1]=>
    array(4) {
      ["file"]=>
      string(23) "F:\server\www\other.php"
      ["line"]=>
      int(33)
      ["function"]=>
      string(2) "f2"
      ["args"]=>
      array(0) {
      }
    }
    [2]=>
    array(4) {
      ["file"]=>
      string(23) "F:\server\www\other.php"
      ["line"]=>
      int(36)
      ["function"]=>
      string(2) "f3"
      ["args"]=>
      array(0) {
      }
    }
  }
  ["previous":"Exception":private]=>
  object(Exception)#1 (7) {
    ["message":protected]=>
    string(17) "hello exception 0"
    ["string":"Exception":private]=>
    string(0) ""
    ["code":protected]=>
    int(0)
    ["file":protected]=>
    string(23) "F:\server\www\other.php"
    ["line":protected]=>
    int(18)
    ["trace":"Exception":private]=>
    array(3) {
      [0]=>
      array(4) {
        ["file"]=>
        string(23) "F:\server\www\other.php"
        ["line"]=>
        int(26)
        ["function"]=>
        string(2) "f1"
        ["args"]=>
        array(0) {
        }
      }
      [1]=>
      array(4) {
        ["file"]=>
        string(23) "F:\server\www\other.php"
        ["line"]=>
        int(33)
        ["function"]=>
        string(2) "f2"
        ["args"]=>
        array(0) {
        }
      }
      [2]=>
      array(4) {
        ["file"]=>
        string(23) "F:\server\www\other.php"
        ["line"]=>
        int(36)
        ["function"]=>
        string(2) "f3"
        ["args"]=>
        array(0) {
        }
      }
    }
    ["previous":"Exception":private]=>
    NULL
  }
}
array(2) {                                          // $ex->getTrace()
  [0]=>
  array(4) {
    ["file"]=>
    string(23) "F:\server\www\other.php"
    ["line"]=>
    int(33)
    ["function"]=>
    string(2) "f2"
    ["args"]=>
    array(0) {
    }
  }
  [1]=>
  array(4) {
    ["file"]=>
    string(23) "F:\server\www\other.php"
    ["line"]=>
    int(36)
    ["function"]=>
    string(2) "f3"
    ["args"]=>
    array(0) {
    }
  }
}
*/

#0 F:\server\www\other.php(33): f2()                // $ex->getTraceAsString()
#1 F:\server\www\other.php(36): f3()
#2 {main}

这段信息表示异常从f2()中抛出,f2()被f3()调用,f3()被main调用


array(2) {                                          // $ex->getTrace()
  [0]=>
  array(4) {
    ["file"]=>
    string(23) "F:\server\www\other.php"
    ["line"]=>
    int(33)
    ["function"]=>
    string(2) "f2"
    ["args"]=>
    array(0) {
    }
  }
  [1]=>
  array(4) {
    ["file"]=>
    string(23) "F:\server\www\other.php"
    ["line"]=>
    int(36)
    ["function"]=>
    string(2) "f3"
    ["args"]=>
    array(0) {
    }
  }
}

这段信息记录函数调用栈的详细信息


下面是一个相对清晰一些的打印方式:

<?php

function getException(Throwable $ex)
{
  do{
    echo $ex->getMessage()."\n";
    echo $ex->getFile()." : ".$ex->getLine()."\n";
    $arr = $ex->getTrace();
    $count = count($arr);
    for($i=0; $i<$count; $i++) {
      echo $arr[$i]["function"]."()";
      if($i+1 < $count)
        echo " -> ";
    }
    echo "\n|--------------------\n";
  }
  while($ex = $ex->getPrevious());
}
set_exception_handler("getException");

function f1() {
  try {
    throw new Exception("hello exception 0");
  }
  finally {
    throw new Exception("hello exception 1");
  }
}
function f2() {
  try {
    f1();
  }
  finally {
    throw new Exception("hello exception 2");
  }
}
function f3() {
  f2();
}

f3();
/*
hello exception 2
F:\server\www\other.php : 36
f2() -> f3()
|--------------------
hello exception 1
F:\server\www\other.php : 28
f1() -> f2() -> f3()
|--------------------
hello exception 0
F:\server\www\other.php : 25
f1() -> f2() -> f3()
|--------------------
*/

异常处理(回调函数)

set_error_handler() 

mixed set_error_handler ( callable $error_handler [, int $error_types = E_ALL | E_STRICT ] )
设置用户自定义的错误处理函数

$error_handler

用户自定义错误处理,可以传入 NULL 重置处理程序到默认状态。 除了可以传入函数名,还可以传入引用对象和对象方法名的数组。具体的函数或方法应按如下格式定义: 
bool handler ( int $errno , string $errstr [, string $errfile [, int $errline ]] )
errno,包含了错误的级别,
errstr,包含了错误的信息,
errfile, 包含了发生错误的文件名,
errline, 包含了错误发生的行号,
如果函数返回 FALSE,标准错误处理处理程序将会继续调用。 

$error_types 

本参数里指定的错误类型都会绕过 PHP 标准错误处理程序, 除非回调函数返回了 FALSE。 error_reporting() 设置将不会起到作用而你的错误处理函数继续会被调用 —— 不过你仍然可以获取 error_reporting 的当前值,并做适当处理。 需要特别注意的是带 @ error-control operator 前缀的语句发生错误时,这个值会是 0。 
此参数用于屏蔽 error_handler 的触发。 如果没有该掩码, 无论 error_reporting 是如何设置的, error_handler 都会在每个错误发生时被调用。 

注意,以下级别的错误不能由用户定义的函数来处理: E_ERROR、 E_PARSE、 E_CORE_ERROR、 E_CORE_WARNING、 E_COMPILE_ERROR、 E_COMPILE_WARNING,和在 调用 set_error_handler() 函数所在文件中产生的大多数 E_STRICT。 
对于本函数(set_error_handler)的返回值,如果之前有定义过错误处理程序,则返回该程序名称的 string;如果是内置的错误处理程序,则返回 NULL。 如果你指定了一个无效的回调函数,同样会返回 NULL。 如果之前的错误处理程序是一个类的方法,此函数会返回一个带类和方法名的索引数组(indexed array)。 


通过set_error_handler()可以将运行错误转换为异常,只需要自定义一个错误处理函数,该函数抛出一个异常即可。php已经预定义了一个专门用于此场合的异常类,即ErrorException,该类继承自Exception。该类的构造函数为:
public __construct ([ string $message = "" [, int $code = 0 [, int $severity = E_ERROR [, string $filename = __FILE__ [, int $lineno = __LINE__ [, Exception $previous = NULL ]]]]]] )

function getError($errno, $errstr, $errfile, $errline)
{
  throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
}
set_error_handler ("getError");

try{
  $a = 3 / 0;
}
catch(ErrorException $e)
{
  echo "error";
}


set_exception_handler()

callable set_exception_handler ( callable $exception_handler )
设置默认的异常处理程序,用于没有用 try/catch 块来捕获的异常。 在 exception_handler 调用后异常会中止。 

$exception_handler
用户自定义的异常处理函数,也可以传递 NULL 值用于重置异常处理函数为默认值。 函数以以下方式定义:
void handler ( Throwable $ex )
大多数错误抛出 Error 异常,也能被此函数捕获。 Error 和 Exception 都实现了 Throwable 接口。

set_exception_handler()函数的返回值为之前定义的异常处理程序的名称,或者在错误时返回 NULL。 如果之前没有定义错误处理程序,也会返回 NULL。 

function getException(Throwable $ex) {
  echo "my Error: ".$ex->getMessage();
}
set_exception_handler("getException");
throw new Exception("a err");
/*
my Error: a err
*/

bool restore_exception_handler ( void )

在使用 set_exception_handler() 改变异常处理函数之后,此函数可以用于还原之前的异常处理程序(可以是内置的或者也可以是用户所定义的函数)。 


bool restore_error_handler ( void )

在使用 set_error_handler() 改变错误处理函数之后,此函数可以用于还原之前的错误处理程序(可以是内置的或者也可以是用户所定义的函数)。


扩展Exception异常类

可以通过派生Exception类来实现一些自定义的功能,为此,需要了解Exception类中成员的可访问性:

class Exception
{
    protected $message = 'Unknown exception';   // 异常信息
    private   $string;                          // __toString cache
    protected $code = 0;                        // 用户自定义异常代码
    protected $file;                            // 发生异常的文件名
    protected $line;                            // 发生异常的代码行号
    private   $trace;                           // backtrace
    private   $previous;                        // 当异常嵌套时,保存之前的异常

    public function __construct($message = null, $code = 0, Exception $previous = null);

    final private function __clone();           // 异常对象不能克隆

    final public  function getMessage();        // 返回异常信息
    final public  function getCode();           // 返回异常代码
    final public  function getFile();           // 返回发生异常的文件名
    final public  function getLine();           // 返回发生异常的代码行号
    final public  function getTrace();          // backtrace() 数组
    final public  function getPrevious();       // 之前的 exception
    final public  function getTraceAsString();  // 已格成化成字符串的 getTrace() 信息

    // 可覆盖的方法
    public function __toString();               // 输出字符串
}


class MyException extends Exception
{
  // 添加一个简短消息
  private $msg;

  // 重定义构造器使 message 变为必须被指定的属性
  public function __construct($msg, $message, $code = 0, Exception $previous = null)
  {
    // 自定义的代码
    $this->msg = $msg;

    // 确保所有变量都被正确赋值
    parent::__construct($message, $code, $previous);
  }

  // 自定义字符串输出的样式
  public function __toString()
  {
    return __CLASS__ . ": [{$this->code}]: {$this->msg}\n";
  }
}

try {
  throw new MyException("出错了", "我是故意抛出异常的");
}
catch(MyException $e) {
  echo $e;
  echo $e->getMessage();
}
/*
MyException: [0]: 出错了
我是故意抛出异常的
*/



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值