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上面的代码是对原始代码的剥离。有命名空间和其他一些对这篇文章来说不是必需的东西。
正如所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.php,Posts控制器扩展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在传递数组时抛出异常。