这是在PHP for MVC中将URI与类/方法匹配的好方法

Jas*_*vis 6 php regex model-view-controller performance uri

我是MVC的新手,所以这是我的第一次尝试,我相信你们可以给我改进,感谢任何提示或帮助!

下面是我为我的个人框架设计的路由器/调度程序系统,这是我第一次尝试使用MVC模式.

第一个代码块只是我的.htaccess文件,它通过我的index.php文件路由所有请求.

第二个代码块是我的"路由"数组,它将告诉Router对象,调用哪个类和方法以及任何ID或分页号(如果存在).

第三块代码是路由器类.

第四块就是运行这个类

因此,路由器类必须使用正则表达式来匹配URI与路由映射中的路由,理论上,这听起来像是一个包含正则表达式必须运行的50多个路由的列表时的糟糕性能,我应该这样做这不一样吗?我使用正则表达式的主要原因是匹配路由中存在的页码和ID号.

另外请不要只是告诉我使用框架,我这样做是为了更好地学习它,我这样学习得更好,只是不喜欢现在使用现有的框架,我研究了所有主要框架和一些不太常见的框架.已有的想法.

1)所以主要问题,做什么只是不正确?

2)有没有更好的方法来检测URI中的内容,而不是像我一样在数组上使用正则表达式,在高流量网站上考虑它?

3)由于所有内容都通过index.php文件进行路由,我将如何处理AJAX请求?

对不起,如果这令人困惑,我自己有点困惑!


.htaccess文件

RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$   index.php?uri=$1    [NC,L,QSA]
Run Code Online (Sandbox Code Playgroud)

地图数组()

/**
 * Map URI to class/method and ID and Page numbers
 * Must be an array
 */
$uri_route_map = array( 
    //forums
    'forums/' => array(
        'controller' => 'forums',
        'method' => 'index',
        'id_number' => '',
        'page_number' => ''),

    'forums/viewforum/(?<id_number>\d+)' =>  array(
        'controller' => 'forums',
        'method' => 'viewforum',
        'id_number' => isset($id_number),
        'page_number' => ''),  

    'forums/viewthread/(?<id_number>\d+)' =>  array(
        'controller' => 'forums',
        'method' => 'viewthread',
        'id_number' => isset($id_number),
        'page_number' => ''),

    'forums/viewthread/(?<id_number>\d+)/page-(?<page_number>\d+)' =>  array(
        'controller' => 'forums',
        'method' => 'viewthread',
        'id_number' => isset($id_number),
        'page_number' => isset($page_number)),

    // user routes
    // account routes
    // blog routes 
    // mail routes
    // various other routes
);
Run Code Online (Sandbox Code Playgroud)

读取和匹配上面的Map数组的路由器类

/**
 * Run URI against our Map array to get class/method/id-page numbers
 */
 class Router
{
    private $_controller = '';
    private $_method = '';
    public $page_number = '';
    public $id_number = '';

    public function __construct($uri, array $uri_route_map)
    {
        foreach ($uri_route_map as $rUri => $rRoute)
        {
            if (preg_match("#^{$rUri}$#Ui", $uri, $uri_digits))
            {
                //if page number and ID number in uri then set it locally
                $this->page_number = (isset($uri_digits['page_number']) ? $uri_digits['page_number'] : null);
                $this->id_number = (isset($uri_digits['id_number']) ? $uri_digits['id_number'] : null);
                $this->_controller = $rRoute['controller'];
                $this->_method = $rRoute['method'];

                // just for debug and testing while working on it / will be removed from final code
                echo '<hr> $page_number = ' . $this->page_number . '<br><br>';
                echo '<hr> $id_number = ' . $this->id_number . '<br><br>';
                echo '<hr> $controller = ' . $this->_controller . '<br><br>';
                echo '<hr> $method = ' . $this->_method . '<br><br>';
                break;
            }else{
                $this->page_number = '';
                $this->id_number = '';
                $this->_controller = '404';
                $this->_method = '404';
            }
        }
    }

    public function getController()
    {
        return $this->_controller;
    }

    public function getMethod()
    {
        return $this->_method;
    }

    public function getPageNumber()
    {
        return $this->page_number;
    }

    public function getIDNumber()
    {
        return $this->id_number;
    }

    /**
     * Call our class and method from values in the URI
     */
    public function dispatch()
    {
        if (file_exists('controller' . $this->_controller . '.php'))
        {
            include ('controller' . $this->_controller . '.php');
            $controllerName = 'Controller' . $this->_controller;
            $controller = new $controllerName($this->getIDNumber(),$this->getPageNumber());
            $method = $this->_method;
            if (method_exists($this->_controller, $this->_method))
            {
                return $controller->$method();
            } else {
                // method does not exist
            }
        } else {
            // Controller does not exist
        }
    }

}
Run Code Online (Sandbox Code Playgroud)

运行

/**
 * Testing the class
 */
$uri = isset($_GET['uri']) ? $_GET['uri'] : null;
$router = new Router($uri, $uri_route_map);
$router->dispatch();

?>
Run Code Online (Sandbox Code Playgroud)

Hal*_*yon 4

1)在我看来还不错。不过代码看起来有点乱。

2)是的,有更好的方法。您执行正则表达式是因为您想要匹配 URL 中您不知道的部分。为什么不看看$parts = explode("/", $uri)是否可以找到您要查找的页面呢?您需要定义每个页面需要多少个参数,否则您将不知道是选择forumswith 参数array("viewform", 123)还是forums/viewforumwith 参数array(123)

explode感觉负载比正则表达式更好。它还增加了改进错误处理的好处。如果传递给的参数viewforum不是数字怎么办?当然你可以做得更好"404";)

3)创建一个单独的ajax处理程序。无论如何,Ajax 都是隐藏的,因此您无需费心提供语义 URL。

例子:

function find_route($parts) {
    foreach ($uri_route_map as $route => $route_data) {
        $route_check = implode("/", array_slice($parts, 0, count($parts) - $route_data['num_arguments']));
        if ($route_check === $route) {
            return $route_data;
        }
    }
    throw new Exception("404?");
}

$uri = "forum/viewforum/522";

$parts = explode("/", $uri);
$route = find_route($parts);
$arguments = array_slice($parts, count($parts) - $route['num_arguments']);

$controller = $rRoute['controller'];
$method = $rRoute['method'];
$controller_instance = new $controller();
call_user_func_array(array($controller_instance, $method), $arguments);
Run Code Online (Sandbox Code Playgroud)

(未经测试)

插件

由于 $uri_route_map,您无​​法“动态”注册更多插件或页面或“路由”。我会添加一个函数来动态添加更多路由到Router.

此外,您可以考虑一些自动发现方案,例如,将检查文件夹中plugins/是否有名为“manifest.php”的文件的文件夹,当调用该文件时,将有选择地向路由器添加更多路由。