8ctopus/nano-router

实验性的PSR-7, PSR-17路由器

13.0.0 2024-08-07 07:49 UTC

README

packagist downloads min php version license tests code coverage badge lines of code

实验性的PSR-7, PSR-17路由器

特性

  • 非常快(简单路由低于2ms)
  • 使用PSR-7和PSR-17标准
  • 除了PSR-7/17外无任何依赖

虽然我认为它仍然是实验性的,但我已经在生产环境中使用它来托管legend.octopuslabs.io,迄今为止没有遇到任何问题。

初学者介绍

路由器的作用是将用户(客户端)的HTTP请求匹配到特定的函数,该函数将处理用户请求并向客户端发送响应。

PSR-7 定义了请求和响应接口,而 PSR-17 定义了创建它们的工厂。换句话说,工厂用于创建请求和响应对象。

以下是一些伪代码,用于解释这个概念

$router = new Router();

$router->addRoute(new Route(RouteType::Exact, 'GET', '/test.php', function (ServerRequestInterface $request) : ResponseInterface {
    return new Response(200, ['content-type' => 'text/plain'], 'You\'ve reached page /test.php');
}));

// create user request from globals
$request = ServerRequestCreator::createFromGlobals($_SERVER, $_FILES, $_COOKIE, $_GET, $_POST);

// resolve finds the function that handles the user request, calls it and returns the function's response
$response = $router->resolve($request);

// send response to client (echoes internally)
(new SapiEmitter())
    ->emit($response);

演示

要玩演示,请克隆仓库,运行 php -S localhost:80 demo/public/index.php -t demo/public/ 并在浏览器中打开 http://localhost。或者您可以在Docker容器中运行演示 docker-compose up &

安装

  • composer require 8ctopus/nano-router

  • 如果您对PSR-7实现没有偏好,请安装 HttpSoft composer require httpsoft/http-message httpsoft/http-emitter

  • 对于使用Apache的用户,在 .htaccess 中将所有流量(除现有文件外)重定向到路由器

RewriteEngine on

# redirect all not existing files and directories to router
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^ index.php [END]

以及对于nginx(未经测试)

location / {
    try_files $uri $uri/ /index.php$is_args$args;
}
  • 创建 index.php
use Oct8pus\NanoRouter\NanoRouter;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;

// use any PSR-7, PSR-17 implementations, here HttpSoft
use HttpSoft\Emitter\SapiEmitter;
use HttpSoft\Message\Response;
use HttpSoft\Message\ServerRequestFactory;
use HttpSoft\Message\Stream;
use HttpSoft\ServerRequest\ServerRequestCreator;

require_once __DIR__ . '/vendor/autoload.php';

$router = new NanoRouter(Response::class, ServerRequestFactory::class);

$router
    // add simple route
    ->addRoute(new Route(RouteType::Exact, 'GET', '/test.php', function (ServerRequestInterface $request) : ResponseInterface {
        $stream = new Stream();
        $stream->write('test.php');

        return new Response(200, [], $stream);
    }))
    // add starts with route
    ->addRoute(new Route(RouteType::StartsWith, ['GET', 'POST'], '/test/', function (ServerRequestInterface $request) : ResponseInterface {
        $stream = new Stream();
        $stream->write('request target - '. $request->getRequestTarget());

        return new Response(200, [], $stream);
    }))
    // add regex route
    ->addRoute(new Route(RouteType::Regex, '*', '~/php(.*)/~', function (ServerRequestInterface $request) : ResponseInterface {
        $stream = new Stream();
        $stream->write('request target - '. $request->getRequestTarget());

        return new Response(200, [], $stream);
    }))
    ->addErrorHandler(404, function (ServerRequestInterface $request) : ResponseInterface {
        $stream = new Stream();
        $stream->write('page not found - ' . $request->getRequestTarget());

        return new Response(404, [], $stream);
    })
    ->addMiddleware('*', '~(.*)~', MiddlewareType::Post, function (ResponseInterface $response, ServerRequestInterface $request) : ResponseInterface {
        return $response->withHeader('X-Powered-By', '8ctopus');
    });

// create request from globals
$request = ServerRequestCreator::createFromGlobals($_SERVER, $_FILES, $_COOKIE, $_GET, $_POST);

// resolve request into a response
$response = $router->resolve($request);

// send response to client
(new SapiEmitter())
    ->emit($response);

高级功能

还有很多其他的功能,但还没有在README中说明,例如

  • 前置和后置中间件
  • 路由异常和通用异常处理

但大部分都可以在演示中尝试

运行测试

composer test

代码整洁

composer fix(-risky)

待办事项想法

  • 添加basePath
  • 子路由的类包装器
  • 前置中间件是否只应该对有效请求起作用?现在无效路由仍然通过中间件,我们可能需要两者
  • 检查PSR-15中间件
  • 添加startsWith中间件
  • 如何在类内部轻松路由?