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
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.
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.
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.