我目前正在编写自己的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
。