使用注册表模式获取路由器参数
Getting router parameters using the Registry pattern
我的应用程序的核心是 Router.php。
Router.php
<?php
final class Router
{
protected $routes = [];
protected $params = [];
public function add($route, $params = [])
{
$route = preg_replace('/\//', '\/', $route);
$route = preg_replace('/\{([a-z]+)\}/', '(?P<>[a-z-]+)', $route);
$route = preg_replace('/\{([a-z]+):([^\}]+)\}/', '(?P<>)', $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;
}
}
为了实现对象的中央存储,我实现了这个注册表模式,它是结构的核心。
Registry.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]);
}
}
base/core 控制器在其构造函数中还有 $registry。
CoreController.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);
}
}
CoreController被所有app controller扩展继承属性
Posts.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>';
}
}
要实例化注册表和路由器,这就是
中的内容
index.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']);
urlhttp://localhost/mvcsix/posts/1235/edit
之后显示的是这个
所有这些看起来都不错,而且工作正常。
不知何故,这感觉不对。我 var_dumped $this->registry 并且显示了路由参数,但我觉得要从路由获取参数我应该 var_dumped $this->router->getParams()。当我 var_dump $this->router->getParams() 时,我得到一个错误
Fatal error: Call to a member function get() on array in
我这样说是因为我在注册表中也有数据库对象,为了显示查询,我这样做 $result = $this->db->query("SELECT * FROM members");
为什么我的参数显示在 $this->registry 而不是 $this->router->getParams(); ?
P.S。上面的代码是原始代码的剥离。有名称空间和其他一些不需要的东西post。
无法测试您在此处发布的代码,因为它缺少 HomeController
class 定义,而且也不太清楚 var_dump(...)
在何处以及在何时被调用。但是我试图根据你提到的致命错误来猜测你的问题,并在你的 Posts
class 中调用 edit()
函数中的 var_dump()
。看起来您试图从该函数中转储 $this->router->getParams()
。
"Fatal error: Call to a member function get() on array in" 表示您试图在 $arr
上调用 $arr->get()
,那是一个数组(不是对象)。您在 CoreController 的 class getter 中调用了这样的 get()
函数。该调用是从 $registry
属性 的范围进行的,因此应该具有对象类型。
因此在这种情况下,您应该在尝试转储 $this->router->getParams()
之前检查 protected $registry
的类型。它可能不是你所期望的那样。
我没有找到您在代码中实例化 Posts
class 的对象的位置以及您在 __constructor()
中作为 $registry
放置的内容,所以我可以不检查我的猜测。如果你澄清这一点,就会更容易找到问题。
正如 alex_edev
注意到的那样,您正试图在数组上调用 get
方法。但它从何而来?
怎么了
Posts
控制器在路由器的方法dispatch
中初始化。 url /posts/1235/edit
确实匹配第二条路由规则,所以执行下面几行
$controller_object = new $controller($this->params);
$action = $this->params['action'];
$action = $this->convertToCamelCase($action);
注意传递给控制器构造函数的内容。您通过路线 params
属性!查看 Posts.php
、Posts
controller extends CoreController
,因此它期望 Registry
作为构造函数的参数,但您传递了一个数组 - Route::params
属性.所以是错误的对象构造阻碍了聚会。
为什么可以正常使用
没有 var_dump
一切正常,因为您不调用 Posts::__get
方法。当您在 Posts
控制器中调用 $this->router->getParams()
时,它会尝试使用 getter 获取未定义的 router
属性 并且由于注册表错误而失败 - 请记住,您注入了一个数组到控制器。
应该做什么
你应该这样启动控制器
$controller_object = new $controller($this->registry);
其中 registry
被注入 __construct
:
final class Router
{
// add definition
private $registry;
// pass it to the router
public function __construct($registry) {
$this->registry = $registry;
}
....
}
路由器初始化如下
$registry->set('db', $db);
$router = new \system\core\Router($registry);
所以,你只需要编辑 6 行代码。
P.S。使用 Type declarations 来避免这种错误。如果你写 public function __construct(Registry $registry)
php 在传递数组时抛出 TypeError
异常。
我的应用程序的核心是 Router.php。
Router.php
<?php
final class Router
{
protected $routes = [];
protected $params = [];
public function add($route, $params = [])
{
$route = preg_replace('/\//', '\/', $route);
$route = preg_replace('/\{([a-z]+)\}/', '(?P<>[a-z-]+)', $route);
$route = preg_replace('/\{([a-z]+):([^\}]+)\}/', '(?P<>)', $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;
}
}
为了实现对象的中央存储,我实现了这个注册表模式,它是结构的核心。
Registry.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]);
}
}
base/core 控制器在其构造函数中还有 $registry。
CoreController.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);
}
}
CoreController被所有app controller扩展继承属性
Posts.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>';
}
}
要实例化注册表和路由器,这就是
中的内容index.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']);
urlhttp://localhost/mvcsix/posts/1235/edit
之后显示的是这个
所有这些看起来都不错,而且工作正常。
不知何故,这感觉不对。我 var_dumped $this->registry 并且显示了路由参数,但我觉得要从路由获取参数我应该 var_dumped $this->router->getParams()。当我 var_dump $this->router->getParams() 时,我得到一个错误
Fatal error: Call to a member function get() on array in
我这样说是因为我在注册表中也有数据库对象,为了显示查询,我这样做 $result = $this->db->query("SELECT * FROM members");
为什么我的参数显示在 $this->registry 而不是 $this->router->getParams(); ?
P.S。上面的代码是原始代码的剥离。有名称空间和其他一些不需要的东西post。
无法测试您在此处发布的代码,因为它缺少 HomeController
class 定义,而且也不太清楚 var_dump(...)
在何处以及在何时被调用。但是我试图根据你提到的致命错误来猜测你的问题,并在你的 Posts
class 中调用 edit()
函数中的 var_dump()
。看起来您试图从该函数中转储 $this->router->getParams()
。
"Fatal error: Call to a member function get() on array in" 表示您试图在 $arr
上调用 $arr->get()
,那是一个数组(不是对象)。您在 CoreController 的 class getter 中调用了这样的 get()
函数。该调用是从 $registry
属性 的范围进行的,因此应该具有对象类型。
因此在这种情况下,您应该在尝试转储 $this->router->getParams()
之前检查 protected $registry
的类型。它可能不是你所期望的那样。
我没有找到您在代码中实例化 Posts
class 的对象的位置以及您在 __constructor()
中作为 $registry
放置的内容,所以我可以不检查我的猜测。如果你澄清这一点,就会更容易找到问题。
正如 alex_edev
注意到的那样,您正试图在数组上调用 get
方法。但它从何而来?
怎么了
Posts
控制器在路由器的方法dispatch
中初始化。 url /posts/1235/edit
确实匹配第二条路由规则,所以执行下面几行
$controller_object = new $controller($this->params);
$action = $this->params['action'];
$action = $this->convertToCamelCase($action);
注意传递给控制器构造函数的内容。您通过路线 params
属性!查看 Posts.php
、Posts
controller extends CoreController
,因此它期望 Registry
作为构造函数的参数,但您传递了一个数组 - Route::params
属性.所以是错误的对象构造阻碍了聚会。
为什么可以正常使用
没有 var_dump
一切正常,因为您不调用 Posts::__get
方法。当您在 Posts
控制器中调用 $this->router->getParams()
时,它会尝试使用 getter 获取未定义的 router
属性 并且由于注册表错误而失败 - 请记住,您注入了一个数组到控制器。
应该做什么
你应该这样启动控制器
$controller_object = new $controller($this->registry);
其中 registry
被注入 __construct
:
final class Router
{
// add definition
private $registry;
// pass it to the router
public function __construct($registry) {
$this->registry = $registry;
}
....
}
路由器初始化如下
$registry->set('db', $db);
$router = new \system\core\Router($registry);
所以,你只需要编辑 6 行代码。
P.S。使用 Type declarations 来避免这种错误。如果你写 public function __construct(Registry $registry)
php 在传递数组时抛出 TypeError
异常。