From 47f1dcbf8362448c4c8e597cb594b5d7aecf5049 Mon Sep 17 00:00:00 2001 From: Ruud Kamphuis Date: 2021年2月12日 10:46:53 +0100 Subject: [PATCH 1/7] Move to Config namespace --- extension.neon | 9 +++++++-- .../TreeBuilderDynamicReturnTypeExtension.php | 2 +- ...eBuilderGetRootNodeDynamicReturnTypeExtension.php | 2 +- src/Type/Symfony/{ => Config}/TreeBuilderType.php | 2 +- .../TreeBuilderTest.php} | 12 ++++++++---- tests/Type/Symfony/{ => Config}/tree_builder.php | 6 +++--- 6 files changed, 21 insertions(+), 12 deletions(-) rename src/Type/Symfony/{ => Config}/TreeBuilderDynamicReturnTypeExtension.php (97%) rename src/Type/Symfony/{ => Config}/TreeBuilderGetRootNodeDynamicReturnTypeExtension.php (96%) rename src/Type/Symfony/{ => Config}/TreeBuilderType.php (91%) rename tests/Type/Symfony/{TreeBuilderDynamicReturnTypeExtensionTest.php => Config/TreeBuilderTest.php} (81%) rename tests/Type/Symfony/{ => Config}/tree_builder.php (100%) diff --git a/extension.neon b/extension.neon index 819442f3..f0a79cf1 100644 --- a/extension.neon +++ b/extension.neon @@ -148,10 +148,15 @@ services: # new TreeBuilder() return type - - factory: PHPStan\Type\Symfony\TreeBuilderDynamicReturnTypeExtension + factory: PHPStan\Type\Symfony\Config\TreeBuilderDynamicReturnTypeExtension tags: [phpstan.broker.dynamicStaticMethodReturnTypeExtension] # TreeBuilder::getRootNode() return type - - factory: PHPStan\Type\Symfony\TreeBuilderGetRootNodeDynamicReturnTypeExtension + factory: PHPStan\Type\Symfony\Config\TreeBuilderGetRootNodeDynamicReturnTypeExtension + tags: [phpstan.broker.dynamicMethodReturnTypeExtension] + + # NodeDefinition::end() return type + - + factory: PHPStan\Type\Symfony\Config\NodeDefinitionEndDynamicReturnTypeExtension tags: [phpstan.broker.dynamicMethodReturnTypeExtension] diff --git a/src/Type/Symfony/TreeBuilderDynamicReturnTypeExtension.php b/src/Type/Symfony/Config/TreeBuilderDynamicReturnTypeExtension.php similarity index 97% rename from src/Type/Symfony/TreeBuilderDynamicReturnTypeExtension.php rename to src/Type/Symfony/Config/TreeBuilderDynamicReturnTypeExtension.php index 56f29d35..a666907f 100644 --- a/src/Type/Symfony/TreeBuilderDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/Config/TreeBuilderDynamicReturnTypeExtension.php @@ -1,6 +1,6 @@ getRootNode(); +$arrayTreeBuilder = new TreeBuilder('my_tree', 'array'); +$arrayRootNode = $arrayTreeBuilder->getRootNode(); + $variableTreeBuilder = new TreeBuilder('my_tree', 'variable'); $variableRootNode = $variableTreeBuilder->getRootNode(); @@ -20,9 +23,6 @@ $floatTreeBuilder = new TreeBuilder('my_tree', 'float'); $floatRootNode = $floatTreeBuilder->getRootNode(); -$arrayTreeBuilder = new TreeBuilder('my_tree', 'array'); -$arrayRootNode = $arrayTreeBuilder->getRootNode(); - $enumTreeBuilder = new TreeBuilder('my_tree', 'enum'); $enumRootNode = $enumTreeBuilder->getRootNode(); From 8cee0ad17b9ea7226047acbd9ab33cc665b724d1 Mon Sep 17 00:00:00 2001 From: Ruud Kamphuis Date: 2021年2月12日 13:55:46 +0100 Subject: [PATCH 2/7] Add full support for Config/TreeBuilder Fixes #130 --- extension.neon | 40 +++- ...ionPrototypeDynamicReturnTypeExtension.php | 76 ++++++++ ...prBuilderEndDynamicReturnTypeExtension.php | 39 ++++ ...erCreateNodeDynamicReturnTypeExtension.php | 56 ++++++ ...deBuilderEndDynamicReturnTypeExtension.php | 39 ++++ ...tionChildrenDynamicReturnTypeExtension.php | 35 ++++ ...efinitionEndDynamicReturnTypeExtension.php | 39 ++++ ...tionValidateDynamicReturnTypeExtension.php | 35 ++++ src/Type/Symfony/Config/ParentObjectType.php | 26 +++ ...rGetRootNodeDynamicReturnTypeExtension.php | 12 +- tests/Type/Symfony/Config/TreeBuilderTest.php | 171 +++++++++++++++++- tests/Type/Symfony/ExtensionTestCase.php | 2 +- 12 files changed, 560 insertions(+), 10 deletions(-) create mode 100644 src/Type/Symfony/Config/ArrayNodeDefinitionPrototypeDynamicReturnTypeExtension.php create mode 100644 src/Type/Symfony/Config/ExprBuilderEndDynamicReturnTypeExtension.php create mode 100644 src/Type/Symfony/Config/NodeBuilderCreateNodeDynamicReturnTypeExtension.php create mode 100644 src/Type/Symfony/Config/NodeBuilderEndDynamicReturnTypeExtension.php create mode 100644 src/Type/Symfony/Config/NodeDefinitionChildrenDynamicReturnTypeExtension.php create mode 100644 src/Type/Symfony/Config/NodeDefinitionEndDynamicReturnTypeExtension.php create mode 100644 src/Type/Symfony/Config/NodeDefinitionValidateDynamicReturnTypeExtension.php create mode 100644 src/Type/Symfony/Config/ParentObjectType.php diff --git a/extension.neon b/extension.neon index f0a79cf1..41f6c2f4 100644 --- a/extension.neon +++ b/extension.neon @@ -146,17 +146,47 @@ services: factory: PHPStan\Type\Symfony\InputInterfaceHasOptionDynamicReturnTypeExtension tags: [phpstan.broker.dynamicMethodReturnTypeExtension] - # new TreeBuilder() return type + # ArrayNodeDefinition::*prototype() return type - - factory: PHPStan\Type\Symfony\Config\TreeBuilderDynamicReturnTypeExtension - tags: [phpstan.broker.dynamicStaticMethodReturnTypeExtension] + factory: PHPStan\Type\Symfony\Config\ArrayNodeDefinitionPrototypeDynamicReturnTypeExtension + tags: [phpstan.broker.dynamicMethodReturnTypeExtension] - # TreeBuilder::getRootNode() return type + # ExprBuilder::end() return type - - factory: PHPStan\Type\Symfony\Config\TreeBuilderGetRootNodeDynamicReturnTypeExtension + factory: PHPStan\Type\Symfony\Config\ExprBuilderEndDynamicReturnTypeExtension + tags: [phpstan.broker.dynamicMethodReturnTypeExtension] + + # NodeBuilder::*node() return type + - + factory: PHPStan\Type\Symfony\Config\NodeBuilderCreateNodeDynamicReturnTypeExtension + tags: [phpstan.broker.dynamicMethodReturnTypeExtension] + + # NodeBuilder::end() return type + - + factory: PHPStan\Type\Symfony\Config\NodeBuilderEndDynamicReturnTypeExtension + tags: [phpstan.broker.dynamicMethodReturnTypeExtension] + + # NodeDefinition::children() return type + - + factory: PHPStan\Type\Symfony\Config\NodeDefinitionChildrenDynamicReturnTypeExtension tags: [phpstan.broker.dynamicMethodReturnTypeExtension] # NodeDefinition::end() return type - factory: PHPStan\Type\Symfony\Config\NodeDefinitionEndDynamicReturnTypeExtension tags: [phpstan.broker.dynamicMethodReturnTypeExtension] + + # NodeDefinition::validate() return type + - + factory: PHPStan\Type\Symfony\Config\NodeDefinitionValidateDynamicReturnTypeExtension + tags: [phpstan.broker.dynamicMethodReturnTypeExtension] + + # new TreeBuilder() return type + - + factory: PHPStan\Type\Symfony\Config\TreeBuilderDynamicReturnTypeExtension + tags: [phpstan.broker.dynamicStaticMethodReturnTypeExtension] + + # TreeBuilder::getRootNode() return type + - + factory: PHPStan\Type\Symfony\Config\TreeBuilderGetRootNodeDynamicReturnTypeExtension + tags: [phpstan.broker.dynamicMethodReturnTypeExtension] diff --git a/src/Type/Symfony/Config/ArrayNodeDefinitionPrototypeDynamicReturnTypeExtension.php b/src/Type/Symfony/Config/ArrayNodeDefinitionPrototypeDynamicReturnTypeExtension.php new file mode 100644 index 00000000..93e41d5b --- /dev/null +++ b/src/Type/Symfony/Config/ArrayNodeDefinitionPrototypeDynamicReturnTypeExtension.php @@ -0,0 +1,76 @@ + 'Symfony\Component\Config\Definition\Builder\VariableNodeDefinition', + 'scalar' => 'Symfony\Component\Config\Definition\Builder\ScalarNodeDefinition', + 'boolean' => 'Symfony\Component\Config\Definition\Builder\BooleanNodeDefinition', + 'integer' => 'Symfony\Component\Config\Definition\Builder\IntegerNodeDefinition', + 'float' => 'Symfony\Component\Config\Definition\Builder\FloatNodeDefinition', + 'array' => 'Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition', + 'enum' => 'Symfony\Component\Config\Definition\Builder\EnumNodeDefinition', + ]; + + public function getClass(): string + { + return 'Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition'; + } + + public function isMethodSupported(MethodReflection $methodReflection): bool + { + return $methodReflection->getName() === 'prototype' || in_array($methodReflection->getName(), self::PROTOTYPE_METHODS, true); + } + + public function getTypeFromMethodCall( + MethodReflection $methodReflection, + MethodCall $methodCall, + Scope $scope + ): Type + { + $calledOnType = $scope->getType($methodCall->var); + + $defaultType = ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType(); + + if ($methodReflection->getName() === 'prototype') { + if (!isset($methodCall->args[0])) { + return $defaultType; + } + + $argStrings = TypeUtils::getConstantStrings($scope->getType($methodCall->args[0]->value)); + if (count($argStrings) === 1 && isset(self::MAPPING[$argStrings[0]->getValue()])) { + $type = $argStrings[0]->getValue(); + + return new ParentObjectType(self::MAPPING[$type], $calledOnType); + } + } + + return new ParentObjectType( + $defaultType->describe(VerbosityLevel::typeOnly()), + $calledOnType + ); + } + +} diff --git a/src/Type/Symfony/Config/ExprBuilderEndDynamicReturnTypeExtension.php b/src/Type/Symfony/Config/ExprBuilderEndDynamicReturnTypeExtension.php new file mode 100644 index 00000000..33029e8f --- /dev/null +++ b/src/Type/Symfony/Config/ExprBuilderEndDynamicReturnTypeExtension.php @@ -0,0 +1,39 @@ +getName() === 'end'; + } + + public function getTypeFromMethodCall( + MethodReflection $methodReflection, + MethodCall $methodCall, + Scope $scope + ): Type + { + $calledOnType = $scope->getType($methodCall->var); + if ($calledOnType instanceof ParentObjectType) { + return $calledOnType->getParent(); + } + + return ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType(); + } + +} diff --git a/src/Type/Symfony/Config/NodeBuilderCreateNodeDynamicReturnTypeExtension.php b/src/Type/Symfony/Config/NodeBuilderCreateNodeDynamicReturnTypeExtension.php new file mode 100644 index 00000000..b0d4a4a5 --- /dev/null +++ b/src/Type/Symfony/Config/NodeBuilderCreateNodeDynamicReturnTypeExtension.php @@ -0,0 +1,56 @@ +getName(), self::CREATE_NODE_METHODS, true); + } + + public function getTypeFromMethodCall( + MethodReflection $methodReflection, + MethodCall $methodCall, + Scope $scope + ): Type + { + $calledOnType = $scope->getType($methodCall->var); + + $defaultType = ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType(); + + if ($calledOnType instanceof ParentObjectType) { + return new ParentObjectType( + $defaultType->describe(VerbosityLevel::typeOnly()), + $calledOnType + ); + } + + return $defaultType; + } + +} diff --git a/src/Type/Symfony/Config/NodeBuilderEndDynamicReturnTypeExtension.php b/src/Type/Symfony/Config/NodeBuilderEndDynamicReturnTypeExtension.php new file mode 100644 index 00000000..4009bf29 --- /dev/null +++ b/src/Type/Symfony/Config/NodeBuilderEndDynamicReturnTypeExtension.php @@ -0,0 +1,39 @@ +getName() === 'end'; + } + + public function getTypeFromMethodCall( + MethodReflection $methodReflection, + MethodCall $methodCall, + Scope $scope + ): Type + { + $calledOnType = $scope->getType($methodCall->var); + if ($calledOnType instanceof ParentObjectType) { + return $calledOnType->getParent(); + } + + return ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType(); + } + +} diff --git a/src/Type/Symfony/Config/NodeDefinitionChildrenDynamicReturnTypeExtension.php b/src/Type/Symfony/Config/NodeDefinitionChildrenDynamicReturnTypeExtension.php new file mode 100644 index 00000000..5d01ca25 --- /dev/null +++ b/src/Type/Symfony/Config/NodeDefinitionChildrenDynamicReturnTypeExtension.php @@ -0,0 +1,35 @@ +getName() === 'children'; + } + + public function getTypeFromMethodCall( + MethodReflection $methodReflection, + MethodCall $methodCall, + Scope $scope + ): Type + { + $calledOnType = $scope->getType($methodCall->var); + + return new ParentObjectType('Symfony\Component\Config\Definition\Builder\NodeBuilder', $calledOnType); + } + +} diff --git a/src/Type/Symfony/Config/NodeDefinitionEndDynamicReturnTypeExtension.php b/src/Type/Symfony/Config/NodeDefinitionEndDynamicReturnTypeExtension.php new file mode 100644 index 00000000..7b5ec5a5 --- /dev/null +++ b/src/Type/Symfony/Config/NodeDefinitionEndDynamicReturnTypeExtension.php @@ -0,0 +1,39 @@ +getName() === 'end'; + } + + public function getTypeFromMethodCall( + MethodReflection $methodReflection, + MethodCall $methodCall, + Scope $scope + ): Type + { + $calledOnType = $scope->getType($methodCall->var); + if ($calledOnType instanceof ParentObjectType) { + return $calledOnType->getParent(); + } + + return ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType(); + } + +} diff --git a/src/Type/Symfony/Config/NodeDefinitionValidateDynamicReturnTypeExtension.php b/src/Type/Symfony/Config/NodeDefinitionValidateDynamicReturnTypeExtension.php new file mode 100644 index 00000000..1eb62a78 --- /dev/null +++ b/src/Type/Symfony/Config/NodeDefinitionValidateDynamicReturnTypeExtension.php @@ -0,0 +1,35 @@ +getName() === 'validate'; + } + + public function getTypeFromMethodCall( + MethodReflection $methodReflection, + MethodCall $methodCall, + Scope $scope + ): Type + { + $calledOnType = $scope->getType($methodCall->var); + + return new ParentObjectType('Symfony\Component\Config\Definition\Builder\ExprBuilder', $calledOnType); + } + +} diff --git a/src/Type/Symfony/Config/ParentObjectType.php b/src/Type/Symfony/Config/ParentObjectType.php new file mode 100644 index 00000000..699017df --- /dev/null +++ b/src/Type/Symfony/Config/ParentObjectType.php @@ -0,0 +1,26 @@ +parent = $parent; + } + + public function getParent(): Type + { + return $this->parent; + } + +} diff --git a/src/Type/Symfony/Config/TreeBuilderGetRootNodeDynamicReturnTypeExtension.php b/src/Type/Symfony/Config/TreeBuilderGetRootNodeDynamicReturnTypeExtension.php index 1917f5e6..b1747449 100644 --- a/src/Type/Symfony/Config/TreeBuilderGetRootNodeDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/Config/TreeBuilderGetRootNodeDynamicReturnTypeExtension.php @@ -5,8 +5,8 @@ use PhpParser\Node\Expr\MethodCall; use PHPStan\Analyser\Scope; use PHPStan\Reflection\MethodReflection; +use PHPStan\Reflection\ParametersAcceptorSelector; use PHPStan\Type\DynamicMethodReturnTypeExtension; -use PHPStan\Type\ObjectType; use PHPStan\Type\Type; final class TreeBuilderGetRootNodeDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension @@ -29,11 +29,17 @@ public function getTypeFromMethodCall( ): Type { $calledOnType = $scope->getType($methodCall->var); + + $defaultType = ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType(); + if ($calledOnType instanceof TreeBuilderType) { - return new ObjectType($calledOnType->getRootNodeClassName()); + return new ParentObjectType( + $calledOnType->getRootNodeClassName(), + $calledOnType + ); } - return $methodReflection->getVariants()[0]->getReturnType(); + return $defaultType; } } diff --git a/tests/Type/Symfony/Config/TreeBuilderTest.php b/tests/Type/Symfony/Config/TreeBuilderTest.php index f70b6808..2b02f96f 100644 --- a/tests/Type/Symfony/Config/TreeBuilderTest.php +++ b/tests/Type/Symfony/Config/TreeBuilderTest.php @@ -4,6 +4,7 @@ use Iterator; use PHPStan\Type\Symfony\ExtensionTestCase; +use Symfony\Component\Config\Definition\Builder\TreeBuilder; final class TreeBuilderTest extends ExtensionTestCase { @@ -13,13 +14,33 @@ final class TreeBuilderTest extends ExtensionTestCase */ public function testGet(string $expression, string $type): void { + $arrayTreeBuilder = new TreeBuilder('my_tree', 'array'); + $arrayRootNode = $arrayTreeBuilder->getRootNode(); + $r = $arrayRootNode + ->children() + ->arrayNode('methods') + ->prototype('scalar') + ->validate() + ->ifNotInArray(['one', 'two']) + ->thenInvalid('%s is not a valid method.') + ->end() + ->end() + ->end() + ->end(); + $this->processFile( __DIR__ . '/tree_builder.php', $expression, $type, [ + new ArrayNodeDefinitionPrototypeDynamicReturnTypeExtension(), + new ExprBuilderEndDynamicReturnTypeExtension(), + new NodeBuilderCreateNodeDynamicReturnTypeExtension(), + new NodeBuilderEndDynamicReturnTypeExtension(), + new NodeDefinitionChildrenDynamicReturnTypeExtension(), + new NodeDefinitionEndDynamicReturnTypeExtension(), + new NodeDefinitionValidateDynamicReturnTypeExtension(), new TreeBuilderGetRootNodeDynamicReturnTypeExtension(), - new NodeDefinitionEndDynamicReturnTypeExtension() ], [new TreeBuilderDynamicReturnTypeExtension()] ); @@ -31,13 +52,161 @@ public function testGet(string $expression, string $type): void public function getProvider(): Iterator { yield ['$treeRootNode', 'Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition']; + yield [' + $treeRootNode + ->children() + ->end() + ', 'Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition']; + yield [' + $treeRootNode + ->children() + ->scalarNode("protocol") + ->end() + ->end() + ', 'Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition']; + yield [' + $treeRootNode + ->children() + ', 'Symfony\Component\Config\Definition\Builder\NodeBuilder']; + yield [' + $treeRootNode + ->children() + ->arrayNode("protocols") + ', 'Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition']; + yield [' + $treeRootNode + ->children() + ->arrayNode("protocols") + ->children() + ', 'Symfony\Component\Config\Definition\Builder\NodeBuilder']; + yield [' + $treeRootNode + ->children() + ->arrayNode("protocols") + ->children() + ->scalarNode("protocol") + ', 'Symfony\Component\Config\Definition\Builder\ScalarNodeDefinition']; + yield [' + $treeRootNode + ->children() + ->arrayNode("protocols") + ->children() + ->scalarNode("protocol") + ->end() + ', 'Symfony\Component\Config\Definition\Builder\NodeBuilder']; + yield [' + $treeRootNode + ->children() + ->arrayNode("protocols") + ->children() + ->scalarNode("protocol") + ->end() + ->end() + ', 'Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition']; + yield [' + $treeRootNode + ->children() + ->arrayNode("protocols") + ->children() + ->scalarNode("protocol") + ->end() + ->end() + ->end() + ', 'Symfony\Component\Config\Definition\Builder\NodeBuilder']; + yield [' + $treeRootNode + ->children() + ->arrayNode("protocols") + ->children() + ->scalarNode("protocol") + ->end() + ->end() + ->end() + ->end() + ', 'Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition']; + yield [' + $treeRootNode + ->children() + ->arrayNode("protocols") + ->children() + ->booleanNode("auto_connect") + ->defaultTrue() + ->end() + ->scalarNode("default_connection") + ->defaultValue("default") + ->end() + ->integerNode("positive_value") + ->min(0) + ->end() + ->floatNode("big_value") + ->max(5E45) + ->end() + ->enumNode("delivery") + ->values(["standard", "expedited", "priority"]) + ->end() + ->end() + ->end() + ->end() + ', 'Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition']; + yield ['$arrayRootNode', 'Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition']; + yield ['$arrayRootNode->end()', 'Symfony\Component\Config\Definition\Builder\TreeBuilder']; + yield [' + $arrayRootNode + ->children() + ->arrayNode("methods") + ->prototype("scalar") + ->defaultNull() + ->end() + ->end() + ->end() + ', 'Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition']; + yield [' + $arrayRootNode + ->children() + ->arrayNode("methods") + ->scalarPrototype() + ->defaultNull() + ->end() + ->end() + ->end() + ', 'Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition']; + yield [' + $arrayRootNode + ->children() + ->arrayNode("methods") + ->prototype("scalar") + ->validate() + ->ifNotInArray(["one", "two"]) + ->thenInvalid("%s is not a valid method.") + ->end() + ->end() + ->end() + ->end() + ', 'Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition']; + yield ['$variableRootNode', 'Symfony\Component\Config\Definition\Builder\VariableNodeDefinition']; + yield ['$variableRootNode->end()', 'Symfony\Component\Config\Definition\Builder\TreeBuilder']; + yield ['$scalarRootNode', 'Symfony\Component\Config\Definition\Builder\ScalarNodeDefinition']; + yield ['$scalarRootNode->defaultValue("default")', 'Symfony\Component\Config\Definition\Builder\ScalarNodeDefinition']; + yield ['$scalarRootNode->defaultValue("default")->end()', 'Symfony\Component\Config\Definition\Builder\TreeBuilder']; + yield ['$booleanRootNode', 'Symfony\Component\Config\Definition\Builder\BooleanNodeDefinition']; + yield ['$booleanRootNode->defaultTrue()', 'Symfony\Component\Config\Definition\Builder\BooleanNodeDefinition']; + yield ['$booleanRootNode->defaultTrue()->end()', 'Symfony\Component\Config\Definition\Builder\TreeBuilder']; + yield ['$integerRootNode', 'Symfony\Component\Config\Definition\Builder\IntegerNodeDefinition']; + yield ['$integerRootNode->min(0)', 'Symfony\Component\Config\Definition\Builder\IntegerNodeDefinition']; + yield ['$integerRootNode->min(0)->end()', 'Symfony\Component\Config\Definition\Builder\TreeBuilder']; + yield ['$floatRootNode', 'Symfony\Component\Config\Definition\Builder\FloatNodeDefinition']; + yield ['$floatRootNode->max(5E45)', 'Symfony\Component\Config\Definition\Builder\FloatNodeDefinition']; + yield ['$floatRootNode->max(5E45)->end()', 'Symfony\Component\Config\Definition\Builder\TreeBuilder']; + yield ['$enumRootNode', 'Symfony\Component\Config\Definition\Builder\EnumNodeDefinition']; + yield ['$enumRootNode->values(["standard", "expedited", "priority"])', 'Symfony\Component\Config\Definition\Builder\EnumNodeDefinition']; + yield ['$enumRootNode->values(["standard", "expedited", "priority"])->end()', 'Symfony\Component\Config\Definition\Builder\TreeBuilder']; } } diff --git a/tests/Type/Symfony/ExtensionTestCase.php b/tests/Type/Symfony/ExtensionTestCase.php index 26a805a6..21b95b69 100644 --- a/tests/Type/Symfony/ExtensionTestCase.php +++ b/tests/Type/Symfony/ExtensionTestCase.php @@ -84,7 +84,7 @@ function (Node $node, Scope $scope) use ($expression, $type, &$run): void { } /** @var \PhpParser\Node\Stmt\Expression $expNode */ $expNode = $this->getParser()->parseString(sprintf('getType($expNode->expr)->describe(VerbosityLevel::typeOnly())); + self::assertSame($type, $scope->getType($expNode->expr)->describe(VerbosityLevel::typeOnly()), sprintf('Expression "%s"', $expression)); $run = true; } ); From cb850780db3c10c1746d013c9a62e8374429fdc0 Mon Sep 17 00:00:00 2001 From: Ruud Kamphuis Date: 2021年2月12日 13:56:56 +0100 Subject: [PATCH 3/7] Update readme --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index f95f93dc..fa126c0c 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,8 @@ This extension provides following features: * Provides correct return type for `Request::getContent()` method based on the `$asResource` parameter. * Provides correct return type for `HeaderBag::get()` method based on the `$first` parameter. * Provides correct return type for `Envelope::all()` method based on the `$stampFqcn` parameter. +* Provides correct return type for `TreeBuilder::getRootNode()` method based on the optional `$type` parameter. +* Provides correct return type for `NodeDefinition::end()`. * Notifies you when you try to get an unregistered service from the container. * Notifies you when you try to get a private service from the container. * Optionally correct return types for `InputInterface::getArgument()`, `::getOption`, `::hasArgument`, and `::hasOption`. From 70ebbd29b36caf567eda04466fe1bdcda0f3d682 Mon Sep 17 00:00:00 2001 From: Ruud Kamphuis Date: 2021年2月12日 19:09:28 +0100 Subject: [PATCH 4/7] Add EndDynamicReturnTypeExtension --- extension.neon | 9 +++-- ....php => EndDynamicReturnTypeExtension.php} | 12 +++++- ...deBuilderEndDynamicReturnTypeExtension.php | 39 ------------------- ...efinitionEndDynamicReturnTypeExtension.php | 39 ------------------- tests/Type/Symfony/Config/TreeBuilderTest.php | 6 +-- 5 files changed, 19 insertions(+), 86 deletions(-) rename src/Type/Symfony/Config/{ExprBuilderEndDynamicReturnTypeExtension.php => EndDynamicReturnTypeExtension.php} (78%) delete mode 100644 src/Type/Symfony/Config/NodeBuilderEndDynamicReturnTypeExtension.php delete mode 100644 src/Type/Symfony/Config/NodeDefinitionEndDynamicReturnTypeExtension.php diff --git a/extension.neon b/extension.neon index 41f6c2f4..a785c281 100644 --- a/extension.neon +++ b/extension.neon @@ -153,7 +153,8 @@ services: # ExprBuilder::end() return type - - factory: PHPStan\Type\Symfony\Config\ExprBuilderEndDynamicReturnTypeExtension + factory: PHPStan\Type\Symfony\Config\EndDynamicReturnTypeExtension + arguments: ['Symfony\Component\Config\Definition\Builder\ExprBuilder'] tags: [phpstan.broker.dynamicMethodReturnTypeExtension] # NodeBuilder::*node() return type @@ -163,7 +164,8 @@ services: # NodeBuilder::end() return type - - factory: PHPStan\Type\Symfony\Config\NodeBuilderEndDynamicReturnTypeExtension + factory: PHPStan\Type\Symfony\Config\EndDynamicReturnTypeExtension + arguments: ['Symfony\Component\Config\Definition\Builder\NodeBuilder'] tags: [phpstan.broker.dynamicMethodReturnTypeExtension] # NodeDefinition::children() return type @@ -173,7 +175,8 @@ services: # NodeDefinition::end() return type - - factory: PHPStan\Type\Symfony\Config\NodeDefinitionEndDynamicReturnTypeExtension + factory: PHPStan\Type\Symfony\Config\EndDynamicReturnTypeExtension + arguments: ['Symfony\Component\Config\Definition\Builder\NodeDefinition'] tags: [phpstan.broker.dynamicMethodReturnTypeExtension] # NodeDefinition::validate() return type diff --git a/src/Type/Symfony/Config/ExprBuilderEndDynamicReturnTypeExtension.php b/src/Type/Symfony/Config/EndDynamicReturnTypeExtension.php similarity index 78% rename from src/Type/Symfony/Config/ExprBuilderEndDynamicReturnTypeExtension.php rename to src/Type/Symfony/Config/EndDynamicReturnTypeExtension.php index 33029e8f..5be12b50 100644 --- a/src/Type/Symfony/Config/ExprBuilderEndDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/Config/EndDynamicReturnTypeExtension.php @@ -9,12 +9,20 @@ use PHPStan\Type\DynamicMethodReturnTypeExtension; use PHPStan\Type\Type; -final class ExprBuilderEndDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension +final class EndDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension { + /** @var string */ + private $className; + + public function __construct(string $className) + { + $this->className = $className; + } + public function getClass(): string { - return 'Symfony\Component\Config\Definition\Builder\ExprBuilder'; + return $this->className; } public function isMethodSupported(MethodReflection $methodReflection): bool diff --git a/src/Type/Symfony/Config/NodeBuilderEndDynamicReturnTypeExtension.php b/src/Type/Symfony/Config/NodeBuilderEndDynamicReturnTypeExtension.php deleted file mode 100644 index 4009bf29..00000000 --- a/src/Type/Symfony/Config/NodeBuilderEndDynamicReturnTypeExtension.php +++ /dev/null @@ -1,39 +0,0 @@ -getName() === 'end'; - } - - public function getTypeFromMethodCall( - MethodReflection $methodReflection, - MethodCall $methodCall, - Scope $scope - ): Type - { - $calledOnType = $scope->getType($methodCall->var); - if ($calledOnType instanceof ParentObjectType) { - return $calledOnType->getParent(); - } - - return ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType(); - } - -} diff --git a/src/Type/Symfony/Config/NodeDefinitionEndDynamicReturnTypeExtension.php b/src/Type/Symfony/Config/NodeDefinitionEndDynamicReturnTypeExtension.php deleted file mode 100644 index 7b5ec5a5..00000000 --- a/src/Type/Symfony/Config/NodeDefinitionEndDynamicReturnTypeExtension.php +++ /dev/null @@ -1,39 +0,0 @@ -getName() === 'end'; - } - - public function getTypeFromMethodCall( - MethodReflection $methodReflection, - MethodCall $methodCall, - Scope $scope - ): Type - { - $calledOnType = $scope->getType($methodCall->var); - if ($calledOnType instanceof ParentObjectType) { - return $calledOnType->getParent(); - } - - return ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType(); - } - -} diff --git a/tests/Type/Symfony/Config/TreeBuilderTest.php b/tests/Type/Symfony/Config/TreeBuilderTest.php index 2b02f96f..e2d7048a 100644 --- a/tests/Type/Symfony/Config/TreeBuilderTest.php +++ b/tests/Type/Symfony/Config/TreeBuilderTest.php @@ -34,11 +34,11 @@ public function testGet(string $expression, string $type): void $type, [ new ArrayNodeDefinitionPrototypeDynamicReturnTypeExtension(), - new ExprBuilderEndDynamicReturnTypeExtension(), + new EndDynamicReturnTypeExtension('Symfony\Component\Config\Definition\Builder\ExprBuilder'), + new EndDynamicReturnTypeExtension('Symfony\Component\Config\Definition\Builder\NodeBuilder'), + new EndDynamicReturnTypeExtension('Symfony\Component\Config\Definition\Builder\NodeDefinition'), new NodeBuilderCreateNodeDynamicReturnTypeExtension(), - new NodeBuilderEndDynamicReturnTypeExtension(), new NodeDefinitionChildrenDynamicReturnTypeExtension(), - new NodeDefinitionEndDynamicReturnTypeExtension(), new NodeDefinitionValidateDynamicReturnTypeExtension(), new TreeBuilderGetRootNodeDynamicReturnTypeExtension(), ], From 92ffde9b05610c5410b12689ecf36bb9b5ff41b0 Mon Sep 17 00:00:00 2001 From: Ruud Kamphuis Date: 2021年2月12日 19:29:41 +0100 Subject: [PATCH 5/7] Make it simpler --- extension.neon | 23 +++++------ ...ionPrototypeDynamicReturnTypeExtension.php | 1 + ...tionChildrenDynamicReturnTypeExtension.php | 35 ---------------- ...tionValidateDynamicReturnTypeExtension.php | 35 ---------------- ...arentObjectDynamicReturnTypeExtension.php} | 40 +++++++++---------- ...eturnParentDynamicReturnTypeExtension.php} | 15 +++++-- .../TreeBuilderDynamicReturnTypeExtension.php | 1 + ...rGetRootNodeDynamicReturnTypeExtension.php | 2 + .../{ => ValueObject}/ParentObjectType.php | 2 +- .../{ => ValueObject}/TreeBuilderType.php | 2 +- tests/Type/Symfony/Config/TreeBuilderTest.php | 11 +++-- 11 files changed, 53 insertions(+), 114 deletions(-) delete mode 100644 src/Type/Symfony/Config/NodeDefinitionChildrenDynamicReturnTypeExtension.php delete mode 100644 src/Type/Symfony/Config/NodeDefinitionValidateDynamicReturnTypeExtension.php rename src/Type/Symfony/Config/{NodeBuilderCreateNodeDynamicReturnTypeExtension.php => PassParentObjectDynamicReturnTypeExtension.php} (54%) rename src/Type/Symfony/Config/{EndDynamicReturnTypeExtension.php => ReturnParentDynamicReturnTypeExtension.php} (68%) rename src/Type/Symfony/Config/{ => ValueObject}/ParentObjectType.php (87%) rename src/Type/Symfony/Config/{ => ValueObject}/TreeBuilderType.php (89%) diff --git a/extension.neon b/extension.neon index a785c281..d3eb9192 100644 --- a/extension.neon +++ b/extension.neon @@ -153,35 +153,32 @@ services: # ExprBuilder::end() return type - - factory: PHPStan\Type\Symfony\Config\EndDynamicReturnTypeExtension - arguments: ['Symfony\Component\Config\Definition\Builder\ExprBuilder'] + factory: PHPStan\Type\Symfony\Config\ReturnParentDynamicReturnTypeExtension + arguments: ['Symfony\Component\Config\Definition\Builder\ExprBuilder', ['end']] tags: [phpstan.broker.dynamicMethodReturnTypeExtension] # NodeBuilder::*node() return type - - factory: PHPStan\Type\Symfony\Config\NodeBuilderCreateNodeDynamicReturnTypeExtension + factory: PHPStan\Type\Symfony\Config\PassParentObjectDynamicReturnTypeExtension + arguments: ['Symfony\Component\Config\Definition\Builder\NodeBuilder', ['arrayNode', 'scalarNode', 'booleanNode', 'integerNode', 'floatNode', 'enumNode', 'variableNode']] tags: [phpstan.broker.dynamicMethodReturnTypeExtension] # NodeBuilder::end() return type - - factory: PHPStan\Type\Symfony\Config\EndDynamicReturnTypeExtension - arguments: ['Symfony\Component\Config\Definition\Builder\NodeBuilder'] + factory: PHPStan\Type\Symfony\Config\ReturnParentDynamicReturnTypeExtension + arguments: ['Symfony\Component\Config\Definition\Builder\NodeBuilder', ['end']] tags: [phpstan.broker.dynamicMethodReturnTypeExtension] # NodeDefinition::children() return type - - factory: PHPStan\Type\Symfony\Config\NodeDefinitionChildrenDynamicReturnTypeExtension + factory: PHPStan\Type\Symfony\Config\PassParentObjectDynamicReturnTypeExtension + arguments: ['Symfony\Component\Config\Definition\Builder\NodeDefinition', ['children', 'validate']] tags: [phpstan.broker.dynamicMethodReturnTypeExtension] # NodeDefinition::end() return type - - factory: PHPStan\Type\Symfony\Config\EndDynamicReturnTypeExtension - arguments: ['Symfony\Component\Config\Definition\Builder\NodeDefinition'] - tags: [phpstan.broker.dynamicMethodReturnTypeExtension] - - # NodeDefinition::validate() return type - - - factory: PHPStan\Type\Symfony\Config\NodeDefinitionValidateDynamicReturnTypeExtension + factory: PHPStan\Type\Symfony\Config\ReturnParentDynamicReturnTypeExtension + arguments: ['Symfony\Component\Config\Definition\Builder\NodeDefinition', ['end']] tags: [phpstan.broker.dynamicMethodReturnTypeExtension] # new TreeBuilder() return type diff --git a/src/Type/Symfony/Config/ArrayNodeDefinitionPrototypeDynamicReturnTypeExtension.php b/src/Type/Symfony/Config/ArrayNodeDefinitionPrototypeDynamicReturnTypeExtension.php index 93e41d5b..71e4fb17 100644 --- a/src/Type/Symfony/Config/ArrayNodeDefinitionPrototypeDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/Config/ArrayNodeDefinitionPrototypeDynamicReturnTypeExtension.php @@ -7,6 +7,7 @@ use PHPStan\Reflection\MethodReflection; use PHPStan\Reflection\ParametersAcceptorSelector; use PHPStan\Type\DynamicMethodReturnTypeExtension; +use PHPStan\Type\Symfony\Config\ValueObject\ParentObjectType; use PHPStan\Type\Type; use PHPStan\Type\TypeUtils; use PHPStan\Type\VerbosityLevel; diff --git a/src/Type/Symfony/Config/NodeDefinitionChildrenDynamicReturnTypeExtension.php b/src/Type/Symfony/Config/NodeDefinitionChildrenDynamicReturnTypeExtension.php deleted file mode 100644 index 5d01ca25..00000000 --- a/src/Type/Symfony/Config/NodeDefinitionChildrenDynamicReturnTypeExtension.php +++ /dev/null @@ -1,35 +0,0 @@ -getName() === 'children'; - } - - public function getTypeFromMethodCall( - MethodReflection $methodReflection, - MethodCall $methodCall, - Scope $scope - ): Type - { - $calledOnType = $scope->getType($methodCall->var); - - return new ParentObjectType('Symfony\Component\Config\Definition\Builder\NodeBuilder', $calledOnType); - } - -} diff --git a/src/Type/Symfony/Config/NodeDefinitionValidateDynamicReturnTypeExtension.php b/src/Type/Symfony/Config/NodeDefinitionValidateDynamicReturnTypeExtension.php deleted file mode 100644 index 1eb62a78..00000000 --- a/src/Type/Symfony/Config/NodeDefinitionValidateDynamicReturnTypeExtension.php +++ /dev/null @@ -1,35 +0,0 @@ -getName() === 'validate'; - } - - public function getTypeFromMethodCall( - MethodReflection $methodReflection, - MethodCall $methodCall, - Scope $scope - ): Type - { - $calledOnType = $scope->getType($methodCall->var); - - return new ParentObjectType('Symfony\Component\Config\Definition\Builder\ExprBuilder', $calledOnType); - } - -} diff --git a/src/Type/Symfony/Config/NodeBuilderCreateNodeDynamicReturnTypeExtension.php b/src/Type/Symfony/Config/PassParentObjectDynamicReturnTypeExtension.php similarity index 54% rename from src/Type/Symfony/Config/NodeBuilderCreateNodeDynamicReturnTypeExtension.php rename to src/Type/Symfony/Config/PassParentObjectDynamicReturnTypeExtension.php index b0d4a4a5..5d19b60f 100644 --- a/src/Type/Symfony/Config/NodeBuilderCreateNodeDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/Config/PassParentObjectDynamicReturnTypeExtension.php @@ -7,30 +7,37 @@ use PHPStan\Reflection\MethodReflection; use PHPStan\Reflection\ParametersAcceptorSelector; use PHPStan\Type\DynamicMethodReturnTypeExtension; +use PHPStan\Type\Symfony\Config\ValueObject\ParentObjectType; use PHPStan\Type\Type; use PHPStan\Type\VerbosityLevel; -final class NodeBuilderCreateNodeDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension +final class PassParentObjectDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension { - private const CREATE_NODE_METHODS = [ - 'arrayNode', - 'scalarNode', - 'booleanNode', - 'integerNode', - 'floatNode', - 'enumNode', - 'variableNode', - ]; + /** @var string */ + private $className; + + /** @var string[] */ + private $methods; + + /** + * @param string $className + * @param string[] $methods + */ + public function __construct(string $className, array $methods) + { + $this->className = $className; + $this->methods = $methods; + } public function getClass(): string { - return 'Symfony\Component\Config\Definition\Builder\NodeBuilder'; + return $this->className; } public function isMethodSupported(MethodReflection $methodReflection): bool { - return in_array($methodReflection->getName(), self::CREATE_NODE_METHODS, true); + return in_array($methodReflection->getName(), $this->methods, true); } public function getTypeFromMethodCall( @@ -43,14 +50,7 @@ public function getTypeFromMethodCall( $defaultType = ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType(); - if ($calledOnType instanceof ParentObjectType) { - return new ParentObjectType( - $defaultType->describe(VerbosityLevel::typeOnly()), - $calledOnType - ); - } - - return $defaultType; + return new ParentObjectType($defaultType->describe(VerbosityLevel::typeOnly()), $calledOnType); } } diff --git a/src/Type/Symfony/Config/EndDynamicReturnTypeExtension.php b/src/Type/Symfony/Config/ReturnParentDynamicReturnTypeExtension.php similarity index 68% rename from src/Type/Symfony/Config/EndDynamicReturnTypeExtension.php rename to src/Type/Symfony/Config/ReturnParentDynamicReturnTypeExtension.php index 5be12b50..8dd47a90 100644 --- a/src/Type/Symfony/Config/EndDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/Config/ReturnParentDynamicReturnTypeExtension.php @@ -7,17 +7,26 @@ use PHPStan\Reflection\MethodReflection; use PHPStan\Reflection\ParametersAcceptorSelector; use PHPStan\Type\DynamicMethodReturnTypeExtension; +use PHPStan\Type\Symfony\Config\ValueObject\ParentObjectType; use PHPStan\Type\Type; -final class EndDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension +final class ReturnParentDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension { /** @var string */ private $className; - public function __construct(string $className) + /** @var string[] */ + private $methods; + + /** + * @param string $className + * @param string[] $methods + */ + public function __construct(string $className, array $methods) { $this->className = $className; + $this->methods = $methods; } public function getClass(): string @@ -27,7 +36,7 @@ public function getClass(): string public function isMethodSupported(MethodReflection $methodReflection): bool { - return $methodReflection->getName() === 'end'; + return in_array($methodReflection->getName(), $this->methods, true); } public function getTypeFromMethodCall( diff --git a/src/Type/Symfony/Config/TreeBuilderDynamicReturnTypeExtension.php b/src/Type/Symfony/Config/TreeBuilderDynamicReturnTypeExtension.php index a666907f..25cd12dc 100644 --- a/src/Type/Symfony/Config/TreeBuilderDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/Config/TreeBuilderDynamicReturnTypeExtension.php @@ -7,6 +7,7 @@ use PHPStan\Analyser\Scope; use PHPStan\Reflection\MethodReflection; use PHPStan\Type\DynamicStaticMethodReturnTypeExtension; +use PHPStan\Type\Symfony\Config\ValueObject\TreeBuilderType; use PHPStan\Type\Type; use PHPStan\Type\TypeUtils; diff --git a/src/Type/Symfony/Config/TreeBuilderGetRootNodeDynamicReturnTypeExtension.php b/src/Type/Symfony/Config/TreeBuilderGetRootNodeDynamicReturnTypeExtension.php index b1747449..4b7c1b3a 100644 --- a/src/Type/Symfony/Config/TreeBuilderGetRootNodeDynamicReturnTypeExtension.php +++ b/src/Type/Symfony/Config/TreeBuilderGetRootNodeDynamicReturnTypeExtension.php @@ -7,6 +7,8 @@ use PHPStan\Reflection\MethodReflection; use PHPStan\Reflection\ParametersAcceptorSelector; use PHPStan\Type\DynamicMethodReturnTypeExtension; +use PHPStan\Type\Symfony\Config\ValueObject\ParentObjectType; +use PHPStan\Type\Symfony\Config\ValueObject\TreeBuilderType; use PHPStan\Type\Type; final class TreeBuilderGetRootNodeDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension diff --git a/src/Type/Symfony/Config/ParentObjectType.php b/src/Type/Symfony/Config/ValueObject/ParentObjectType.php similarity index 87% rename from src/Type/Symfony/Config/ParentObjectType.php rename to src/Type/Symfony/Config/ValueObject/ParentObjectType.php index 699017df..87435e56 100644 --- a/src/Type/Symfony/Config/ParentObjectType.php +++ b/src/Type/Symfony/Config/ValueObject/ParentObjectType.php @@ -1,6 +1,6 @@ Date: 2021年2月12日 19:37:29 +0100 Subject: [PATCH 6/7] Tweak readme --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index fa126c0c..02bfb1e2 100644 --- a/README.md +++ b/README.md @@ -13,8 +13,7 @@ This extension provides following features: * Provides correct return type for `Request::getContent()` method based on the `$asResource` parameter. * Provides correct return type for `HeaderBag::get()` method based on the `$first` parameter. * Provides correct return type for `Envelope::all()` method based on the `$stampFqcn` parameter. -* Provides correct return type for `TreeBuilder::getRootNode()` method based on the optional `$type` parameter. -* Provides correct return type for `NodeDefinition::end()`. +* Provides correct return types for `TreeBuilder` and `NodeDefinition` objects. * Notifies you when you try to get an unregistered service from the container. * Notifies you when you try to get a private service from the container. * Optionally correct return types for `InputInterface::getArgument()`, `::getOption`, `::hasArgument`, and `::hasOption`. From f728dc271eaa46ac68ec7b4d8a9e8ea699674a5c Mon Sep 17 00:00:00 2001 From: Ruud Kamphuis Date: 2021年2月12日 20:21:08 +0100 Subject: [PATCH 7/7] Apply feedback --- extension.neon | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/extension.neon b/extension.neon index d3eb9192..5046baa2 100644 --- a/extension.neon +++ b/extension.neon @@ -44,7 +44,8 @@ services: # console resolver - factory: PHPStan\Symfony\ConsoleApplicationResolver - arguments: [%symfony.console_application_loader%] + arguments: + consoleApplicationLoader: %symfony.console_application_loader% # service map symfony.serviceMapFactory: @@ -154,32 +155,42 @@ services: # ExprBuilder::end() return type - factory: PHPStan\Type\Symfony\Config\ReturnParentDynamicReturnTypeExtension - arguments: ['Symfony\Component\Config\Definition\Builder\ExprBuilder', ['end']] tags: [phpstan.broker.dynamicMethodReturnTypeExtension] + arguments: + className: Symfony\Component\Config\Definition\Builder\ExprBuilder + methods: [end] # NodeBuilder::*node() return type - factory: PHPStan\Type\Symfony\Config\PassParentObjectDynamicReturnTypeExtension - arguments: ['Symfony\Component\Config\Definition\Builder\NodeBuilder', ['arrayNode', 'scalarNode', 'booleanNode', 'integerNode', 'floatNode', 'enumNode', 'variableNode']] tags: [phpstan.broker.dynamicMethodReturnTypeExtension] + arguments: + className: Symfony\Component\Config\Definition\Builder\NodeBuilder + methods: [arrayNode, scalarNode, booleanNode, integerNode, floatNode, enumNode, variableNode] # NodeBuilder::end() return type - factory: PHPStan\Type\Symfony\Config\ReturnParentDynamicReturnTypeExtension - arguments: ['Symfony\Component\Config\Definition\Builder\NodeBuilder', ['end']] tags: [phpstan.broker.dynamicMethodReturnTypeExtension] + arguments: + className: Symfony\Component\Config\Definition\Builder\NodeBuilder + methods: [end] # NodeDefinition::children() return type - factory: PHPStan\Type\Symfony\Config\PassParentObjectDynamicReturnTypeExtension - arguments: ['Symfony\Component\Config\Definition\Builder\NodeDefinition', ['children', 'validate']] tags: [phpstan.broker.dynamicMethodReturnTypeExtension] + arguments: + className: Symfony\Component\Config\Definition\Builder\NodeDefinition + methods: [children, validate] # NodeDefinition::end() return type - factory: PHPStan\Type\Symfony\Config\ReturnParentDynamicReturnTypeExtension - arguments: ['Symfony\Component\Config\Definition\Builder\NodeDefinition', ['end']] tags: [phpstan.broker.dynamicMethodReturnTypeExtension] + arguments: + className: Symfony\Component\Config\Definition\Builder\NodeDefinition + methods: [end] # new TreeBuilder() return type -

AltStyle によって変換されたページ (->オリジナル) /