diff --git a/phpstan.neon b/phpstan.neon index b8c59c96..2e1a5d77 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -9,3 +9,13 @@ parameters: paths: - 'src/TwigExtra/src' - 'src/TwigHooks/src' + + ignoreErrors: + # Symfony Config component relies on magic methods in NodeBuilder. + # PHPStan struggles to resolve these methods on the builder class when complex generics are involved, + # resulting in false positive "unknown class" errors. + - + message: '#Call to method \w+Node\(\) on an unknown class .*NodeBuilder.*#' + paths: + - 'src/TwigHooks/src/DependencyInjection/Configuration.php' + - 'src/TwigExtra/src/Symfony/DependencyInjection/Configuration.php' diff --git a/src/TwigExtra/src/Symfony/DependencyInjection/Configuration.php b/src/TwigExtra/src/Symfony/DependencyInjection/Configuration.php index 14880ae1..406aa6e1 100644 --- a/src/TwigExtra/src/Symfony/DependencyInjection/Configuration.php +++ b/src/TwigExtra/src/Symfony/DependencyInjection/Configuration.php @@ -14,15 +14,23 @@ namespace Sylius\TwigExtra\Symfony\DependencyInjection; use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition; +use Symfony\Component\Config\Definition\Builder\NodeParentInterface; use Symfony\Component\Config\Definition\Builder\TreeBuilder; use Symfony\Component\Config\Definition\ConfigurationInterface; final class Configuration implements ConfigurationInterface { + /** + * @phpstan-return TreeBuilder<'array'> + */ public function getConfigTreeBuilder(): TreeBuilder { $treeBuilder = new TreeBuilder('sylius_twig_extra'); + /** + * @var ArrayNodeDefinition $rootNode + * @phpstan-var ArrayNodeDefinition $rootNode + */ $rootNode = $treeBuilder->getRootNode(); $this->addTwigUxConfiguration($rootNode); @@ -30,6 +38,9 @@ public function getConfigTreeBuilder(): TreeBuilder return $treeBuilder; } + /** + * @phpstan-param ArrayNodeDefinition $rootNode + */ private function addTwigUxConfiguration(ArrayNodeDefinition $rootNode): void { $rootNode diff --git a/src/TwigHooks/src/DependencyInjection/Configuration.php b/src/TwigHooks/src/DependencyInjection/Configuration.php index 35e652d1..b6c29e78 100644 --- a/src/TwigHooks/src/DependencyInjection/Configuration.php +++ b/src/TwigHooks/src/DependencyInjection/Configuration.php @@ -17,15 +17,23 @@ use Sylius\TwigHooks\Hookable\HookableComponent; use Sylius\TwigHooks\Hookable\HookableTemplate; use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition; +use Symfony\Component\Config\Definition\Builder\NodeParentInterface; use Symfony\Component\Config\Definition\Builder\TreeBuilder; use Symfony\Component\Config\Definition\ConfigurationInterface; final class Configuration implements ConfigurationInterface { + /** + * @phpstan-return TreeBuilder<'array'> + */ public function getConfigTreeBuilder(): TreeBuilder { $treeBuilder = new TreeBuilder('sylius_twig_hooks'); + /** + * @var ArrayNodeDefinition $rootNode + * @phpstan-var ArrayNodeDefinition $rootNode + */ $rootNode = $treeBuilder->getRootNode(); $rootNode @@ -41,6 +49,9 @@ public function getConfigTreeBuilder(): TreeBuilder return $treeBuilder; } + /** + * @phpstan-param ArrayNodeDefinition $rootNode + */ private function addSupportedHookableTypesConfiguration(ArrayNodeDefinition $rootNode): void { $rootNode @@ -58,6 +69,9 @@ private function addSupportedHookableTypesConfiguration(ArrayNodeDefinition $roo ; } + /** + * @phpstan-param ArrayNodeDefinition $rootNode + */ private function addHooksConfiguration(ArrayNodeDefinition $rootNode): void { $rootNode @@ -69,6 +83,7 @@ private function addHooksConfiguration(ArrayNodeDefinition $rootNode): void ->arrayPrototype() ->beforeNormalization() ->always(function ($v) { + $isTypeDefined = isset($v['type']); $isComponentDefined = isset($v['component']); $isTemplateDefined = isset($v['template']); $isDisabled = isset($v['enabled']) && $v['enabled'] === false; @@ -77,6 +92,10 @@ private function addHooksConfiguration(ArrayNodeDefinition $rootNode): void return $v; } + if (true === $isTypeDefined && false === $isDisabled) { + return $v; + } + $v['type'] = match (true) { $isDisabled => 'disabled', $isComponentDefined => 'component', @@ -89,6 +108,16 @@ private function addHooksConfiguration(ArrayNodeDefinition $rootNode): void ->end() ->validate() ->always(static function ($v) { + $type = $v['type'] ?? null; + if ('template' === $type) { + $v['component'] = null; + $v['props'] = []; + } + + if ('component' === $type) { + $v['template'] = null; + } + $component = $v['component'] ?? null; $template = $v['template'] ?? null; $enabled = $v['enabled'] ?? true; diff --git a/src/TwigHooks/tests/Integration/DependencyInjection/ConfigurationTest.php b/src/TwigHooks/tests/Integration/DependencyInjection/ConfigurationTest.php index 823fbd0e..d6484198 100644 --- a/src/TwigHooks/tests/Integration/DependencyInjection/ConfigurationTest.php +++ b/src/TwigHooks/tests/Integration/DependencyInjection/ConfigurationTest.php @@ -174,6 +174,7 @@ public function testItThrowsExceptionWhenBothTemplateAndComponentShortcutsAreDef 'some_hookable' => [ 'component' => 'MyAwesomeComponent', 'template' => 'some_target.html.twig', + 'type' => 'disabled', ], ], ], @@ -193,6 +194,7 @@ public function testItThrowsExceptionWhenPropsAreDefinedForNonComponentHookable( 'some_hookable' => [ 'template' => 'some_target.html.twig', 'props' => ['key' => 'value'], + 'type' => 'disabled', ], ], ],