使用注册表模式获取路由器参数

Dak*_*h B 1 php registry model-view-controller router

我在我的应用程序的核心有这个 Router.php。

路由器.php

<?php

final class Router
{
    protected $routes = [];
    protected $params = [];

    public function add($route, $params = [])
    {
        $route = preg_replace('/\//', '\\/', $route);
        $route = preg_replace('/\{([a-z]+)\}/', '(?P<\1>[a-z-]+)', $route);
        $route = preg_replace('/\{([a-z]+):([^\}]+)\}/', '(?P<\1>\2)', $route);
        $route = '/^' . $route . '$/i';

        $this->routes[$route] = $params;
    }

    public function getRoutes()
    {
        return $this->routes;
    }

    public function match($url)
    {
        foreach ($this->routes as $route => $params) {
            if (preg_match($route, $url, $matches)) {
                foreach ($matches as $key => $match) {
                    if (is_string($key)) {
                        $params[$key] = $match;
                    }
                }

                $this->params = $params;
                return true;
            }
        }

        return false;
    }

    public function getParams()
    {
        return $this->params;
    }

    public function dispatch($url)
    {
        $url = $this->removeQueryStringVariables($url);

        if ($this->match($url)) {
            $controller = $this->params['controller'];
            $controller = $this->convertToStudlyCaps($controller);
            $controller = $this->getNamespace() . $controller;

            if (class_exists($controller)) {
                $controller_object = new $controller($this->params);
                $action = $this->params['action'];
                $action = $this->convertToCamelCase($action);

                if (is_callable([$controller_object, $action])) {
                    $controller_object->$action();

                } else {
                    echo "Method $action (in controller $controller) not found";
                }
            } else {
                echo "Controller class $controller not found";
            }
        } else {
            echo 'No route matched.';
        }
    }

    protected function convertToStudlyCaps($string)
    {
        return str_replace(' ', '', ucwords(str_replace('-', ' ', $string)));
    }

    protected function convertToCamelCase($string)
    {
        return lcfirst($this->convertToStudlyCaps($string));
    }

    protected function removeQueryStringVariables($url)
    {
        if ($url != '') {
            $parts = explode('&', $url, 2);

            if (strpos($parts[0], '=') === false) {
                $url = $parts[0];
            } else {
                $url = '';
            }
        }

        return $url;
    }

    protected function getNamespace()
    {
        $namespace = 'catalog\controller\\';

        if (array_key_exists('namespace', $this->params)) {
            $namespace .= $this->params['namespace'] . '\\';
        }

        return $namespace;
    }
}
Run Code Online (Sandbox Code Playgroud)

为了实现对象的中央存储,我实现了这个注册表模式,这是结构的核心。

注册表.php

<?php
final class Registry
{
    private $data = array();

    public function get($key)
    {
        return (isset($this->data[$key]) ? $this->data[$key] : null);
    }

    public function set($key, $value)
    {
        $this->data[$key] = $value;
    }

    public function has($key)
    {
        return isset($this->data[$key]);
    }
}
Run Code Online (Sandbox Code Playgroud)

基本/核心控制器在其构造函数中还具有 $registry。

核心控制器.php

<?php
abstract class CoreController
{
    protected $registry;

    public function __construct($registry)
    {
        $this->registry = $registry;
    }

    public function __get($key)
    {
        return $this->registry->get($key);
    }

    public function __set($key, $value)
    {
        $this->registry->set($key, $value);
    }
}
Run Code Online (Sandbox Code Playgroud)

CoreController 由所有应用程序控制器扩展以继承属性。

帖子.php

<?php
class Posts extends CoreController
{
    public function index() {
        echo 'Hello from the index action in the posts controller';
    }

    public function addNew() {
        echo 'Hello from the addNew action in the posts controller';
    }

    public function edit() {
        echo '<p>Route parameters: <pre>'.var_dump($this->registry).'</pre></p>';
    }
}
Run Code Online (Sandbox Code Playgroud)

要实例化注册表和路由器,这是

索引.php

<?php
// Instantiate registry
$registry = new \system\core\Registry();

// Database
$db = new DB(DB_HOSTNAME, DB_USERNAME, DB_PASSWORD, DB_DATABASE);
$registry->set('db', $db);


$router = new \system\core\Router();
$registry->set('router', $router);


// Add the routes
$router->add('', ['controller'=>'HomeController', 'action'=>'index']);
$router->add('posts', ['controller'=>'posts', 'action'=>'index']);
//$router->add('posts/new', ['controller'=>'posts', 'action'=>'new']);
$router->add('{controller}/{action}');
$router->add('{controller}/{id:\d+}/{action}');
$router->add('admin/{controller}/{action}');

$router->dispatch($_SERVER['QUERY_STRING']);
Run Code Online (Sandbox Code Playgroud)

在网址之后,http://localhost/mvcsix/posts/1235/edit这是显示的内容

在此处输入图片说明

所有这些看起来都不错,而且效果很好。

不知怎的,这感觉不对。我 var_dumped $this->registry 并且我显示了路由的参数,但我觉得要从路由中获取参数我应该 var_dumped $this->router->getParams()。当我 var_dump $this->router->getParams() 时,我收到一个错误消息

致命错误:在数组中调用成员函数 get()

我这样说是因为我在注册表中也有数据库对象并且要显示查询 $result = $this->db->query("SELECT * FROM members");

为什么我的参数显示在 $this->registry 而不是 $this->router->getParams(); ?

PS上面的代码是对原始代码的剥离。有命名空间和其他一些对这篇文章来说不是必需的东西。

shu*_*van 5

正如所alex_edev注意到的,您正在尝试get在数组上调用方法。但它从哪里来?

怎么了。

Posts控制器在路由器的方法中初始化dispatch。url/posts/1235/edit确实匹配第二条路由规则,因此执行以下几行

$controller_object = new $controller($this->params);
$action = $this->params['action'];
$action = $this->convertToCamelCase($action);
Run Code Online (Sandbox Code Playgroud)

注意传递给控制器​​构造函数的内容。你通过路由params属性!看着Posts.phpPosts控制器扩展CoreController,所以它期望Registry作为构造函数的参数,但是你传递了一个数组 -Route::params属性。因此,是错误的对象构造使派对陷入困境。

为什么它可以正常工作。

var_dump由于您没有调用Posts::__get方法,因此一切正常。当你打电话$this->router->getParams()Posts控制器,它会尝试获取未定义的router属性与getter和失败,因为错误的注册表-记住,你注入一个阵列控制器。

应该做什么

您应该以这种方式启动控制器

$controller_object = new $controller($this->registry);
Run Code Online (Sandbox Code Playgroud)

在哪里registry注入__construct

final class Router
{
    // add definition
    private $registry;

    // pass it to the router
    public function __construct($registry) {
         $this->registry = $registry;
    }
    ....
 }
Run Code Online (Sandbox Code Playgroud)

路由器启动如下

$registry->set('db', $db);


$router = new \system\core\Router($registry);
Run Code Online (Sandbox Code Playgroud)

所以,你只需要编辑 6 行代码。

PS 使用类型声明来避免这种错误。如果你写public function __construct(Registry $registry)phpTypeError在传递数组时抛出异常。