首页 > 解决方案 > 在自定义路由器 PHP 中匹配 URL 上的参数

问题描述

我正在向这个自定义路由器和自定义请求类添加一个功能,以便能够提供页面和 json 响应。

我被困在路由器部分,其中 Route 在 url 中有一个参数,例如:

example.com/apply/{变量}

这些是类:

路由器类:

<?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();
    }
} 

请求类:

<?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) {});

哪种方法会更好?我不知道如何解决。

标签: phproutingmezziopsr-7

解决方案


我强烈建议在从头开始重新发明轮子之前查看现有的http 工厂实现。即使自定义实现在短期内看起来也提供了一些灵活性和好处,但您可以通过基于这种方法构建应用程序来轻松地在中/长期中立足。

语言本身和 PHP 生态系统都发生了很大的变化,我们是 2019 年的一个,我们周围有几十个编写良好、可重用的库。只需选择你的武器并专注于你的真正目标。任何没有测试的代码,包括魔法,缺乏作曲家、适当的自动加载机制、编写良好的路由器或快速的模板引擎;大多数时候会造成比它提供的价值更多的痛苦。我们应该停止重复自己。

据我了解,您的目标是在特定的 URI 路径上提供 JSON 内容,但您正在尝试发明一个路由器。如果您的目标是编写一个合适的路由器,那么与问题中提到的请求/响应接口无关。我建议先看看一些可重用、独立于框架的路由器的实现,例如FastRouteZend 路由器Aura 路由器等,以便先了解一下。实现一个合适的路由器当然不是火箭科学,但它并不像您可能意识到的那样简单。尽管如此,尝试编写该组件也可以具有教育意义,如果您的目标是这个,那就去做吧。

以下是一些提示(以及需要考虑的新问题):

  • 有一个PSR-15 请求处理程序标准。在名为 requestHandler 的私有方法中发送标头可能不是一个好主意。
  • 请求处理程序和路由器是不同的组件,需要不同的工作流程。您将它们混合在您的代码中,这是一个警告信号。
  • 任何涉及__magic的代码都是为自己和潜在的未来开发人员设置陷阱。
  • 我不确定include_once 'RequestInterface'线路的结果,但我们有HTTP 消息接口use Psr\Http\Message\ServerRequestInterface在处理请求时,我会考虑在任何类型的自定义实现中进行导入。
  • 回响__destruct也很有趣。你需要的是一个发射器。几个例子:Http EmitterZend Http Runner
  • 这是您实际问题的高级答案:您需要实现一种机制(可能使用正则表达式)来捕获 URI 部分中的模式并解析和检测“路径”中的可选或必需的命名部分。

就个人而言,我建议查看Zend Expressive。在编写轻量级、中间件驱动的应用程序时,它对开发人员有很大帮助。表现力的最大特点是您可以根据需要选择任何武器。它不是一个成熟的 MVC 框架,它提供了一种编写 Web 应用程序的新方法,而且速度非常快。您可以自由选择所需的任何组件,例如;Twig 用于渲染需求,Symfony Console 用于 CLI,Zend Service Manager 作为依赖注入容器,Aura Router 用于路由等。

您可以只使用几个命令来试一试(假设您已经全局安装了 composer):

composer create-project zendframework/zend-expressive-skeleton my-app
cd my-app
composer run --timeout=0 serve

并打开浏览器:http://localhost:8080

祝你好运!


推荐阅读