DEV Community

Freek Van der Herten
Freek Van der Herten

Posted on • Originally published at freek.dev on

★ The mixin PHP DocBlock

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.

Top comments (0)