Add stringable access check to ClassConstantRule#3910
Conversation
| private RuleLevelHelper $ruleLevelHelper, | ||
| private ClassNameCheck $classCheck, | ||
| private PhpVersion $phpVersion, | ||
| private bool $checkNonStringableDynamicAccess = true, |
There was a problem hiding this comment.
I understand that we shouldn't use the default value, but I was getting the following error, probably due to a missing DI configuration, so I temporarily added = true to test.
% make phpstan
php bin/phpstan clear-result-cache -q && php -d memory_limit=448M bin/phpstan
In Resolver.php line 677:
Service 'rules.26' (type of PHPStan\Rules\Classes\ClassConstantRule): Parameter $checkNonStringableDynamicAccess in ClassConstantRule::__construct() has no class type or default value, so its value must be
specified.
There was a problem hiding this comment.
My suggestion above will help you get rid of this error.
There was a problem hiding this comment.
- Do not make this optional.
- Put
#[AutowiredParameter(ref: '%featureToggles.checkNonStringableDynamicAccess%')]above the parameter.
There was a problem hiding this comment.
After you composer dump it should work.
25dfd4f to
4e57718
Compare
| } | ||
|
|
||
| if ($this->checkNonStringableDynamicAccess) { | ||
| $accepts = $this->ruleLevelHelper->accepts(new StringType(), $nameType, true); |
There was a problem hiding this comment.
Delete this line, it's not used anywhere.
| $node->name, | ||
| '', | ||
| static fn (Type $type) => $type->toString()->isString()->yes() | ||
| ); |
There was a problem hiding this comment.
After this call there should be:
$type = $typeResult->getType();
if ($type instanceof ErrorType) {
return [];
}Some rules return "unknown class errors" but that's irrelevant here.
There was a problem hiding this comment.
Dynamic fetching of constants doesn't do implicit casting, so it seems appropriate to simply check isString()->yes() without toString().
I was wrong in the early stages of this PR.
There was a problem hiding this comment.
I'd rename the variable to $nameTypeResult.
|
|
||
| private int $phpVersion; | ||
|
|
||
| private bool $checkNonStringableDynamicAccess; |
There was a problem hiding this comment.
If all the test cases use true then this property isn't needed at all. Just use true in the new expression in getRule.
| $typeResult->getType()->toString()->isNumericString()->yes() | ||
| ) { | ||
| $errors[] = RuleErrorBuilder::message(sprintf('Cannot fetch class constant with a non-stringable type %s.', $nameType->describe(VerbosityLevel::typeOnly()))) | ||
| ->identifier('classConstant.fetchInvalidExpression') |
There was a problem hiding this comment.
The error message and the identifier should reflect we're trying to access a class constant by non-stringable name. That's not obvious.
There was a problem hiding this comment.
Also it might be nice to mention the class name here.
There was a problem hiding this comment.
The error message is wrong regarding the isString/toString point you made above. This is no longer about "stringables", but about "strings". Also the identifier should reflect it's about "name".
conf/config.neon
Outdated
| - | ||
| class: PHPStan\Rules\Classes\ClassConstantRule | ||
| arguments: | ||
| checkNonStringableDynamicAccess: %featureToggles.checkNonStringableDynamicAccess% |
There was a problem hiding this comment.
Why are you adding the service here in this file? Remove it please.
There was a problem hiding this comment.
Instead, config.level0.neon should be modified:
- Remove the rule from
rulessection. - Add the service same way you did here.
- But also add
tagssection with the corresponding tag.
| private RuleLevelHelper $ruleLevelHelper, | ||
| private ClassNameCheck $classCheck, | ||
| private PhpVersion $phpVersion, | ||
| private bool $checkNonStringableDynamicAccess = true, |
There was a problem hiding this comment.
My suggestion above will help you get rid of this error.
|
I'd love if you could finish this (and the other rules) 😊 |
9667b8e to
9e7e1a8
Compare
5a4d54a to
cfab079
Compare
cfab079 to
4e59d4f
Compare
|
This pull request has been marked as ready for review. |
conf/config.level0.neon
Outdated
|
|
||
| services: | ||
| - | ||
| class: PHPStan\Rules\Classes\ClassConstantRule |
There was a problem hiding this comment.
Why is this added here? It's not needed at all, the rule has #[RegisteredRule(level: 0)] attribute.
| private RuleLevelHelper $ruleLevelHelper, | ||
| private ClassNameCheck $classCheck, | ||
| private PhpVersion $phpVersion, | ||
| private bool $checkNonStringableDynamicAccess = true, |
There was a problem hiding this comment.
- Do not make this optional.
- Put
#[AutowiredParameter(ref: '%featureToggles.checkNonStringableDynamicAccess%')]above the parameter.
| private RuleLevelHelper $ruleLevelHelper, | ||
| private ClassNameCheck $classCheck, | ||
| private PhpVersion $phpVersion, | ||
| private bool $checkNonStringableDynamicAccess = true, |
There was a problem hiding this comment.
After you composer dump it should work.
| $node->name, | ||
| '', | ||
| static fn (Type $type) => $type->toString()->isString()->yes() | ||
| ); |
There was a problem hiding this comment.
I'd rename the variable to $nameTypeResult.
| $typeResult->getType()->toString()->isNumericString()->yes() | ||
| ) { | ||
| $errors[] = RuleErrorBuilder::message(sprintf('Cannot fetch class constant with a non-stringable type %s.', $nameType->describe(VerbosityLevel::typeOnly()))) | ||
| ->identifier('classConstant.fetchInvalidExpression') |
There was a problem hiding this comment.
The error message is wrong regarding the isString/toString point you made above. This is no longer about "stringables", but about "strings". Also the identifier should reflect it's about "name".
d9cc0de to
df669bb
Compare
df669bb to
b0539eb
Compare
|
This pull request has been marked as ready for review. |
ondrejmirtes
left a comment
There was a problem hiding this comment.
After this I'll merge this and you can make the same changes to other rules 👍
| static fn (Type $type) => $type->isString()->yes(), | ||
| ); | ||
|
|
||
| $type = $nameTypeResult->getType(); |
|
|
||
| $type = $nameTypeResult->getType(); | ||
|
|
||
| if (!$type->isString()->yes()) { |
There was a problem hiding this comment.
| if (!$type->isString()->yes()) { | |
| if (!$type instanceof ErrorType && !$type->isString()->yes()) { |
There was a problem hiding this comment.
All calls to findTypeToCheck follow this pattern. ErrorType is returned if unknown classes were encountered there.
|
@ondrejmirtes Fixed it. I will prepare a PR for the rules for each node. |
|
Thank you! |
refs #3885 (review), #3886 (comment), phpstan/phpstan#13238
In the context of class constant fetching, string keys are not implicitly cast.