1

我目前正在使用 HMVC 设计模式编写自己的 PHP 框架作为学习练习。一切正常:),但我读过很多次,在 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 . '".');
    }
}

如您所见,我使用 static call 获取当前应用程序Dispatcher::getApplicationName(),这在许多人看来很糟糕,因为它引入了依赖关系。我也可以使用 获取 applicationName debug_backtrace(),因为启动模型的类包含 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);
    }
}
4

2 回答 2

2

Can't you just set static member of autoload class during startup containing all necessary information?

debug_backtrace() cannot be reliable source of information. What if somebody would like to use your libraries and your autoloader, but without one of the starting layers? Would it be possible that way?

All data used by class/function should be placed within that class or as parameter for the function. Because autoloader can be any callback, you can do something like this:

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

That's all. You will be able to set path and appname dynamically - just by changing object's variables using setters.

Why we should do that this way? In this code there is no "magic" going around. You can write documentation using PHPDOC to every function and user will know where all params come from. Another advantage is that I can use this code anywhere, I don't need to know, that the class uses Dispatcher::getApplicationName().

于 2011-08-06T23:50:58.600 回答
0

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

于 2011-08-06T22:51:58.120 回答