iassasin/easyroute

非常轻量级且简单的PHP路由器

v0.10.0 2018-11-09 16:27 UTC

This package is auto-updated.

Last update: 2024-09-10 06:33:08 UTC


README

PHP Version Build Status Coverage Status

简单的路由器不需要很多设置,只需几分钟即可安装并运行。

安装

要开始使用路由器,请完成以下4个简单步骤

  1. 通过composer安装 easyroute
composer require iassasin/easyroute
  1. 在项目根目录设置您的路由。例如,在文件 routes.php
require_once 'vendor/autoload.php';
use Iassasin\Easyroute\Router;
use Iassasin\Easyroute\Route;

$router = new Router();
$router->setControllersPath($_SERVER['DOCUMENT_ROOT'].'/controllers/');
$router->addRoutes([
	new Route('/(:controller:(/:action:(/:arg)?)?)?', ['controller' => 'home', 'action' => 'index', 'arg' => null]),
]);
$router->processRoute();
  1. 通过 .htaccess 让web服务器将所有请求(除静态 assets/)重定向到路由器
RewriteEngine On
RewriteCond %{REQUEST_URI} !^/assets/
RewriteRule ^(.*)$ routes.php [B,QSA,L]
  1. controllers/home.php 中创建第一个控制器
class ControllerHome {
	public function index($arg){
		return '<html><body>Home page! '.($arg !== null ? 'Argument: '.$arg : 'Argument not set').'</body></html>';
		// or use "return new Response('...')" which is more flexible
	}
}

这就完了!

请注意,文件名必须与URL模板中的:controller名称匹配,控制器类名必须与带有前缀Controller:controller名称匹配。在上面的例子中,路由 /home/index 与文件 controllers/home.php 匹配,并具有类 ControllerHome

有用功能

带参数的路由

路由是一个正则字符串,具有用于命名路由参数的简单语法。例如

  • /:controller/:action/:arg(所有参数都是必需的)
  • /(foo|bar)-:arg/gotcha(正则表达式和部分参数匹配。有效的有 /foo-123/gotcha/bar-qwerty/gotcha,但不包括 /qwerty/gotcha
  • /:arg?(参数 arg 可选,但不能是 /。有效的有 //param,但不包括 /sub/param
  • /(:controller:(/:action:(/:arg)?)?)?(所有参数都是可选的。有效的有 //home/home/index/val,但不包括 /home//home/index/

参数名称必须匹配正则表达式 [a-zA-Z_0-9]+
默认情况下,:parameter 匹配正则表达式 [^\/]+,但您可以使用自己的过滤器

new Route('/:arg(\d+)?', // arg not required and can contains only numerics
	['controller' => 'home', 'action' => 'index', 'arg' => null], // default values
)

如果您需要使用 ( 来匹配分组,但不是过滤参数,请在参数名称中使用尾随的 :/:controller:(postfix1|postfix2) 将匹配 /homepostfix1,其中 controller = 'home'

内置简单的依赖注入容器

您可以使用内置的依赖注入容器将应用程序代码拆分为服务(这些服务将通过自动加载加载)并在控制器操作中简单使用它们

class ControllerHome {
	public function index($arg, Request $request, DatabaseService $db){
		// ...
	}
}

服务构造函数也可以使用依赖注入,并通过相同的语法要求其他服务。

SampleContainer 实现 Psr\Container\ContainerInterface

use Iassasin\Easyroute\Router;

$router = new Router();
/** @var SimpleContainer $container */
$container = $router->getContainer(); // get default container

// Instantiate and add services by hand
$container->setService(Database::class, new Database('login', 'password'));
// Disable automatic instantiation for services
$container->setAutowireEnabled(false);

您还可以使用另一个依赖注入容器

use Iassasin\Easyroute\Router;
use Iassasin\Easyroute\Http\Request;

$router = new Router();
$request = Request::createFromGlobals();

// some implementation of ContainerInterface
$container = new MyContainer();
// Router don't know how to register Request class in your container implementation
// You should to do it by self if you want to use Request class in controllers
// See next section for details of Request class
$container->register(Request::class, $request);

$router->setContainer($container);

请求类

通过依赖注入容器,您有权访问提供对请求参数访问的 Request 服务。

Request 包含以下字段

  • query - $_GET 数组
  • request - $_POST 数组
  • attributes - 关联数组,包含任何数据,您可以在创建 Request 实例时设置
  • cookies - $_COOKIE 数组
  • files - $_FILES 数组
  • server - $_SERVER 数组

每个字段都是 Parameters 类的实例,并具有以下方法

  • get(string $name) - 通过其名称获取参数
  • has(string $name) - 检查具有 $name 的参数是否存在
  • all() - 获取所有参数的数组

Request 提供一些有用的方法

  • getContent() - 获取整个http post内容作为字符串
  • getClientIP() - 返回 $this->server->get('REMOTE_ADDR')
  • getScriptName() - 返回 $this->server->get('SCRIPT_NAME')
  • getScheme() - 返回 $this->server->get('REQUEST_SCHEME')
  • getHost() - 返回 $this->server->get('SERVER_NAME')
  • getUri() - 返回 $this->server->get('REQUEST_URI')
  • getMethod() - 返回 $this->server->get('REQUEST_METHOD')
  • getProtocol() - 返回 $this->server->get('SERVER_PROTOCOL')

响应类

Response 类用于向客户端返回数据。在控制器操作中返回 Response 实例,而不是使用 echo 等手动发送数据。

内置响应类

存在 3 个内置响应类

  • Response - 所有响应的基础类,具有以下方法
    • __construct(string $content, int $statusCode = 200, array $headers = [])
    • getStatusCode(), setStatusCode(int $code) - 获取/设置 HTTP 状态码
    • getContent(), setContent(string $content) - 获取/设置 HTTP 响应内容
    • getHeaders(), setHeaders(array $headers) - 获取/设置 HTTP 头部信息
    • setHeader(string $name, string $value) - 设置单个头部
    • send() - 向客户端发送响应,由处理程序调用
  • Response404 - 扩展 Request 用于默认 HTTP 404 错误,具有以下属性
    • __construct(string $url, array $headers = [])
    • getUrl() - 获取导致 404 错误的 URL
  • Response500 - 扩展 Request 用于默认 HTTP 500 错误。如果在路由处理期间抛出任何异常,则会生成。
    • __construct(\Throwable $exception = null, array $headers = [])
    • getException() - 获取在路由处理期间抛出的异常
  • ResponseJson - 扩展 Request 用于发送 JSON 响应,同时设置正确的 HTTP Content-Type 头部。
    • __construct(object|array $data, int $statusCode = 200, array $headers = [])
    • getData(), setData(object|array $data) - 设置要发送到客户端的源数据
    • 注意:不能使用 setContent 并抛出 LogicException

响应类的自定义处理程序

Response 类设置自定义处理程序

use Iassasin\Easyroute\Http\Responses\Response404;

$router->setResponseHandler(Response404::class, function(Response404 $resp){
	$resp->setContent('<html><body><h1>Custom 404 Not Found</h1> The requested url "<i>'.htmlspecialchars($resp->getUrl()).'</i>" not found!');
	$resp->send();
	return true; // do not call other handlers
});

并为任何 HTTP 状态码设置自定义处理程序

use Iassasin\Easyroute\Http\Response;

$router->setStatusHandler(302, function(Response $resp){
	$resp->setContent('You have redirected');
	$resp->send();
	return true; // do not call other handlers
});

注意 1:您可以同时使用这两个处理程序,但响应处理程序将被首先调用(并且可以停止调用状态处理程序)。

注意 2:您可以为父响应类设置处理程序以匹配所有子类,子处理程序将始终首先被调用:从最子类到最父类。因此,您可以通过为 Response::class 设置处理程序来匹配 所有 响应。

控制器子目录

使用子目录分隔区域

(new Route('/admin/:controller/:action?', ['action' => 'index']))
	->setControllersSubpath('zones/admin')
	// '/admin/home/index' will match 'controllers/zones/admin/home.php'
	// and class 'ControllerHome'

访问过滤

使用 RouteFilter 防止访问某些路由

use Iassasin\Easyroute\RouteFilter;

class RouteFilterAdmin extends RouteFilter {
	public function preRoute($path, $controller, $action, $args){
		if (!isCurrentUserAdmin()){
			(new Response('Access denied!', 403))->send();
			return Router::COMPLETED; // Do not call controller's action
		}

		return Router::CONTINUE; // Call controller's action
	}
}
//...
(new Route('/admin/:controller/:action?', ['action' => 'index']))
	->setFilter(new RouteFilterAdmin())

自定义控制器类名前缀

设置自定义控制器类名前缀(默认为 Controller

$router->setControllerClassPrefix('TheController');