PHP Reflection Mechanism

Posted by jason_kraft on Wed, 29 May 2019 13:44:14 +0200

PHP Reflection Mechanism

PHP reflection mechanism is supported from PHP5, and it should be less exposed to reflection when doing business development. In fact, I do not have much contact with it. Recently, I have been learning about the "elegance" of laravel, and I have come into contact with its reflective usage. I have my own views and ideas.

reflex

According to the previous routine, let's take a look at the official manual and what the official said.

Reflection

PHP 5 has a complete reflection API, adding the ability to reverse-engineer classes, interfaces, functions, methods, and extensions. In addition, the reflection API provides a way to extract document annotations from functions, classes, and methods. My understanding is that the php reflection mechanism can get the attribute methods in the class, and so can private and protected.

  • That's what the official document says. To be honest, I don't feel much.
  • The point to get is that we can peep through it all the information of a class, just like a hole in a bucket in someone else's window.
  • How should I use it, or based on what scenario? It's still hurting.

Reflection in laravel

The "elegance" of laravel's overall framework design is container, IOC, and dependency injection. Let's look at a piece of reflection code in the container:
IlluminateContainerContainer:

/**
     * Instantiate a concrete instance of the given type.
     *
     * @param  string  $concrete
     * @param  array   $parameters
     * @return mixed
     *
     * @throws \Illuminate\Contracts\Container\BindingResolutionException
     */
    public function build($concrete, array $parameters = [])
    {
        // If the concrete type is actually a Closure, we will just execute it and
        // hand back the results of the functions, which allows functions to be
        // used as resolvers for more fine-tuned resolution of these objects.
        if ($concrete instanceof Closure) {
            return $concrete($this, $parameters);
        }

        $reflector = new ReflectionClass($concrete);

        // If the type is not instantiable, the developer is attempting to resolve
        // an abstract type such as an Interface of Abstract Class and there is
        // no binding registered for the abstractions so we need to bail out.
        if (! $reflector->isInstantiable()) {
            if (! empty($this->buildStack)) {
                $previous = implode(', ', $this->buildStack);

                $message = "Target [$concrete] is not instantiable while building [$previous].";
            } else {
                $message = "Target [$concrete] is not instantiable.";
            }

            throw new BindingResolutionException($message);
        }

        $this->buildStack[] = $concrete;

        $constructor = $reflector->getConstructor();

        // If there are no constructors, that means there are no dependencies then
        // we can just resolve the instances of the objects right away, without
        // resolving any other types or dependencies out of these containers.
        if (is_null($constructor)) {
            array_pop($this->buildStack);

            return new $concrete;
        }

        $dependencies = $constructor->getParameters();

        // Once we have all the constructor's parameters we can create each of the
        // dependency instances and then use the reflection instances to make a
        // new instance of this class, injecting the created dependencies in.
        $parameters = $this->keyParametersByArgument(
            $dependencies, $parameters
        );

        $instances = $this->getDependencies(
            $dependencies, $parameters
        );

        array_pop($this->buildStack);

        return $reflector->newInstanceArgs($instances);
    }
    

It's the way to implement the binding class, the build method. Now let's analyze:

  • Parameter: $concreate string is similar to Model::class, which is not difficult to understand. The $parameters array parameter is much easier to understand.
  • Determine whether $concreate is an anonymous class (closure) or an anonymous class to execute the function.
  • Create a reflection class to map this class.
  • To determine whether this class can be instantiated, that is, to see whether the constructor is private. Whether to throw an exception.
  • The class is maintained by the array in the container member variable, and the reflection instance calls the constructor to get the return value.
  • Determine whether the return value is empty, and if it is empty, it means that no parameter dependency is required, then it is instantiated directly. Otherwise, the parameter dependency of the constructor is obtained, and the incoming parameters and dependent parameters are compared.
  • Finally, the instance is instantiated by calling newInstanceArgs, and then returned.

Epilogue

In fact, the example in the above laravel illustrates the use of reflection mechanism very well. Perhaps you may not be able to use this mechanism in your current business scenario. However, when you encounter it, remember that there are other ways to use it.

  • When you need to instantiate a class, but the class is completely closed or unknown to you, you can create reflection to map the class, through a series of probes to ultimately instantiate the class, especially in dynamic operation.
  • Based on this mechanism, we can actually play a lot of tricks. For example, it can automatically generate documents.
  • Implementing MVC architecture, using reflection automatic invocation
$class = new ReflectionClass(ucfirst($controller));
$controller = $class->newInstance();
if ($class->hasMethod($method)) {
    $method = $class->getMethod($method);
    $method->invokeArgs($controller, $arguments);
} else {
    throw new Exception("{$controller} controller method {$method} not exists!");
}
  • Implementing unit testing
    $class = new ReflectionClass($class);
    if ($class->hasMethod($method)) {
        $method = $class->getMethod($method);
        $object = $class->newInstance();
        $class = $method->invokeArgs(new $object, $params);
        var_dump($res === $assert);
    }
  • Reflection in laravel helps it solve the dependency injection problem of DI containers.

There are many interesting things waiting for you to try. How many tricks this mechanism can play depends on how you play it.

Topics: PHP Laravel less Attribute