I spent a few days implementing @phpstan-require-extends and @phpstan-require-implements semantics in PHPStan. People using psalm might find this feature familiar as it is already supported in psalm.

The idea is to define at interface or trait level, which requirements the usage class has to fulfill.

The development of this feature was possible, thanks to sponsoring by Pixel & Tonic, the team behind Craft CMS. In addition Ondřej Mirtes provided excellent feedback and guidance during the development.

The feature was implemented in separate Pull Requests, which built on top of each other:

… which fixed the following issues:

… and eventually became the headlining feature of PHPStan 1.10.56.

If you are in need of a certain feature or bugfix in PHPStan, Rector or related tooling, please get in touch.

@phpstan-require-extends trait-example

It’s best described with an example, so have a look at the psalm documentation example:

/**
 * @phpstan-require-extends DatabaseModel
 */
trait SoftDeletingTrait {
  // useful but scoped functionality, that depends on methods/properties from DatabaseModel
}

With this declaration we define that a class wich uses the SoftDeletingTrait has to extend the DatabaseModel class. If not, PHPStan will report an error. See the full example running in the PHPStan sandbox

@phpstan-require-extends interface-example

Similar to what was shown above, the same is possible with interfaces:

/**
 * @phpstan-require-extends DatabaseModel
 */
interface SoftDeletingMarkerInterface {
}

With this declaration we define that a class which implements the SoftDeletingMarkerInterface has to extend the DatabaseModel class.

When using interfaces we can achieve more though, because the interface type can be used as e.g. a parameter-type:

/**
 * @phpstan-require-extends DatabaseModel
 */
interface SoftDeletingMarkerInterface {
}

class DatabaseModel {
    public string $tableName;

    public function softDelete():void { /* … */ }
}

// its allowed to call lookup properties and call method of the require-extends type, when using the interface-type
function runSoftDelete(SoftDeletingMarkerInterface $model):void {
    $tableName = $model->tableName;
    $model->softDelete();
    // …
}

Since its only valid to implement the SoftDeletingMarkerInterface when extending the DatabaseModel class, PHPStan will not error when accessing public properties or calling public methods of DatabaseModel, based on the SoftDeletingMarkerInterface type.

See the full example running in the PHPStan sandbox

NOTE: Looking up properties/calling methods on the interface type is currently only possible in PHPStan. I have opened a dedicated psalm feature request #10538 for discussion.

@phpstan-require-implements trait-example

Similar to the @phpstan-require-extends trait example, its supported to use @phpstan-require-implements on traits:

/**
 * @phpstan-require-implements DatabaseModelInterface
 */
trait SoftDeletingTrait {
  // useful but scoped functionality, that depends on methods/properties from DatabaseModel
}

With this declaration we define that a class wich uses the SoftDeletingTrait has to implement a DatabaseModelInterface interface.

See the full example running in the PHPStan sandbox

psalm compatibility

As with most phpDoc annotations, PHPStan will happily accept a psalm-prefixed @psalm-require-implements.

NOTE: Looking up properties/calling methods on the interface type is currently only possible in PHPStan. I have opened a dedicated psalm feature request #10538 for discussion.

read more

The new feature is mentioned in the PHPStan docs and PHPStan blog and was recently announced by Ondřej Mirtes on Twitter and mastodon.

Future scope: generics support

We plan to support generics in these phpDoc annotations in the future, see the described idea by Ondřej Mirtes. If you are interessted in this or any other feature addition, please considering sponsoring it.

Found a bug? Please help improve this article.


<
Previous Post
Published: Open source contributions statistics generator
>
Blog Archive
Archive of all previous blog posts