Heim  >  Artikel  >  Backend-Entwicklung  >  yii2 Essay (7) Abhängigkeitsinjektion – (3) Abhängigkeitsinjektion von yii2

yii2 Essay (7) Abhängigkeitsinjektion – (3) Abhängigkeitsinjektion von yii2

黄舟
黄舟Original
2017-01-17 10:36:511098Durchsuche


Der Kerncode der yii2-Abhängigkeitsinjektion befindet sich in yiidi. Unter diesem Paket (Ordner) befinden sich 3 Dateien, nämlich Container.php (Container), Instance.php (Instanz), ServiceLocator (Service Locator). Lassen Sie uns nun die ersten beiden besprechen. Der Dienst-Locator kann die Registrierung eines Dienstes verstehen. Dies hat keinen Einfluss auf unsere Diskussion der Abhängigkeitsinjektion.

Beginnen wir mit dem Code, um zu erklären, wie yii2 die Abhängigkeitsinjektion verwendet.

// yii\base\application
//这个是yii2的依赖注入使用入口,参数的解释请参考源码,这里不多解释
public static function createObject($type, array $params = [])
{
    if (is_string($type)) {//type 是字符串的话,它就把type当做一个对象的“原材料”,直接把它传给容器并通过容器得到想要的对象。
        return static::$container->get($type, $params);
    } elseif (is_array($type) && isset($type['class'])) {
    //type 是数组,并且有class的键,经过简单处理后,得到对象的“原材料”,然后把得到的“原材料”传给容器并通过容器得到想要的对象。
        $class = $type['class'];
        unset($type['class']);
        return static::$container->get($class, $params, $type);
    } elseif (is_callable($type, true)) {//如果type是可调用的结构,就直接调用
        return call_user_func($type, $params);
    } elseif (is_array($type)) {//如果type是array,并且没有'class'的键值,那么就抛出异常
        throw new InvalidConfigException('Object configuration must be an array containing a "class" element.');
    } else {//其他情况,均抛出另一个异常,说type不支持的配置类型
        throw new InvalidConfigException("Unsupported configuration type: " . gettype($type));
    }
}

Durch das Lesen des obigen Codes gibt Yii::createObject() qualifizierte „Rohstoffe“ an den „Container ($container)“ weiter, um das Zielobjekt zu generieren. Dann ist der Container unsere „Abhängigkeit“. Inject“ dort, wo das Produktionsobjekt produziert wird. Wann wurde also $container eingeführt (beachten Sie, dass hier static::$container und nicht self::$container verwendet wird)? Erinnern Sie sich noch an die Aussage beim Import des yii-Frameworks auf der Homepage?

//导入yii框架
require(__DIR__ . '/../vendor/yiisoft/yii2/Yii.php');

Der Code lautet wie folgt

//引入基本的yii框架
require(__DIR__ . '/BaseYii.php');
//只是做了继承,这里给我们留了二次开发的余地,虽然很少能用到
class Yii extends \yii\BaseYii
{
}
//设置自动加载
spl_autoload_register(['Yii', 'autoload'], true, true);
//注册 classMap
Yii::$classMap = require(__DIR__ . '/classes.php');
//注册容器
Yii::$container = new yii\di\Container();

Du hast es richtig gelesen! Das ist der letzte Satz: yii2 nutzt die Implementierung von yiidiContainer für den eigenen Gebrauch. Lassen Sie uns als Nächstes besprechen, wie Container implementiert werden.

Nach der obigen static::$container->get()-Methode müssen wir vor der Erläuterung der get-Methode zunächst einige Eigenschaften des Containers verstehen, die dabei helfen, die Implementierung von get

$_singletons; // 单例数组,它的键值是类的名字,如果生成的对象是单例,则把他保存到这个数组里,值为null的话,表示它还没有被实例化
$_definitions;// 定义数组,它的键值是类的名字,值是生成这个类所需的“原材料”,在set 或 setSingleton的时候写入
$_params; // 参数,它的键值是类的名字,值是生成这个类所需的额外的“原材料”,在set 或 setSingleton的时候写入
$_reflections; //反射,它的键值是类的名字,值是要生成的对象的反射句柄,在生成对象的时候写入
$_dependencies;//依赖,它的键值是类的名字,值是要生成对象前的一些必备“原材料”,在生成对象的时候,通过反射函数得到。
ok, wenn Sie vorsichtig genug sind und die oben genannten Attribute verstehen, werden Sie wahrscheinlich ein allgemeines Verständnis des yii2-Containers haben. Beginnen wir mit get.

public function get($class, $params = [], $config = [])
{
    if (isset($this->_singletons[$class])) {//查看将要生成的对象是否在单例里,如果是,则直接返回
        // singleton
        return $this->_singletons[$class];
    } elseif (!isset($this->_definitions[$class])) {//如果没有要生成类的定义,则直接生成,yii2自身大部分走的是这部分,并没有事先在容器里注册什么,
    那么配置文件是在哪里注册呢?还记的文章最开始的时候的"服务定位器"么?我们在服务定位器里讲看到这些。
        return $this->build($class, $params, $config);
    }
    //如果已经定义了这个类,则取出这个类的定义
    $definition = $this->_definitions[$class];

    if (is_callable($definition, true)) {//如果定义是可调用的结构
        //先整合一下参数,和$_params里是否有这个类的参数,如果有则和传入的参数以传入覆盖定义的方式整和在一起
        //然后再检查整合后的参数是否符合依赖,就是说是否有必填的参数,如果有直接抛出异常,否则返回参数。检查依赖的时候,需要判断是否为实例(Instance),如果是,
        则要实现实例。注意:这里出现了Instance。
        $params = $this->resolveDependencies($this->mergeParams($class, $params));
        //把参数专递给可调用结果,返回结果
        $object = call_user_func($definition, $this, $params, $config);
    } elseif (is_array($definition)) {//如果定义是一个数组
        //把代表要生成的class取出
        $concrete = $definition['class'];
        //注销这个键值
        unset($definition['class']);
        //把定义 和 配置整合成新的定义
        $config = array_merge($definition, $config);
        //整合参数
        $params = $this->mergeParams($class, $params);
        //如果传入的$class 和 定义里的class完全一样,则直接生成,build第一个参数确保为真实的类名,而传入的$type可能是别名
        if ($concrete === $class) {
            $object = $this->build($class, $params, $config);
        } else {//如果是别名,则回调自己,生成对象,因为这时的类也有可能是别名
            $object = $this->get($concrete, $params, $config);
        }
    } elseif (is_object($definition)) {//如果定义是一个对象,则代表这个类是个单例,保存到单例里,并返回这个单例,这里要自己动脑想一下,
    为什么是个对象就是单例?只可意会不可言传,主要是我也组织不好语言怎么解释它。
        return $this->_singletons[$class] = $definition;
    } else {//什么都不是则抛出异常
        throw new InvalidConfigException("Unexpected object definition type: " . gettype($definition));
    }
    //判断这个类的名字是否在单例里,如果在,则把生成的对象放到单例里
    if (array_key_exists($class, $this->_singletons)) {
        // singleton
        $this->_singletons[$class] = $object;
    }
    //返回生成的对象
    return $object;
}
Nachdem wir diesen Punkt untersucht hatten, stellten wir fest, dass die Get-Funktion nur ein „Eintrag“ ist, die Hauptfunktion befindet sich im Build

//创建对象
protected function build($class, $params, $config)
{
    //通过类名得到反射句柄,和依赖(依赖就是所需参数)
    //所以前面提到,传输buile的第一个参数必须为有效的“类名”否则,会直接报错
    list ($reflection, $dependencies) = $this->getDependencies($class);
    //把依赖和参数配置,因为依赖可能有默认参数,这里覆盖默认参数
    foreach ($params as $index => $param) {
        $dependencies[$index] = $param;
    }
    //确保依赖没问题,所有原材料是否都ok了,否则抛出异常
    $dependencies = $this->resolveDependencies($dependencies, $reflection);
    if (empty($config)) {//如果config为空,则返回目标对象
        return $reflection->newInstanceArgs($dependencies);
    }
    
    if (!empty($dependencies) && $reflection->implementsInterface('yii\base\Configurable')) 
    {//如果目标对象是 Configurable的接口
        // set $config as the last parameter (existing one will be overwritten)
        $dependencies[count($dependencies) - 1] = $config;
        return $reflection->newInstanceArgs($dependencies);
    } else {//其他的情况下
        $object = $reflection->newInstanceArgs($dependencies);
        foreach ($config as $name => $value) {
            $object->$name = $value;
        }
        return $object;
    }
}
Okay, der Build ist hier drüben , wie folgt Schauen wir uns an, wie der Container Reflektionshandles und Abhängigkeiten erhält

protected function getDependencies($class)
{
    if (isset($this->_reflections[$class])) {//是否已经解析过目标对象了
        return [$this->_reflections[$class], $this->_dependencies[$class]];
    }
   
    $dependencies = [];//初始化依赖数组
    $reflection = new ReflectionClass($class);//得到目标对象的反射,请参考php手册

    $constructor = $reflection->getConstructor();//得到目标对象的构造函数
    if ($constructor !== null) {//如果目标对象有构造函数,则说明他有依赖
        //解析所有的参数,注意得到参数的顺序是从左到右的,确保依赖时也是按照这个顺序执行
        foreach ($constructor->getParameters() as $param) {
            if ($param->isDefaultValueAvailable()) {//如果参数的默认值可用
                $dependencies[] = $param->getDefaultValue();//把默认值放到依赖里
            } else {//如果是其他的
                $c = $param->getClass();//得到参数的类型,如果参数的类型不是某类,是基本类型的话,则返回null
                //如果,是基本类型,则生成null的实例,如果不是基本类型,则生成该类名的实例。
                注意:这里用到了实例(Instance)
                $dependencies[] = Instance::of($c === null ? null : $c->getName());
            }
        }
    }
    //把引用保存起来,以便下次直接使用
    $this->_reflections[$class] = $reflection;
    //把依赖存起来,以便下次直接使用
    $this->_dependencies[$class] = $dependencies;
    //返回结果
    return [$reflection, $dependencies];
}
Schauen wir uns an, wie der Container Abhängigkeiten sicherstellt

protected function resolveDependencies($dependencies, $reflection = null)
{
    //拿到依赖关系
    foreach ($dependencies as $index => $dependency) {
        //如果依赖是一个实例,因为经过处理的依赖,都是Instance的对象
        if ($dependency instanceof Instance) {
            if ($dependency->id !== null) {//这个实例有id,则通过这个id生成这个对象,并且代替原来的参数
                $dependencies[$index] = $this->get($dependency->id);
            } elseif ($reflection !== null) {//如果反射句柄不为空,注意这个函数是protected 类型的,
            所以只有本类或者本类的衍生类可访问,但是本类里只有两个地方用到了,一个是 get 的时候,
            如果目标对象是可调用的结果(is_callable),那么$reflection===null,另外一个build的时候,
            $reflection不为空,这个时候代表目标对象有一个必须参数,但是还不是一个实例(Instance的对象),
            这个时候代表缺乏必须的“原材料”抛出异常
                //则拿到响应的必填参数名字,并且抛出异常
                $name = $reflection->getConstructor()->getParameters()[$index]->getName();
                $class = $reflection->getName();
                throw new InvalidConfigException("Missing required parameter \"$name\" when instantiating \"$class\".");
            }
        }
    }

    //确保了所有的依赖后,返回所有依赖,如果目标是is_callable($definition, true),则不会抛出异常,仅仅把Instance类型的参数实例化出来。
    return $dependencies;
}
Da wir das sehen, haben wir jetzt das Sie können verstehen, wie yii2 Container verwendet, um „Abhängigkeitsinjektion“ zu implementieren. Es stellt sich die Frage, wie die Abhängigkeit von Schließungen sichergestellt werden kann. Ich denke, das liegt daran, dass yii2 glaubt, dass die Existenz von Abschlüssen das Problem der Einschränkungen löst, es keine Abhängigkeiten gibt oder die Abhängigkeiten den Entwicklern überlassen bleiben, um sie selbst zu lösen. Wenn der Parameter des yii2-Containers außerdem ein Abschluss ist, tritt aufgrund der Abhängigkeit vom Abschluss beim Parsen der Abschlussparameter ein Fehler auf: $dependencies[]

= Instance::of($ c == = null ? null : $c->getName()); Sie erhalten eine Instanz von Closure. Wenn Sie diese Instanz später instanziieren, treten Probleme auf, wenn Sie den yii2-Container zum Implementieren des Objekts verwenden , es ist implementiert Das Objekt darf keine Abschlussparameter enthalten, es muss einen Standardwert haben oder künstlich garantieren, dass dieser Abschlussparameter unter Umgehung der automatisch generierten Anweisung übergeben wird.

Dies sind die Hauptfunktionen des ok-Containers, set, setSingleton, has, hasSingleton, klar, Sie werden auf einen Blick wissen, was sie bedeuten. Darüber hinaus werden diese Methoden grundsätzlich nicht verwendet Framework (Sie können diese Funktionen im Write-Exit verwenden und sehen, ob Ihre Seite leer ist), oder wenn Sie den Container verwenden, um selbst etwas zu generieren, können Sie die Verwendung dieser Funktionen selbst überprüfen.

Schauen wir uns abschließend an, welche Rolle die Instanz spielt

//yii\di\Instance
//很诧异吧,就是实例化一个自己,注意这个自己是 static,以后你可能需要用到这个地方
public static function of($id)
{
    return new static($id);
}
[/php]
那么这个函数的构造函数呢?
[php]
//禁止外部实例化
protected function __construct($id)
{
    //赋值id
    $this->id = $id;
}
Im Container werden diese beiden Instanzmethoden verwendet, was bedeutet, dass sich die Instanz in der Instanz befindet und nur die Abhängigkeit gewährleistet Verfügbarkeit. Darüber hinaus bietet die Instanz auch andere Funktionen, unter denen get das instanziierte Objekt der ID abruft, die der aktuellen Instanz entspricht. Darüber hinaus gibt es eine statische Funktion, um sicherzustellen

//确保 $reference 是 $type类型的,如果不是则抛出异常
//在框架中多次用到,请自行查找
//另外,如果$type==null的时候,他也可以当做依赖注入的入口,使用方法请自行查看源码,到现在你应该可以自己看懂这些代码了。
public static function ensure($reference, $type = null, $container = null)
{
    //...
}
Das Obige ist yii2 Essay (7 ) Abhängigkeitsinjektion - (3) Der Inhalt der Abhängigkeitsinjektion von yii2. Weitere verwandte Inhalte finden Sie auf der chinesischen PHP-Website (www.php.cn)!


Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn