在Yii.php中:php
<?php class ServiceLocator extends Component { //保存實例化的對象,每一個對象都是單例,且有惟一string類型的ID作區分 private $_components = []; //保存設置的對象或者其定義,用於實例化 private $_definitions = []; //將對象ID做爲ServiceLocator的屬性,可經過$serviceLocator->{ID}直接獲取 public function __get($name) { if ($this->has($name)) { return $this->get($name); } return parent::__get($name); } //檢驗是否有屬性$name public function __isset($name) { if ($this->has($name)) { return true; } return parent::__isset($name); } //檢驗是否有對象$id public function has($id, $checkInstance = false) { return $checkInstance ? isset($this->_components[$id]) : isset($this->_definitions[$id]); } //獲取一個對象$id public function get($id, $throwException = true) { //已經實例化的,直接返回 if (isset($this->_components[$id])) { return $this->_components[$id]; } //有該對象的定義,且定義已是一個對象,設置$_components並直接返回 if (isset($this->_definitions[$id])) { $definition = $this->_definitions[$id]; if (is_object($definition) && !$definition instanceof Closure) { return $this->_components[$id] = $definition; } //有定義但不是現成對象,則交給DI Container去實例化,而且設置$_components return $this->_components[$id] = Yii::createObject($definition); } elseif ($throwException) { throw new InvalidConfigException("Unknown component ID: $id"); } return null; } //設置、存放一個對象 public function set($id, $definition) { unset($this->_components[$id]); if ($definition === null) { unset($this->_definitions[$id]); return; } //若是$definition是對象或者類名或者callable,則註冊到$_definitions中 if (is_object($definition) || is_callable($definition, true)) { // an object, a class name, or a PHP callable $this->_definitions[$id] = $definition; } elseif (is_array($definition)) { //若是是帶'class'的配置數組,也註冊到$_definitions中 // a configuration array if (isset($definition['class'])) { $this->_definitions[$id] = $definition; } else { throw new InvalidConfigException("The configuration for the \"$id\" component must contain a \"class\" element."); } } else { throw new InvalidConfigException("Unexpected configuration type for the \"$id\" component: " . gettype($definition)); } } //清楚註冊的對象 public function clear($id) { unset($this->_definitions[$id], $this->_components[$id]); } }
這裏用到了註冊樹。數組
註冊樹模式(Registry Pattern)又叫註冊模式、註冊器模式。註冊樹模式經過將對象實例註冊到一棵全局的對象樹上,須要的時候從對象樹上採摘的模式設計方法。this
單例模式在整個項目中建立惟一實例的問題;工廠模式封裝了對象的建立方式(工廠方法——用一個抽象方法,抽象工廠——用一簇抽象方法),使得沒必要總用new關鍵詞去獲取對象;建立者模式則是分步驟的建立實例的各個部分;在Yii2中則經過依賴注入容器DI去獲取實例...spa
這些方法實際上都是解決一個問題——如何合理的產生一個對象。但對象既然已經產生出來了,怎麼方便的調用這些對象呢?咱們在項目內部創建的對象好像散兵遊勇同樣,不方便統籌管理安排啊。於是註冊數模式應運而生。無論你是何種方式產生的對象,都給我「插到」註冊樹上。我用某個對象的時候,直接從註冊樹上去取一下就行了,是否是很是方便?註冊時模式還爲其餘模式提供了一種很是好的想法。設計
看看註冊樹模式的實現:code
class Register { //存放對象的數組 protected static $objects; /** * 存放一個對象 * @param $alias * @param $object */ public static function set($alias,$object) { self::$objects[$alias] = $object; } /** * 獲取一個對象 * @param $alias * @return mixed */ public static function get($alias) { return self::$objects[$alias]; } /** * 銷燬一個對象 * @param $alias */ public static function _unset($alias) { unset(self::$objects[$alias]); } }
註冊樹模式很相似服務定位器模式,優勢是集中管理,使用方便。缺點是隱藏了對象和對象之間的依賴關係。component
PHP註冊樹模式主要用於建立對象的時候將咱們的對象與相應的變量進行綁定,從這個角度上說,Yii2的Service Locator和DI Container都用到註冊樹模式。這兩者都在內部維護一個數組(key => value),value爲對象或者對象定義,在獲取時經過惟一的key來獲取,若是是定義再去容器裏面實例化一下。對象