triplepoint/groundhog-router

此包已被废弃且不再维护。未建议替代包。

简单的独立请求路由库。

v1.0 2012-09-15 20:09 UTC

This package is not auto-updated.

Last update: 2020-01-24 15:00:25 UTC


README

Build Status

介绍

此库提供了一种请求路由器,它可以解析传入的请求,确定请求映射到的类,并返回一个准备执行的动作处理器类。

将依赖项保持在零,并提供扩展点的接口。

免责声明

尽管我非常注意编写没有冗余依赖和短视假设的代码,但我认为我应该警告你,这段代码 不是 主要面向公众使用的。换句话说,如果你想在你的工作中使用这段代码,你可以自由地在提供的许可下这样做,但我不能保证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 的启动保持简洁。