dcp/router

提供可扩展的事件驱动、MVC和REST路由器

v1.0.0 2014-01-11 18:28 UTC

This package is not auto-updated.

Last update: 2024-09-24 06:06:18 UTC


README

#DCP-Router DCP-Router提供可扩展的事件驱动、MVC和REST路由器。

项目的目标是始终追求简洁、简单、可扩展和可测试。

Build Status Coverage Status

入门

安装此包最简单、最推荐的方式是通过composer。

只需在您的项目中创建一个composer.json文件,并添加以下行

{
    "require": {
        "dcp/router": "1.0.*"
    }
}

然后,运行composer命令安装DCP-Router

$ composer install

或者,您可以克隆仓库并手动安装包。

基本用法

创建MVC或REST路由器

您需要做的第一件事是实例化路由器。您可以选择MVC或REST路由器。

use DCP\Router;

$mvcRouter = new Router\MvcRouter();

$restRouter = new Router\RestRouter();

路由分配

一旦您创建了路由器,您就可以立即分配路由,而无需其他配置。

默认情况下,路由器将在根命名空间中查找控制器类。这是可配置的,在README的其他部分有所体现。

例如,如果您选择MVC路由器来分配路由

use DCP\Router\MvcRouter;

class TestController
{
    public function indexAction($arg = null)
    {
        echo __METHOD__ . ', ' . $arg;
    }
}

class HomeController
{
    public function testAction()
    {
        echo __METHOD__;
    }
}

$router = new MvcRouter();

$router->dispatch('/test/index/hello');
// This will output "TestController::indexAction, hello"

$router->dispatch('/home/test');
// This will output "HomeController::testAction"

或者,如果您选择REST路由器来分配路由

use DCP\Router\RestRouter;

class UsersController
{
    public function get()
    {
        echo __METHOD__;
    }

    public function post($arg = null)
    {
        echo __METHOD__ . ', ' . $arg;
    }
}

$router = new RestRouter();

$router->dispatch('/users/hello', 'post');
// This will output "UsersController::post, hello"

$router->dispatch('/users', 'get');
// This will output "UsersController::get"

默认路由

当没有URL或部分URL呈现给路由器时,它将假设一个默认路由以避免抛出NotFound异常。

在MVC路由器的情况下,如果没有呈现路由,它将假设一个index控制器和一个index动作。如果指定了控制器但没有动作,它将假设调用index动作。

use DCP\Router\MvcRouter;

class IndexController
{
    public function indexAction()
    {
        echo __METHOD__;
    }
}

class HomeController
{
    public function indexAction()
    {
        echo __METHOD__;
    }
}

$router = new MvcRouter();

$router->dispatch('/');
// This will output "IndexController::indexAction"

$router->dispatch('/home');
// This will output "HomeController::indexAction"

在REST路由器的情况下,如果没有呈现路由,它将假设一个index控制器和一个GET方法。如果指定了控制器但没有HTTP方法,它将假设调用GET方法。

use DCP\Router\RestRouter;

class IndexController
{
    public function get()
    {
        echo __METHOD__;
    }
}

class HomeController
{
    public function get()
    {
        echo __METHOD__;
    }

    public function post()
    {
        echo __METHOD__;
    }
}

$router = new RestRouter();

$router->dispatch('/');
// This will output "IndexController::get"

$router->dispatch('/home');
// This will output "HomeController::get"

$router->dispatch('/home', 'post');
// This will output "HomeController::post"

未找到错误

当路由器无法找到指定的资源时,它将抛出NotFoundException,允许您显示一个404页面(如果需要的话)。

use DCP\Router\MvcRouter;

$router = new MvcRouter(); // RestRouter can be used here, too.

try {
    $router->dispatch('/home');
} catch (NotFoundException $e) {
    echo 'Could not find /home!!';
}

控制器命名空间前缀

当您的应用程序足够复杂,需要使用命名空间进行结构化时,您可以开始为路由器设置命名空间前缀。路由器将在您指定的任何命名空间中查找控制器。

// App/Controller/TestController.php
namespace App\Controller;

class TestController
{
    public function helloAction()
    {
        echo 'Hello, world!';
    }
}
// index.php
use DCP\Router\MvcRouter;

$router = new MvcRouter();
$router->setControllerPrefix('App\Controller');

$router->dispatch('/test/hello');
// This will output "Hello, world!"

路由层次结构

DCP-Router让您能够创建具有自己的URL前缀的独立站点区域,例如位于/admin下的管理站点部分。

为了便于这种功能,项目依赖于分层路由,其中路由器将在看到已知前缀时将URL传递给其他路由器实例。

例如,我们将创建一个只能通过/admin URL前缀访问的控制器。

// App/Admin/Controller/TestController.php
namespace App\Admin\Controller;

class TestController
{
    public function helloAction()
    {
        echo 'Hello, world!';
    }
}

然后,我们将创建一个AdminRouter类来便于将路由到站点的/App/Admin/Controller命名空间。

// App/Admin/AdminRouter.php
namespace App\Admin;

use DCP\Router\MvcRouter;

class AdminRouter extends MvcRouter
{
    public function __construct()
    {
        parent::__construct();
        $this->setControllerPrefix('App\Admin\Controller');
    }
}

最后,我们实例化一个路由器,并告诉它任何位于/admin URL前缀下的内容都应该由AdminRouter处理。

// index.php

use DCP\Router\MvcRouter;

$router = new MvcRouter();
$router->setComponents([
    'admin' => 'App\Admin\AdminRouter'
]);

$router->dispatch('/admin/test/hello');
// This will output "Hello, world!"

当路由器被告知路由到/admin/test/hello时,它将看到URL的第一部分是admin。由于已注册了具有admin键的组件,路由器将分配路由的/test/hello部分给AdminRouter,然后最终调用TestController#helloAction()。

详细用法

事件系统

DCP-Router的核心采用了Evenement,并为路由过程中的每个步骤都提供了事件。这允许您在过程中的任何一点进行连接,并在需要时执行自定义逻辑。

总共有12个事件,其中6个在控制器被分发时调用,另外6个在次要路由器被分发时调用。事件按照列表中的顺序调用它们各自区域的事件。

控制器事件

  • ControllerEvents::CREATING在路由器解析要分发的控制器的完全限定类名时发出。
    • 此事件可用于自定义控制器解析逻辑。
  • ControllerEvents::CREATE在路由器实例化控制器时发出。
    • 此事件可用于自定义类创建逻辑。
  • ControllerEvents::CREATED在路由器实例化控制器后发出。
  • ControllerEvents::DISPATCHING在路由器解析要调用的控制器上的方法名称时发出。
    • 此事件可用于自定义控制器方法解析逻辑。
  • ControllerEvents::DISPATCH在路由器调用解析后的控制器方法时发出。
    • 此事件可用于自定义控制器动作分发逻辑。
  • ControllerEvents::DISPATCHED在路由器调用解析后的控制器方法后发出。

组件事件

  • ComponentEvents::CREATING在路由器解析要分发的组件路由器的完全限定类名时发出。默认情况下,它是*Router#setComponents()调用中指定的类名。
  • ComponentEvents::CREATE在路由器实例化组件路由器时发出。
    • 此事件可用于自定义类创建逻辑。
  • ComponentEvents::CREATED在路由器实例化组件路由器后发出。
  • ComponentEvents::DISPATCHING在路由器准备将控制权传递给组件路由器时发出。
  • ComponentEvents::DISPATCH在路由器将控制权传递给组件路由器时发出。
    • 此事件可用于提供自定义组件分发逻辑。
  • ComponentEvents::DISPATCHED在路由器将控制权传递给组件路由器后,以及组件路由器完成后发出。

要连接到事件,您可以简单地为事件附加一个监听器。当路由器进入路由的特定阶段时,它将被调用。

use DCP\Router\MvcRouter;
use DCP\Router\ControllerEvents;
use DCP\Router\Event\CreatingEvent;

class TestControllerHi
{
    public function indexAction()
    {
        echo 'Hello!!'
    }
}

$router = new MvcRouter();
$router->on(ControllerEvents::CREATING, function (CreatingEvent $event) {
    $event->setClass($event->getClass() . 'Hi');
});

$router->dispatch('/test');
// This will output "Hello!!"

与依赖注入容器的耦合

事件系统的一个重要方面是它允许轻松集成到各种依赖注入容器中。

ControllerEvents::CREATEComponentEvents::CREATE事件专门用于实例化一个类,因此它们可以被覆盖以从DI容器中提取实例。

use DCP\Router\MvcRouter;
use DCP\Router\ControllerEvents;
use DCP\Router\ComponentEvents;
use DCP\Di\Container;
use DCP\Di\ContainerAwareInterface;

class DiMvcRouter extends MvcRouter implements ContainerAwareInterface
{
    protected $container;

    public function setContainer(Container $container)
    {
        $this->container = $container;
    }

    public function __construct()
    {
        parent::__construct();

        $createCallback = function (CreateEvent $event) {
            $container = $this->container;
            $class = $event->getClass();
            $event->setInstance($container->get($class));
        };

        $this->removeAllListeners(ControllerEvents::CREATE);
        $this->on(ControllerEvents::CREATE, $createCallback);

        $this->removeAllListeners(ComponentEvents::CREATE);
        $this->on(ComponentEvents::CREATE, $createCallback);
    }
}

贡献

如果您想为DCP-Router做出贡献,您可以通过以下两种方式之一进行

  • 提交您发现的错误或可以改进项目的功能。
  • 分支存储库,并提交拉取请求。

测试

DCP-Router使用PHPUnit 3.7.x进行自动化测试。

代码库的所有更改都伴随着单元测试。