PHP 中的错误和异常处理

2020-11-19 08:00 PHP在线

PHP 当然也有自己的异常处理,不过那已经是 PHP5 以后的事了。就我自己这些年写程序的现状看,我基本上就没有真正明白什么是异常处理,经常把异常和错误处理混为一谈,关于代码中的那些写法,不是写错了,就是写的太特么烂了。恰好最近在写一些类时用到异常处理了,顺便就把这个整理下,但是这个仅代表我个人的一些理解和使用,也可能是错误的,还请谨慎阅读。

概述

错误处理定义

错误是指导致系统不能按照用户意图工作的一切原因、事件。在程序设计过程中,由于某些错误的存在,致使程序无法正常运行,处理这些错误以使程序正确运行就称为错误处理。错误处理功能是衡量编译器性能的重要方面,它在帮助程序员尽快修改程序方面起到了非常重要的作用。

错误类型:

  • 语法错误

  • 语义错误

  • 逻辑错误

异常处理定义

编程语言或计算机硬件里的一种机制,用于处理软件或信息系统中出现的异常状况(即超出程序正常执行流程的某些特殊条件)

预定义的错误代码

当然你也可以参见官网的手册 预定义常量,我抄一遍纯粹是为了加深记忆。至于每种错误在什么情况下产生,你可以参看 PHP的错误机制总结

相关文章

  • 深入理解PHP原理之异常机制

  • 我们什么时候应该使用异常

  • PHP 最佳实践之异常和错误

  • Throwable Exceptions and Errors in PHP 7

早有人把 PHP 各个版本的错误和异常处理类树整理出来供我等参观。如果在本地执行此代码,依据本地对 PHP 安装的扩展的不同,会把扩展中的相关异常处理类一并打印出来

  • 打印错误和异常处理类树代码

  • 打印输出结果

错误处理

  • Note that other types of errors such as warnings and notices remain unchanged in PHP 7. Only fatal and recoverable errors throw exceptions.

  • Prior to PHP 7 alpha-2, the exception hierarchy in PHP 7 was different. Fatal and recoverable errors threw instances of EngineException, which did not inherit from Exception. Both Exception and EngineException inherited from BaseException. The hierarchy was revised with the RFC I authored, Throwable Interface. I felt switching to Throwable and Error was important to avoid confusion from classes using the suffix Exception that did not extend Exception, as well as being more concise and appealing names.

E_ERROR 和 E_RECOVERABLE_ERROR 级别的错误在 PHP7 之前是不能被捕获到的,也就是说,你无法使用 try...catch... 这样的语句捕获到这种级别的错误,但不管是 PHP7 还是 PHP5 对于未捕获的异常依然是一个致命错误

在 PHP5 中

<?php
$object = null;$object->method();
// 结果Fatal error: Call to a member function method() on null in

在 PHP7 中:

<?php
$object = null;
try { $object->method();} catch (Error $error) { echo $error->getMessage();}
// 结果Call to a member function method() on null

是的,PHP7 内置了一个名为 Error 的类来捕获一些致命错误和异常了,其中 Error 和 Exception 类实现了 Throwable 接口。

// Throwable 接口Throwable {    /* Methods */    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 )    abstract public string __toString ( void )}



开发者不能直接实现 Throwable 接口,必须通过 Error 或者 Exception 等类去扩展

也可以用以下的方式去扩展自己的错误和异常类。

<?php
interface TestThrowable extends \Throwable{}
class TestException extends \Exception implements TestThrowable{}
class DemoError extends \Error implements TestThrowable{}

异常处理

由于 Error 和 Exception 都实现了 Throwable 接口,那么问题就来了,在程序中我们究竟要用哪个?通过一些最佳实践我们知道,错误一般是去约束程序员和代码语法、语义及逻辑方面的,而异常是程序在运行过程出现的一些不合期望的情况。因此,在代码中我们应当使用异常而不是错误。

PHP 7.2.0 - 7.2.3 版本中提供的内置异常:

Exception  ClosedGeneratorException  DOMException  ErrorException  IntlException  LogicException    BadFunctionCallException      BadMethodCallException    DomainException    InvalidArgumentException    LengthException    OutOfRangeException  PharException  ReflectionException  RuntimeException    OutOfBoundsException    OverflowException    PDOException    RangeException    UnderflowException    UnexpectedValueException  SodiumException

兼容 PHP5 和 PHP7 的异常处理

<?php
try { // Code that may throw an Exception or Error.} catch (Throwable $t) { // Executed only in PHP 7, will not match in PHP 5.x.} catch (Exception $e) { // Executed only in PHP 5.x, will not be reached in PHP 7.}

自定义捕获错误和异常

  • 未捕获的 Error 和 Exception 通过 set_exception_handler() 做后续清理和日志记录

  • 其他错误仍然通过 set_error_handler() 来处理,在处理的时候使用更加明确的 E_* 类型,并抛出 ErrorException 记录调用栈

以下实例中使用 set_error_handler() 函数去捕获错误,设置一个用户自定义的错误处理函数,这里我就粗暴的抛出 了 ErrorException 异常,当然官方文档也明确说了这个函数不能捕获 E_ERROR、E_PARSE、E_CORE_ERROR、E_CORE_WARNING、E_COMPILE_ERROR、E_COMPILE_WARNING 和在调用 set_error_handler() 函数所在文件中产生的大多数 E_STRICT 异常,言外之语也就是仅能捕获用户定义的错误和异常。使用 set_exception_handler() 函数去设置默认的异常处理程序,用在没有用 try/catch 块来捕获的异常,也就是说不管你抛出的异常有没有人捕获,如果没有人捕获就会进入到该方法中,并且在回调函数调用后异常会中止。使用 register_shutdown_function() 函数去捕获 PHP 的错误:Fatal or Parse Error 等,正由于这个函数是 PHP 脚本执行结束前的最后一个有效调用,所以我们才用它来完成一些不可思议的事情。一般情况下,都会在这个函数的回调函数中使用 error_get_last() 函数来获取执行产生的错误,顺便说一下,像语法这样的错误是捕获不到的

正常情况时的异常捕获

<?php
class ErrorHandler{ public static function register(){ error_reporting(E_ALL); set_error_handler([__CLASS__, 'handleError']); set_exception_handler([__CLASS__, 'handleException']); register_shutdown_function([__CLASS__, 'handleShutdown']); }
public static function handleError($code, $message, $file, $line){ throw new \ErrorException($message, $code, 1, $file, $line); }
public static function handleException($e){ self::handle($e); }
public static function handleShutdown(){ $error = error_get_last();
if (null !== $error && self::isFatal($error['type'])) { self::handleException($error); } }
/** * @param \Exception|\Throwable $e */ public static function handle($e){ $message = $e->getMessage() . PHP_EOL; echo $message;
error_log($message, 3, __DIR__ . '/run.log'); }
protected static function isFatal($type): bool{ $types = [E_ERROR, E_CORE_ERROR, E_COMPILE_ERROR, E_PARSE];
return in_array($type, $types, true); }}
ErrorHandler::register();
$object = null;$object->toArray();
// 结果Call to a member function toArray() on null此时错误已经记录到日志文件中

写错语法时的异常捕获

<?php
// 此处略去 ErrorHandler 类代码,详见上面ErrorHandler::register();
$object = null;
// 这儿故意不写分号$object->toArray()
// 结果PHP Parse error: syntax error, unexpected end of file in ...PHPPHP Dump $_SERVERPHP
Parse error: syntax error, unexpected end of file in ...

事情进展到这里,按理来说,我应该就此停笔收尾,弄个总结之类的一忽悠一下正在阅读的你。但是呢,我又忽然想起来 PHP 那么多的框架是如何处理这个事情的,我想你也好奇,不妨我们去看看

框架中的错误异常处理

很遗憾的告诉你,我不会剖析框架源码,我只想做个 demo 得瑟一下,剩下全交给你。此处我以 symfony debug 为例

  • Symfony debug GitHub

  • Symfony debug Docs

// 安装 symfony/debug$ composer require symfony/debug

<?php

require __DIR__ . '/vendor/autoload.php';
\Symfony\Component\Debug\Debug::enable();\Symfony\Component\Debug\ErrorHandler::register();
$object = null;$object->toArray();

本文章转载自公众号:phpdaily

首页 - PHP 相关的更多文章: