triplepoint / groundhog-router
简单的独立请求路由库。
Requires
- php: >=5.4.0
This package is not auto-updated.
Last update: 2020-01-24 15:00:25 UTC
README
介绍
此库提供了一种请求路由器,它可以解析传入的请求,确定请求映射到的类,并返回一个准备执行的动作处理器类。
将依赖项保持在零,并提供扩展点的接口。
免责声明
尽管我非常注意编写没有冗余依赖和短视假设的代码,但我认为我应该警告你,这段代码 不是 主要面向公众使用的。换句话说,如果你想在你的工作中使用这段代码,你可以自由地在提供的许可下这样做,但我不能保证API将是稳定的,或者代码是否一定会满足你的需求。
所以,请随意将此代码分支并适应你的需求——或者更好的是,提出如何改进代码的通用性的评论。但也要接受,最终,这只是一个你自己的工作的起点。
基本结构
此库中有 3 个核心组件:路由器、路由解析器和路由表存储。还有一些次要元素作为消息传递或作为辅助工具使用:RequestInterface、Route 等。最后,结果是实现 RouteHandlerInterface 的某个对象。
路由器
路由器接收一个表示传入请求的对象,该对象实现 RequestInterface。然后将此请求委托给路由表存储,以尝试找到匹配的路由。一旦找到路由,路由器将构建实现 RouteHandlerInterface 的相应对象,这在典型框架术语中是控制器。
路由解析器
路由解析器是任何实现 RouteParserInterface 的对象。它的责任是获取项目可以响应的路由集合。这是故意抽象的——包含的路由解析器 RouteParserAnnotation 通过一组 phpdoc 属性来确定路由,但任何路由编码策略都可以使用,只需要编写适当的路由解析器来解释它。
路由表存储
路由表存储实现 RoutingTableStoreInterface,它表示一个缓存,用于在路由解析器生成路由集合后存储路由表。包含支持 APC 和 SQLite 的路由表存储,以及一个特殊的“无缓存”存储,它根本不缓存,而是提示路由解析器始终重新生成路由表。可以通过实现新的对象针对 RoutingTableStoreInterface 添加其他存储机制。
RequestInterface
实现 RequestInterface 的对象表示传入的请求。通常只需要实现一个这样的对象,为了保持与其他库的独立性,将其实现留给用户自行完成。RequestInterface 中定义的方法通常基于 Symfony 的 Http-Foundation 库,但任何正确实现此接口的对象都是有效的。
路由
此对象表示单个路由规则,并用作路由器、路由解析器和路由表存储之间的信使容器。
RouteHandlerInterface
这些对象是 MVC 架构中的传统控制器。为了在允许测试的同时包含依赖关系,这些对象可以声明它们首选的依赖注入容器,然后将其传递回它们进行使用。此外,这些对象由路由器加载,并包含任何可能存在的传入请求的调用参数。
示例
首先,让我们定义一些必须实现的一些类
<?php // ### HttpRequestWrapper.php ### <?php namespace MyProject; use \Symfony\Component\HttpFoundation\Request; use \Groundhog\Router\RequestInterface; class HttpRequestWrapper implements Router\RequestInterface { protected $request; public function __construct( Request $request ) { $this->request = $request; } public function getPathInfo() { return $this->request->getPathInfo(); } public function getUri() { return $this->request->getUri(); } public function getHost() { return $this->request->getHost(); } public function getMethod() { return $this->request->getMethod(); } }
<?php // ### SimpleRouteHandler.php ### namespace MyProject; use \Groundhog\Router\RouteHandlerInterface; use \Groundhog\Router\RouteHandlerServiceContainerInterface; class SimpleRouteHandler implements RouteHandlerInterface { /** * This dependency will be provided by the service container returned from getDefaultServiceContainer() */ protected $some_dependency; /** * this call parameter will be present in the HTTP request, and will be passed in an array by the Router */ protected $some_request_parameter; static public function getDefaultServiceContainer() { return new ServiceContainer(); } public function processServiceContainer(RouteHandlerServiceContainerInterface $service_container = null) { $this->some_dependency = $service_container['some_dependency']; } /** * Route handlers are required to accept call parameters, even if they don't need them. * * @see Groundhog\Router\RouteHandlerInterface::setCallParameters() */ public function setCallParameters(array $call_parameters) { $this->some_request_parameter = $call_parameters[1]; } /** * In this example, we've chosen the convention of all our Route Handlers use the execute() method to perform their controller action. * * The Annotations here are the ones that the RouteParserAnnotation implementation of the RouteParserInterface is designed to detect. * * !HttpRoute GET //www.mysite.com/some_route */ public function execute() { echo "Hello World!"; echo "for some_request_parameter, you provided:". $this->some_request_parameter; } }
<?php // ### ServiceContainer.php ### namespace MyProject; use \Groundhog\Router\RouteHandlerServiceContainerInterface; use \Pimple class ServiceContainer extends Pimple implements RouteHandlerServiceContainerInterface { public function __construct() { parent::__construct(); $this['some_dependency'] = function ($c) { // This dependency is destined to end up in the Route Handler's $some_dependency property return new SimpleXmlElement(); }; } }
现在这些类已经定义,我们可以设置路由器并使用它。
<?php // ### index.php ### // Here we're writing a thin wrapper around Symfony's HttpFoundation\Request object to implement RequestInterface. $symfony_request = \Symfony\Component\HttpFoundation\Request::createFromGlobals(); $request = new \MyProject\HttpRequestWrapper($symfony_request); // The routing Table Store we're using here is the simple "NoCache" store which provides no caching ability. It's convenient for development. $routing_table_store = new \Groundhog\Router\RoutingTableStoreNoCache(); // The route parser here is the one which reads annotations. It is being asked to start in the 'source' directory to search for classes with annotations. $parser = new \Groundhog\Router\RouteParserAnnotation('source'); // The Router takes in all these elements as dependencies $router = new \Groundhog\Router\Router(); $router->route_parser = $parser; $router->routing_table = $routing_table_store; $router->request = $request; // Command the router to find the appropriate Route Handler for the request it was given and configure it against the request. $route_handler = $router->getRouteHandler(); // Command the returned Route Handler to perform its route-handling action $route_handler->execute();
在实践中,许多依赖关系的创建和配置可以移入依赖容器,从而使 index.php 的启动保持简洁。