Laravel has many undocumented features, and unfortunately, unless you're dedicating large chunks of time to digging through the source code, and tracking what's going on, these features are likely to pass you by. Fortunately, I have done, and continue to do, exactly that. So in this article, I'll be introducing you to a feature built into Laravel's service container, that makes decorating bound services a breeze.

What does decorating mean?

Decorating in the context of software development, refers to the process of adding functionality to an existing class, without modifying its code. It's the implementation of the decorator pattern, and will typically involve the creation of a new class that extends the original that it wishes to decorate, while also wrapping the original instance.

Consider the following example interface.

interface ServiceContract
{
    public function multiply(int $a, int $b): int;

    public function isPositive(int $n): bool;
}

And an example implementation.

class MyService implements ServiceContract
{
    public function multiply(int $a, int $b): int
    {
        return $a * $b;
    }

    public function isPositive(int $n): bool
    {
        return $n > 0;
    }
}

To decorate this service, we'd decorate the interface ServiceContract, not its implementation. The decorated service would look like this.

class MyServiceDecorator implements ServiceContract
{
    public function __construct(private ServiceContract $service) {}

    public function multiply(int $a, int $b): int
    {
        return $this->service->multiply($a, $b);
    }

    public function isPositive(int $n): bool
    {
        return $this->service->isPositive($n);
    }
}

The decorator implements the contract, and proxies all calls to the service that is injected when it is created. Technically speaking, this isn't strictly a decorator yet, because it's not performing any decoration. To do this, all the decorator needs to do, is do something with the values that are passed to the service, or the return values. Let's say, you want to make sure that only positive numbers are multiplied, you'd modify the multiply method to look like this.

public function multiply(int $a, int $b): int
{
    if ($this->isPositive($a) === false) {
        $a = abs($a);
    }

    if ($this->isPositive($b) === false) {
        $b = abs($b);
    }

    return $this->service->multiply($a, $b);
}

Or this.

public function multiply(int $a, int $b): int
{
    return $this->service->multiply(abs($a), abs($b));
}

Or even this.

public function multiply(int $a, int $b): int
{
    if ($this->isPositive($a) === false || $this->isPositive($b) === false) {
        throw new InvalidArgumentException('Both numbers must be positive');
    }

    return $this->service->multiply($a, $b);
}

Only the method that needed to be decorated has been changed, and beyond a simple call too $this->service for the other methods, we've avoided having to reimplement everything.

A Simplified Example

It is worth noting that this example is very short, and in reality you would probably create a whole new implementation of the service to achieve what I did, but that's because I needed a short example. A realistic example would have you staring at a lot of unnecessary code. I could go into a lot more detail about how to use the decorator pattern, but then the content explaining the related concept would be considerably longer than the main reason for the article.

Decorating Laravel Services

Service decoration in Laravel can be achieved using something similar to the above example, however, there are no restrictions or limitations on how you achieve it. As long as the decorator is a subclass of the appropriate class or interface, and implements the same methods, it will be fine.

Laravel actually provides a convenient way to do this, to ensure that the resolved instance of a service is decorated. It's done using the Application::extend method, which is inherited from the Container class, which can be seen here.

The method accepts the binding abstract that is being decorated (the class or alias used by the service), and a closure that is called to "extend" the provided service. If we were to register the example from the previous section, it would be bound in the container as ServiceContract, so we'd register the decorator like this.

public function register()
{
    $this->app->extend(ServiceContract::class, function (ServiceContract $service, $app) {
        return new ServiceDecorator($service);
    });
}

When the ServiceContract is resolved from the service container, the provided "extender" will be called, and its return value used in place of the original service.

That's all there is to it, it really is as simple as that. Though, in the spirit of full disclosure, there are a few additional things to be aware of, or at least consider.

Service Aliases

The decorator/extender functionality is alias-aware, meaning that when you register an "extender", the provided service will be replaced with its proper alias, if one exists. For example, if you were registering an "extender" for the Illuminate\Filesystem\FilesystemManager class, it would actually be registered as the filesystem alias, because that's the "main alias" used for it.

Multiple Decorators

It is entirely possible that a given service could have multiple decorators or extenders. In this situation, the "extenders" are called in the order that they are registered. This may be an issue if you're expecting the previously resolved service to be of a particular concrete implementation.

Removing Decorators

It is not currently possible to remove individual decorators from a service. Instead, you'd need to clear all registered for a particular service, using the Application::forgetExtenders method.

A Working Example

The beacon-hq/pennant-driver package, developed by Davey Shafik wraps the Laravel\Pennant\FeatureManager class and decorates the define method, which you can see here. The decorator class is registered using the extend method, which can also be seen here.

Something important to consider here is that while this shows an example of using Laravels decorator functionality, the decorator itself isn't actually a true implementation of the decorator pattern. Because the Laravel\Pennant\FeatureManager class, the one being decorated, is a known concrete whose state is immediately available, this implementation simply subclasses it, sets its state, overrides a method, and then falls back to the original code for everything else.

This is a prime example of where design patterns can be confusing, as they're more conceptual than people treat them. The best way to look at it is that this example is a decorator, not a Decorator (small 'd' vs big 'D'). That being said, it's not terribly important as it does what it needs to do, and while I'm all for clean code and structure, I'm not going to nitpick beyond clarifying with an explanation.

The Hidden Parts of Laravel

Laravels service decoration functionality is one of many hidden parts of Laravel, that are both undocumented and rarely spoken about. I'll be covering more of these topics in coming articles, but for now I hope that you find this useful, whether as solution to a problem you're having, or as a way to learn more about Laravel.

If you'd like to stay updated with my latest articles, tutorials, projects, and thoughts on everything Laravel and PHP, you can subscribe to my newsletter.

Got thoughts, questions, or just want to chat? I'm always up for a good conversation— get in touch, or find me on BlueSky, X or Discord