Code clips

容器和注入

Slim中依赖容器的注入主要有如下几种操作方式。

简单注入

<?php
$app = new \Slim\Slim();
$app->foo = 'bar';

这种注入适合对简单对象的操作,如number/string/array等基本数据结构。

其实现首先使用Slim.php中的魔术方法__set,然后调用了依赖容器Helper/Set.php中的set方法,将数据存储到容器的data属性中。

资源定位器

如下的代码将Slim作为了一个资源的提供者,将资源构造的方法以闭包的形式注入到Slim的依赖容器中,然后以KV方式通过Slim__get方法(依赖容器Setget方法)进行获取。

注入的闭包在被请求时,会被调用并返回闭包的返回值。

<?php
$app = new \Slim\Slim();

// 定义一个创建 UUID 的方法
$app->uuid = function(){
    return exec('uuidgen');
};

// 获取一个新的 UUID
$uuid_1 = $app->uuid;
$uuid_2 = $app->uuid;

// 断言两者不同
assert($uuid_1 !== $uuid_2);

这一部分的实现如下。

<?php
namespace Slim\Helper;

class Set implements \ArrayAccess, \Countable, \IteratorAggregate
{
    // ...

    /**
     * Get data value with key
     * @param  string $key     The data key
     * @param  mixed  $default The value to return if data key does not exist
     * @return mixed           The data value, or the default value
     */
    public function get($key, $default = null)
    {
        if ($this->has($key)) {
            // 魔术方法`__invoke`
            $isInvokable = is_object($this->data[$this->normalizeKey($key)]) && method_exists($this->data[$this->normalizeKey($key)], '__invoke');
            // 传入$this,也就是容器Set自身
            return $isInvokable ? $this->data[$this->normalizeKey($key)]($this) : $this->data[$this->normalizeKey($key)];
        }

        return $default;
    }

    // ...
}

单例资源

这里的单例资源指的是每次请求是一样的资源。资源定位器一栏中生成UUID的代码示例如果调用两次$app->uuid会返回不同的UUID值,单例资源就是解决这个问题的。

<?php
$app = new \Slim\Slim();

// 定义一个 stdClass
$app->container->singleton('std', function(){
    $obj = new \stdClass;
    $obj = microtime(true);
    return $obj;            
});

// 获取资源
$std_1 = $app->std;
$std_2 = $app->std;

// 断言两者是同一个实例
assert(spl_object_hash($std_1) === spl_object_hash($std_2));

它的实现是利用了PHP匿名函数中的static修饰符,实现如下。

<?php
namespace Slim\Helper;

class Set implements \ArrayAccess, \Countable, \IteratorAggregate
{
    // ...

    /**
     * Ensure a value or object will remain globally unique
     * @param  string   $key   The value or object name
     * @param  \Closure $value The closure that defines the object
     * @return mixed
     */
    public function singleton($key, $value)
    {
        $this->set($key, function ($c) use ($value) {
            // 静态修饰符,执行结果存储到这里了
            static $object;

            if (null === $object) {
                $object = $value($c);
            }

            return $object;
        });
    }

    // ...
}

匿名函数

如果作为依赖的是匿名函数,那么如何存储的是没有被调用过的原样的闭包呢?

Slim中我们可以使用依赖容器的protect()方法进行闭包的保存。

<?php
$app = new \Slim\Slim();

// 定义一个闭包
$app->myClosure = $app->container->portect(function(){});

// 返回没有调用的原始闭包
$myClosure = $app->myClosure;

其实现利用了PHP闭包中的use关键字进行状态保存。

<?php
namespace Slim\Helper;

class Set implements \ArrayAccess, \Countable, \IteratorAggregate
{
    // ...

    /**
     * Protect closure from being directly invoked
     * @param  \Closure $callable A closure to keep from being invoked and evaluated
     * @return \Closure
     */
    public function protect(\Closure $callable)
    {
        return function () use ($callable) {
            // 没被invoke过的Closure
            return $callable;
        };
    }

    // ...
}