Laravel自动依赖解析的背后实现——PHP映射解析(reflection api\reflection class)功能

本文来自pilishen.com----原文连接; 欢迎来和pilishen一块儿学习php&Laravel;学习群:109256050php

该篇属于《Laravel底层核心技术实战揭秘》这一课程《laravel底层核心概念解析》这一章的扩展阅读。因为要真正学好laravel底层,有些PHP相关的知识必须得了解,考虑到学员们的基础差别,为了不视频当中过于详细而连篇累牍,故将一些laravel底层实现相关的PHP知识点以文章形式呈现,供你们预习和随时查阅。laravel

以前的文章里,咱们知道laravel的IOC Container能自动解析依赖,很逆天很神奇,那么它背后的实现原理是怎么样的呢?里面有什么rocket science呢?api

其实也没啥,背后用的是PHP5开始自带的映射(reflection)功能,或者说反射功能,又常常称做是reflection api,它能反向地解析提交给它的class、method、extension等,基于这些信息,你能够分析出一个class的类型,须要哪些依赖,有哪些属性,父类子类状况等等,而后去相应地构建实例,就能够实现laravel的自动依赖解析功能了。bash

这里呢,咱们先不看laravel自动依赖解析的具体代码,咱们先来看看这个PHP的reflection api是什么鬼,尤为是其中的ReflectionClass,也便是专门用来反向解析class的。ide

class Foo
{
    public $name = 'pilishen';
    public $project = 'laravel';
    protected $bar;

    //Constructor
    public function __construct(Bar $bar)
    {
        $this->bar = $bar;
    }
   
    public function name()
    {
        echo $this->name."\n";
    }

    public function project()
    {
        echo $this->project."\n";
    }
}
复制代码

获取类名、命名空间、文件名:

$reflection = new ReflectionClass('Foo');
echo $reflection->getName();
复制代码

就能输出Foo也即这个class的name,相关的还有一个很明显的getShortName().post

若是你想获取该class所在的文件路径及名称,那么可使用getFileName()方法,好比个人显示:学习

string '/home/vagrant/Code/php-test/index.php' (length=37)
复制代码

固然,获取命名空间(namespace)就是getNamespaceName()ui

获取各种属性或参数:

var_dump($reflection->getDefaultProperties());
复制代码

就能获取其默认属性及值:this

array (size=3)
  'name' => string 'pilishen' (length=8)
  'project' => string 'laravel' (length=7)
  'bar' => null
复制代码

可能你会想到get_class_vars或者get_object_vars,假设这个时候咱们只想获取其protected属性怎么办呢?spa

$props = $reflection->getProperties(ReflectionProperty::IS_PROTECTED);
var_dump($props);
复制代码

这个时候显示:

array (size=1)
  0 => 
    object(ReflectionProperty)[2]
      public 'name' => string 'bar' (length=3)
      public 'class' => string 'Foo' (length=3)
复制代码

也便可以经过在getProperties()中传递filter参数来筛选要获取的属性,固然实际当中,你能够经过下面的方式来分别获取每一个属性的name:

foreach ($props as $prop) {
    print $prop->getName() . "\n";
}
复制代码

属性相关的其余方法:

getProperty() :获取某一个特定属性,好比 $class->getProperty('name');

getStaticProperties() :获取全部的静态属性

getStaticPropertyValue() :获取特定的静态属性的value

`setStaticPropertyValue()` :将某个已有的静态属性值设为新的值,注意必须是已有的,你不能经过它来添加新的静态属性

`hasProperty()` :查看某个特定的属性是否存在

`hasConstant()` :查看某个特定的常量(const)是否存在
复制代码

获取constructor信息:

说白了一旦获取到了constructor,每每也就能知道这个class的依赖有哪些了,执行:

var_dump($reflection->getConstructor());
复制代码

就能够看到:

object(ReflectionMethod)[2]
  public 'name' => string '__construct' (length=11)
  public 'class' => string 'Foo' (length=3)
复制代码

若是不存在constructor就会返回null,因此实际当中能够经过is_null()来作进一步判断。接下来执行:

$constructor = $reflection->getConstructor();
var_dump($constructor->getParameters());
复制代码

就会以array的形式返回constructor里的具体信息,每一条都是一个object:

array (size=1)
  0 => 
    object(ReflectionParameter)[3]
      public 'name' => string 'bar' (length=3)
复制代码

而后咱们就能够经过遍历的形式获取每个具体的parameter,在每一个parameter上去获取它相应的类型声明(type declaration)

$constructor = $reflection->getConstructor();
$parameters = $constructor->getParameters();
foreach ($parameters as $parameter) {
	var_dump($parameter->getClass());
}
复制代码

就能够看到:

object(ReflectionClass)[4]
  public 'name' => string 'Bar' (length=3)
复制代码

若是不存在class,那么返回的是null,说明传的只是一个普通参数,没有进行类声明,就能够进行其余相应操做,好比能够调用isDefaultValueAvailable()来判断这个参数有没有默认值,而后经过getDefaultValue()来获取其默认值。而返回的$class = $parameter->getClass(),能够进一步经过$class->name获取其class名称,而后就能够相应地去构建依赖实例了。

跟自动构建实例相关的其余方法:

isInstantiable() : 判断一个Class或者传参可否被实例化,好比interface和abstract class就不能被实例化,这个通常用在进行反向解析最开始的地方,好比若是不能实例化,也就不必去获取其constructor相关信息了;

newInstanceArgs() : 基于你传递的参数来建立一个新的实例,这里传进去的参数,也就是constructor里须要传进去的参数,若是是相应的依赖,你须要传递相应依赖的实例,接收的是array的形式;

知道了以上的方法,你就能够自行尝试反向解析某一个class,而后分析出其从属依赖,而后返回一个自动构建依赖的class实例,试试吧~ :stuck_out_tongue_winking_eye:

固然,这期间你能够看看laravel Container class的相关源码,相信理解起来就不会太难,在接下来《Laravel底层核心技术实战揭秘》里,咱们将在视频里带领你们一块儿写一个本身的自动依赖解析功能,而后带你们一块儿看看laravel是怎么实现的,一块儿揭开laravel IOC Container的魔力所在~ :muscle:

相关文章
相关标签/搜索