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


Best way to autoload a model class in specific folder 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);
    }
}

你不能在启动时设置自动加载类的静态成员,包含所有必要的信息吗?

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'));

。您将能够动态地设置path和appname—只需使用setter更改对象的变量。

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

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