| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264 | <?php/** * @link http://www.yiiframework.com/ * @copyright Copyright (c) 2008 Yii Software LLC * @license http://www.yiiframework.com/license/ */namespace yii\di;use Closure;use Yii;use yii\base\Component;use yii\base\InvalidConfigException;/** * ServiceLocator implements a [service locator](http://en.wikipedia.org/wiki/Service_locator_pattern). * * To use ServiceLocator, you first need to register component IDs with the corresponding component * definitions with the locator by calling [[set()]] or [[setComponents()]]. * You can then call [[get()]] to retrieve a component with the specified ID. The locator will automatically * instantiate and configure the component according to the definition. * * For example, * * ```php * $locator = new \yii\di\ServiceLocator; * $locator->setComponents([ *     'db' => [ *         'class' => 'yii\db\Connection', *         'dsn' => 'sqlite:path/to/file.db', *     ], *     'cache' => [ *         'class' => 'yii\caching\DbCache', *         'db' => 'db', *     ], * ]); * * $db = $locator->get('db');  // or $locator->db * $cache = $locator->get('cache');  // or $locator->cache * ``` * * Because [[\yii\base\Module]] extends from ServiceLocator, modules and the application are all service locators. * Modules add [tree traversal](guide:concept-service-locator#tree-traversal) for service resolution. * * For more details and usage information on ServiceLocator, see the [guide article on service locators](guide:concept-service-locator). * * @property array $components The list of the component definitions or the loaded component instances (ID => * definition or instance). * * @author Qiang Xue <qiang.xue@gmail.com> * @since 2.0 */class ServiceLocator extends Component{    /**     * @var array shared component instances indexed by their IDs     */    private $_components = [];    /**     * @var array component definitions indexed by their IDs     */    private $_definitions = [];    /**     * Getter magic method.     * This method is overridden to support accessing components like reading properties.     * @param string $name component or property name     * @return mixed the named property value     */    public function __get($name)    {        if ($this->has($name)) {            return $this->get($name);        }        return parent::__get($name);    }    /**     * Checks if a property value is null.     * This method overrides the parent implementation by checking if the named component is loaded.     * @param string $name the property name or the event name     * @return bool whether the property value is null     */    public function __isset($name)    {        if ($this->has($name)) {            return true;        }        return parent::__isset($name);    }    /**     * Returns a value indicating whether the locator has the specified component definition or has instantiated the component.     * This method may return different results depending on the value of `$checkInstance`.     *     * - If `$checkInstance` is false (default), the method will return a value indicating whether the locator has the specified     *   component definition.     * - If `$checkInstance` is true, the method will return a value indicating whether the locator has     *   instantiated the specified component.     *     * @param string $id component ID (e.g. `db`).     * @param bool $checkInstance whether the method should check if the component is shared and instantiated.     * @return bool whether the locator has the specified component definition or has instantiated the component.     * @see set()     */    public function has($id, $checkInstance = false)    {        return $checkInstance ? isset($this->_components[$id]) : isset($this->_definitions[$id]);    }    /**     * Returns the component instance with the specified ID.     *     * @param string $id component ID (e.g. `db`).     * @param bool $throwException whether to throw an exception if `$id` is not registered with the locator before.     * @return object|null the component of the specified ID. If `$throwException` is false and `$id`     * is not registered before, null will be returned.     * @throws InvalidConfigException if `$id` refers to a nonexistent component ID     * @see has()     * @see set()     */    public function get($id, $throwException = true)    {        if (isset($this->_components[$id])) {            return $this->_components[$id];        }        if (isset($this->_definitions[$id])) {            $definition = $this->_definitions[$id];            if (is_object($definition) && !$definition instanceof Closure) {                return $this->_components[$id] = $definition;            }            return $this->_components[$id] = Yii::createObject($definition);        } elseif ($throwException) {            throw new InvalidConfigException("Unknown component ID: $id");        }        return null;    }    /**     * Registers a component definition with this locator.     *     * For example,     *     * ```php     * // a class name     * $locator->set('cache', 'yii\caching\FileCache');     *     * // a configuration array     * $locator->set('db', [     *     'class' => 'yii\db\Connection',     *     'dsn' => 'mysql:host=127.0.0.1;dbname=demo',     *     'username' => 'root',     *     'password' => '',     *     'charset' => 'utf8',     * ]);     *     * // an anonymous function     * $locator->set('cache', function ($params) {     *     return new \yii\caching\FileCache;     * });     *     * // an instance     * $locator->set('cache', new \yii\caching\FileCache);     * ```     *     * If a component definition with the same ID already exists, it will be overwritten.     *     * @param string $id component ID (e.g. `db`).     * @param mixed $definition the component definition to be registered with this locator.     * It can be one of the following:     *     * - a class name     * - a configuration array: the array contains name-value pairs that will be used to     *   initialize the property values of the newly created object when [[get()]] is called.     *   The `class` element is required and stands for the the class of the object to be created.     * - a PHP callable: either an anonymous function or an array representing a class method (e.g. `['Foo', 'bar']`).     *   The callable will be called by [[get()]] to return an object associated with the specified component ID.     * - an object: When [[get()]] is called, this object will be returned.     *     * @throws InvalidConfigException if the definition is an invalid configuration array     */    public function set($id, $definition)    {        unset($this->_components[$id]);        if ($definition === null) {            unset($this->_definitions[$id]);            return;        }        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)) {            // a configuration array            if (isset($definition['class']) || 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));        }    }    /**     * Removes the component from the locator.     * @param string $id the component ID     */    public function clear($id)    {        unset($this->_definitions[$id], $this->_components[$id]);    }    /**     * Returns the list of the component definitions or the loaded component instances.     * @param bool $returnDefinitions whether to return component definitions instead of the loaded component instances.     * @return array the list of the component definitions or the loaded component instances (ID => definition or instance).     */    public function getComponents($returnDefinitions = true)    {        return $returnDefinitions ? $this->_definitions : $this->_components;    }    /**     * Registers a set of component definitions in this locator.     *     * This is the bulk version of [[set()]]. The parameter should be an array     * whose keys are component IDs and values the corresponding component definitions.     *     * For more details on how to specify component IDs and definitions, please refer to [[set()]].     *     * If a component definition with the same ID already exists, it will be overwritten.     *     * The following is an example for registering two component definitions:     *     * ```php     * [     *     'db' => [     *         'class' => 'yii\db\Connection',     *         'dsn' => 'sqlite:path/to/file.db',     *     ],     *     'cache' => [     *         'class' => 'yii\caching\DbCache',     *         'db' => 'db',     *     ],     * ]     * ```     *     * @param array $components component definitions or instances     */    public function setComponents($components)    {        foreach ($components as $id => $component) {            $this->set($id, $component);        }    }}
 |