How does reflection in Laravel actually work?
I tried to debug it to see how Laravel uses reflection in a controller's constructor or methods to resolve their dependencies and sub-dependencies and then and give it back to us.
But I found it hard, and it's very complicated to see and to even understand 50% of. Jumping from class to class, I can't really see it. I tried a few times by debugging it with low results of understanding.
I am very impressed by this and by reflection, and the way Laravel uses it makes my heart burn—it's just beautiful. And I wish to fully understand that—the whole process—in general, and step by step.
Beginning from hitting the route to finally having, let's say, dd($x)
, where $x
is from a method argument and is a TestClass
that has another dependency of TestClass2
that should be constructed through: $x = new TestClass(new TestClass2());
I think those are beautiful mechanics and architecture, and understanding this is something I want so badly.
So again, my question is: how does reflection in Laravel actually work?
It's not about dd
guys... Let's say without dd
. Just as I said earlier - when we have this object instantiated from the class method
. It's not about dumping it, it's just about having it from method injection
by reflection
.
The dd
was only an example. It can even be die(var_dump());
and it will work
The ReflectionMethod::invoke() function is an inbuilt function in PHP which is used to invoke the specified reflected method and returns the result of the method. Syntax: public mixed ReflectionMethod::invoke ( $object, $parameter )
The reflection class is used to get information about the current state of the application. It's called reflection, because it looks at it's self, and can tell you information about the program your running, at run time.
In Laravel, dependency injection is the process of injecting class dependencies into a class through a constructor or setter method. This allows your code to look clean and run faster. Dependency injection involves the use of a Laravel service container, a container that is used to manage class dependencies.
Laravel uses PHP's reflection API for several components. Of these, the inverson-of-control (IoC) dependency injection container and controller method injection are most visible to developers.
To more clearly illustrate the use of reflection, here's a dramatically simplified version of the routine Laravel's IoC container class uses to build up an object's dependencies through constructor injection:
function build($className) { $reflector = new ReflectionClass($className); $constructor = $reflector->getConstructor(); foreach ($constructor->getParameters() as $dependency) { $instances[] = build($dependency->getClass()->name); } return $reflector->newInstanceArgs($instances); }
As we can see, the concept isn't too difficult to understand. The container uses PHP's ReflectionClass
to find the names of the classes in an object's constructor, and then loops through each of these names recursively to create instances of each object in the dependency tree. With these instances, build()
finally instantiates the original class and passes the dependencies as arguments to the constructor.
Controller method injection uses the same container functionality shown above to resolve instances of dependencies declared as method parameters, but there's a bit of extra logic needed to separate class dependencies from route parameters:
function dispatch(Route $route, Controller $controller, $methodName) { $routeParameters = $route->parametersWithoutNulls(); $method = new ReflectionMethod($controller, $methodName); foreach ($method->getParameters() as $index => $parameter) { $class = $parameter->getClass(); if ($class !== null) { $instance = build($class->name); array_splice($routeParameters, $index, 0, [ $instance ]); } } $controller->callAction($methodName, $routeParameters); }
Again, this adaptation is slimmed-down to highlight the role reflection plays and relies on our build()
function shown previously. The ControllerDispatcher
class uses the getParameters()
method of PHP's ReflectionMethod
to determine which parameters a controller method expects, and then loops through these to find parameters that represent dependencies that it can resolve from the container. Then, it splices each dependency it finds back into the array of route parameters that it passes back to the controller method defined for the route. See RouteDependencyResolverTrait
for details.
If we ignore the application bootstrapping process, this dependency injection cascade typically starts for a request when Laravel maps a request to a route, and then determines which controller to pass the request to. Laravel first resolves an instance of the controller from the container, which builds out any constructor-injected dependencies. Then, Laravel finds the appropriate controller method and resolves any more dependencies for the arguments as needed.
As shown here, Laravel uses relatively simple techniques to implement these tools using reflection. However, unlike the examples shown in this answer, the framework adds a lot of additional code to make them as robust and flexible as they are today.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With