Photo by Sajad Nori on Unsplash

How does Reflection in Laravel work?

#[Pragmatic(Kiwi)]
2 min readFeb 13, 2024

--

In PHP, reflection is the ability of the program to inspect itself.

It can get information about its properties, methods, types, objects, etc.

Using this, Laravel can inject the dependencies of your classes through the Service Container.

<?php 

class InvoiceController {
public function __construct(private Invoice $invoice) {

}

public function __invoke() {
. . .

$this->invoice->validate();

. . .
}
}

Laravel knows to inject an instance of an Invoice. It inspects the type hinted argument and resolves the dependency on its own.

All of this looks like magic and may make our lives as developers better, but it’s not magic, and we’ll dive into it right now.

The Laravel implementation

You can take a look at the flow before we dive into the code:

Container build flowchart

Inside the class Illuminate\Container\Container.php we can see the implementation in plain sight. The part that is most important to us is:

try {
$reflector = new ReflectionClass($concrete);
} catch (ReflectionException $e) {
throw new BindingResolutionException("Target class [$concrete] does not exist.", 0, $e);
}

If the code has passed and it resolved to a class, next we have to check if the class is instantiable, or just an Interface or Abstract Class:

if(!$reflector->isInstantiable() {
return $this->notInstantiable($concrete);
}

We simply stop the execution there and throw a BindingResolutionException.

You might find this exception familiar if you are doing Domain Driven Design (DDD) and you forget to add your Service Provider inside your app config.

As the next step in the execution, it retrieves the class’s constructor:

$constructor = $reflector->getConstructor();

From this point on there are two things that can happen.

In the first case, the constructor has no parameters — we return the concrete class.

If there are parameters, we retrieve all of them then we use the reflection instance to inject all of them.

try {
$instances = $this->resolveDependencies($dependencies);
} catch (BindingResolutionException $e) {
array_pop($this->buildStack);

throw $e;
}

Now that we have the concrete implementation and all its dependencies, we can finally return the concrete implementation:

return $reflector->newInstanceArgs($instances);

Closing notes

As you can see, the magic behind Laravel is not magic. It is plain PHP used in creative or intended ways.

The reflection API can be used for more than just this. In future articles, I’ll explore how we can use it in our Unit Tests to assert private class parameters, as this way an issue I stumbled upon in the past.

--

--