-
-
Notifications
You must be signed in to change notification settings - Fork 946
Closed
Labels
Description
Feature request
As per the earlier discussion:
Commit ondrejmirtes/BetterReflection@30fd205 deprecates creating a new attribute instance, but there's no sane replacement at the moment. You currently need an actual instance if you want to check the attribute's properties though. Combined example (without namespaces to keep it minimal):
#[Attribute(Attribute::TARGET_CLASS)]
class MyAttrib {
/** @var class-string */
public string $my_class_name;
/** @param class-string $my_class_name */
public function __construct(string $my_class_name) {
$this->my_class_name = $my_class_name;
}
}
#[MyAttrib(Foo::class)]
class Bar {
}
// Custom PHPStan rule -> processNode() function
$class_reflection = $scope->getClassReflection();
$class_attributes = $class_reflection->getNativeReflection()->getAttributes(MyAttrib::class);
if(count($class_attributes)) {
$attribute_instance = $class_attributes[0]->newInstance(); // PHPStan reports deprecation here
// Do something with $attribute_instance->my_class_name;
}Since PHPStan uses a (mostly) static reflection engine, the only "real" way would be to also analyse these attributes statically. So with #[MyAttrib(Foo::class)]:
- PHPStan sees it references an attribute class named
MyAttriband follows it to the constructor. - The argument is
string $my_class_name, so PHPStan internally stores something sayingmy_class_name = Foo::class. - Instead of
$class_reflection->getNativeReflection()->getAttributes()from within a rule, you can use just$class_attributes = $class_reflection->getAttributes(MyAttrib::class)(nogetNativeReflection()). - Then ask PHPStan for information about the argument:
$class_attributes[0]->getArgument('my_class_name')->getValue(), or even typed likegetStringValue(). It could also return an array via the pluralgetArguments(), although I'm not sure if someone would ever need to loop over these properties instead of just accessing them directly.
Some potential issues:
- What if the attribute's constructor has argument
string $foobut the class property isstring $bar? I think realistically people will tend to use equal names, or even better: constructor property promotion. The documentation could simply mention that only the constructor's argument names are used, or perhaps PHPStan could emit an error/warning when you run->getArgument('bar')(since no argument with that name actually exists). Or it could simply returnNULLand people will have to check for that in their own rules, then emit an error as needed. Having PHPStan generate an error/warning automatically would help people with pre-existing rules to "migrate" to the new style though. - What if the constructor modifies the passed argument? E.g. it accepts
string $foobut stores it as$this->foo = intval($foo). In my opinion this is kind of misusing attributes, because now your attribute "annotation" no longer matches what sort of information the attribute actually gives you. Attributes themselves don't need to do anything besides just storing some key-value pairs really, so having any logic in them should probably be avoided. PHP intended for them to provide machine-readable metadata only, why mess with the values? - PHPStan will likely be unable to return
getAttributes(MyAttrib::class)actually typed asMyAttrib[](since there's no actual instance of that class), which means LSP engines can no longer inform you about existing properties (and their types) fromMyAttrib. I currently have/** @var MyAttrib $attribute_instance */for this, but using that forgetAttributes(...)[0]would not work because->my_class_namestill doesn't exist. Maybe PHPStan could simply create anstdClasswith properties based on the attribute arguments, also removing the need for->getValue()and even solving the problem of people having to "migrate" their custom rules? So basically emulate creating an instance, populated with only static data. I personally try to avoid free-form objects though, so not quite sure how PHPStan should realistically deal with this (would it even fit within its static data model?). ;_;
Did PHPStan help you today? Did it make you happy in any way?
PHPStan helps me every day yo. :>
Reactions are currently unavailable