2

I'm looking at the code of Laravel Fortify. In the service provider, you can register Action classes to perform specific jobs like this:

Fortify::createUsersUsing(CreateNewUser::class);
Fortify::updateUserProfileInformationUsing(UpdateUserProfileInformation::class);

These bind the given Action class to an interface in the service container.

The interface CreateNewUser implements is typical enough:

interface CreatesNewUsers
{
 /**
 * Validate and create a newly registered user.
 *
 * @param array $input
 * @return \Illuminate\Foundation\Auth\User
 */
 public function create(array $input);
}

But the interface UpdateUserProfileInformation implements seems strange:

/**
 * @method void update(\Illuminate\Foundation\Auth\User $user, array $input)
 */
interface UpdatesUserProfileInformation
{
 //
}

Why not define a method signature in the usual way? Something like this:

interface UpdatesUserProfileInformation
{
 public function update(\Illuminate\Foundation\Auth\User $user, array $input): void;
}

What's the purpose of using a @method annotation instead? What does it achieve?

Barmar
787k57 gold badges552 silver badges664 bronze badges
asked Aug 12 at 0:47
1

2 Answers 2

3

This is to achieve contravariance in interfaces.

Take a look at the implementation of the UpdateUserProfileInformation class:

use App\Models\User; //inherits from \Illuminate\Foundation\Auth\User
class UpdateUserProfileInformation implements UpdatesUserProfileInformation
{
 public function update(User $user, array $input): void
 {
 }
}

If you put the update method in the interface, PHP will require the type of the first parameter of the implementer to be \Illuminate\Foundation\Auth\User, so the above implementation will generate a compile error. Using PHPDoc for annotation can define the first parameter as any derived type.

answered Aug 12 at 7:17
-1

What's the purpose of using a @method annotation instead? What does it achieve?

It keeps the method signature out of the interface. Might sound counter-indicative, but spares you to create multiple extending interfaces and instead use class-interfaces and this single, concrete interface as a "tagging interface" only.

The @method doc-block annotation full-fills the need for documenting this tagging (not defining) behaviour, e.g. suggesting a virtual function signature, that only exists in the commentary of the interface.

The PHP language syntax for parameter types in method signatures does not offer another way to do this for that Lavaral migration towards PHP native types.

Why not define a method signature in the usual way?

To short-cut writing PHP scripts. The Laravel Project would be required to add one PHP script file per interface IIRC, so next to concrete implementations (the classes and their traits), per each interfacing class (or at least for two of them), an extending interface would be required additionally (one for each sub-typing-type for that method's signature.)

This won't stop there. As the User is an interface used across module boundaries, this would require just another abstraction for such module boundaries between these two and all other Laravel libraries.

That is not practicable feasible to achieve only to preserve backwards compatible interfaces for some single-method interfaces that can already benefit from the @method annotation.

So the definition(s) of the method signature(s) themselves are defined as usual, they are just not part of the interface.

The underlying "limitation" in the PHP language lies within its inability to have method signature definitions with a case for each (tagged) parameter type. E.g. one to choose based on sub- or base-or module- type. I put "limitation" in quotes, because I don't think that this would be a good language feature, YMMV.

Using a "tagging interface" as a stand-in for the parameter type allows to pass all classes during the messaging that now implement the tagging interface, as before the generic mixed data was allowed to pass. This prevents the need to create abstract class and interface trees across multiple repositories as the tagging interface suffices. However, it does not help enforcing method signatures, e.g. for a concrete type of a parameter or the return value, but in PHP you can do that when building your project, especially in Laravel, as the scaffolding may reveal your own refined types.

This is a common technique in languages that do not support object type casting, the tagging of parameter on function signature level to find the events case during interpretation (compiling and running the script at PHP run time), generics, or multiple inheritance. I'd say it is especially of benefit in a scripting langauge like PHP, e.g. you can type-hint for the user and progress on with the solution as long as you keep it building and maintained. Instead patch the language for a missing feature and go on (e.g. someone needed a namespace, now PHP has namespaces, someone needed a mixin, now PHP has traits (and then Laravel), etc.)

answered Aug 16 at 11:03

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.