# 异常说明

houdunren.com @ 向军大叔

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

<?php
class Code
{
    protected $len;
    public function make(int $len)
    {
        $this->len = $len;
        if ($this->line() === false) {
            return false;
        }
    }
    public function line()
    {
        if ($this->len > 5) {
            return false;
        }
    }
}
$code = new Code;
if ($code->make(10) === false) {
    echo '验证码创建失败';
}

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

<?php
class Code
{
    protected $len;
    public function make(int $len)
    {
        $this->len = $len;
        $this->line();
    }
    public function line()
    {
        if ($this->len > 5) {
            throw new Exception('长度不能超过五位');
        }
    }
}
try {
    $code = new Code;
    $code->make(10);
} catch (Exception $e) {
    echo $e->getMessage();
}

# 基本使用

# try/catch

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

try{
	...
}catch(){
	...
}

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

throw new Exception($message,$code)

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

<?php
class ValidateException extends Exception
{ }
try {
    throw new ValidateException('is exception', 403);
} catch (ValidateException $e) {
    echo 'httpException' . $e->getMessage() . ';code:' . $e->getCode();
} catch (Exception $e) {
    echo $e->getMessage();
} finally {
    echo '无论是否抛出异常都将执行' . ';code:' . $e->getCode();;
}

# finally

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

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

# Throwable

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

Exception implements Throwable {
 	...
}

部分错误也实现了 Throwable接口

Error implements Throwable {
	...
}

比如以下错误

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

# 异常类

# 基类方法

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

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

# 异常实例

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

因为代码量比较大,大家请查看视频教程来学习。

# 自定义异常

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

# 目录结构

app
-- Exceptions
	-- ValidateException.php
	-- ViewException.php
-- Servers
	-- Validate.php
	-- View.php
-- vendor
-- view
	-- error.blade.php
	-- index.blade.php
	-- success.blade.php
bootstrap.php
composer.json
controller.php
index.php

# 文件内容

# app\Exceptions\ValidateException.php

<?php
namespace App\Exceptions;

class ValidateException extends \Exception
{
    public function render()
    {
        $_SESSION['VALIDATE_ERROR'] = '表单参数错误';
        header('location:index.php');
    }
}

# app\Exceptions\ViewException.php

<?php
namespace App\Exceptions;

use App\Servers\View;

class ViewException extends \Exception
{
    public function render()
    {
        View::make('error', ['error' => $this->getMessage()]);
    }
}

# app\Servers\Validate.php

<?php
namespace App\Servers;

use App\Exceptions\ValidateException;

class Validate
{
    public static function make()
    {
        $_SESSION['VALIDATE_ERROR'] = '';
        if (empty($_POST['title'])) {
            throw new ValidateException('表单错误');
        }
    }
}

# app\Servers\View.php

<?php
namespace App\Servers;

use App\Exceptions\ViewException;

class View
{
    public static function make(string $tpl, array $vars = [])
    {
        $file = 'view/' . $tpl . '.blade.php';
        if (!is_file($file)) {
            throw new ViewException($file . '模板不存在');
        }
        include $file;
    }
}

# app\view\error.blade.php

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
    <h1><?php echo $vars['error'];?></h1>
</body>
</html>

# app\view\index.blade.php

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

# app\view\error.blade.php

<h1>操作成功</h1>

# app\bootstrap.php

<?php
session_start();
include 'vendor/autoload.php';
class Boot
{
    public function init()
    {
        set_exception_handler([$this, 'exception']);
    }
    public function exception($e)
    {
        if (method_exists($e, 'render')) {
            $e->render();
        }
    }
}
(new Boot)->init();

# composer.json

{
    "name": "hd/app",
    "authors": [
        {
            "name": "houdunren.com",
            "email": "2300071698@qq.com"
        }
    ],
    "autoload": {
        "psr-4": {
            "App\\": "."
        }
    },
    "require": {}
}

# controller.php

<?php
include 'bootstrap.php';
use App\Servers\Validate;
use App\Servers\View;

include 'vendor/autoload.php';
// try {
Validate::make();
View::make('success');
// } catch (ValidateException $e) {
//     $e->render();
// } catch (ViewException $e) {
//     $e->render();
// }

# index.php

<?php
namespace App;

include 'bootstrap.php';
use App\Servers\View;

View::make('index');