aurasidera / sextant
灵活且无偏见的PHP路由库。
Requires
- php: >=7.0.0
Requires (Dev)
- phpunit/phpunit: >=7
README
灵活且无偏见的PHP路由库。
Sextant 允许用户轻松定义路由,而不强制用户遵循任何特定约定,也不实现过度复杂的模式。您将路由声明为一个对(条件,动作):满足第一个路由条件的动作将被执行。
条件是任何可以返回布尔值的 可调用 对象。默认情况下,提供了一些现成的条件模型供您使用,但您可能会最终使用强大的 Simple 条件,它原生支持方法和URL匹配,以及URL中的命名参数。
动作是任何 可调用 对象。真的,这里没有太多花哨的东西。Sextant 已经提供了现成的动作模型,例如 "显示此字符串","显示此JSON","包含此PHP脚本" 等。
您可以定义一个默认动作,在没有任何路由匹配时执行(对于自定义404页面很有用)。
安装
Sextant 可以通过 Composer 安装,尽管也提供了手动安装的方法。
要求
php>= 7.0.0
Composer
将 aura/sextant
添加到您的 composer.json
文件中,或者运行
composer require aura/sextant
手动
克隆或下载此存储库
git clone https://github.com/AuraSidera/Sextant.git
或者
wget https://github.com/AuraSidera/Sextant/archive/master.zip
文件结构遵循 PSR-4:包含文件时没有太多惊喜。
示例
基本用法:涵盖了大多数用例!
对于大多数应用程序,您只需要一些将动作关联到URL的机制。这太棒了,而且非常简单!您可以复制粘贴这些内容,无需提问
// Initializes just a couple of stuffs $router = new \AuraSidera\Sextant\Router(); $router->setConditionFactory(new \AuraSidera\Sextant\ConditionFactory\Simple()) ->setActionFactory(new \AuraSidera\Sextant\ActionFactory\Script()); // Declares routes $router->addRoute(['GET', '/'], 'homepage.php') ->addRoute(['GET', 'users/{id}'], 'user.php') ->addRoute(['GET', 'users/{id}/activities/{from:date}/{to:date}'], 'user_activities.php') ->setDefaultAction(new \AuraSidera\Sextant\ActionFactory\NotFound()); // Reads request state $state = \AuraSidera\Sextant\State::getStateFromServer(); // Gets the job done! $router->match()
这里发生了什么?
- 首先,我们需要初始化路由器。显然。
- 然后,我们设置条件和动作的默认抽象工厂。这不是必需的,但可以使事情变得容易得多!
- 我们声明路由!
- 我们将
ConditionFactory\Simple
设置为条件的默认工厂,因此我们可以仅将它们写成数组[method, URL模式]
。它还会将值绑定到括号中的占位符,如{id}
一样!您还可以要求值必须是特定类型,如{from:date}
- 我们将
ActionFactory\Script
设置为动作的默认工厂,因此我们可以仅将PHP文件的名称写入其中,它将被执行;GET/POST参数,头部,URL中的命名参数将传递给动作,因此无需担心
- 我们将
- 我们设置一个默认动作,使用Sextant内置的
ActionFactory\NotFound
工厂 - 我们告诉路由器继续寻找匹配的路由。就是这样,真的。
高级用法
为了充分利用Sextant,您需要了解一些关于系统的知识。在继续之前,请务必阅读(并理解)概念部分。
使用默认工厂很酷,但有时您需要更多的灵活性。没关系:您仍然可以设置默认工厂,但对于某些路由器,可以使用特定的工厂覆盖它们。假设您大多数情况下对 ConditionFactory\Simple
的行为很满意,但您希望无论条件如何都执行特定的URL模式下的动作:您可以使用基本用法示例并添加
$url_pattern_condition = new \AuraSidera\ConditionFactory\UrlPattern(); ... $router->addRoute($url_pattern_condition('respond-to-any-method'), 'script.php');
对于动作也是如此,假设您想显示JSON文档
$json_action = new \AuraSidera\ActionFactory\Json(); ... $router->addRoute(['GET', 'my-json'], $json_action('path-to-json.json'));
猜猜看?您可以混合条件和动作
$url_pattern_condition = new \AuraSidera\ConditionFactory\UrlPattern(); $json_action = new \AuraSidera\ActionFactory\Json(); ... $router->addRoute($url_pattern_condition('respond-to-any-method'), $json_action('path-to-json.json'));
这会变得更多:这是简洁和灵活之间的权衡。
六分仪提供了一些现成的条件和动作工厂,您可以根据需要进行操作。如果您想更进一步,可以尝试使用元条件:它们是特殊的条件,允许构建任意复杂、基于布尔的条件。以下是一个故意设计得过于复杂的条件
$method = new \AuraSidera\ConditionFactory\Method(); $url = new \AuraSidera\ConditionFactory\UrlPattern(); $and = new \AuraSidera\ConditionFactory\Conjunction(); $or = new \AuraSidera\ConditionFactory\Disjunction(); $not = new \AuraSidera\ConditionFactory\Negation(); ... $route->addRoute( $and( $or($not($method('GET')), $url('users/{id}')), $or($method('GET'), $url('users'), $url('users/{id}/edit')), $not($method('POST')) ), 'some-action.php' );
这是什么东西?这是一条需要
(method != GET OR URL = users/{id}) AND (method = GET OR URL = users OR url = users/{id}/edit) AND (method != POST)
显然这不是一条现实的路,但您可以看到任何在合取范式中的公式都可以通过这些机制来表示。因此,六分仪在布尔代数方面是完备的!太棒了!
TLDR:您可以在不接触代码的情况下动态创建自定义复杂布尔条件。
技术使用
为什么您要这么做?我想,您是那种...
您可以为自定义条件和动作定义自己的。请记住以下咒语
condition: State => bool
action: State => nothing
在实现自定义动作或条件时,匹配这些类型,您将一切顺利。您可以使用函数、匿名函数、闭包或可调用对象(实现__invoke
魔法方法)。以下是一个示例
$router->addRoute( function (\AuraSidera\Sextant\State $state) { if ($state->getUrl() == 'X') { $state->addMatch('X') = 42; return true; } else { return count($state->getParametersAsDictionary()) < count($state->getHeadersAsDictionary()); } }, function (\AuraSidera\Sextant\Sate $state) { echo "This is the URL: " . $state->getUrl() . "<br>"; echo "And these are the matches I got: "; print_r($state->getMatchesAsDictionary()); } );
相当不错,不是吗?
概念
六分仪中的主要概念。
状态
状态是一个表示请求当前状态的对象。状态总是包括
- URL
- 方法
- 请求参数(GET、POST等)作为关联数组
- HTTP头部作为关联数组
- 匹配,一个包含URL匹配信息的关联数组(任意)
所有这些都可以通过适当的getter方法检索。状态可以通过使用辅助静态工厂方法getStateFromServer
自动从服务器读取
$state = \AuraSidera\Sextant\State::getStateFromServer(); $url = $state->getUrl(); $method = $state->getMethod(); $parameters = $state->getParametersAsDictionary(); $headers = $state->getHeadersAsDictionary(); $matches = $state->getMatchesAsDictionary();
状态可以装饰为命名实体,可以通过成员和数组访问语法访问。尝试读取未定义的实体将返回null
$state = \AuraSidera\Sextant\State::getStateFromServer(); $state->some_name = 42; echo $state->some_name; // Displays 42 echo isset($state->fake) ? 'set' : 'unset'; // Displays 'unset' echo $state->fake; // Displays '' (a null value is print) $state['other_name'] = 21; // Array access works too, both for writing... echo $state['other_name']; // ... and reading values echo $state->other_name; // It is fine to mix both styles
条件
条件是任何返回布尔值的可调用对象。六分仪将自动传递一个测试路由的状态。六分仪还提供了一些现成的条件,作为抽象工厂的形式,在ConditionFactory
命名空间下。工厂可以被实例化并在路由中使用,如下面的示例所示
// Instantiates a condition factory which produces conditions matching methods $method_factory = new \AuraSidera\ConditionFactory\Method(); // Matches only HTTP/GET requests $method_condition_get = $method_factory('GET'); // Matches only HTTP/POST requests $method_condition_post = $method_factory('POST'); // Conditions can be used in routes $router->addRoute($method_condition_get, 'action_1'); $router->addRoute($method_condition_post, 'action_2');
通常更方便使用以下等效、更简洁、更清晰的语法
$method = new \AuraSidera\ConditionFactory\Method(); $router->addRoute($method('GET'), 'action_1'); $router->addRoute($method('POST'), 'action_2');
请注意,尽管六分仪使用类来实现产生条件的工厂,但这种模式不是强制的。可以使用任何类型的函数(包括匿名函数、闭包和可调用对象)
function my_condition(\AuraSidera\Sextant\State $state): bool { return true; } $router->addRoute('my_condition', 'action');
动作
动作是可调用的,没有其他特别要求。它们通常在页面上生成某种类型的输出,设置一些头部,操作数据库,或者这些的组合。六分仪在调用动作时将自动注入状态。六分仪提供了一些现成的动作,如默认的404页面设置适当的头部,JSON渲染器,文件渲染器和脚本执行器。《ActionFactory》包含这样的动作工厂。动作工厂是一个构建动作的对象,可以在路由中使用
// Istantiates an action factory rendering a JSON document $json_action_factory = new \AuraSidera\ActionFactory\Json(); // Renders a JSON when called $json_action = $json_action_factory('path-to-document.json'); // Actions can be used in routes $router->addRoute('condition', $json_action);
以下等效且更简洁的语法是首选
$json = new \AuraSidera\ActionFactory\Json(); $router->addRoute('condition', $json('path-to-document.json'));
六分仪使用类来实现产生动作的工厂,但这不是强制的。也可以使用自定义函数
function my_action(\AuraSidera\Sextant\State $state) { echo "Hello, world!"; $state->track = 1; }
动作可以更改与$state
关联的命名实体,但不能修改URL、方法、参数或头部。
路由
路由是条件和动作之间的关联。路由在路由器内声明,最终将尝试在给定上下文中(URL、方法、头部等)匹配它们。第一个条件在上下文中得到满足的路由将被选择,并执行其动作。路由的测试顺序与它们声明的顺序相同。