Skip to content

过滤器

Alex edited this page Aug 28, 2019 · 2 revisions

Fluff 的路由只提供了简单匹配机制,如果你想添加过滤条件,可以使用过滤器 (Filter). 过滤器是一个可调用对象,通过给定的参数决定请求是否匹配。过滤器有两种方案:预先注册的过滤器和直接使用的过滤器对象:

(方案一)注册过滤器

use Psr\Http\Message\ServerRequestInterface;

NumericFilter
{
    public function __invoke(ServerRequestInterface $serverRequest, array $options, $params)
    {
        foreach ($options as $option) {
           if (!is_numeric($params[$option])) {
               return false;
           }
        }
        return true;
    }
}

$app->withFilter('numeric', new NumericFilter());

Filter 必须是一个可调动对象,可以预先注册的 Filter 接受三个参数:

  1. $serverRequest: server-side request 对象。
  2. $options: 在路由中传入的选项,与 filter 的注册名称相对应。
  3. $params: 获取到的 URL 参数。

在路由中使用 Filter:

$app->get('/user/{id}', 'userController', [
    'filters' => [
        'numeric' => ['id',]  // `name => options`
    ]
]);

Application 会在 pattern 匹配成功后,直接调用各个 Filter,当 Filter 返回 true 时匹配成功。返回 false 匹配失败,并且会抛出 ConstanzeStandard\Fluff\Exception\NotFoundException 异常。

(方案二)直接使用过滤器对象

过滤器对象也可以直接传入路由的 filters 的选项中:

NumericFilter
{
    public function __construct(array $options)
    {
        $this->options = $options;
    }

    public function __invoke(ServerRequestInterface $serverRequest, $params)
    {
        foreach ($this->options as $option) {
           if (!is_numeric($params[$option])) {
               return false;
           }
        }
        return true;
    }
}

$app->get('/user/{id}', 'userController', [
    'filters' => [
        new NumericFilter(['id'])
    ]
]);

如果你选择直接传入 filter 对象,Application 就无法拿到 options 数据了,但你可以将 options 以参数的形式直接传入 filter 对象中。

路由缓存与过滤器的序列化问题

当我们开启路由缓存,并选择直接向 filters 字段传入过滤器对象(方案二)时,必须在过滤器中实现 __set_state 魔术方法。而且必须明确一个事实:闭包是无法被序列化的,也就是说,如果你想使用缓存,就不能直接传入闭包对象,但可以使用 withFilter 方法先将闭包注册进 Application 中(我觉得直接传入闭包的意义也不是很大,因为没有位置给你传递过滤器选项(options))。

NumericFilter
{
    public function __construct(array $options)
    {
        $this->options = $options;
    }

    public function __invoke(ServerRequestInterface $serverRequest, $params)
    {
        foreach ($this->options as $option) {
           if (!is_numeric($params[$option])) {
               return false;
           }
        }
        return true;
    }

    /**
     * 必须实现 __set_state 方法才能使用路由缓存
     */
    public function __set_state($array)
    {
        return new static($array['options']);
    }
}