PHP中,变量有全局的概念,比如$_GET $_POST等数组中保存的值都是全局的,如果直接使用一个变量,那么它也是全局可用的,如果在函数中使用global关键字声明一个变量,那这个函数就可以使用来自外部的这个变量值,函数中的这个变量值还可以从函数带出到外部,所有直接声明的变量或用global关键字声明的变量,实际上全部注册到$GLOBALS数组中。比如声明了$var,当要引用这个变量时可以直接用$var或$GLOBALS['var']。这种搞法其实已经非常怪异了,不过还有更加怪异的,比如$_GET['name'],你可能可以使用$name直接引用或用$GLOBALS['name']来引用这个变量(具体决定于PHP的配置,不过一般都是关闭的了)。 这些诡异的用法是PHP的历史原因导致的。不过作为一个PHP程序员,仍然需要知道这个。
这里的全局变量的使用有很大风险,比如我声明了一个$var,然后在其它地方再次声明一个同名的$var,那么后面的将悄悄覆盖前面的。更加糟糕的是,如果这个变量引用一个对象,但是由于一些第三方代码的引入,也声明了一个同名的变量,那么这个变量在其它地方调用方法时将出现错误,这个问题很令人烦恼。所以我们得到的教训是:慎用全局变量。
以上说得情况在面向过程的代码中中,尤为明显。由于这个问题,全局变量设计模式就产生了。以下主要探讨Magento中的全局设计模式和单态设计模式。
Magento中使用如下方法实现了全局设计模式:
Mage::register
Mage::unregister
Mage::registry
分别对应注册 和 注销 和 获取全局变量。首先看看注册一个全局变量时发生了什么事情:
public static function register($key, $value, $graceful = false)
{
if (isset(self::$_registry[$key])) {
if ($graceful) {
return;
}
self::throwException('Mage registry key "'.$key.'" already exists');
}
self::$_registry[$key] = $value;
}
首先检查$key是否存在$_registry数组中,如果存在就根据$graceful的设置,返回空或者抛出异常。如果不存在就直接赋值。可见,一个变量注册了,就不能再注册一个同名的变量了,这个很好解决了变量被覆盖的威胁。但是还是有不足,因为程序员可以先调用unregister来注销变量,然后设置自己的变量:
public static function unregister($key)
{
if (isset(self::$_registry[$key])) {
if (is_object(self::$_registry[$key]) && (method_exists(self::$_registry[$key], '__destruct'))) {
self::$_registry[$key]->__destruct();
}
unset(self::$_registry[$key]);
}
}
注销全局变量时如果全局变量是对象并且设置了__destruct析构函数,那么直接调用它的析构函数释放资源。否则就是简单unset,这个就依赖PHP的垃圾回收器来回收了。
当想要获取这个全局变量时,直接调用registry,可以把它看做是注册表:
public static function registry($key)
{
if (isset(self::$_registry[$key])) {
return self::$_registry[$key];
}
return null;
}
Magento内核高度使用这个模式,我们知道Magento中的控制器并不会直接和视图交互,真正和视图交互的是Block,控制器中只要使用renderLayout就可以获取输出:
1
2
3
$this->loadLayout();
$this->getLayout()->getBlock('content')->setSomeVar($var);
$this->renderLayout();
我们可以在控制器中添加:
Mage::register('product', $product);
一个Block中可能包含如下方法:
public function getProduct()
{
return Mage::registry('product');
}
这样就可以在视图中使用:
echo $this->getProduct()->getName();
Magento中的单态设计模式(Singleton Pattern)
获取一个对象的一份单态实例:
Mage::getSingleton('group/class');
过多解释在面对代码时非常苍白,所以我们这里直接看看这个方法的实现:
#File: app/Mage.php
public static function getSingleton($modelClass='', array $arguments=array())
{
$registryKey = '_singleton/'.$modelClass;
if (!self::registry($registryKey)) {
self::register($registryKey, self::getModel($modelClass, $arguments));
}
return self::registry($registryKey);
}
它使用’_singleton/’.$modelClass组建名字,然后调用register把getModel()返回的实例记录到注册表中,你可以看到,如果这个名称对应的值存在了,直接就放回这个实例,就是这样保证了全局唯一(看起来不是严格的单态模式,因为可以unregister)。这个封装另一个模式来实现的别的设计模式,也算是一个亮点吧。不过一般单态设计模式是控制类的构造函数来保证的,常见的使用方法是:
1
$bar = Object_Type::getInstance();
可是Magento中并没有采用这种方式。尽管可以unregister,但是它的灵活性也体现出来了。
(责任编辑:最模板) |