zoukankan      html  css  js  c++  java
  • Laravel源码解析之反射的使用

    前言

    PHP的反射类与实例化对象作用相反,实例化是调用封装类中的方法、成员,而反射类则是拆封类中的所有方法、成员变量,并包括私有方法等。就如“解刨”一样,我们可以调用任何关键字修饰的方法、成员。当然在正常业务中是建议不使用,比较反射类已经摒弃了封装的概念。

    本章讲解反射类的使用及Laravel对反射的使用。

    反射

    反射类是PHP内部类,无需加载即可使用,你可以通过实例化 ReflectionClass 类去使用它。

    方法

    这里列举下PHP反射类常用的方法

    方法名 注释
    ReflectionClass::getConstant 获取定义过的一个常量
    ReflectionClass::getConstants 获取一组常量
    ReflectionClass::getConstructor 获取类的构造函数
    ReflectionClass::getDefaultProperties 获取默认属性
    ReflectionClass::getDocComment 获取文档注释
    ReflectionClass::getEndLine 获取最后一行的行数
    ReflectionClass::getFileName 获取定义类的文件名
    ReflectionClass::getInterfaceNames 获取接口(interface)名称
    ReflectionClass::getMethods 获取方法的数组
    ReflectionClass::getModifiers 获取类的修饰符
    ReflectionClass::getName 获取类名
    ReflectionClass::getNamespaceName 获取命名空间的名称
    ReflectionClass::getParentClass 获取父类

    等等等等.... 所有关于类的方法、属性及其继承的父类、实现的接口都可以查询到。
    详细文档请参考官网: http://php.net/manual/zh/clas...

    栗子

    
    <?php
        namespace AB;
        
        class Foo { }
        
        $function = new ReflectionClass('stdClass');
        
        var_dump($function->inNamespace());
        var_dump($function->getName());
        var_dump($function->getNamespaceName());
        var_dump($function->getShortName());
        
        $function = new ReflectionClass('A\B\Foo');
        
        var_dump($function->inNamespace());
        var_dump($function->getName());
        var_dump($function->getNamespaceName());
        var_dump($function->getShortName());
    ?>
    

    输出结果

    
    bool(false)
    string(8) "stdClass"
    string(0) ""
    string(8) "stdClass"
    
    bool(true)
    string(7) "ABFoo"
    string(3) "AB"
    string(3) "Foo"
    

    Laravel

    Laravel在实现服务容器加载时使用了反射类。现在我们开启“解刨”模式

    入口文件

    index.php

    
    $app = require_once __DIR__.'/../bootstrap/app.php';
    
    /*
    |--------------------------------------------------------------------------
    | Run The Application
    |--------------------------------------------------------------------------
    |
    | Once we have the application, we can handle the incoming request
    | through the kernel, and send the associated response back to
    | the client's browser allowing them to enjoy the creative
    | and wonderful application we have prepared for them.
    |
    */
    
    $kernel = $app->make(IlluminateContractsHttpKernel::class);
    
    $response = $kernel->handle(
        $request = IlluminateHttpRequest::capture()
    );
    
    $response->send();
    
    $kernel->terminate($request, $response);
    

    是引用语句发生的下一行调用了make方法。各位很清楚,make方法用于解析类,所有make方法的实现一定是在引用的文件内。

    bootstrapapp.php

    
    $app = new IlluminateFoundationApplication(
        realpath(__DIR__.'/../')
    );
    

    laravel开始加载它的核心类,所有的实现从 IlluminateFoundationApplication 开始。

    IlluminateFoundationApplication

    
    public function make($abstract, array $parameters = [])
    {
            $abstract = $this->getAlias($abstract);
    
            if (isset($this->deferredServices[$abstract]) && ! isset($this->instances[$abstract])) {
                $this->loadDeferredProvider($abstract);
            }
    
            return parent::make($abstract, $parameters);
    }
    
    

    在核心类中你可能准确的查找到make方法的存在,它加载了服务提供者随后调用了父类的方法make,要知道作为独立的模块 “服务容器”是绝对不能写在核心类的。懂点设计模式的都很清楚。

    IlluminateContainerContainer

    $api = $this->app->make('HelpSpotAPI',['id'=>1]); 为例来讲解

    
    // 真正的make方法,它直接调用了resolve继续去实现make的功能
    // $abstract = 'HelpSpotAPI'
    public function make($abstract, array $parameters = [])
    {
        // $abstract = 'HelpSpotAPI'
        return $this->resolve($abstract, $parameters);
    }
    
    ...
    
    protected function resolve($abstract, $parameters = [])
    {
        ...
        // 判断是否可以合理反射
        // $abstract = 'HelpSpotAPI'
        if ($this->isBuildable($concrete, $abstract)) {
            // 实例化具体实例 (实际并不是实例化,而是通过反射“解刨”了)
            $object = $this->build($concrete);
        } else {
            $object = $this->make($concrete);
        }
        ...
    }
    
    public function build($concrete)
    {
            // $concrete = 'HelpSpotAPI'
            if ($concrete instanceof Closure) {
                return $concrete($this, $this->getLastParameterOverride());
            }
            // 实例化反射类
            $reflector = new ReflectionClass($concrete);
    
            // 检查类是否可实例化
            if (! $reflector->isInstantiable()) {
                return $this->notInstantiable($concrete);
            }
    
            $this->buildStack[] = $concrete;
    
            // 获取类的构造函数
            $constructor = $reflector->getConstructor();
            
            if (is_null($constructor)) {
                array_pop($this->buildStack);
    
                return new $concrete;
            }
    
            $dependencies = $constructor->getParameters();
    
            $instances = $this->resolveDependencies(
                $dependencies
            );
    
            array_pop($this->buildStack);
               
            //  从给出的参数创建一个新的类实例。
            return $reflector->newInstanceArgs($instances);
    }
    

    可见一个服务容器就加载成功了。

    致谢

    感谢你看到这里,本篇文章源码解析靠个人理解。如有出入请拍砖。

    希望本篇文章可以帮到你。谢谢

    原文地址:https://segmentfault.com/a/1190000016480587

  • 相关阅读:
    cinder支持nfs快照
    浏览器输入URL到返回页面的全过程
    按需制作最小的本地yum源
    创建可执行bin安装文件
    RPCVersionCapError: Requested message version, 4.17 is incompatible. It needs to be equal in major version and less than or equal in minor version as the specified version cap 4.11.
    惠普IPMI登陆不上
    Linux进程状态——top,ps中看到进程状态D,S,Z的含义
    openstack-neutron基本的网络类型以及分析
    openstack octavia的实现与分析(二)原理,架构与基本流程
    flask上下文流程图
  • 原文地址:https://www.cnblogs.com/lalalagq/p/9970044.html
Copyright © 2011-2022 走看看