I'm currently organising the third edition Full Stack Europe. It's a conference in Antwerp, Belgium in October for developers who want to learn across the stack. We have a great line up with lots of familiar faces! Register your ticket now!

The mixin PHP DocBlock

Original – by Freek Van der Herten – 5 minute read

When using PHP, you've probably used DocBlocks. They can be used to add additional information that can't be inferred by looking at the source code alone. DocBlocks can be used by IDEs, like PhpStorm, to improve autocomplete suggestions.

In this blogpost, I'd like to highlight a not so well known DocBlock: mixin.

A first example of a DocBlock

Before looking at a mixin DocBlock, let's first get a feel of how DocBlocks can make autocompletion better.

/**
 * @return \App\Models\Post[]
 */
public function allPosts(): array
{
    // ...
}

With regular type hints, you can only specify that the function returns an array. The DocBlock to signals that the function returns an array of \App\Models\Post objects.

When you loop through the value returned by allPosts an IDE knows which kind of object is inside the array and can provide autocompletion for that object.

A theoretical first example

The mixin DocBlock allows you to signal that all properties and methods of another class are available on the class where the mixin is applied upon.

Here's some contrived code to illustrate it:

class ClassA
{
    public function methodOfClassA(): string
    {
        return 'This is classA';
    }
}

/** @mixin ClassA */
class ClassB
{
    public function methodOfClassB(): string
    {
        return 'This is classB';
    }

    public function __call($name, $arguments)
    {
        (new ClassA())->$name(...$arguments);
    }
}

If you call a method a method on classB that isn't available, it will defer to calling classA. The intention here is to make every method that is available on classA part of classB as well.

Thanks to that mixin DocBlock, an IDE knows that intention as well and will also suggest functions of classA when calling methods on classB

Screenshot

A practical example

Let's take a look at some practical example of how this can be used.

laravel-medialibrary is a package that can be used to associate Eloquent models with media. One of its features is the ability to generate conversions whenever an image is being associated with a model.

A conversion can be defined on a model like this.

public function registerMediaConversions(Media $media = null)
{
    $this->addMediaConversion('thumb')
          ->width(368)
          ->height(232)
          ->sharpen(10);
}

So you first call addMediaConversion and then you can call any manipulations, like width and height, you want.

Behind the scenes, the addMediaConversion will return an instance of Spatie\MediaLibrary\Conversion\Conversion. This class is responsible for handling the registration of the image conversions. It does not directly handle image manipulations like width or height. Those are handled by Spatie\Image\Manipulations. Whenever we call a function on Conversion that doesn't exist on itself, it will defer the call to Manipulations. The deferring is done by this piece of code.

public function __call($name, $arguments)
{
    if (! method_exists($this->manipulations, $name)) {
        throw new BadMethodCallException("Manipulation `{$name}` does not exist");
    }

    $this->manipulations->$name(...$arguments);

    return $this;
}

With this code in place, it will all work, but the developer experience won't be great. The IDE will offer no autocompletion when typing $this->addMediaConversion('thumb')->. To make autocompletion work we added this mixin at the top of the Conversion class.

/** @mixin \Spatie\Image\Manipulations */

That mixin allows the IDE to add autocompletion, so developers can just pick the image manipulation method they want.

Screenshot

Using a mixin DocBlock to autocomplete Laravel API resources

In the previous example the mixin DocBlock was used within package code, but you can use it inside projects too. At Spatie, they are often using in Laravel's API resources.

An API resource is a class that maps the attributes of an model to the properties on an API response. Here's an example of such a resource.

namespace App\Http\Resources;

use Illuminate\Http\Resources\Json\JsonResource;

class UserResource extends JsonResource
{
    public function toArray($request)
    {
        return [
            'id' => $this->id,
            'name' => $this->name,
            'email' => $this->email,
            'avatar_url' => $this->getFirstMediaUrl('avatar'),
        ];
    }
}

In an API resource in Laravel, calling a property on $this will get the property of the underlying model. But as it stands, typing $this-> will not offer autocompletion of the model attributes.

We must perform two steps to get autocompletions. First, we must let our IDE know which attributes there are on the model. We can use the excellent barryvdh/laravel-ide-helper for this. With the package installed, executing php artisan ide-helper:models will generate a file that lets your IDE know which properties there are available on a model.

The second and final step to get autocompletion working on API resource is adding a mixin DocBlock on it.

/** @mixin \App\User */
class UserResource extends JsonResource

With this in place, autocompletion will work.

Screenshot

Closing thoughts

A mixin DocBlock can be used to hint that methods from another class are available on the class where the DocBlock is applied on.

There is no formal standard of which DocBlocks are available. The de facto standard is this list on the documentation of phpDocumentor. You'll notice that the mixin DocBlock isn't there. So be aware that this DocBlock probably doesn't work in any IDE. In PhpStorm, it works perfectly.

Stay up to date with all things Laravel, PHP, and JavaScript.

You can follow me on these platforms:

On all these platforms, regularly share programming tips, and what I myself have learned in ongoing projects.

Every month I send out a newsletter containing lots of interesting stuff for the modern PHP developer.

Expect quick tips & tricks, interesting tutorials, opinions and packages. Because I work with Laravel every day there is an emphasis on that framework.

Rest assured that I will only use your email address to send you the newsletter and will not use it for any other purposes.

Comments

What are your thoughts on "The mixin PHP DocBlock"?

Comments powered by Laravel Comments
Want to join the conversation? Log in or create an account to post a comment.

Webmentions

Adam Lambert liked on 17th November 2020
Michael Smith liked on 17th November 2020
Khai Rahman retweeted on 17th November 2020
Khai Rahman liked on 17th November 2020
Vaggelis Yfantis liked on 17th November 2020
Qrczak liked on 16th November 2020
Jorge González liked on 16th November 2020
Max Eckel retweeted on 16th November 2020
Chris liked on 16th November 2020
Max Eckel liked on 16th November 2020
Adib Hanna retweeted on 16th November 2020
PHP Synopsis retweeted on 16th November 2020
ダビッド トレス retweeted on 16th November 2020
ダビッド トレス liked on 16th November 2020
Mike liked on 16th November 2020
Gianluca Bine liked on 16th November 2020
Tauseef shah liked on 16th November 2020
Barnabas Ifebude liked on 16th November 2020
Willan Correia liked on 16th November 2020
David Smith liked on 16th November 2020
Jr 👨‍💻 liked on 16th November 2020
Jakub Hanák liked on 16th November 2020
Stéphane Reynders (Spout) liked on 16th November 2020
JOHN DOE liked on 16th November 2020
Nick Jorens liked on 16th November 2020
Lambert Traccard liked on 16th November 2020
Stéphane Reynders (Spout) retweeted on 16th November 2020
Lukáš Neuschl replied on 16th November 2020
What is this @mixin? How does it work?
Freek Van der Herten 🐘 replied on 16th November 2020
See the blogpost mentioned in the tweet
Kenshim liked on 16th November 2020
Sérgio Jardim liked on 16th November 2020
João Machado liked on 16th November 2020
Steve Thomas 🤯 liked on 16th November 2020
Yunus Emre Deligöz liked on 16th November 2020
Paulius Jasiulis liked on 16th November 2020
Hamza Makraz liked on 16th November 2020
Lukáš Neuschl liked on 16th November 2020
Clément Baconnier liked on 16th November 2020
Florian Voutzinos liked on 16th November 2020
Mohammad Nur liked on 16th November 2020
Mikael Jorhult liked on 16th November 2020
Tony Messias liked on 16th November 2020
Haneef Ansari 🍭 liked on 16th November 2020
Niels liked on 16th November 2020
Kai Sassnowski liked on 16th November 2020
Daniel Matthews ⚡️ liked on 16th November 2020
Grant Williams liked on 16th November 2020
Jigal Sanders liked on 16th November 2020
Jaime Sares liked on 16th November 2020
Steph Gill liked on 16th November 2020
O'cakes liked on 16th November 2020
Mark Topper liked on 16th November 2020
Yaniv Harush liked on 16th November 2020
Doug Black Jr 🦃🦃🦃🦃 liked on 16th November 2020
Bernardo liked on 16th November 2020
Elmarzougui Abdelghafour replied on 16th November 2020
Thank you for that topic 🙂🙂🙂🙂
Jakub Jo {🧑‍🚀} liked on 16th November 2020
Mike liked on 18th January 2020
Atanas Ginev liked on 17th January 2020
Roman Pronskiy liked on 15th January 2020
Njogu Amos liked on 15th January 2020
Sobhan liked on 15th January 2020
Christian liked on 15th January 2020
Pierre Rios liked on 15th January 2020
Fabian Blechschmidt retweeted on 15th January 2020
Fabian Blechschmidt liked on 15th January 2020
Bijaya Prasad Kuikel liked on 15th January 2020
Marvin liked on 15th January 2020
James Hemery liked on 14th January 2020
Salman Zafar liked on 14th January 2020
The Web Tier liked on 14th January 2020
ダビッド トレス retweeted on 14th January 2020
ダビッド トレス liked on 14th January 2020
pxgamer retweeted on 14th January 2020
Lambert Traccard liked on 14th January 2020
pxgamer liked on 14th January 2020
Elie Andraos replied on 14th January 2020
Is there any trick that makes phptorm autocomplete scopes and accessors ?
Focus Ifeanyi liked on 14th January 2020
Guus liked on 14th January 2020
Lee Overy liked on 14th January 2020
Rolf den Hartog retweeted on 14th January 2020
Mike Peters retweeted on 14th January 2020
Rezza liked on 14th January 2020
Petr Myazin liked on 14th January 2020
Tony Messias liked on 14th January 2020
Mike Peters liked on 14th January 2020
Parth Kharecha liked on 14th January 2020
José Cage liked on 14th January 2020
Choirool liked on 14th January 2020
Niels liked on 14th January 2020
Cyril de Wit liked on 14th January 2020
Niels liked on 24th December 2019
Clevon Noel 🇬🇩 liked on 24th December 2019
Grzegorz Gutkowski liked on 17th October 2019
Claudson Martins liked on 16th October 2019
Ruslan liked on 15th October 2019
Neil Carlo Faisan Sucuangco liked on 15th October 2019
Taste The Code liked on 15th October 2019
Nemanja Ivankovic liked on 15th October 2019
Grebenita Vladin T liked on 15th October 2019
Roman Pronskiy liked on 15th October 2019
Freek Van der Herten replied on 15th October 2019
Damn, busted :-)
Freek Van der Herten replied on 15th October 2019
In the first real world example, it doesn't really make sense to put image manipulations in a trait, it's in a class (and even package) of it's own. In the second real world example (API resources) you don't have control over the implementation of the framework.
Gav Chap replied on 15th October 2019
This seems like an obfuscated trait to me. Why not use traits? Or am I missing something?
Grammar Police replied on 15th October 2019
In this update, it could be better if you, @freekmurze, had said “package) of [its] own” instead. ‘It's’ means ‘it is’ or ‘it has’, but ‘its’ is possessive.
oluwajubelo loves VueJS ? liked on 14th October 2019
Eric Junker replied on 14th October 2019
If Laravel added a mixin DocBlock to the Facade classes then would PHPStorm be able to autocomplete methods? If so, might be a good PR to Laravel.
oluwajubelo loves VueJS ? retweeted on 14th October 2019
Spatie retweeted on 14th October 2019
이현석 Hyunseok Lee liked on 14th October 2019
Beater liked on 14th October 2019
Volkan Metin liked on 14th October 2019
Johan Alvarez liked on 14th October 2019
Niels liked on 14th October 2019
Gaurav Makhecha liked on 14th October 2019
Gaurav Makhecha replied on 14th October 2019
Always wondered how this works, thanks.
Daniel Puente liked on 14th October 2019
Rias Van der Veken liked on 14th October 2019
Sendoa Portuondo liked on 14th October 2019
Robert Boes liked on 14th October 2019
Jino Antony liked on 14th October 2019
Ryan Chandler liked on 14th October 2019
Ⓜ️ fuz liked on 14th October 2019
Christian Leo-Pernold liked on 14th October 2019
pxgamer liked on 14th October 2019
Muhammad Sumon Molla Selim liked on 14th October 2019
Elie Andraos liked on 14th October 2019
Elie Andraos retweeted on 14th October 2019
Clément Baconnier liked on 14th October 2019
Isern Palaus liked on 14th October 2019
. liked on 14th October 2019
David Cottila liked on 14th October 2019