tbolner / flex-php-router
适用于Web和CLI应用的路由器(PHP)。
Requires
- php: >=7.1
This package is auto-updated.
Last update: 2024-09-26 23:53:03 UTC
README
简单灵活的Web和命令行应用路由库。
Packagist页面: https://packagist.org.cn/packages/tbolner/flex-php-router
推荐IDE: PhpStorm
安装
将库包含到项目的 composer.json 文件中
{
"require": {
"tbolner/flex-php-router": "dev-master"
}
}
然后执行
composer update
此库需要PHP 7.1或更高版本。
Web应用的用法示例
在您的项目中创建一个文件夹,用于存放控制器。例如
project1/src/Controller/
然后创建控制器。文件名必须与URI的第一部分匹配
如你所见,当查找相应的PHP文件时,URI的第一部分所有特殊字符都被移除。(出于安全考虑。)更准确地说:从第一部分移除了所有不是:a-z、A-Z、0-9、-(破折号)、_(下划线)的字符。(URL的其他部分不受此机制的影响。)
控制器将通过模式匹配来决定执行什么。这些匹配的部分被称为“动作”。
project1/src/Controller/items.php
<?php declare(strict_types=1); namespace MyProject\Controller; use FlexPhpRouter\Router; Router::get( "items/{id:int}", function (int $id) { /* ... your code ... */ /* you can also throw exceptions to catch them at your router start-up code */ } ); Router::post( "items/{id:int}/list", function (int $id) { /* ... your code ... */ } ); Router::put( "items/{id:int}/details{other:string}", function (int $id, string $other) { /* ... your code ... */ } );
注意
- 在路径参数中,模式有4种类型
- 字符串,整数,浮点数,布尔值
- 要添加额外的类型,只需使用“字符串”并在动作中进行类型转换。
- 末尾的斜线(/)被忽略。无论是否有结束斜线,模式都将匹配。
- 要匹配站点根目录,只需在默认控制器中匹配“/”即可。(有关如何指定默认控制器,请参阅下一点。)
- 请注意,在第三个动作中,参数跟随“/details”文本而没有斜线分隔它们,这是一个可接受的解决方案。
- 支持的方法有
- get,post,put,delete,any
最后将路由启动代码添加到您的应用中
project1/web/index.php
<?php declare(strict_types=1); namespace Web; use FlexPhpRouter\Router; use MyProject\Exceptions\MySpecialException; require_once (dirname(__FILE__)."/../vendor/autoload.php"); try { Router::route(dirname(__FILE__)."/../src/Controller", 'default'); } catch (MySpecialException $ex) { /* ... */ } catch (\Exception $ex) { /* ... */ }
注意
- Router::route()的第一个参数定义了控制器目录的路径。使用类似上面的方式,以相对方式定义路径。
- Router::route()命令的第二个参数期望的是默认控制器的名称,当未找到控制器时会调用它。您可以在那里处理站点根目录等。名称“default”将导致“default.php”控制器文件。
- 处理控制器或从控制器中调用的代码中抛出的所有异常。
路径相关认证等
您可以将一个可选的回调作为Router::route()的第四个参数传递
class MyAuthClass { public static function authenticate(bool $isWeb, string $path) { ... } } ... Router::route(dirname(__FILE__)."/../src/Controller", 'default', '', // The third parameter is the API prefix [MyAuthClass::class, 'authenticate'] ); ...
它将在任何控制器代码之前执行,并了解所选控制器路径。回调接收2个参数。第一个参数'$isWeb'当执行Web控制器时为true,当执行CLI时为false。第二个参数告诉选择了哪个路径。
此功能允许您为API条目集合实现认证。建议在认证失败的情况下,抛出您创建的异常,例如:MyAuthException,并在主入口点处理它,通常所有异常都在这里被捕获。
特性
API路径前缀
Router::route(...) 的第三个参数指定了一个API前缀,在路由过程中将始终被忽略,就像它从URI的开头被截断一样。
示例
Router::route(dirname(__FILE__)."/../src/Controller", 'default', 'api');
注意
- 上一个示例也适用于以下前缀:'/api'、'api/'、'/api/'。
默认页面 / 404 页面
默认控制器(前面已描述)在两种情况下会被调用
- 如果没有找到控制器,根据URI的第一部分
- 如果加载的控制器中没有动作匹配的模式
因此,我们可以在默认动作的末尾始终放置一个动作,当没有执行任何操作时会被调用。
Controller/default.php
<?php declare(strict_types=1); namespace Controller; use FlexPhpIO\Response; use FlexPhpRouter\Router; Router::any( "/", function () { Response::printJson([ "message" => "This is the site root." ]); } ); Router::any( "*", function () { Response::printHtmlFile(dirname(__FILE__).'/../web/notFound.html', 404); } );
注意
- 注意最后一个动作中的 "*" 字符。它表示“任意”。
- 第一个动作——处理站点根请求——也可以使用一个空的模式:""。
CLI应用程序的用法示例
在您的项目中创建一个文件夹,用于存放CLI控制器。例如
- project1/src/CLI
然后创建控制器。文件名必须匹配第一个参数中传递的路径的第一部分
示例控制器
project1/src/CLI/test.php
<?php declare(strict_types=1); namespace MyProject/CLI; use FlexPhpRouter\Router; use FlexPhpRouter\CliParameter; Router::cli("test/cleanup") ->matchRoute(function () { /* ... your code ... */ /* you can also throw exceptions to catch them at your router start-up code */ }); Router::cli("test/run") ->addParameter("param1", CliParameter::TYPE_INT, true, "Description of first parameter.") ->addParameter("param2", CliParameter::TYPE_STRING, false, "Description of second parameter.") ->matchRoute(function (int $param1, string $param2 = null) { /* ... your code ... */ });
在这种情况下,路由启动代码必须放入一个非Web相关的PHP文件中(因此它应该在Web文件夹之外)。例如
project1/console.php
#!/usr/bin/php <?php declare(strict_types=1); namespace CLI; use FlexPhpRouter\Router; use MyProject\Exceptions\MySpecialException; require_once (dirname(__FILE__)."/vendor/autoload.php"); try { Router::route(dirname(__FILE__)."/src/CLI", 'default'); } catch (MySpecialException $ex) { /* ... */ } catch (\Exception $ex) { /* ... */ }
注意
- 您不需要告诉 Router::route() 方法它需要期望控制台参数,因为它将自动检测脚本是在控制台模式下运行的。
设计考虑
- 用法与Laravel路由类似。
- 没有库依赖。
- 没有解析和预构建缓存。请参见下一点,它解释了如何在不需要使用代码预生成的情况下实现高性能。
- 动作的位置不是完全自定义的。它们的控制器由URL的第一部分决定。这迫使开发者保持动作代码整洁,同时也提高了应用程序的性能,因为可以通过明确的方式来找到控制器的PHP文件。(有关更多信息,请参见“Web应用程序的用法示例”)
- 类型安全。所有参数都转换为指定的类型。
- 它不处理输出。在大多数路由库中,动作使用 return 命令将数据返回以进行输出(例如,作为JSON的数组)。我发现这比解决的问题更多。更好的做法是将I/O功能放入一个单独的库中,并从您的控制器中调用其方法。请参见示例: FlexPhpIO。
- 它只支持一个“任意”方法指定器,但不支持每个动作的多个(特定)方法(如POST+GET),因为这些方法很少需要,所以不值得为每个动作(如Laravel)在API中添加一个额外的参数。当需要时,请将您的动作代码放入静态方法中,并从单独的POST、GET等动作中调用它们。