Laravel自动依赖解析的实现,其实是PHP映射解析

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


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


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

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也即这个classname,相关的还有一个很明显的getShortName().

如果你想获取该class所在的文件路径及名称,那么可以使用getFileName()方法,比如我的显示:

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

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



获取各类属性或参数:

var_dump($reflection->getDefaultProperties());

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

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

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

$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或者传参能否被实例化,比如interfaceabstract class就不能被实例化,这个一般用在进行反向解析最开始的地方,比如如果不能实例化,也就没必要去获取其constructor相关信息了;

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

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

本作品采用《CC 协议》,转载必须注明作者和本文链接
程序员的福利:免费获取 JetBrains 全家桶激活码 推荐:【点击这里获取 。。。激活码适用:AppCode, CLion, DataGrip, DataSpell, dotCover, dotMemory, dotTrace, goland, IntelliJ IDEA Ultimate, phpstorm, pycharm, ReSharper
Laravel00
《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!
资深程序猿 @ XX科技
文章
62
粉丝
37
喜欢
249
收藏
571
排名:283
访问:3.3 万
私信
所有博文
社区赞助商