异常说明

异常时推荐的错误处理方式,传统的错误处理方式要判断并一层一层返回到调用点,如下所示:

  1. <?php
  2. class Code
  3. {
  4. protected $len;
  5. public function make(int $len)
  6. {
  7. $this->len = $len;
  8. if ($this->line() === false) {
  9. return false;
  10. }
  11. }
  12. public function line()
  13. {
  14. if ($this->len > 5) {
  15. return false;
  16. }
  17. }
  18. }
  19. $code = new Code;
  20. if ($code->make(10) === false) {
  21. echo '验证码创建失败';
  22. }

通过上面代码我们发现处理错误时及其不方便,需要多个判断语句。下面是改用异常的处理方式。

  1. <?php
  2. class Code
  3. {
  4. protected $len;
  5. public function make(int $len)
  6. {
  7. $this->len = $len;
  8. $this->line();
  9. }
  10. public function line()
  11. {
  12. if ($this->len > 5) {
  13. throw new Exception('长度不能超过五位');
  14. }
  15. }
  16. }
  17. try {
  18. $code = new Code;
  19. $code->make(10);
  20. } catch (Exception $e) {
  21. echo $e->getMessage();
  22. }

基本使用

try/catch

PHP 需要手动抛出异常,这与其他语言不同,异常使用 try…catch 触发。

  1. try{
  2. ...
  3. }catch(){
  4. ...
  5. }

try 代码块中对出现的错误可以抛出异常,下面是手动抛出异常的方法。

  1. throw new Exception($message,$code)

catch 用于接收异常,可以设置多个 catch 代码块,参数为 Exception 类或继承于 Execption 的类。

  1. <?php
  2. class ValidateException extends Exception
  3. { }
  4. try {
  5. throw new ValidateException('is exception', 403);
  6. } catch (ValidateException $e) {
  7. echo 'httpException' . $e->getMessage() . ';code:' . $e->getCode();
  8. } catch (Exception $e) {
  9. echo $e->getMessage();
  10. } finally {
  11. echo '无论是否抛出异常都将执行' . ';code:' . $e->getCode();;
  12. }

finally

finally 需要放在 catch 后, finally 无论是否抛出异常都会执行。

  1. ...
  2. } catch (Exception $e) {
  3. echo $e->getMessage();
  4. } finally {
  5. echo '无论是否抛出异常都将执行';
  6. }
  7. ...

Throwable

异常和部分错误实现了 Throwable 接口。

  1. Exception implements Throwable {
  2. ...
  3. }

部分错误也实现了 Throwable 接口

  1. Error implements Throwable {
  2. ...
  3. }

比如以下错误:

  • ArithmeticError 数学运算错误
  • DivisionByZeroError 除数为0的错误
  • ParseError 解析代码时发生错误如调用 eval 函数时
  • TypeError 函数参数类型错误,函数返回值错误

异常类

基类方法

PHP 为异常处理提供了基础类 Exception ,Exception 类可用方法如下:

方法 说明 重写
getFile 产生异常错误的文件 NO,final
getCode 错误码 NO,final
getLine 错误行号 NO,final
getMessage 错误信息 NO,final
__toString 对象转字符串后输出内容 YES
  1. <?php
  2. class ValidateException extends Exception
  3. {
  4. //对象转字符串时执行的魔术方法
  5. public function __toString()
  6. {
  7. return $this->getFile();
  8. }
  9. }
  10. try {
  11. throw new ValidateException('is exception', 403);
  12. } catch (ValidateException $e) {
  13. echo "文件:" . $e->getFile() . "<hr/>";
  14. echo "消息:" . $e->getMessage() . "<hr/>";
  15. echo "错误码:" . $e->getCode() . "<hr/>";
  16. echo "错误行:" . $e->getLine() . "<hr/>";
  17. echo $e . "<hr/>";
  18. }

异常实例

实际开发中需要根据不同业务创建处理错误的异常类,推荐使用异常来处理错误而不是 PHP 的错误处理机制。

自定义异常

下面是通过实例讲解自定义异常的使用方法。

目录结构

  1. app
  2. -- Exceptions
  3. -- ValidateException.php
  4. -- ViewException.php
  5. -- Servers
  6. -- Validate.php
  7. -- View.php
  8. -- vendor
  9. -- view
  10. -- error.blade.php
  11. -- index.blade.php
  12. -- success.blade.php
  13. bootstrap.php
  14. composer.json
  15. controller.php
  16. index.php

文件内容

app\Exceptions\ValidateException.php

  1. <?php
  2. namespace App\Exceptions;
  3. class ValidateException extends \Exception
  4. {
  5. public function render()
  6. {
  7. $_SESSION['VALIDATE_ERROR'] = '表单参数错误';
  8. header('location:index.php');
  9. }
  10. }

app\Exceptions\ViewException.php

  1. <?php
  2. namespace App\Exceptions;
  3. use App\Servers\View;
  4. class ViewException extends \Exception
  5. {
  6. public function render()
  7. {
  8. View::make('error', ['error' => $this->getMessage()]);
  9. }
  10. }

app\Servers\Validate.php

  1. <?php
  2. namespace App\Servers;
  3. use App\Exceptions\ValidateException;
  4. class Validate
  5. {
  6. public static function make()
  7. {
  8. $_SESSION['VALIDATE_ERROR'] = '';
  9. if (empty($_POST['title'])) {
  10. throw new ValidateException('表单错误');
  11. }
  12. }
  13. }

app\Servers\View.php

  1. <?php
  2. namespace App\Servers;
  3. use App\Exceptions\ViewException;
  4. class View
  5. {
  6. public static function make(string $tpl, array $vars = [])
  7. {
  8. $file = 'view/' . $tpl . '.blade.php';
  9. if (!is_file($file)) {
  10. throw new ViewException($file . '模板不存在');
  11. }
  12. include $file;
  13. }
  14. }

app\view\error.blade.php

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6. <meta http-equiv="X-UA-Compatible" content="ie=edge">
  7. <title>Document</title>
  8. </head>
  9. <body>
  10. <h1><?php echo $vars['error'];?></h1>
  11. </body>
  12. </html>

app\view\index.blade.php

  1. <?php if(isset($_SESSION['VALIDATE_ERROR'])):?>
  2. <?php echo $_SESSION['VALIDATE_ERROR'];?>
  3. <?php endif;?>
  4. <form method="post" action="controller.php">
  5. <input type="text" name="title">
  6. <button>提交</button>
  7. </form>

app\view\success.blade.php

  1. <h1>操作成功</h1>

app\bootstrap.php

  1. <?php
  2. session_start();
  3. include 'vendor/autoload.php';
  4. class Boot
  5. {
  6. public function init()
  7. {
  8. set_exception_handler([$this, 'exception']);
  9. }
  10. public function exception($e)
  11. {
  12. if (method_exists($e, 'render')) {
  13. $e->render();
  14. }
  15. }
  16. }
  17. (new Boot)->init();

composer.json

  1. {
  2. "name": "14778/app",
  3. "authors": [
  4. {
  5. "name": "ixysec",
  6. "email": "1477821088@qq.com"
  7. }
  8. ],
  9. "autoload": {
  10. "psr-4": {
  11. "App\\": "."
  12. }
  13. },
  14. "require": {}
  15. }

controller.php

  1. <?php
  2. include 'bootstrap.php';
  3. use App\Servers\Validate;
  4. use App\Servers\View;
  5. Validate::make();
  6. View::make('success');

index.php

  1. <?php
  2. namespace App;
  3. include 'bootstrap.php';
  4. use App\Servers\View;
  5. View::make('index');