diff --git a/conf/config.neon b/conf/config.neon index 9497dde6d4..e4e9c85a5f 100644 --- a/conf/config.neon +++ b/conf/config.neon @@ -1027,9 +1027,6 @@ services: - class: PHPStan\Rules\Properties\PropertyReflectionFinder - - - class: PHPStan\Rules\RegistryFactory - - class: PHPStan\Rules\RuleLevelHelper arguments: @@ -1891,8 +1888,9 @@ services: autowired: false registry: - class: PHPStan\Rules\Registry - factory: @PHPStan\Rules\RegistryFactory::create + class: PHPStan\Rules\LazyRegistry + autowired: + - PHPStan\Rules\Registry stubPhpDocProvider: class: PHPStan\PhpDoc\StubPhpDocProvider diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 7b7d712ce8..be5dbffb4d 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -303,19 +303,54 @@ parameters: path: src/Reflection/SignatureMap/Php8SignatureMapProvider.php - - message: "#^Method PHPStan\\\\Rules\\\\Registry\\:\\:__construct\\(\\) has parameter \\$rules with generic interface PHPStan\\\\Rules\\\\Rule but does not specify its types\\: TNodeType$#" + message: "#^Function class_implements\\(\\) is a runtime reflection concept that might not work in PHPStan because it uses fully static reflection engine\\. Use objects retrieved from ReflectionProvider instead\\.$#" count: 1 - path: src/Rules/Registry.php + path: src/Rules/DirectRegistry.php - - message: "#^Property PHPStan\\\\Rules\\\\Registry\\:\\:\\$cache with generic interface PHPStan\\\\Rules\\\\Rule does not specify its types\\: TNodeType$#" + message: "#^Function class_parents\\(\\) is a runtime reflection concept that might not work in PHPStan because it uses fully static reflection engine\\. Use objects retrieved from ReflectionProvider instead\\.$#" count: 1 - path: src/Rules/Registry.php + path: src/Rules/DirectRegistry.php - - message: "#^Property PHPStan\\\\Rules\\\\Registry\\:\\:\\$rules with generic interface PHPStan\\\\Rules\\\\Rule does not specify its types\\: TNodeType$#" + message: "#^Method PHPStan\\\\Rules\\\\DirectRegistry\\:\\:__construct\\(\\) has parameter \\$rules with generic interface PHPStan\\\\Rules\\\\Rule but does not specify its types\\: TNodeType$#" count: 1 - path: src/Rules/Registry.php + path: src/Rules/DirectRegistry.php + + - + message: "#^Property PHPStan\\\\Rules\\\\DirectRegistry\\:\\:\\$cache with generic interface PHPStan\\\\Rules\\\\Rule does not specify its types\\: TNodeType$#" + count: 1 + path: src/Rules/DirectRegistry.php + + - + message: "#^Property PHPStan\\\\Rules\\\\DirectRegistry\\:\\:\\$rules with generic interface PHPStan\\\\Rules\\\\Rule does not specify its types\\: TNodeType$#" + count: 1 + path: src/Rules/DirectRegistry.php + + - + message: "#^Function class_implements\\(\\) is a runtime reflection concept that might not work in PHPStan because it uses fully static reflection engine\\. Use objects retrieved from ReflectionProvider instead\\.$#" + count: 1 + path: src/Rules/LazyRegistry.php + + - + message: "#^Function class_parents\\(\\) is a runtime reflection concept that might not work in PHPStan because it uses fully static reflection engine\\. Use objects retrieved from ReflectionProvider instead\\.$#" + count: 1 + path: src/Rules/LazyRegistry.php + + - + message: "#^Method PHPStan\\\\Rules\\\\LazyRegistry\\:\\:getRulesFromContainer\\(\\) return type with generic interface PHPStan\\\\Rules\\\\Rule does not specify its types\\: TNodeType$#" + count: 1 + path: src/Rules/LazyRegistry.php + + - + message: "#^Property PHPStan\\\\Rules\\\\LazyRegistry\\:\\:\\$cache with generic interface PHPStan\\\\Rules\\\\Rule does not specify its types\\: TNodeType$#" + count: 1 + path: src/Rules/LazyRegistry.php + + - + message: "#^Property PHPStan\\\\Rules\\\\LazyRegistry\\:\\:\\$rules with generic interface PHPStan\\\\Rules\\\\Rule does not specify its types\\: TNodeType$#" + count: 1 + path: src/Rules/LazyRegistry.php - message: "#^Anonymous function has an unused use \\$container\\.$#" diff --git a/src/DependencyInjection/ConditionalTagsExtension.php b/src/DependencyInjection/ConditionalTagsExtension.php index 9d4137dcf3..0b9248a1d1 100644 --- a/src/DependencyInjection/ConditionalTagsExtension.php +++ b/src/DependencyInjection/ConditionalTagsExtension.php @@ -10,7 +10,7 @@ use PHPStan\Collectors\RegistryFactory as CollectorRegistryFactory; use PHPStan\Parser\RichParser; use PHPStan\PhpDoc\TypeNodeResolverExtension; -use PHPStan\Rules\RegistryFactory as RuleRegistryFactory; +use PHPStan\Rules\LazyRegistry; use PHPStan\ShouldNotHappenException; use function array_reduce; use function count; @@ -30,7 +30,7 @@ public function getConfigSchema(): Nette\Schema\Schema BrokerFactory::DYNAMIC_STATIC_METHOD_RETURN_TYPE_EXTENSION_TAG => $bool, BrokerFactory::DYNAMIC_FUNCTION_RETURN_TYPE_EXTENSION_TAG => $bool, BrokerFactory::OPERATOR_TYPE_SPECIFYING_EXTENSION_TAG => $bool, - RuleRegistryFactory::RULE_TAG => $bool, + LazyRegistry::RULE_TAG => $bool, TypeNodeResolverExtension::EXTENSION_TAG => $bool, TypeSpecifierFactory::FUNCTION_TYPE_SPECIFYING_EXTENSION_TAG => $bool, TypeSpecifierFactory::METHOD_TYPE_SPECIFYING_EXTENSION_TAG => $bool, diff --git a/src/DependencyInjection/RulesExtension.php b/src/DependencyInjection/RulesExtension.php index 391fdb9a1d..0c15bfb8b9 100644 --- a/src/DependencyInjection/RulesExtension.php +++ b/src/DependencyInjection/RulesExtension.php @@ -5,7 +5,7 @@ use Nette\DI\CompilerExtension; use Nette\Schema\Expect; use Nette\Schema\Schema; -use PHPStan\Rules\RegistryFactory; +use PHPStan\Rules\LazyRegistry; class RulesExtension extends CompilerExtension { @@ -25,7 +25,7 @@ public function loadConfiguration(): void $builder->addDefinition($this->prefix((string) $key)) ->setFactory($rule) ->setAutowired($rule) - ->addTag(RegistryFactory::RULE_TAG); + ->addTag(LazyRegistry::RULE_TAG); } } diff --git a/src/PhpDoc/StubValidator.php b/src/PhpDoc/StubValidator.php index 7458702918..4b2c484ef9 100644 --- a/src/PhpDoc/StubValidator.php +++ b/src/PhpDoc/StubValidator.php @@ -19,6 +19,7 @@ use PHPStan\Rules\Classes\ExistingClassesInInterfaceExtendsRule; use PHPStan\Rules\Classes\ExistingClassInClassExtendsRule; use PHPStan\Rules\Classes\ExistingClassInTraitUseRule; +use PHPStan\Rules\DirectRegistry as DirectRuleRegistry; use PHPStan\Rules\FunctionDefinitionCheck; use PHPStan\Rules\Functions\MissingFunctionParameterTypehintRule; use PHPStan\Rules\Functions\MissingFunctionReturnTypehintRule; @@ -185,7 +186,7 @@ private function getRuleRegistry(Container $container): RuleRegistry new MissingPropertyTypehintRule($missingTypehintCheck), ]; - return new RuleRegistry($rules); + return new DirectRuleRegistry($rules); } private function getCollectorRegistry(Container $container): CollectorRegistry diff --git a/src/Rules/DirectRegistry.php b/src/Rules/DirectRegistry.php new file mode 100644 index 0000000000..7ee53824ee --- /dev/null +++ b/src/Rules/DirectRegistry.php @@ -0,0 +1,59 @@ +rules[$rule->getNodeType()][] = $rule; + } + } + + /** + * @template TNodeType of Node + * @phpstan-param class-string $nodeType + * @param Node $nodeType + * @phpstan-return array> + * @return Rule[] + */ + public function getRules(string $nodeType): array + { + if (!isset($this->cache[$nodeType])) { + $parentNodeTypes = [$nodeType] + class_parents($nodeType) + class_implements($nodeType); + + $rules = []; + foreach ($parentNodeTypes as $parentNodeType) { + foreach ($this->rules[$parentNodeType] ?? [] as $rule) { + $rules[] = $rule; + } + } + + $this->cache[$nodeType] = $rules; + } + + /** + * @phpstan-var array> $selectedRules + * @var Rule[] $selectedRules + */ + $selectedRules = $this->cache[$nodeType]; + + return $selectedRules; + } + +} diff --git a/src/Rules/LazyRegistry.php b/src/Rules/LazyRegistry.php new file mode 100644 index 0000000000..a9a1a728ec --- /dev/null +++ b/src/Rules/LazyRegistry.php @@ -0,0 +1,74 @@ + $nodeType + * @param Node $nodeType + * @phpstan-return array> + * @return Rule[] + */ + public function getRules(string $nodeType): array + { + if (!isset($this->cache[$nodeType])) { + $parentNodeTypes = [$nodeType] + class_parents($nodeType) + class_implements($nodeType); + + $rules = []; + $rulesFromContainer = $this->getRulesFromContainer(); + foreach ($parentNodeTypes as $parentNodeType) { + foreach ($rulesFromContainer[$parentNodeType] ?? [] as $rule) { + $rules[] = $rule; + } + } + + $this->cache[$nodeType] = $rules; + } + + /** + * @phpstan-var array> $selectedRules + * @var Rule[] $selectedRules + */ + $selectedRules = $this->cache[$nodeType]; + + return $selectedRules; + } + + /** + * @return Rule[][] + */ + private function getRulesFromContainer(): array + { + if ($this->rules !== null) { + return $this->rules; + } + + $rules = []; + foreach ($this->container->getServicesByTag(self::RULE_TAG) as $rule) { + $rules[$rule->getNodeType()][] = $rule; + } + + return $this->rules = $rules; + } + +} diff --git a/src/Rules/Registry.php b/src/Rules/Registry.php index c13d06d02c..36c609811b 100644 --- a/src/Rules/Registry.php +++ b/src/Rules/Registry.php @@ -3,28 +3,10 @@ namespace PHPStan\Rules; use PhpParser\Node; -use function class_implements; -use function class_parents; -class Registry +interface Registry { - /** @var Rule[][] */ - private array $rules = []; - - /** @var Rule[][] */ - private array $cache = []; - - /** - * @param Rule[] $rules - */ - public function __construct(array $rules) - { - foreach ($rules as $rule) { - $this->rules[$rule->getNodeType()][] = $rule; - } - } - /** * @template TNodeType of Node * @phpstan-param class-string $nodeType @@ -32,28 +14,6 @@ public function __construct(array $rules) * @phpstan-return array> * @return Rule[] */ - public function getRules(string $nodeType): array - { - if (!isset($this->cache[$nodeType])) { - $parentNodeTypes = [$nodeType] + class_parents($nodeType) + class_implements($nodeType); - - $rules = []; - foreach ($parentNodeTypes as $parentNodeType) { - foreach ($this->rules[$parentNodeType] ?? [] as $rule) { - $rules[] = $rule; - } - } - - $this->cache[$nodeType] = $rules; - } - - /** - * @phpstan-var array> $selectedRules - * @var Rule[] $selectedRules - */ - $selectedRules = $this->cache[$nodeType]; - - return $selectedRules; - } + public function getRules(string $nodeType): array; } diff --git a/src/Rules/RegistryFactory.php b/src/Rules/RegistryFactory.php deleted file mode 100644 index ebf00801d1..0000000000 --- a/src/Rules/RegistryFactory.php +++ /dev/null @@ -1,23 +0,0 @@ -container->getServicesByTag(self::RULE_TAG), - ); - } - -} diff --git a/src/Testing/RuleTestCase.php b/src/Testing/RuleTestCase.php index 89e738dda9..e7d809f7c3 100644 --- a/src/Testing/RuleTestCase.php +++ b/src/Testing/RuleTestCase.php @@ -20,10 +20,10 @@ use PHPStan\PhpDoc\PhpDocInheritanceResolver; use PHPStan\PhpDoc\StubPhpDocProvider; use PHPStan\Reflection\InitializerExprTypeResolver; +use PHPStan\Rules\DirectRegistry as DirectRuleRegistry; use PHPStan\Rules\Properties\DirectReadWritePropertiesExtensionProvider; use PHPStan\Rules\Properties\ReadWritePropertiesExtension; use PHPStan\Rules\Properties\ReadWritePropertiesExtensionProvider; -use PHPStan\Rules\Registry as RuleRegistry; use PHPStan\Rules\Rule; use PHPStan\Type\FileTypeMapper; use function array_map; @@ -69,7 +69,7 @@ protected function getTypeSpecifier(): TypeSpecifier private function getAnalyser(): Analyser { if ($this->analyser === null) { - $ruleRegistry = new RuleRegistry([ + $ruleRegistry = new DirectRuleRegistry([ $this->getRule(), ]); $collectorRegistry = new CollectorRegistry($this->getCollectors()); @@ -138,7 +138,7 @@ public function analyse(array $files, array $expectedErrors): void $actualErrors = $analyserResult->getUnorderedErrors(); $ruleErrorTransformer = new RuleErrorTransformer(); if (count($analyserResult->getCollectedData()) > 0) { - $ruleRegistry = new RuleRegistry([ + $ruleRegistry = new DirectRuleRegistry([ $this->getRule(), ]); diff --git a/tests/PHPStan/Analyser/AnalyserTest.php b/tests/PHPStan/Analyser/AnalyserTest.php index f6c8d9795e..fa13f7a170 100644 --- a/tests/PHPStan/Analyser/AnalyserTest.php +++ b/tests/PHPStan/Analyser/AnalyserTest.php @@ -17,8 +17,8 @@ use PHPStan\PhpDoc\StubPhpDocProvider; use PHPStan\Reflection\InitializerExprTypeResolver; use PHPStan\Rules\AlwaysFailRule; +use PHPStan\Rules\DirectRegistry as DirectRuleRegistry; use PHPStan\Rules\Properties\ReadWritePropertiesExtensionProvider; -use PHPStan\Rules\Registry as RuleRegistry; use PHPStan\Testing\PHPStanTestCase; use PHPStan\Type\FileTypeMapper; use function array_map; @@ -571,7 +571,7 @@ private function runAnalyser( private function createAnalyser(bool $reportUnmatchedIgnoredErrors): Analyser { - $ruleRegistry = new RuleRegistry([ + $ruleRegistry = new DirectRuleRegistry([ new AlwaysFailRule(), ]); $collectorRegistry = new CollectorRegistry([]); diff --git a/tests/PHPStan/DependencyInjection/ConditionalTagsExtensionTest.php b/tests/PHPStan/DependencyInjection/ConditionalTagsExtensionTest.php index a244aa9441..5c2b2e28d5 100644 --- a/tests/PHPStan/DependencyInjection/ConditionalTagsExtensionTest.php +++ b/tests/PHPStan/DependencyInjection/ConditionalTagsExtensionTest.php @@ -2,7 +2,7 @@ namespace PHPStan\DependencyInjection; -use PHPStan\Rules\RegistryFactory as RuleRegistryFactory; +use PHPStan\Rules\LazyRegistry; use PHPStan\Testing\PHPStanTestCase; use function array_map; use function get_class; @@ -12,7 +12,7 @@ class ConditionalTagsExtensionTest extends PHPStanTestCase public function testConditionalTags(): void { - $enabledServices = self::getContainer()->getServicesByTag(RuleRegistryFactory::RULE_TAG); + $enabledServices = self::getContainer()->getServicesByTag(LazyRegistry::RULE_TAG); $enabledServices = array_map(static fn ($service) => get_class($service), $enabledServices); $this->assertNotContains(TestedConditionalServiceDisabled::class, $enabledServices); $this->assertContains(TestedConditionalServiceEnabled::class, $enabledServices); diff --git a/tests/PHPStan/Rules/RegistryTest.php b/tests/PHPStan/Rules/DirectRegistryTest.php similarity index 90% rename from tests/PHPStan/Rules/RegistryTest.php rename to tests/PHPStan/Rules/DirectRegistryTest.php index cd9aeb6c5b..2eb284a52a 100644 --- a/tests/PHPStan/Rules/RegistryTest.php +++ b/tests/PHPStan/Rules/DirectRegistryTest.php @@ -6,14 +6,14 @@ use PHPStan\Analyser\Scope; use PHPStan\Testing\PHPStanTestCase; -class RegistryTest extends PHPStanTestCase +class DirectRegistryTest extends PHPStanTestCase { public function testGetRules(): void { $rule = new DummyRule(); - $registry = new Registry([ + $registry = new DirectRegistry([ $rule, ]); @@ -29,7 +29,7 @@ public function testGetRulesWithTwoDifferentInstances(): void $fooRule = new UniversalRule(Node\Expr\FuncCall::class, static fn (Node\Expr\FuncCall $node, Scope $scope): array => ['Foo error']); $barRule = new UniversalRule(Node\Expr\FuncCall::class, static fn (Node\Expr\FuncCall $node, Scope $scope): array => ['Bar error']); - $registry = new Registry([ + $registry = new DirectRegistry([ $fooRule, $barRule, ]);