mezon/router

小巧快速的路由框架

1.5.1 2022-09-13 12:18 UTC

README

Latest Stable Version Open Collective Build Status Scrutinizer Code Quality codecov Twitter

简介

Mezon Framework 提供简单路由类,满足您的需求。它已被用于 Web应用服务CRUD服务

贡献者

Mezon之所以变得更好,是因为有贡献者。也要感谢他们。

再次感谢大家的贡献。

如果您也想支持我们的项目,请使用此链接

常见问题解答

有问题请使用此服务提问。

安装

只需在控制台打印

composer require mezon/router

这就是全部。

使用理由

mezon/router 是

  • 比 klein/klein 路由快 25 倍
  • 比 Symfony 路由快 7 到 15 倍
  • 比 Laravel 路由快 30 到 50 倍
  • 比 nikic/fast-route 快 1.5 倍

更多基准测试信息请见这里

了解更多

更多信息请见此处

Twitter

dev.to

什么是“第一种情况”和“第二种情况”?

  1. 第一种情况 - http服务器接受请求,启动php脚本,处理该请求,然后将所有脚本数据从内存中上传。所有后续请求都以此方式处理。在这种情况下,尽快启动脚本至关重要,我们无法花时间进行长时间的预编译和准备,因为所有这些都将随着脚本完成工作而丢失。

  2. 第二种情况 - php脚本启动,初始化所有内部组件(其中之一是路由器)然后开始处理请求。这种情况可以通过例如react-php来组织。与之前的情况不同,因为我们可以花合理的时间预先编译路由以加快速度。

在此表中,您可以查看每秒的请求数量。数字越大越好。

results

mezon和klein比较

mezon和symfony比较

mezon和laravel比较

mezon和fast-route比较

mezon和yii2路由比较

如果您能按下“STAR”按钮,我将非常高兴

简单路由

路由器允许您将URL映射到您的PHP代码上,并在需要时调用。

路由器支持简单的路由,如上面的示例 - example.com/contacts/

每个Application对象都会隐式创建其action[action-name]方法的路由,其中action-name将作为路由存储。这里有一个小(如往常一样)的例子

class MySite
{
    /**
     * Main page
     */
    public function actionIndex()
    {
        return 'This is the main page of our simple site';
    }

    /**
     * FAQ page
     */
    public function actionFaq()
    {
        return 'This is the "FAQ" page';
    }

    /**
     * Contacts page
     */
    public function actionContacts()
    {
        return 'This is the "Contacts" page';
    }

    /**
     * Some custom action handler
     */
    public function someOtherPage()
    {
        return 'Some other page of our site';
    }
    
    /**
     * Some static method
     */
    public static function someStaticMethod()
    {
        return 'Result of static method';
    }
}

这段代码

$router = new \Mezon\Router\Router();
$router->fetchActions($mySite = new MySite());

将创建路由器对象,并加载其动作信息以及创建路由。严格来说,它将创建两个路由,因为MySite类只有两个以action[Suffix]开头的方法。方法someOtherPage不会自动转换为路由。默认情况下,此方法将创建处理POST和GET请求方法的路由。

然后只需通过URL调用回调函数即可

$router->callRoute('/index/');

有一种方法可以指定每个动作的请求方法

$router->fetchActions($mySite = new MySite(), [
	'Index' => 'GET',
	'Contacts' => 'POST',
	'Faq' => ['GET', 'POST'],
]);

您可以手动指定应用程序中每个URL的回调函数

$router->addRoute('/some-any-other-route/', [$mySite, 'someOtherPage']);

您还可以使用静态方法

$router->addRoute('/static-route/', ['MySite', 'someStaticMethod']);
// or in this way
$router->addRoute('/static-route/', 'MySite::someStaticMethod');

我们只需要显式地创建它。

我们还可以使用简单函数来创建路由

function sitemap()
{
    return 'Some fake sitemap';
}

$router->addRoute('/sitemap/', 'sitemap');

您可以在不启动它的情况下找到回调

$router->addRoute('/static-route/', 'MySite::someStaticMethod');
$callback = $router->getCallback('/static-route/');
var_dump($callback());

支持请求方法

Mezon Router支持

  • GET
  • POST
  • PUT
  • DELETE
  • OPTION
  • PATCH

要获取这些方法的列表,您可以使用getListOfSupportedRequestMethods方法

$router = new \Mezon\Router\Router();
var_dump($router->getListOfSupportedRequestMethods());

一个处理器处理所有路由

您可以为所有路由指定一个处理器,如下所示

$router->addRoute('/*/', function(){});

请注意,如果找到*处理器,路由搜索将停止。例如

$router->addRoute('/*/', function(){});
$router->addRoute('/index/', function(){});

在这个例子中,路由/index/永远不会被访问。所有请求都将传递到*处理器。但是在本例中

$router->addRoute('/contacts/', function(){});
$router->addRoute('/*/', function(){});
$router->addRoute('/index/', function(){});

路由/contacts/将由其处理器处理,而所有其他路由(包括/index/)将由*处理器处理。

路由变量

现在是一些更复杂的路由

$router->addRoute('/catalogue/[i:cat_id]/', function($route, $variables){});
$router->addRoute('/catalogue/[a:cat_name]/', function($route, $variables){});

这里

  • i - 任何整数数字
  • a - 任何[a-z0-9A-Z_/-.@]+字符串
  • il - 以逗号分隔的整数ID列表
  • s - 任何字符串

参数名必须由以下字符组成:[a-zA-Z0-9_-]

所有这些变量都作为第二个函数参数传递,如下例所示 - $variables。所有变量都以关联数组的形式传递。

请求类型和REST API的第一步

您可以将处理器绑定到不同的请求类型,如下所示

$router->addRoute('/contacts/', function(){}, 'POST'); // this handler will be called for POST requests
$router->addRoute('/contacts/', function(){}, 'GET');  // this handler will be called for GET requests
$router->addRoute('/contacts/', function(){}, 'PUT');  // this handler will be called for PUT requests
$router->addRoute('/contacts/', function(){}, 'DELETE');  // this handler will be called for DELETE requests
$router->addRoute('/contacts/', function(){}, 'OPTION');  // this handler will be called for OPTION requests
$router->addRoute('/contacts/', function(){}, 'PATCH');  // this handler will be called for PATCH requests

反向路由

您可以通过路由的名称反向路由和编译URL。例如

$router = new \Mezon\Router\Router();
$router->addRoute('/some-route/[i:id]', function(){}, 'GET', 'name of the route');
// will output /some-route/123
var_dump($router->reverse('name of the route', ['id' => 123]));

路由缓存

从版本1.1.0开始,您可以将路由缓存到磁盘并从中读取。

要将其导出到磁盘,请使用

$router->dumpOnDisk('./cache/cache.php');

然后您可以加载路由

$router->loadFromDisk('./cache/cache.php');

但是,这些方法有一些限制 - 由于明显的原因,它们不能导出和加载闭包。

您还可以在不导出的情况下预热缓存

$router->warmCache();

中间件和参数修改

您可以在路由处理器执行之前添加的中间件类型。此中间件可以将常见的参数$ route和$ parameters转换为不同的内容。

  • 多个全局中间件,将按附加顺序调用。
  • 多个路由特定中间件,将按附加顺序调用。

中间件的执行顺序

  1. 全局中间件$router->addRoute('*', ...)
  2. 在调用路由回调$router->addRoute('/example', ...)之前,将执行所有匹配的路由。

让我们看看一个简单的例子

$router = new Router();
$router->addRoute('/user/[i:id]', function(string $route, array $parameters){
    $userModel = new UserModel();
    $userObject = $userModel->getUserById($parameters['id']);

    // use $userObject for any purpose you need
});

现在让我们看看一个包含所有可能性的例子。

$router = new Router();

// First step. We have an API that talks JSON, convert the body
$router->registerMiddleware('*', function (string $route, array $parameters){
    $request = Request::createFromGlobals();
    
    $parameters['_request'] = $request;
    $parameters['_body'] = json_decode($request->getContent(), true);

    return $parameters;    
});

// Second step. Ensure that we are logged in when we are in the private area
$router->registerMiddleware('*', function (string $route, array $parameters){
    // Is not a private area
    if (mb_strpos($route, '/user') !== 0 || empty($parameters['user_id'])) {
        return $parameters;
    }

    $token = $parameters['_request']->headers->get('oauth_token');

    $auth = new SomeAuth();
    $auth->validateTokenOrFail(
        $token,
        $parameters['user_id']
    );

    // We don't need to return nothing
});

// Last step. Now we will modify the parameters so the handler can work with them
$router->registerMiddleware('/user/[i:user_id]', function(string $route, array $parameters){
    $userModel = new UserModel();
    
    return $userModel->getUserById(
        $parameters['user_id']
    );
});

// Final destination. We have ended the middlewares, now we can work with the processed data
$router->addRoute('/user/[i:user_id]', function (UserObject $userObject){
    // Do everything
});

PSR-7路由处理

最初,Mezon Router并非为PSR-7兼容而设计。但最新功能之一使其成为可能。您可以使用中间件来实现此目的。例如

$router = new Router();
$router->addRoute('/user/[i:id]', function(\Nyholm\Psr7\Request $request){
    // work here with the request in PSR-7 way

    $psr17Factory = new \Nyholm\Psr7\Factory\Psr17Factory();

    $responseBody = $psr17Factory->createStream('Hello world');
    $response = $psr17Factory->createResponse(200)->withBody($responseBody);
    (new \Zend\HttpHandlerRunner\Emitter\SapiEmitter())->emit($response);
});

$router->registerMiddleware('/user/[i:id]', function(string $route, array $parameters){
    $psr17Factory = new \Nyholm\Psr7\Factory\Psr17Factory();

    $creator = new \Nyholm\Psr7Server\ServerRequestCreator(
        $psr17Factory, // ServerRequestFactory
        $psr17Factory, // UriFactory
        $psr17Factory, // UploadedFileFactory
        $psr17Factory  // StreamFactory
    );

    return $creator->fromGlobals();
});

最好的事情是 - 如果您在项目中不使用PSR-7,那么您就不会“付费”。

自定义类型

您可以为URL解析器定义自己的类型。让我们尝试创建date类型。

首先,我们应该创建一个简单的类

class DateRouterType
{

    /**
     * Method returns regexp for searching this entity in the URL
     *
     * @return string regexp for searching
     */
    public static function searchRegExp(): string
    {
        return '(\[date:'.BaseType::PARAMETER_NAME_REGEXP.'\])';
    }
}

这里BaseType::PARAMETER_NAME_REGEXP是一个全局设置,它告诉路由器参数名必须由以下字符组成

  • a-z和A-Z字母
  • 0-9
  • 以及_和-符号

现在我们需要定义一个额外的类方法,该方法将在出现日期时解析日期

public static function parserRegExp(): string
{
    // pretty simple regexp
    return '([0-9]{4}-[0-9]{2}-[0-9]{2})';
}

并在您的配置文件中的某个位置启用此类型

$router->addType('date', DateRouterType::class);

现在您可以这样处理路由

/some-url-part/2020-02-02/ending-part/
/posts-for-2020-02-02/

但是要小心。例如,您将定义这样的路由

$router->addRoute('/posts-for-[date:posts-date]/', function(UserObject $userObject){
    // some activities here
});

$router->addRoute('/[s:some-url]/', function(UserObject $userObject){
    // some activities here
});

然后第一个处理器/posts-for-[date:posts-date]/将为路由/posts-for-2020-02-02/调用。