The use of reflection in laravel source code analysis

Time:2021-3-7

The use of reflection in laravel source code analysis

preface

PHP reflection class is opposite to instantiation object. Instantiation is to call methods and members in encapsulation class, while reflection class is to unpack all methods and member variables in class, including private methods, etc. Just like “unwrapping”, we can call any keyword modified method or member. Of course, it is not recommended to use it in normal business. The concept of encapsulation has been abandoned in comparison reflection class.

This chapter explains the use of reflection class and laravel’s use of reflection.

reflex

Reflection class is an internal class in PHP, which can be used without loading. You can instantiate it ReflectionClass Class to use it.

method

Here are the common methods of PHP reflection class

Method name notes
ReflectionClass::getConstant Gets a defined constant
ReflectionClass::getConstants Gets a set of constants
ReflectionClass::getConstructor Gets the constructor of the class
ReflectionClass::getDefaultProperties Get default properties
ReflectionClass::getDocComment Get document comments
ReflectionClass::getEndLine Gets the number of rows in the last row
ReflectionClass::getFileName Gets the file name of the definition class
ReflectionClass::getInterfaceNames Gets the name of the interface
ReflectionClass::getMethods Gets an array of methods
ReflectionClass::getModifiers Gets the modifier of the class
ReflectionClass::getName Get class name
ReflectionClass::getNamespaceName Gets the name of the namespace
ReflectionClass::getParentClass Get parent class

All the methods, properties, inherited parent classes and implemented interfaces of a class can be queried.
For detailed documents, please refer to the official website:http://php.net/manual/zh/clas…

Chestnut

<?php
    namespace A\B;
    
    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());
?>

Output results

bool(false)
string(8) "stdClass"
string(0) ""
string(8) "stdClass"

bool(true)
string(7) "A\B\Foo"
string(3) "A\B"
string(3) "Foo"

Laravel

Laravel uses reflection classes to implement service container loading. Now let’s turn on “stripping” mode

Import file

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(Illuminate\Contracts\Http\Kernel::class);

$response = $kernel->handle(
    $request = Illuminate\Http\Request::capture()
);

$response->send();

$kernel->terminate($request, $response);

The make method is called on the next line of the reference statement. As you all know, the make method is used to parse classes. The implementation of all make methods must be in the referenced file.

bootstrap\app.php

$app = new Illuminate\Foundation\Application(
    realpath(__DIR__.'/../')
);

Laravel starts loading its core classes, and all implementations start from Illuminate\Foundation\Application Here we go.

Illuminate\Foundation\Application

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);
}

In the core class, you can accurately find the existence of the make method, which loads the service provider and then calls the parent class’s method make, as you know, as an independent module, the service container is absolutely not written in the core class. Those who know something about design patterns are very clear.

Illuminate\Container\Container

with $api = $this->app->make('HelpSpot\API',['id'=>1]); For example

//The real make method directly calls resolve to continue to implement the function of make
// $abstract = 'HelpSpot\API'
public function make($abstract, array $parameters = [])
{
    // $abstract = 'HelpSpot\API'
    return $this->resolve($abstract, $parameters);
}

...

protected function resolve($abstract, $parameters = [])
{
    ...
    //Judge whether it can be reflected reasonably
    // $abstract = 'HelpSpot\API'
    if ($this->isBuildable($concrete, $abstract)) {
        //Instantiate a specific instance (it's not instantiated, but "dissected" by reflection)
        $object = $this->build($concrete);
    } else {
        $object = $this->make($concrete);
    }
    ...
}

public function build($concrete)
{
        // $concrete = 'HelpSpot\API'
        if ($concrete instanceof Closure) {
            return $concrete($this, $this->getLastParameterOverride());
        }
        //Instantiate reflection class
        $reflector = new ReflectionClass($concrete);

        //Check if the class is instantiatable
        if (! $reflector->isInstantiable()) {
            return $this->notInstantiable($concrete);
        }

        $this->buildStack[] = $concrete;

        //Gets the constructor of the class
        $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);
           
        //Create a new class instance from the given parameters.
        return $reflector->newInstanceArgs($instances);
}

It can be seen that a service container is loaded successfully.

thank

Thank you for reading here, source code analysis of this article depends on personal understanding. If there is any discrepancy, please make bricks.

I hope this article can help you. thank you