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

“一步一步走,心急吃不来热豆腐呦”    —郭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]);
        }
    }






你可能感兴趣的:(slim)