在特定文件夹HMVC中自动加载模型类的最佳方法

时间:2011-08-06 18:23:50

标签: php oop autoload hmvc

我目前正在编写自己的PHP框架作为使用HMVC设计模式的学习练习。一切正常:),但我已多次读过,在PHP代码中引用静态类是一个坏习惯,这正是我在自动加载函数中所做的:

function __autoload($className) {
    $path = SERVER_ROOT . DS . 'applications' . DS . Dispatcher::getApplicationName() . DS . 'models' . DS . 'class.' . strtolower($className) . '.php';

    if (file_exists($path)) {
        require_once($path);
    } else {
        throw new Exception('Can\'t find a model at "' . $path . '".');
    }
}

正如您所看到的,我使用静态调用Dispatcher::getApplicationName()获取当前应用程序,由于它引入了依赖项,因此很多人都认为这是不好的。我也可以使用debug_backtrace()获取applicationName,因为启动模型的类包含ApplicationName作为属性。这样更好,还是有其他我没有想过的选择?

谢谢!

编辑:忘了提到上面的代码还有另外一个问题:控制器的应用程序并不总是等于调度程序的应用程序,因为我使用的是HMVC设计模式(因此控制器被称为内部控制器)。这只能使用debug_backtrace修复。

修改:而不是Dispatcher::getApplicationName()我现在使用Request::getCurrentApplicationName()。它现在再次起作用,因为我的请求类保存了所有应用程序。这样更好,还是有更好的方法?

<?php

class Request {
    private static $_controllers = array();
    private static $_applicationsNames = array();

    public static function _getCurrentApplicationName() {
        return end(self::$_applicationsNames);
    }

    public static function _load($applicationName, $controllerName, $methodName) {
        // Add the application the the array (for autoloading).
        self::$_applicationsNames[] = $applicationName;

        // Check if the controller has already been instantiated.
        if (!isset(self::$_controllers[$applicationName . DS . $controllerName])) {
            require_once(APPLICATIONS_ROOT . DS . $applicationName . DS . 'controllers' . DS . 'class.' . $controllerName . '.php');
            self::$_controllers[$applicationName . DS . $controllerName] = new $controllerName($applicationName);
        }

        // Get the user arguments.
        $arguments = array_slice(func_get_args(), 3);

        // Call the method.
        $result = call_user_func_array(array(self::$_controllers[$applicationName . DS . $controllerName], $methodName), $arguments);

        // Remove the last value from the applications array.
        array_pop(self::$_applicationsNames);
    }
}

2 个答案:

答案 0 :(得分:2)

难道你不能在启动过程中设置包含所有必要信息的autoload类的静态成员吗​​?

debug_backtrace()不能是可靠的信息源。如果有人想使用您的库您的自动加载器,但没有其中一个起始层,该怎么办?这样可能吗?

类/函数使用的所有数据都应放在该类中或作为函数的参数。因为自动加载器可以是任何回调,所以你可以这样做:

class FrameworkAutoloader
{
    public $appName;
    public $path;

    public function setAppName($name) { $this->appName = $name; }
    public function setPath($path) { $this->path= $path; }


    function __autoload($className) {
        $path = $this->path. DS . 'applications' . DS . $this->appName . DS . 'models' . DS . 'class.' . strtolower($className) . '.php';

        if (file_exists($path)) {
             require_once($path);
        } else {
             throw new Exception('Can\'t find a model at "' . $path . '".');
        }
    }
}

$autoloader = new FrameworkAutoloader();
$autoloader->setAppName('asd'); //you can also apply those within constructor, but leave setters
$autoloader->setPath('asd');
spl_autoload_register(array($autoloader, '__autoload'));

这就是全部。您将能够动态设置路径和appname - 只需使用setter更改对象的变量即可。

为什么我们应该这样做?在这段代码中,没有“神奇”的存在。您可以使用PHPDOC为每个函数编写文档,用户将知道所有参数的来源。另一个优点是我可以在任何地方使用此代码,我不需要知道,该类使用Dispatcher :: getApplicationName()。

答案 1 :(得分:0)

我会考虑在任何文件引导应用程序时设置APPLICATION_ROOT定义。这是一个有用的东西,一直可用,而不仅仅是__autoload