Slim研读笔记五之依赖注入容器(中)

来源:互联网 发布:淘宝的聚划算怎么抢 编辑:程序博客网 时间:2024/06/05 03:12

“一步一步走,心急吃不来热豆腐呦”    —郭X村口大爷


这节我们继续读上节没读完的代码。上节我们说到,应用主体传入了一个属性container,属性值为容器实例。然后,我们又了解了该容器类继承自Pimple容器实现自ContainerInterface接口,且这个容器接口继承自PsrContainerInterface。所以,Slim默认容器是符合PSR7规范的。之后,我们又从Container类构造方法开始研读,

   /**     * 创建新容器     * Create new container     *     * @param array $values The parameters or objects.     */    public function __construct(array $values = [])    {        // 继承父类的构造方法        parent::__construct($values);        // 获取配置文件中settings配置项的值        $userSettings = isset($values['settings']) ? $values['settings'] : [];        // 注册Slim需要的一些默认服务        $this->registerDefaultServices($userSettings);    }
第一行代码是parent::__construct($values);  
继承了父类的构造方法,那我们就不得不了解其父类构造方法。
   /**     * 实例化容器     * Instantiates the container.     *     * Objects and parameters can be passed as argument to the constructor.     *     * @param array $values The parameters or objects     */    public function __construct(array $values = array())    {        // 实例化factories,protected属性,且赋值为\SplObjectStorage()实例化对象        // SplObjectStorage提供了从对象到数据的映射,或者忽略数据提供对象集        $this->factories = new \SplObjectStorage();        $this->protected = new \SplObjectStorage();        // 将传入的配置项整合到容器对象        foreach ($values as $key => $value) {            $this->offsetSet($key, $value);        }    }

下面,我们继续研读之后的代码...
该$values存储着组合之后默认配置与用户配置。而由$this->offsetSet方法了解到这些配置项中的参数和值存储在$this->values数组中。父类构造方法我们已经了解,下面继续回到Container类构造方法中。
// 注册Slim需要的一些默认服务$this->registerDefaultServices($userSettings);

查看之...

    private function registerDefaultServices($userSettings)    {        $defaultSettings = $this->defaultSettings;        /**         * settings属性赋值集合对象,该对象是defaultSettings和userSettings的合并数组经处理后产生的集合对象         * This service MUST return an array or an         * instance of \ArrayAccess.         *         * @return array|\ArrayAccess         */        $this['settings'] = function () use ($userSettings, $defaultSettings) {            return new Collection(array_merge($defaultSettings, $userSettings));        };        // 注册Slim默认服务        $defaultProvider = new DefaultServicesProvider();        $defaultProvider->register($this);    }
$this['settings']赋值为Slim\Collection类实例。

DefaultServicesProvider 类作用是注册一些默认服务,譬如环境模拟服务(将php全局变量集成到服务)、Request服务、Response服务、Router服务、各种异常错误处理服务以及CallableResolver服务(该类将格式为“class:method”字符串解析为一个闭包)。这些服务我们暂且了解下,下面继续往下研读。
我们跳到index.php页面,了解到:
// 依赖注入容器$container = $app->getContainer();/** * Enable access to the DI container by consumers of $app * * @return ContainerInterface */public function getContainer(){        return $this->container;}
getContainer()方法返回Container类对象,然后
// 在容器中注册Monolog日志组件$container['logger'] = function($c) {    $logger = new \Monolog\Logger('my_logger');    $file_handler = new \Monolog\Handler\StreamHandler('../logs/app.log');    $logger->pushHandler($file_handler);    return $logger;};
可看到以闭包形式传递一个对象给容器对象数组$container['logger']。且我们在之后使用时,可直接以属性方式调用该logger对象。这步是如何完成的呢?
$this->logger->addInfo("发生了一件有趣的事,并把它记录了下来”);
这和\ArrayAccess接口相关,并非实现了\ArrayAccess接口的类,只要给他的实例对象传入$test['a'] = 'hello',$test->a属性就可以获取到hello的,如果你要实现的话,会很遗憾的发现Undefined property: Test::$a…,而且即使你这样定义
Class Test implements ArrayAccess { ... }$test = new Test;$test['a'] = 'hello’;echo $test['a’];
若没做任何处理,也不会有任何输出的。
这是由于我们的类实现了\ArrayAccess接口之后还要实现它的一些方法。让我们查看容器具体是如何实现的吧。


实现\ArrayAccess必须实现这四个方法:
offsetExists($offset) //判断是否存在偏移位置
offsetGet($offset) //获取偏移量
offsetSet($offset, $value) //设置偏移量
offsetUnset($offset) //去除偏移量


之前我们已经了解到,它在处理一些配置项时,使用$this->values数组存储键值对,使用$this->keys数组存储判断值,记录该id是否已被values存储。

    /**     * 设置参数或闭包对象     * Sets a parameter or an object.     *     * Objects must be defined as Closures.     *     * Allowing any PHP callable leads to difficult to debug problems     * as function names (strings) are callable (creating a function with     * the same name as an existing parameter would break your container).     *     * @param string $id    The unique identifier for the parameter or object     * @param mixed  $value The value of the parameter or a closure to define an object     *     * @throws FrozenServiceException Prevent override of a frozen service     */    public function offsetSet($id, $value)    {        if (isset($this->frozen[$id])) {            throw new FrozenServiceException($id);        }        // 将赋值给values数组,并用keys数组记录        $this->values[$id] = $value;        $this->keys[$id] = true;    }
offsetGet是比较重要的函数,学习之

    /**     * 得到一个参数或者对象     * Gets a parameter or an object.     * $id是一个唯一标识符     * @param string $id The unique identifier for the parameter or object     * 返回值为参数或对象     * @return mixed The value of the parameter or an object     *     * @throws UnknownIdentifierException If the identifier is not defined     */    public function offsetGet($id)    {        // 不存在则抛出异常        if (!isset($this->keys[$id])) {            throw new UnknownIdentifierException($id);        }        // raw数组尚不知何时赋值,但!is_object($this->values[$id])可知,若非对象则直接返回值。        if (            isset($this->raw[$id])            || !is_object($this->values[$id])            || isset($this->protected[$this->values[$id]])            || !method_exists($this->values[$id], '__invoke')        ) {            return $this->values[$id];        }        // factories也不知有何作用        if (isset($this->factories[$this->values[$id]])) {            return $this->values[$id]($this);        }        // 下面这几句解释了之前的疑惑,若值为闭包则将执行后的值赋给$val        // raw数组存储原生闭包函数,并在获取过程中冻结该$id,即fronzen数组存储冻结的id        // 最后返回闭包执行结果        $raw = $this->values[$id];        $val = $this->values[$id] = $raw($this);        $this->raw[$id] = $raw;        $this->frozen[$id] = true;        return $val;    }

由此知,在第一次执行闭包后,会将执行结果存储在raw数组中,下次就无需再次执行,直接根据id调用raw[]中的值。

   /**     * 检查参数或对象是否设置     * Checks if a parameter or an object is set.     *     * @param string $id The unique identifier for the parameter or object     *     * @return bool     */    public function offsetExists($id)    {        return isset($this->keys[$id]);    }    /**     * 去除参数或对象     * Unsets a parameter or an object.     *     * @param string $id The unique identifier for the parameter or object     */    public function offsetUnset($id)    {        if (isset($this->keys[$id])) {            if (is_object($this->values[$id])) {                unset($this->factories[$this->values[$id]], $this->protected[$this->values[$id]]);            }            unset($this->values[$id], $this->frozen[$id], $this->raw[$id], $this->keys[$id]);        }    }