- Notifications
You must be signed in to change notification settings - Fork 545
Add stringable access check to ClassConstantRule #3910
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My suggestion above will help you get rid of this error.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- Do not make this optional.
- Put
#[AutowiredParameter(ref: '%featureToggles.checkNonStringableDynamicAccess%')]above the parameter.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd rename the variable to $nameTypeResult.
| | ||
| private int $phpVersion; | ||
| | ||
| private bool $checkNonStringableDynamicAccess; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also it might be nice to mention the class name here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are you adding the service here in this file? Remove it please.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- 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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After you composer dump it should work.
| $node->name, | ||
| '', | ||
| static fn (Type $type) => $type->toString()->isString()->yes() | ||
| ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
$type -> $nameType
| | ||
| $type = $nameTypeResult->getType(); | ||
| | ||
| if (!$type->isString()->yes()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| if (!$type->isString()->yes()) { | |
| if (!$type instanceof ErrorType && !$type->isString()->yes()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.