ddn / router
Web应用的路由
Requires
- php: >=7.4
- phug/phug: >=1.9
- pug-php/pug: >=3.5.0
This package is auto-updated.
Last update: 2024-09-10 09:53:32 UTC
README
一个用于简化PHP Web应用开发的库。
安装
待定
composer require ddn/router dev-main
文档
基本Web应用
基本思想是提供处理类似以下URL的工具
https://my.server/app/path/to/op?token=12345
服务器将URL https://my.server/app
指向 app
目录。在该目录中,我们需要一个名为 index.php
的文件,这是应用的入口点。在目录中我们还需要一个文件 .htaccess
,用于配置对应用的访问。
.htaccess 的示例内容
RewriteEngine On
RewriteBase /
RewriteRule ^/index\.php$ - [L]
RewriteRule ^/favicon\.ico$ - [L]
# If it is a php file, but it is not index.php, pass it as _OPERATION parameter
RewriteCond %{REQUEST_URI} ^(.*)\.php$
RewriteCond %{REQUEST_FILENAME} !index.php$
RewriteRule ^(.*)$ index.php?_OPERATION=$1 [L,QSA]
# If it is other file that exists, serve it
RewriteCond %{REQUEST_FILENAME} -f
RewriteRule ^(.*)$ - [L,QSA]
# Finally, if the file does not exist nor is a folder, pass it as _OPERATION parameter
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php?_OPERATION=$1 [L,QSA]
此 .htaccess
片段配置路由器使用 index.php
文件作为入口点。任何不是 index.php
的文件都将作为 _OPERATION
GET 参数传递给 index.php
文件。
现在我们需要一个名为 index.php
的文件
use ddn\api\Router; use ddn\api\Renderer; use ddn\api\Router\Op; require_once('vendor/autoload.php'); // First create the router for our application; it is a simple router thet we must manage $router = new ddn\api\Router("_OPERATION"); if (defined('__CUSTOM_ROUTES') && (is_callable(__CUSTOM_ROUTES))) call_user_func_array(__CUSTOM_ROUTES, [ $router ]); class SayHello extends ddn\api\Router\Op { function _default_op() { echo "executed the default op"; } } ddn\api\Renderer::set_default("template/renderer.pug"); $router->add("/", 'SayHello', 'template/example.pug'); $ops = $router->exec(); if ($ops === false) { header("HTTP/1.0 404 Not Found"); echo "Not found"; exit; } $ops->render();
Web应用模型
一个Web应用包括
- 一组 端点,这些端点定义了将要服务的Web应用中的路径。
- 一组操作(即从
Op
类派生出的类)实现了端点上的动作(例如创建或删除对象、登录用户等)。 - 一组 视图,用于显示操作的执行结果。
- 一组 渲染器,用于显示视图。
将 渲染器 与 视图 分离,可以轻松创建统一的Web应用。可以将 渲染器 理解为Web应用的 布局,而 视图 则是Web操作的严格输出。
如果不需要这种区分,可以将每个 视图 作为整体输出渲染,并忽略 渲染器(即定义渲染器为 ($view) => { echo $view; }
)。
最后
- 一个 视图 是一个HTML片段,用于显示操作的输出。
- 一个 渲染器 是一个HTML布局,其中放置了视图。
工作流程
当一个请求到达Web服务器时
- 路由器评估是否定义了路由
- 如果没有匹配URI的路由,则结束。
- 从URI中检索参数并实例化实现操作的
Op
类。 - 调用
Op
的_do
函数 _do
将检查是否定义了子操作;如果没有,它将调用_do_default_operation
。如果有子操作,它将被执行。- 为操作生成视图
pug 文件
:可用变量_OP_HANDLER
。- 其他文件或函数:全局变量
$_OP_HANDLER
可用
- 渲染最终布局
pug 文件
:变量_OP_HANDLER
和_VIEW
可用。- 其他文件或函数:全局变量
$_OP_HANDLER
和$_VIEW
可用
(*) 除非调用 exec()
的结果是渲染的(即调用方法 render()
),否则不会生成视图。
路由器
路由器是Web应用的主要类。
要创建Web应用,需要创建一个 路由器 并设置默认渲染器
// _OPERATION is the GET parameter in which the operations are included. If using the suggested setup, apache (or nginx) will set this parameter to the accessed path; so if requesting URI https://my.server/path/to/my/op?v=1, '_OPERATION' will be set to path/to/my/op, and 'v' will still be 1. $router = new ddn\api\Router("_OPERATION"); // This is the default renderer, which contains the default layout for out application (it can be either a function, a php file, an html file or a pug file, which will be interpreted) ddn\api\Renderer::set_default("template/renderer.pug");
然后我们需要定义我们的路由
// In path /hello we will have an operation, which will be served by class 'SayHello' and, after executing _do method of that class, we will show the view 'template/example.pug'. The renderer is the default (so we set it to null). $router->add("/hello", 'SayHello', 'template/example.pug', null);
一旦定义,我们需要根据URI执行操作并渲染输出
$ops = $router->exec(); if ($ops === false) { header("HTTP/1.0 404 Not Found"); echo "Not found"; exit; } $ops->render();
方法
__construct($path_varname)
:构建对象- 变量名 $path_varname:是从“_GET”数组中获取路径的变量名
add($url, $classname, $template)
:向路由器添加路由- $url:是匹配路由的路径定义。可以使用参数来定义路由。
/path/to/<parameter>/<?可选参数=默认值>/end
- $classname:是实现操作的类(它是
Op
子类,或必须实现_do
函数)。如果为 null,则不会执行任何操作。 - $template:是显示操作输出的模板。
- $url:是匹配路由的路径定义。可以使用参数来定义路由。
exec()
:检查是否有 URI 终端被访问,如果是,则执行操作并返回一个Renderer
对象以便渲染输出。
Op
类
Op
类用于在服务器的终端实现操作。例如,如果我们定义以下路由: $router->add("/", 'SayHello', 'template/example.pug');
,如果访问的终端匹配 /
,则会创建一个 SayHello
类的实例。然后调用其类的 _do
方法。
操作的执行可能因用户设置的动作值而异。这就是为什么 Op
类还包括根据 $_GET
或 $_POST
超变量的值执行不同函数的辅助工具。
这样,同一个类就可以用于多个函数。例如,可以有一个实现 login
、logout
或 update_data
的 OpUsers
类,这取决于 $_POST
变量是否设置及其值。
以下是一个类的示例:
class OpUser extends ddn\api\Router\Op { const _FUNCTIONS = [ "login" => "_login", "logout" => "_logout", "update" => [ "" => "_update_data", "password" => "_update_password", ] ]; function _login() { echo "executed the login op"; } function _logout() { echo "executed the logout op"; } function _update_data() { $_POST["name"] = $_POST["name"]??null; $_POST["email"] = $_POST["email"]??null; echo "would set:<br>\n- user to {$_POST['name']}<br>\n- email to {$_POST['email']}<br>"; echo "executed the update data op<br>"; } function _update_password() { echo "executed the update password op"; } function _default_op() { echo "executed the default op"; } }
对 _FUNCTIONS
的读取
- 如果使用
_POST["login"]
执行此类,则执行函数_login
- 如果使用
_POST["logout"]
执行此类,则执行函数_login
- 如果使用
_POST["update"]
设置为password
,则执行函数_update_password
- 如果使用
_POST["update"]
设置为除password
之外的其他内容,则执行函数_update_password
- 否则,执行
_default_op
权限
可以定义常量数组 _PERMS
,它定义了执行此操作的所需权限。
如果已定义,则检查权限,如果不符合要求,则执行此操作的 forbidden
函数。
权限依赖于类 ACLManager
的使用以及根据使用操作的用户的组成员资格定义的 ACL 条目。
权限还依赖于操作(类或对象)的实现中的多个函数
get_app_user
:返回使用操作的用户。用户必须实现ACLUser
接口is_owner
:返回用户是否是处理操作的对象的所有者forbidden
:通知用户操作访问被禁止
可以定义权限,例如:“如果用户是所有者则授予访问权限”、“如果用户已登录则授予访问权限”、“如果用户属于管理员组则授予访问权限”等。
(*) 权限的定义是一个丰富的自动化功能,但描述起来很复杂。因此,目前最好检查代码而不是解释。