andygrn / aquarius
为 Gemini 网罩提供应用框架
Requires
- php: >=7.3.0
Requires (Dev)
- phpstan/phpstan: ^0.12.54
- phpstan/phpstan-strict-rules: ^0.12.5
README
A PHP 应用框架,用于 Gemini 网罩,包括
- 简单的头部/主体响应模型,具有 '中间件' 功能。
- 正则表达式路由,可选命名或未命名的参数。
- 自动将客户端证书链接到会话。
- 所有内容都在一个文件中。
对于之前使用过 PHP 网络框架的人来说,应该相当熟悉。
在 gemini://andygrn.co.uk/apps/aquarius 上查看其功能
安装
通过 Composer:composer require andygrn/aquarius
或者下载文件并 require 'aquarius.php';
。
需要 PHP 7.3 或更高版本。
应用和处理程序
一个 aquarius 应用 有一个或多个 处理程序。
处理程序是一个路径正则表达式和一系列函数。应用将运行第一个正则表达式与请求路径匹配的处理程序的函数栈。处理程序的函数栈必须返回一个 响应,该响应将被发送到客户端。
函数可以通过处理程序的 butFirst
方法添加到处理程序的栈中。添加到栈中的最后一个函数将首先运行。栈函数应通过 return $this->next($request, $response);
将参数传递给栈中的下一个函数,但它们可以选择直接 return $response;
以跳过其余的栈。
处理程序函数可以是任何有效的 可调用。
CGI 变量
为了执行任何有用的操作,aquarius 需要CGI 主机至少定义 PATH_INFO
和 QUERY_STRING
。如果计划使用 Request::getRemoteUser()
,还需要 REMOTE_USER
。
为了启用客户端证书会话行为,aquarius 还需要 TLS_CLIENT_HASH
。我不确定它有多标准化,但 Jetforce、Molly Brown 和 GLV-1.12556 似乎支持它。
API
请求
Request::getPath(): string
获取当前的 PATH_INFO,带有前导斜杠并去除尾部斜杠。
Request::getQuery(): string
获取当前的 QUERY_STRING,URL 解码(使用 rawurldecode()
)。
Request::getRemoteUser(): string
获取当前的 REMOTE_USER(可能是客户端证书的通用名称)。
响应
Response::setHeader(int $status, string $meta): void
设置响应的头部行。默认响应 ($status, $meta)
是 (Response::STATUS_SUCCESS, 'text/gemini')
,因此您可能不需要调用此方法。
Response::getStatus(): int
获取响应状态码。
Response::getMeta(): string
获取响应元字符串。
Response::setBody(string $body): void
设置整个响应体。
Response::appendBody(string $body): void
将内容追加到响应体。
Response::getBody(): string
获取响应体。
处理程序
Handler::next(Request $request, Response $response): Response
调用此处理程序栈中的下一个函数。
Handler::butFirst(callable $callable): self
将一个函数添加到处理程序的栈中。最后添加的将首先被调用。返回自身以便可以链式调用。
Handler::getPathParameters(): array<mixed>
获取从处理程序的 regex 模式捕获的路径参数。
应用
App::addHandler(string $path_regex, callable $callable): Handler
在 $path_regex
上创建一个新的处理程序,其中 $callable
是其栈中的第一个函数。
App::run(): void
将当前路径解析为处理程序并运行它(如果无处理程序匹配,则提供 51 响应)。
示例应用
// A simple greeting and visit counter. $app = new aquarius\App(); $app->addHandler('/', function ($request, $response) { $remote_user = $request->getRemoteUser(); if ('' === $remote_user) { $remote_user = 'stranger'; } $response->appendBody("Hello, $remote_user.\n\n"); if (PHP_SESSION_ACTIVE === session_status()) { if (!isset($_SESSION['visit_count'])) { $_SESSION['visit_count'] = 0; } ++$_SESSION['visit_count']; $response->appendBody("You've been here {$_SESSION['visit_count']} times."); } return $response; }); $app->run();
// Collecting input. $app = new aquarius\App(); $app->addHandler('/add-to-list', function ($request, $response) { $query = $request->getQuery(); if (0 === strlen($query)) { $response->setHeader($response::STATUS_INPUT, 'Enter item name'); return $this->next($request, $response); } if (!isset($_SESSION['list'])) { $_SESSION['list'] = []; } $_SESSION['list'][] = $query; $response->setHeader($response::STATUS_REDIRECT_TEMPORARY, '/list'); return $this->next($request, $response); }); $app->run();
// Path parameters with regex capturing groups. $app = new aquarius\App(); function show_path_parameters($request, $response) { $parameters = $this->getPathParameters(); $response->appendBody(var_export($parameters, true)); return $this->next($request, $response); } $app->addHandler('/page/\d+', 'show_path_parameters'); // Match: /page/1 /page/123 // Parameters: [] [] $app->addHandler('/page/([^/]+)/([^/]+)', 'show_path_parameters'); // Match: /page/hello/world /page/1/2: // Parameters: ['hello','world'] ['1','2'] $app->addHandler('/page/(\d+)(?:/(\d+)(?:/(\d+))?)?', 'show_path_parameters'); // Match: /page/1 /page/1/2 /page/1/2/3 // Parameters: ['1'] ['1','2'] ['1','2','3'] $app->addHandler('/page/(?<foo>\d+)', 'show_path_parameters'); // Match: /page/1 /page/2 // Parameters: ['foo' => '1'] ['foo' => '2'] $app->run();
// Forcing a session (i.e. a client certificate) with middleware. $app = new aquarius\App(); function require_session($request, $response) { if (PHP_SESSION_ACTIVE !== session_status()) { $response->setHeader( $response::STATUS_CLIENT_CERTIFICATE_REQUIRED, 'Certificate required' ); return $response; } return $this->next($request, $response); } $app->addHandler('/private-lounge', function ($request, $response) { $response->setBody('Members only 😎'); return $this->next($request, $response); }) ->butFirst('require_session'); $app->run();