在自定义路由器 PHP 中匹配 URL 上的参数
Matching Param on URL in Custom Router PHP
我正在向此自定义路由器和自定义请求 Class 添加功能,以便能够提供页面和 json 响应。
我被困在路由器部分,路由在 url 中有一个参数,例如:
example.com/apply/{variable}
这些是 classes:
路由器class:
<?php
class Router
{
private $request;
private $supportedHttpMethods = array("GET", "POST");
function __construct(RequestInterface $request)
{
$this->request = $request;
}
function __call($name, $args)
{
list($route, $method) = $args;
if (!in_array(strtoupper($name), $this->supportedHttpMethods)) {
$this->invalidMethodHandler();
}
$this->{strtolower($name)}[$this->formatRoute($route)] = $method;
}
/**
* Removes trailing forward slashes from the right of the route.
*
* @param route (string)
*/
private function formatRoute($route)
{
$result = rtrim($route, '/');
if ($result === '') {
return '/';
}
return $result;
}
private function invalidMethodHandler()
{
header("{$this->request->serverProtocol} 405 Method Not Allowed");
}
private function defaultRequestHandler()
{
header("{$this->request->serverProtocol} 404 Not Found");
}
/**
* Resolves a route
*/
function resolve()
{
$methodDictionary = $this->{strtolower($this->request->requestMethod)};
$formatedRoute = $this->formatRoute($this->request->requestUri);
$method = $methodDictionary[$formatedRoute];
if (is_null($method)) {
$this->defaultRequestHandler();
return;
}
echo call_user_func_array($method, array(
$this->request
));
}
function __destruct()
{
$this->resolve();
}
}
请求Class:
<?php
include_once 'RequestInterface.php';
class Request implements RequestInterface
{
private $params = [];
public function __construct()
{
$this->bootstrapSelf();
}
private function bootstrapSelf()
{
foreach ($_SERVER as $key => $value) {
$this->{$this->toCamelCase($key)} = $value;
}
}
private function toCamelCase($string)
{
$result = strtolower($string);
preg_match_all('/_[a-z]/', $result, $matches);
foreach ($matches[0] as $match) {
$c = str_replace('_', '', strtoupper($match));
$result = str_replace($match, $c, $result);
}
return $result;
}
public function isPost()
{
return $this->requestMethod === "POST";
}
/**
* Implemented method
*/
public function getParams()
{
if ($this->requestMethod === "GET") {
$params = [];
foreach ($_GET as $key => $value) {
$params[$key] = filter_input(INPUT_POST, $key, FILTER_SANITIZE_SPECIAL_CHARS);
}
$this->params = array_merge($this->params, $params);
}
if ($this->requestMethod == "POST") {
$params = [];
foreach ($_POST as $key => $value) {
$params[$key] = filter_input(INPUT_POST, $key, FILTER_SANITIZE_SPECIAL_CHARS);
}
$this->params = array_merge($this->params, $params);
}
return $this->params;
}
}
我会这样称呼路由器:
$router->get('/apply/{code}', function($request) use($myClass) {});
哪种方法更好?我不知道如何解决。
我强烈建议在从头开始重新发明轮子之前查看现有的 http factory implementations。即使自定义实现看起来可能在短期内提供了一些灵活性和好处,您可以通过基于这种方法构建应用程序,在 mid/long 期限内轻松自立。
语言本身和 PHP 生态系统都发生了很大变化,我们在 2019 年,我们周围有数十个 well-written、re-usable 库。只需选择你的武器并专注于你的真正目标。任何未经测试的代码,涉及魔术,缺少作曲家,适当的自动加载机制,well-written 路由器或快速模板引擎;大多数时候,带来的痛苦会超过它提供的价值。我们应该停止重复自己。
据我了解,您的目标是在特定 URI 路径上提供 JSON 内容,但您正试图发明一个路由器。如果您的目标是编写一个合适的路由器,那么与提到的 Request/Response 接口无关。我建议先看看一些可重用的 framework-independent 路由器的实现,例如 FastRoute, Zend Router, Aura Router 等,以便先了解一下。实施合适的路由器当然不是火箭科学,但它并不像您可能意识到的那样简单。尽管如此,尝试编写该组件也可以具有教育意义,如果您的目标是这个,那就去做吧。
这里有一些提示(以及需要思考的新问题):
- 有一个PSR-15 Request Handler标准。在名为 requestHandler 的私有方法中发送 headers 可能不是一个好主意。
- 请求处理程序和路由器是不同的组件,需要不同的工作流程。您将它们混合在您的代码中,这是一个警告信号。
- 任何涉及
__magic
的代码都是为您自己和未来潜在的开发人员设置陷阱。
- 我不确定
include_once 'RequestInterface'
行的结果,但我们有 HTTP Message interfaces。在处理请求时,我会考虑在任何类型的自定义实现中导入 use Psr\Http\Message\ServerRequestInterface
。
- 在
__destruct
中呼应也很有趣。你在这里需要的是一个发射器。几个例子:Http Emitter, Zend Http Runner
- 这里是针对您的实际问题的高级答案:您需要实施一种机制(可能使用正则表达式)来捕获 URI 部分中的模式并解析和检测 "paths" 中可选或必需的命名部分。
就我个人而言,我建议看一下 Zend Expressive。它在编写轻量级 middleware-driven 应用程序时对开发人员有很大帮助。 Expressive 的最大特点是您可以根据需要选择任何武器。它不是 full-blown MVC 框架,提供了一种编写 Web 应用程序的新方法,而且速度非常快。您可以自由选择任何您想要的组件,例如; Twig 用于渲染需求,Symfony Console 用于 CLI,Zend Service Manager 作为依赖注入容器,Aura Router 用于路由等。
您可以只使用几个命令来尝试一下(假设您有 globally installed composer):
composer create-project zendframework/zend-expressive-skeleton my-app
cd my-app
composer run --timeout=0 serve
然后打开浏览器:http://localhost:8080
祝你好运!
我正在向此自定义路由器和自定义请求 Class 添加功能,以便能够提供页面和 json 响应。
我被困在路由器部分,路由在 url 中有一个参数,例如:
example.com/apply/{variable}
这些是 classes:
路由器class:
<?php
class Router
{
private $request;
private $supportedHttpMethods = array("GET", "POST");
function __construct(RequestInterface $request)
{
$this->request = $request;
}
function __call($name, $args)
{
list($route, $method) = $args;
if (!in_array(strtoupper($name), $this->supportedHttpMethods)) {
$this->invalidMethodHandler();
}
$this->{strtolower($name)}[$this->formatRoute($route)] = $method;
}
/**
* Removes trailing forward slashes from the right of the route.
*
* @param route (string)
*/
private function formatRoute($route)
{
$result = rtrim($route, '/');
if ($result === '') {
return '/';
}
return $result;
}
private function invalidMethodHandler()
{
header("{$this->request->serverProtocol} 405 Method Not Allowed");
}
private function defaultRequestHandler()
{
header("{$this->request->serverProtocol} 404 Not Found");
}
/**
* Resolves a route
*/
function resolve()
{
$methodDictionary = $this->{strtolower($this->request->requestMethod)};
$formatedRoute = $this->formatRoute($this->request->requestUri);
$method = $methodDictionary[$formatedRoute];
if (is_null($method)) {
$this->defaultRequestHandler();
return;
}
echo call_user_func_array($method, array(
$this->request
));
}
function __destruct()
{
$this->resolve();
}
}
请求Class:
<?php
include_once 'RequestInterface.php';
class Request implements RequestInterface
{
private $params = [];
public function __construct()
{
$this->bootstrapSelf();
}
private function bootstrapSelf()
{
foreach ($_SERVER as $key => $value) {
$this->{$this->toCamelCase($key)} = $value;
}
}
private function toCamelCase($string)
{
$result = strtolower($string);
preg_match_all('/_[a-z]/', $result, $matches);
foreach ($matches[0] as $match) {
$c = str_replace('_', '', strtoupper($match));
$result = str_replace($match, $c, $result);
}
return $result;
}
public function isPost()
{
return $this->requestMethod === "POST";
}
/**
* Implemented method
*/
public function getParams()
{
if ($this->requestMethod === "GET") {
$params = [];
foreach ($_GET as $key => $value) {
$params[$key] = filter_input(INPUT_POST, $key, FILTER_SANITIZE_SPECIAL_CHARS);
}
$this->params = array_merge($this->params, $params);
}
if ($this->requestMethod == "POST") {
$params = [];
foreach ($_POST as $key => $value) {
$params[$key] = filter_input(INPUT_POST, $key, FILTER_SANITIZE_SPECIAL_CHARS);
}
$this->params = array_merge($this->params, $params);
}
return $this->params;
}
}
我会这样称呼路由器:
$router->get('/apply/{code}', function($request) use($myClass) {});
哪种方法更好?我不知道如何解决。
我强烈建议在从头开始重新发明轮子之前查看现有的 http factory implementations。即使自定义实现看起来可能在短期内提供了一些灵活性和好处,您可以通过基于这种方法构建应用程序,在 mid/long 期限内轻松自立。
语言本身和 PHP 生态系统都发生了很大变化,我们在 2019 年,我们周围有数十个 well-written、re-usable 库。只需选择你的武器并专注于你的真正目标。任何未经测试的代码,涉及魔术,缺少作曲家,适当的自动加载机制,well-written 路由器或快速模板引擎;大多数时候,带来的痛苦会超过它提供的价值。我们应该停止重复自己。
据我了解,您的目标是在特定 URI 路径上提供 JSON 内容,但您正试图发明一个路由器。如果您的目标是编写一个合适的路由器,那么与提到的 Request/Response 接口无关。我建议先看看一些可重用的 framework-independent 路由器的实现,例如 FastRoute, Zend Router, Aura Router 等,以便先了解一下。实施合适的路由器当然不是火箭科学,但它并不像您可能意识到的那样简单。尽管如此,尝试编写该组件也可以具有教育意义,如果您的目标是这个,那就去做吧。
这里有一些提示(以及需要思考的新问题):
- 有一个PSR-15 Request Handler标准。在名为 requestHandler 的私有方法中发送 headers 可能不是一个好主意。
- 请求处理程序和路由器是不同的组件,需要不同的工作流程。您将它们混合在您的代码中,这是一个警告信号。
- 任何涉及
__magic
的代码都是为您自己和未来潜在的开发人员设置陷阱。 - 我不确定
include_once 'RequestInterface'
行的结果,但我们有 HTTP Message interfaces。在处理请求时,我会考虑在任何类型的自定义实现中导入use Psr\Http\Message\ServerRequestInterface
。 - 在
__destruct
中呼应也很有趣。你在这里需要的是一个发射器。几个例子:Http Emitter, Zend Http Runner - 这里是针对您的实际问题的高级答案:您需要实施一种机制(可能使用正则表达式)来捕获 URI 部分中的模式并解析和检测 "paths" 中可选或必需的命名部分。
就我个人而言,我建议看一下 Zend Expressive。它在编写轻量级 middleware-driven 应用程序时对开发人员有很大帮助。 Expressive 的最大特点是您可以根据需要选择任何武器。它不是 full-blown MVC 框架,提供了一种编写 Web 应用程序的新方法,而且速度非常快。您可以自由选择任何您想要的组件,例如; Twig 用于渲染需求,Symfony Console 用于 CLI,Zend Service Manager 作为依赖注入容器,Aura Router 用于路由等。
您可以只使用几个命令来尝试一下(假设您有 globally installed composer):
composer create-project zendframework/zend-expressive-skeleton my-app
cd my-app
composer run --timeout=0 serve
然后打开浏览器:http://localhost:8080
祝你好运!