From d3b9fd4d1dc9e603a7ba13af5b2e1ad005e56438 Mon Sep 17 00:00:00 2001 From: ErickSkrauch Date: Tue, 18 Jul 2023 03:42:20 +0200 Subject: [PATCH] Call acceptsWithReason via RuleLevelHelper --- extension.neon | 2 ++ phpstan.neon.dist | 1 + src/Rule/CreateObjectRule.php | 11 +++++---- .../{YiiConfig.php => YiiConfigHelper.php} | 24 ++++++++++++------- tests/Rule/CreateObjectRuleTest.php | 6 ++++- 5 files changed, 31 insertions(+), 13 deletions(-) rename src/Rule/{YiiConfig.php => YiiConfigHelper.php} (90%) diff --git a/extension.neon b/extension.neon index ec6fed2..663977e 100644 --- a/extension.neon +++ b/extension.neon @@ -44,6 +44,8 @@ parametersSchema: ]) services: + - class: ErickSkrauch\PHPStan\Yii2\Rule\YiiConfigHelper + - class: ErickSkrauch\PHPStan\Yii2\Reflection\ApplicationPropertiesClassReflectionExtension tags: [phpstan.broker.propertiesClassReflectionExtension] diff --git a/phpstan.neon.dist b/phpstan.neon.dist index f4793ec..55dc069 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -15,5 +15,6 @@ parameters: yii2: config_path: tests/assets/yii-config-valid.php ignoreErrors: + - '#Calling PHPStan\\Rules\\RuleLevelHelper::acceptsWithReason\(\) is not covered.+#' - '#Calling PHPStan\\Reflection\\Annotations\\AnnotationsPropertiesClassReflectionExtension\:\:(has|get)Property\(\) is not covered.+#' - '#Creating new PHPStan\\Reflection\\Dummy\\DummyPropertyReflection is not covered.+#' diff --git a/src/Rule/CreateObjectRule.php b/src/Rule/CreateObjectRule.php index 1189519..00c33a0 100644 --- a/src/Rule/CreateObjectRule.php +++ b/src/Rule/CreateObjectRule.php @@ -19,8 +19,11 @@ final class CreateObjectRule implements Rule { private ReflectionProvider $reflectionProvider; - public function __construct(ReflectionProvider $reflectionProvider) { + private YiiConfigHelper $configHelper; + + public function __construct(ReflectionProvider $reflectionProvider, YiiConfigHelper $configHelper) { $this->reflectionProvider = $reflectionProvider; + $this->configHelper = $configHelper; } public function getNodeType(): string { @@ -60,7 +63,7 @@ public function processNode(Node $node, Scope $scope): array { if ($firstArgType->isConstantArray()->yes()) { /** @var \PHPStan\Type\Constant\ConstantArrayType $config */ $config = $firstArgType->getConstantArrays()[0]; - $classNameOrError = YiiConfig::findClass($config); + $classNameOrError = $this->configHelper->findClass($config); if ($classNameOrError instanceof RuleError) { return [$classNameOrError]; } @@ -76,7 +79,7 @@ public function processNode(Node $node, Scope $scope): array { $classReflection = $this->reflectionProvider->getClass($classNameOrError); - $errors = array_merge($errors, YiiConfig::validateArray($classReflection, $config, $scope)); + $errors = array_merge($errors, $this->configHelper->validateArray($classReflection, $config, $scope)); } elseif ($firstArgType->isClassStringType()->yes()) { $classNamesTypes = $firstArgType->getConstantStrings(); // At this moment I'll skip supporting of multiple classes at once @@ -107,7 +110,7 @@ public function processNode(Node $node, Scope $scope): array { $secondArgConstantArrays = $scope->getType($args[1]->value)->getConstantArrays(); if (count($secondArgConstantArrays) === 1) { $argsConfig = $secondArgConstantArrays[0]; - $errors = array_merge($errors, YiiConfig::validateConstructorArgs($classReflection, $argsConfig, $scope)); + $errors = array_merge($errors, $this->configHelper->validateConstructorArgs($classReflection, $argsConfig, $scope)); } } diff --git a/src/Rule/YiiConfig.php b/src/Rule/YiiConfigHelper.php similarity index 90% rename from src/Rule/YiiConfig.php rename to src/Rule/YiiConfigHelper.php index c025ea8..288cdee 100644 --- a/src/Rule/YiiConfig.php +++ b/src/Rule/YiiConfigHelper.php @@ -7,18 +7,25 @@ use PHPStan\Reflection\ClassReflection; use PHPStan\Reflection\ParametersAcceptorSelector; use PHPStan\Rules\RuleErrorBuilder; +use PHPStan\Rules\RuleLevelHelper; use PHPStan\Type\Constant\ConstantArrayType; use PHPStan\Type\Constant\ConstantIntegerType; use PHPStan\Type\Constant\ConstantStringType; use PHPStan\Type\VerbosityLevel; -final class YiiConfig { +final class YiiConfigHelper { + + private RuleLevelHelper $ruleLevelHelper; + + public function __construct(RuleLevelHelper $ruleLevelHelper) { + $this->ruleLevelHelper = $ruleLevelHelper; + } /** * @param ConstantArrayType $config * @return string|\PHPStan\Rules\IdentifierRuleError */ - public static function findClass(ConstantArrayType $config) { + public function findClass(ConstantArrayType $config) { $class = $config->getOffsetValueType(new ConstantStringType('__class')); if (count($class->getConstantStrings()) === 1) { return $class->getConstantStrings()[0]->getValue(); @@ -37,7 +44,7 @@ public static function findClass(ConstantArrayType $config) { /** * @phpstan-return list<\PHPStan\Rules\IdentifierRuleError> */ - public static function validateArray(ClassReflection $classReflection, ConstantArrayType $config, Scope $scope): array { + public function validateArray(ClassReflection $classReflection, ConstantArrayType $config, Scope $scope): array { $errors = []; /** @var ConstantIntegerType|ConstantStringType $key */ foreach ($config->getKeyTypes() as $i => $key) { @@ -115,8 +122,8 @@ public static function validateArray(ClassReflection $classReflection, ConstantA } $target = $property->getWritableType(); - $result = $target->acceptsWithReason($value, true); - if (!$result->yes()) { + $result = $this->ruleLevelHelper->acceptsWithReason($target, $value, $scope->isDeclareStrictTypes()); + if (!$result->result) { $level = VerbosityLevel::getRecommendedLevelByType($target, $value); $errors[] = RuleErrorBuilder::message(sprintf( 'Property %s::$%s (%s) does not accept %s.', @@ -137,7 +144,7 @@ public static function validateArray(ClassReflection $classReflection, ConstantA /** * @phpstan-return list<\PHPStan\Rules\IdentifierRuleError> */ - public static function validateConstructorArgs(ClassReflection $classReflection, ConstantArrayType $config, Scope $scope): array { + public function validateConstructorArgs(ClassReflection $classReflection, ConstantArrayType $config, Scope $scope): array { $constructorParams = ParametersAcceptorSelector::selectSingle($classReflection->getConstructor()->getVariants())->getParameters(); /** @var \PHPStan\Type\Type|null $firstKeyType */ $firstKeyType = null; @@ -194,11 +201,12 @@ public static function validateConstructorArgs(ClassReflection $classReflection, } } + /** @var \PHPStan\Reflection\ParameterReflection $paramReflection */ // TODO: prevent direct pass of 'config' param to constructor args (\yii\base\Configurable) $paramType = $paramReflection->getType(); - $result = $paramType->acceptsWithReason($value, true); - if (!$result->yes()) { + $result = $this->ruleLevelHelper->acceptsWithReason($paramType, $value, $scope->isDeclareStrictTypes()); + if (!$result->result) { $level = VerbosityLevel::getRecommendedLevelByType($paramType, $value); $errors[] = RuleErrorBuilder::message(sprintf( 'Parameter #%d %s of class %s constructor expects %s, %s given.', diff --git a/tests/Rule/CreateObjectRuleTest.php b/tests/Rule/CreateObjectRuleTest.php index 85ad549..85636e4 100644 --- a/tests/Rule/CreateObjectRuleTest.php +++ b/tests/Rule/CreateObjectRuleTest.php @@ -4,6 +4,7 @@ namespace ErickSkrauch\PHPStan\Yii2\Tests\Rule; use ErickSkrauch\PHPStan\Yii2\Rule\CreateObjectRule; +use ErickSkrauch\PHPStan\Yii2\Rule\YiiConfigHelper; use ErickSkrauch\PHPStan\Yii2\Tests\ConfigTrait; use PHPStan\Rules\Rule; use PHPStan\Testing\RuleTestCase; @@ -53,7 +54,10 @@ public function testRule(): void { } protected function getRule(): Rule { - return new CreateObjectRule(self::createReflectionProvider()); + return new CreateObjectRule( + self::createReflectionProvider(), + self::getContainer()->getByType(YiiConfigHelper::class), + ); } }