【问题标题】:PHP Front Controller with MVC带有 MVC 的 PHP 前端控制器
【发布时间】:2012-11-07 08:55:12
【问题描述】:

我正在尝试通过前端控制器设计深入研究 MVC。

我想通过一行来调用我的整个应用程序,例如在 index.php 中:

require_once(myclass.php);
$output = new myClass();

我很想摆脱 require_once 行,但我不明白如何在不包含它的情况下加载我的类?

无论如何,我的主要问题是如何使用一个前端类加载我的各种控制器、模型和视图等。到目前为止,我想出了:

class myClass
{
    private $name;
    public $root = $_SERVER['DOCUMENT_ROOT'];
    private $route = array("Controller" => "", "Model" => "", "View" => "", "Paremeters" => "");
    function __construct() 
    {   $uri = explode("/",$_SERVER['REQUEST_URI']);
        if(isset($uri[2])) {$this->route['Controller'] = $uri[2];}
        if(isset($uri[3])) {$this->route['Model'] = $uri[3];}
        if(isset($uri[4])) {$this->route['View'] = $uri[4];}
        if(isset($this->route['Controller'])) 
        {
            include($this->root."/".$this->route['Controller'].".php");
        }
    }

}

但它似乎有点令人费解而且过于夸张。另外,一旦我在 __construct 中包含了新类,我应该如何加载它?

对于知识的缺乏,我深表歉意,我已经用谷歌搜索了很多次,但我不断找到相同的页面,似乎并没有扩展我对此事的了解。

【问题讨论】:

  • 如果你想摆脱require,请使用autoloading
  • 但是我应该把自动加载功能放在哪里呢?我希望我的索引文件只是 $ouput = new myClass(),它怎么知道在 myClass.php 中查找自动加载?
  • 您可能会发现 thisthis 相关。
  • 您还可以查看现有 mvc 框架的代码。例如Kohana

标签: php model-view-controller front-controller


【解决方案1】:

如果您的网址肯定是http://domain.com/[controller]/[action]/参数,那么您的前端控制器可能如下所示:

<?php
class Application
{
    public function __construct()
    {
        $this->setupAutoloader();
        $this->route();
    }

    private function setupAutoloader()
    {
        // do your autoloading here
    }

    private function route()
    {
        $request = explode('/', trim($_SERVER['REQUEST_URI'], '/'));

        $controller = isset($request[0]) ? ucwords(array_shift($request)) . 'Controller' : 'HomeController';
        $action = isset($request[0]) ? array_shift($request) : 'index';
        $parameters = $request;

        $response = call_user_func_array(array($controller, $action), $parameters);
    }
}

从这里,你可以添加你的自动加载器实现,对你的响应做任何你想做的事情,然后从你的 index.php 调用它,如下所示:

<?php
require 'path/to/Application.php';

$application = new Application();

不幸的是,如果第一个文件存储在文件系统的其他位置,您总是必须包含第一个文件,但如上所述,您可以从这里自动加载其他类,例如库、控制器、模型等。

【讨论】:

  • 只是检查...可能是正确的...array_unshift() 正确还是应该有array_shift()
  • @tastro 再看代码应该是array_shift(),是的。好地方!我的错。
  • 根据这个答案,我同意你的观点 Chud37,我讨厌包含和要求,更喜欢自动加载我的课程。但是,我认为只有前端控制器文件中的 1 条 require 语句是可以接受的。我构建了一个松散地基于 Zend 框架的 MVC 框架,它的工作方式与这个答案完全相同(在 index.php 文件中使用单个 require 调用)。有兴趣的可以去看看,这里github.com/jfingar/tinyphp
【解决方案2】:

令我惊讶的是,在这两个冗长且内容丰富的先前答案中,没有人费心以最简单的方式实际回答您的问题。

您需要__autoload() 函数。您仍然需要在代码中的某处定义它,但它可以简单地添加到全局头文件中,然后您不必为每个类定义显式编写包含。

/* auto load model classes */
function __autoload($class_name) {
        $filename = 'class.' . strtolower($class_name) . '.php';
        $file = __SITE_PATH . '/_model/' . $filename;
        if( file_exists($file) == false ) {
                return false;
        }
        require($file);
}

【讨论】:

    【解决方案3】:

    要在不手动包含的情况下加载类,您应该查看spl_autoload_register,它使您能够在应用程序需要时自动加载类。

    至于另一个问题,这完全取决于您的设计。如果要加载常见的“控制器”和“动作”,则应创建一个可路由文件夹(这意味着只有控制器可以停留的文件夹)并放置所有控制器类,然后创建一个可路由动作空间(可路由组控制器类中的方法,通常是通过定义动作方法以action_为前缀的约定。完成后,只需像这样创建控制器:

    class Blog {
        public function action_posts() { ... };
    }
    

    并管理它,以便 URL /blog/posts/ 将调用它。 (它只需要你已经证明擅长的相同的 URI 操作)。 (请注意,自动加载器应该自己处理实际类文件的包含,而不需要您加载所有控制器类)。获得控制器名称和操作后,您只需要以下内容:

    if (class_exists($controller)) {
        $app = new $controller;
        if (method_exists($app, $action)) {
            $app->$action(); // also take a look at call_user_func and call_user_func_array
        }
    }
    

    您也可以允许使用参数,但这将非常困难,以允许这种结果:

    class Blog {
        public function action_post($id) { ... };
    }
    

    例如被/blog/post/14/调用。

    在处理 URI 时,请记住您可能有错误的输入并且应该处理它们,但是如果您创建了正确的路由空间,那么您已经完成了一半。

    最后,如果您想获得灵感或其他任何东西,只需看看 CodeIgniter(这是目前最容易学习的框架之一)或任何其他框架。

    希望对您有所帮助。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-01-01
      • 2016-12-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-08-31
      相关资源
      最近更新 更多