给力星

Web Developer

重温PHP手册 – 异常

美团点评 2018 届校招内推开始啦!
参与内推 = 简历免筛选 + 多一次笔试 + 提前面试/提前拿 Offer
多一次机会,多一份把握,千万不要错过~
内推申请地址:https://wenjuan.meituan.com/survey/68332

PHP 异常

在 PHP 代码中所产生的异常可被 throw 语句抛出并被 catch 语句捕获。

  • 需要进行异常处理的代码都必须放入 try 代码块内,以便捕获可能存在的异常。
  • 每一个 try 至少要有一个与之对应的 catch。
  • 使用多个 catch 可以捕获不同的类所产生的异常。
  • 当 try 代码块不再抛出异常或者找不到 catch 能匹配所抛出的异常时,PHP 代码就会在跳转到最后一个 catch 的后面继续执行。
  • PHP 允许在 catch 代码块内再次抛出(throw)异常。

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

PHP 内部函数主要使用错误报告, 只有现代面向对象的扩展才使用异常。但错误可以很容易的通过ErrorException转换为异常。

PHP 抛出异常的实例

function inverse($x) {
    if (!$x) {
        throw new Exception('Division by zero.');
    }
    else return 1/$x;
}

try {
    echo inverse(5);        // 0.2
    echo inverse(0);        // Caught exception: Division by zero.
} catch (Exception $e) {
    echo 'Caught exception: ',  $e->getMessage(), "\n";
}

// Continue execution
echo 'Hello World';

Note

在整个页面上定义错误处理函数可以避免渲染一半页面的情况:

ob_start();
try {
    /*contains all page logic 
    and throws error if needed*/
    ...
} catch (Exception $e) {
  ob_end_clean();
  displayErrorPage($e->getMessage());
}

在命令空间中捕获异常时,需指向全局空间:

namespace SomeNamespace;

class SomeClass {
    function SomeFunction() {
        try {
            throw new Exception('Some Error Message');
        } catch (\Exception $e) {
            var_dump($e->getMessage());
        }
    }
}

如果你喜欢创建一些自定义异常,下面的代码会很有用:通过接口和抽象的异常类,保证所有内置的异常类保留在子类中,同时也正确的将所有的信息返回给父类的构造器保证没有信息会丢失。


interface IException { /* Protected methods inherited from Exception class */ public function getMessage(); // Exception message public function getCode(); // User-defined Exception code public function getFile(); // Source filename public function getLine(); // Source line public function getTrace(); // An array of the backtrace() public function getTraceAsString(); // Formated string of trace /* Overrideable methods inherited from Exception class */ public function __toString(); // formated string for display public function __construct($message = null, $code = 0); } abstract class CustomException extends Exception implements IException { protected $message = 'Unknown exception'; // Exception message private $string; // Unknown protected $code = 0; // User-defined exception code protected $file; // Source filename of exception protected $line; // Source line of exception private $trace; // Unknown public function __construct($message = null, $code = 0) { if (!$message) { throw new $this('Unknown '. get_class($this)); } parent::__construct($message, $code); } public function __toString() { return get_class($this) . " '{$this->message}' in {$this->file}({$this->line})\n" . "{$this->getTraceAsString()}"; } } /** 采用上面的代码,创建一个异常类只需要一行代码 */ class TestException extends CustomException {} /** 测试 */ function exceptionTest() { try { throw new TestException(); } catch (TestException $e) { echo "Caught TestException ('{$e->getMessage()}')\n{$e}\n"; } catch (Exception $e) { echo "Caught Exception ('{$e->getMessage()}')\n{$e}\n"; } } echo '<pre>' . exceptionTest() . '</pre>';

上面的代码输出如下:

Caught TestException ('Unknown TestException')
TestException 'Unknown TestException' in C:\xampp\htdocs\CustomException\CustomException.php(31)
#0 C:\xampp\htdocs\CustomException\ExceptionTest.php(19): CustomException->__construct()
#1 C:\xampp\htdocs\CustomException\ExceptionTest.php(43): exceptionTest()
#2 {main}

在 finally 代码块中使用 return 语句可以覆盖其他 return 语句或从 try 语句块和所有定义的 catch 代码块中抛出异常:

function asdf()
{
    try {
        throw new Exception('error');
    }
    catch(Exception $e) {
        echo "An error occurred";
        throw $e;
    }
    finally {
        // This overrides the exception as if it were never thrown
        return "\nException erased";
    }
}

try {
    echo asdf();
}
catch(Exception $e) {
    echo "\nResult: " . $e->getMessage();
}

代码输出如下:

An error occurred
Exception erased

如果没有在 finally 代码块中使用 return,则输出:

An error occurred
Result: error

使用单个 catch() 捕获多个类型的异常。

class DisplayException extends Exception {};
class FileException extends Exception {};
class AccessControl extends FileException {}; // Sub-class of FileException
class IOError extends FileException {}; // Sub-class of FileException

try {
    if(!is_readable($somefile))
        throw new IOError("File is not readable!");
    if(!user_has_access_to_file($someuser, $somefile))
        throw new AccessControl("Permission denied!");
    if(!display_file($somefile))
        throw new DisplayException("Couldn't display file!");

} catch (FileException $e) {
    // 可以捕获 FileException, AccessControl or IOError exceptions,
    // 因为上面抛出的异常是 FileException 的子类。
    // 但不能捕获 Exceptions or DisplayExceptions。
    echo "File error: ".$e->getMessage();
}

扩展 PHP 内置的异常处理类

用户可以用自定义的异常处理类来扩展 PHP 内置的异常处理类。

内置的异常处理类(下面代码只是说明Exception的结构):

class Exception
{
    protected $message = 'Unknown exception';   // 异常信息
    protected $code = 0;                        // 用户自定义异常代码
    protected $file;                            // 发生异常的文件名
    protected $line;                            // 发生异常的代码行号

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

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

    /* 可重载的方法 */
    function __toString();                       // 可输出的字符串
}

如果使用自定义的类来扩展内置异常处理类,并且要重新定义构造函数的话,建议同时调用 parent::__construct() 来检查所有的变量是否已被赋值。当对象要输出字符串的时候,可以重载 __toString() 并自定义输出的样式。

/**
 * 自定义一个异常处理类
 */
class MyException extends Exception
{
    // 重定义构造器使 message 变为必须被指定的属性
    public function __construct($message, $code = 0) {
        // 自定义的代码

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

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

    public function customFunction() {
        echo "A Custom function for this type of exception\n";
    }
}

发表评论

电子邮件地址不会被公开。