fruit/routekit

此包最新版本(2.0.3)没有可用的许可证信息。

fruit框架的快速路由器

2.0.3 2020-10-06 06:30 UTC

This package is auto-updated.

Last update: 2024-09-06 14:53:20 UTC


README

此包是Fruit框架的一部分,需要PHP 7。

RouteKit是一个快速路由器实现。它将您的路由规则存储在树结构中,您可以通过生成内置类生成器来创建路由器类来使其更快。

Build Status

概要

$mux = new Fruit\RouteKit\Mux;

// Create routing rules

$mux->get('/', array('MyClass', 'myMethod'));
// static method
$mux->get('/', 'Foo::Bar');
// function
$mux->get('/', 'foobar');
$mux->get('/', foobar);
// create handler/controller instance with constructor arguments.
$mux->get('/foo', array('MyClass', 'myMethod'), array('args', 'of', 'constructor'));

class FOO {
    public function BAR($arg1, $arg2){}
    public function BAZ($arg1, $arg2){}
}
// uri variables are unnamed, ":x" is identical to ":"
$mux->get('/foo/:/:x/bar', array('FOO', 'BAR'));
$mux->get('/foo/:1/baz/:2', array('FOO', 'BAZ'));

// you cannot use closure as handler
// $mux->get('/', function(){});    // wrong!

// but you can use object if it can be serialized with var_export()
$mux->get('/', array(new FOO, 'BAR'));

// dispatch request
$mux->dispatch($_SERVER['REQUEST_METHOD'], $_SERVER['PATH_INFO']);

// generate class
file_put_content('router.php', $mux->compileFile());

// use generated router class
$mux = require('router.php');
$mux->dispatch('GET', '/foo/123/abc/bar'); // (new Foo())->BAR('123', 'abc');
$mux->dispatch('GET', '/foo'); // (new MyClass('args', 'of', 'constructor'))->myMethod();

// use injector to do DI tasks
function di($url, $obj, $method) {
    $obj->setDB($db);
}
$mux->setInjector('di');
$mux->get('/', array(new FOO, 'BAR'));
$mux->dispatch('GET', '/'); // $obj = new FOO; di('/', $obj, 'BAR'); $obj->BAR();

// auth middleware, implements by input filter
function checkAuth($method, $url, $cb, $params) {
    if (!authed()) {
        header('Location: /login');
        return 'login first';
    }
}
$mux->setFilters(['checkAuth'], []);
$mux->get('/', array(new FOO, 'BAR'));
$mux->dispatch('GET', '/'); // redirect to /login if not logged in

// CORS middleware, implements by output filter
function addCORS($result) {
    // few lines of code adding CORS headers here

    return $result;
}
$mux->setFilters([], ['addCORS']);
$mux->get('/', array(new FOO, 'BAR'));
$mux->dispatch('GET', '/'); // client will receive CORS header!

// Templating middleware
function handler() {
    // real codes

    return ['view' => 'index.tmpl', 'data' => $result];
}
function forgeView($result) {
    return Template::load($result['view'])->render($result['data']);
}
$mux->setFilters([], ['addCORS']);
$mux->get('/', 'handler');
$mux->dispatch('GET', '/');

为什么快速

RouteKit通过两种方式提高性能:更好的规则匹配数据结构和将动态调用转换为静态调用的能力。

匹配过程

RouteKit将路由规则存储在树结构中,因此匹配速度不会受到您有多少条规则的影响。换句话说,匹配过程具有常数时间复杂度。

更进一步,在生成的路由器中,我们使用有限状态机进行匹配过程,消除了所有可能的函数调用。与opcode操作(如if、switch、赋值、算术运算等)和散列表操作相比,函数调用要慢得多。在实践中,FSM方法可以比数组实现快20多倍。

静态调用

大多数路由器实现将分发工作分为两部分:匹配规则以获取正确的处理程序,然后使用cufa执行它;而cufa以其出色的性能而闻名。

通过生成自定义路由器,我们根据您在路由规则中提供的信息生成代码。不再需要cufa,不再需要反射,因此没有性能损失。

由于代码主要使用var_export生成,因此某些情况不支持。

  • 无法使用__set_state恢复类。
  • 闭包和匿名函数。
  • 不可访问的方法。(非public方法)
  • 特殊变量,如资源。

生成的类应该是线程安全的,因为所有属性和方法都是静态的。

类型转换

您可以为处理程序添加类型提示,RouteKit将自动为您进行类型检查和转换。目前我们只支持intfloatboolstring。如果没有类型提示,我们将不会检查/转换参数。

在非编译版本中,这是通过ReflectionParameter::getType()完成的,因此获取反射数据会花费一些性能。

在编译版本中,我们在编译时获取类型信息,因此除了类型检查和转换工作外,没有性能损失。

function handlerOK(int $i) {}
function handlerFAIL(array $i) {}
$mux->get('/ok/:', 'handlerOK');
$mux->get('/fail/:', 'handlerFAIL');

$mux->dispatch('GET', '/ok/1'); // works
$mux->dispatch('GET', '/ok/hello'); // throw a Fruit\RouteKit\TypeMismatchException
$mux->dispatch('GET', '/fail/1'); // throw an Exception with messages telling you array type is not supported
$mux->compile(); // throw an Exception with messages telling you array type is not supported

注入器和过滤器

注入器

注入器旨在解决“全局变量”问题:您可以通过您定义的接口将全局数据注入到您的处理程序对象中。

输入过滤器

输入过滤器旨在过滤控制流。应该在此处实现权限验证层。

输入过滤器接受正好4个参数

  • HTTP方法
  • URL
  • 表示处理程序的调用
  • 传递给处理程序的参数数组。

返回任何非NULL值都会中断分发过程,并立即返回过滤结果。

虽然PHP语法允许您修改这4个参数,但您不应该修改它们。这是因为输入过滤器不是为了过滤输入数据。

输出过滤器

输出过滤器旨在过滤结果。如果您想细化结果或HTTP头,您就到了正确的位置。

输出过滤器只接受1个参数,即处理程序返回的数据:您可以修改它,发送或删除一些头部,然后将结果返回。最常见的例子是添加CORS头部或通过模板系统伪造真实输出。

分发流程

  1. 在路由树中搜索当前处理程序并收集URL参数。
  2. 执行输入过滤器,决定是否执行下一步。
  3. 如果任何输入过滤器阻止执行,则立即返回。
  4. 使用注入器修改处理程序的内部状态。
  5. 执行处理程序。
  6. 通过执行输出过滤器来操作结果。

许可证

任何版本的MIT、GPL或LGPL。