phpstan: PHP8.2 - Interface property annotation not found inside class
Bug report
Similar to #8537, when a property is documented in an interface @property annotation, it’s not recognized by classes using this interface. This is not an issue on PHP 8.0 and 8.1.
Code snippet that reproduces the problem
https://phpstan.org/r/75fa6a47-cbc4-4eb3-b5eb-cf4851f6b805
Access to an undefined property SomeInterface::$var.
Expected output
No errors!
Did PHPStan help you today? Did it make you happy in any way?
PHPStan helped me become a better PHP dev 😃
About this issue
- Original URL
- State: closed
- Created 2 years ago
- Reactions: 3
- Comments: 24 (7 by maintainers)
Commits related to this issue
- Implement temp solution for PHPStan issues See https://github.com/phpstan/phpstan/issues/8550 — committed to userfrosting/sprinkle-account by lcharette a year ago
- Implement temp solution for PHPStan issues See https://github.com/phpstan/phpstan/issues/8550 — committed to userfrosting/sprinkle-core by lcharette a year ago
- Implement temp solution for PHPStan issues See https://github.com/phpstan/phpstan/issues/8550 — committed to userfrosting/sprinkle-admin by lcharette a year ago
The hack @JasonEleventeen applied should be unnecessary.
I am in a similar situation that interfaces are annotated with
@propertyfor Eloquent models in Laravel. Adding the properties are very useful when writing packages for models that can be replaced with your own implementation but need to abide to the interface.Applying the
@mixin \Eloquentto the interface should inherit the magic methods__setand_getfrom the Eloquent model for the interface.The documentation indicates that the annotated properties are detected if these magic methods are present:
Have __get and/or __set magic methods for handling dynamic properties.Therefore, I expect it to work now with the mixin applied.
However, it still does not work. Applying the hack from @JasonEleventeen does fix it though. Despite the Eloquent model also implementing the magic method
__isset.Furthermore, it is not detecting any magic properties from the
@mixin \Eloquentmodel anymore either. An example is that it no longer understands what->existsor->wasRecentlyCreatedis anymore when accessing these properties.This seems more like a bug than a feature request. @ondrejmirtes
Example code.
Access to an undefined property App\Interfaces\StatusableInterface::$statusesAdding
#[\AllowDynamicProperties]works for interfaces as well as shown here: https://phpstan.org/r/ecc5a472-e2a3-4c8a-a314-51124b72edee although I don’t know how to feel about this givenI guess it would be good if phpstan would report if indeed given property was defined on the interface but is not defined on the class, ie if https://phpstan.org/r/57b63210-3875-4f28-871c-cf97d91ef779 would error
edit: apologies - idk if I was running 8.2 or not, but today I’m getting
Fatal error: Cannot apply #[AllowDynamicProperties] to interface; I don’t know why that’s not happening on share linkThis is now possible to solve with
@phpstan-require-extendsand@phpstan-require-implements.See an example: https://phpstan.org/r/2dec4d59-482d-4894-ba05-0c2453e28f2b
When the typehinted interface is implemented by a class, it requires the class to extend a class in
@phpstan-require-extends.The class can then declare the property as native or via other means like
@property.This is fixable by patching https://github.com/phpstan/phpstan-src/blob/19c838bbe9576c83c95ebb1f9fedd5e5b8664306/src/Reflection/ClassReflection.php#L380
would a PR to allow this be accepted? Could put it behind a feature Flag if required
@solomonjames please open a PR in the vendor lib for that change too if you can and didn’t do already 😊
Considering my case is mostly Database Models, I could use the Universal object crates, add each model Interface in my config and the error would be suppressed. However, this isn’t a perfect solution. If my
Foointerface only declare@property int $id, and “Universal object crates” is used,$foo->barwon’t throw an exception (I can’t add a playground to demonstrate this because config can’t be declared on Playground). It’s true because of how Laravel define its setter, assigning a dynamic var to the implementing class won’t throw an error on the PHP execution. However, PHPStan should still obey the interface declaration when using$foo->bar.No other solution provided by the new blog post seams applicable in this case. As @eithed noted,
AllowDynamicPropertiesis not an option, as PHP return this error when dealing with Interfaces (and PHPStan should probably too) :Consider this other example :
This is still an issue IMO. On the new blog post, in Add @property PHPDoc, a warning about this issue should be added too.
I’m hesitant to support this, because
@propertyabove an interface doesn’t mean anything - it doesn’t mean that all interfaces should declare this property…