From 2d9f300b4a844872b808eddee99fc28234180e00 Mon Sep 17 00:00:00 2001 From: matheo Date: Tue, 8 Aug 2023 10:16:45 +0200 Subject: [PATCH 1/8] Fix non scalar value to anonymous component --- src/TwigComponent/src/ComponentFactory.php | 4 +--- src/TwigComponent/tests/Fixtures/User.php | 21 +++++++++++++++++++ ...ymous_component_none_scalar_prop.html.twig | 1 + .../templates/components/UserCard.html.twig | 4 ++++ .../Integration/ComponentExtensionTest.php | 11 ++++++++++ .../Integration/ComponentFactoryTest.php | 8 ------- 6 files changed, 38 insertions(+), 11 deletions(-) create mode 100644 src/TwigComponent/tests/Fixtures/User.php create mode 100644 src/TwigComponent/tests/Fixtures/templates/anonymous_component_none_scalar_prop.html.twig create mode 100644 src/TwigComponent/tests/Fixtures/templates/components/UserCard.html.twig diff --git a/src/TwigComponent/src/ComponentFactory.php b/src/TwigComponent/src/ComponentFactory.php index 3877889f888..b75fc32f19f 100644 --- a/src/TwigComponent/src/ComponentFactory.php +++ b/src/TwigComponent/src/ComponentFactory.php @@ -102,9 +102,7 @@ public function mountFromObject(object $component, array $data, ComponentMetadat continue; } - if (!\is_scalar($value) && null !== $value) { - throw new \LogicException(sprintf('A "%s" prop was passed when creating the "%s" component. No matching %s property or mount() argument was found, so we attempted to use this as an HTML attribute. But, the value is not a scalar (it\'s a %s). Did you mean to pass this to your component or is there a typo on its name?', $key, $componentMetadata->getName(), $key, get_debug_type($value))); - } + $data[$key] = $value; } return new MountedComponent( diff --git a/src/TwigComponent/tests/Fixtures/User.php b/src/TwigComponent/tests/Fixtures/User.php new file mode 100644 index 00000000000..eb25026072a --- /dev/null +++ b/src/TwigComponent/tests/Fixtures/User.php @@ -0,0 +1,21 @@ +name; + } + + public function getEmail(): string + { + return $this->email; + } +} \ No newline at end of file diff --git a/src/TwigComponent/tests/Fixtures/templates/anonymous_component_none_scalar_prop.html.twig b/src/TwigComponent/tests/Fixtures/templates/anonymous_component_none_scalar_prop.html.twig new file mode 100644 index 00000000000..a1c267e1f1c --- /dev/null +++ b/src/TwigComponent/tests/Fixtures/templates/anonymous_component_none_scalar_prop.html.twig @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/TwigComponent/tests/Fixtures/templates/components/UserCard.html.twig b/src/TwigComponent/tests/Fixtures/templates/components/UserCard.html.twig new file mode 100644 index 00000000000..4a3e94dbaf2 --- /dev/null +++ b/src/TwigComponent/tests/Fixtures/templates/components/UserCard.html.twig @@ -0,0 +1,4 @@ +
+

{{ user.name }}

+

{{ user.email }}

+
\ No newline at end of file diff --git a/src/TwigComponent/tests/Integration/ComponentExtensionTest.php b/src/TwigComponent/tests/Integration/ComponentExtensionTest.php index 5c4c71148db..8d6dbabf455 100644 --- a/src/TwigComponent/tests/Integration/ComponentExtensionTest.php +++ b/src/TwigComponent/tests/Integration/ComponentExtensionTest.php @@ -12,6 +12,7 @@ namespace Symfony\UX\TwigComponent\Tests\Integration; use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; +use Symfony\UX\TwigComponent\Tests\Fixtures\User; use Twig\Environment; /** @@ -182,6 +183,16 @@ public function testRenderAnonymousComponentInNestedDirectory(): void $this->assertStringContainsString('class="primary"', $output); } + public function testRenderAnonymousComponentWithNonScalarProps(): void + { + $user = new User('Fabien', 'test@test.com'); + + $output = self::getContainer()->get(Environment::class)->render('anonymous_component_none_scalar_prop.html.twig', ['user' => $user]); + + $this->assertStringContainsString('Fabien', $output); + $this->assertStringContainsString('test@test.com', $output); + } + private function renderComponent(string $name, array $data = []): string { return self::getContainer()->get(Environment::class)->render('render_component.html.twig', [ diff --git a/src/TwigComponent/tests/Integration/ComponentFactoryTest.php b/src/TwigComponent/tests/Integration/ComponentFactoryTest.php index 19e6f889418..61426a650bc 100644 --- a/src/TwigComponent/tests/Integration/ComponentFactoryTest.php +++ b/src/TwigComponent/tests/Integration/ComponentFactoryTest.php @@ -91,14 +91,6 @@ public function testExceptionThrownIfRequiredMountParameterIsMissingFromPassedDa $this->createComponent('component_c'); } - public function testExceptionThrownIfUnableToWritePassedDataToPropertyAndIsNotScalar(): void - { - $this->expectException(\LogicException::class); - $this->expectExceptionMessage('But, the value is not a scalar (it\'s a stdClass)'); - - $this->createComponent('component_a', ['propB' => 'B', 'service' => new \stdClass()]); - } - public function testStringableObjectCanBePassedToComponent(): void { $attributes = $this->factory()->create('component_a', ['propB' => 'B', 'data-item-id-param' => new class() { From a9d422e8d18c1c8802e5006482f1da81e1e21b5f Mon Sep 17 00:00:00 2001 From: matheo Date: Thu, 17 Aug 2023 13:53:04 +0200 Subject: [PATCH 2/8] props tag remove props from attributes --- ...ymousAnonymousComponentTemplateParser.php} | 21 ++++++++++++++++++- src/TwigComponent/src/AnonymousComponent.php | 10 ++++++++- ...ymousComponentTemplateParserInterface.php} | 7 ++++++- src/TwigComponent/src/ComponentFactory.php | 12 +++++++---- .../TwigComponentExtension.php | 6 +++--- .../templates/components/UserCard.html.twig | 4 +++- 6 files changed, 49 insertions(+), 11 deletions(-) rename src/TwigComponent/src/{ComponentTemplateFinder.php => AnonymousAnonymousComponentTemplateParser.php} (60%) rename src/TwigComponent/src/{ComponentTemplateFinderInterface.php => AnonymousComponentTemplateParserInterface.php} (72%) diff --git a/src/TwigComponent/src/ComponentTemplateFinder.php b/src/TwigComponent/src/AnonymousAnonymousComponentTemplateParser.php similarity index 60% rename from src/TwigComponent/src/ComponentTemplateFinder.php rename to src/TwigComponent/src/AnonymousAnonymousComponentTemplateParser.php index 452256a43f4..07cd6b150bf 100644 --- a/src/TwigComponent/src/ComponentTemplateFinder.php +++ b/src/TwigComponent/src/AnonymousAnonymousComponentTemplateParser.php @@ -16,7 +16,7 @@ /** * @author Matheo Daninos */ -final class ComponentTemplateFinder implements ComponentTemplateFinderInterface +final class AnonymousAnonymousComponentTemplateParser implements AnonymousComponentTemplateParserInterface { public function __construct( private Environment $environment @@ -46,4 +46,23 @@ public function findAnonymousComponentTemplate(string $name): ?string return null; } + + public function findComponentProps(string $name): array + { + $loader = $this->environment->getLoader(); + $templateContent = $loader->getSourceContext($this->findAnonymousComponentTemplate($name))->getCode(); + + $pattern = '/{%\s*props\s*(.*?)\s*%}/'; + preg_match($pattern, $templateContent, $matches); + + if (isset($matches[1])) { + $props = $matches[1]; + $props = preg_replace('/\s/', '', $props); + $propsArray = explode(',', $props); + + return array_map(fn (string $propName) => strtok($propName, '='), $propsArray); + } + + return []; + } } diff --git a/src/TwigComponent/src/AnonymousComponent.php b/src/TwigComponent/src/AnonymousComponent.php index 59f07bd3f98..9d65a9c1a12 100644 --- a/src/TwigComponent/src/AnonymousComponent.php +++ b/src/TwigComponent/src/AnonymousComponent.php @@ -22,8 +22,16 @@ final class AnonymousComponent { private array $props; - public function mount($props = []): void + public function mount(&$data = [], $propNames = []): void { + $props = []; + foreach ($propNames as $propName) { + if (isset($data[$propName])) { + $props[$propName] = $data[$propName]; + unset($data[$propName]); + } + } + $this->props = $props; } diff --git a/src/TwigComponent/src/ComponentTemplateFinderInterface.php b/src/TwigComponent/src/AnonymousComponentTemplateParserInterface.php similarity index 72% rename from src/TwigComponent/src/ComponentTemplateFinderInterface.php rename to src/TwigComponent/src/AnonymousComponentTemplateParserInterface.php index 1a76913ea51..f02aea2e7f2 100644 --- a/src/TwigComponent/src/ComponentTemplateFinderInterface.php +++ b/src/TwigComponent/src/AnonymousComponentTemplateParserInterface.php @@ -14,7 +14,12 @@ /** * @author Matheo Daninos */ -interface ComponentTemplateFinderInterface +interface AnonymousComponentTemplateParserInterface { public function findAnonymousComponentTemplate(string $name): ?string; + + /** + * @return array + */ + public function findComponentProps(string $name): array; } diff --git a/src/TwigComponent/src/ComponentFactory.php b/src/TwigComponent/src/ComponentFactory.php index b75fc32f19f..7a47f6d965f 100644 --- a/src/TwigComponent/src/ComponentFactory.php +++ b/src/TwigComponent/src/ComponentFactory.php @@ -30,7 +30,7 @@ final class ComponentFactory * @param array $classMap */ public function __construct( - private ComponentTemplateFinderInterface $componentTemplateFinder, + private AnonymousComponentTemplateParserInterface $componentTemplateFinder, private ServiceLocator $components, private PropertyAccessorInterface $propertyAccessor, private EventDispatcherInterface $eventDispatcher, @@ -77,7 +77,7 @@ public function mountFromObject(object $component, array $data, ComponentMetadat $originalData = $data; $data = $this->preMount($component, $data); - $this->mount($component, $data); + $this->mount($component, $data, $componentMetadata); // set data that wasn't set in mount on the component directly foreach ($data as $property => $value) { @@ -103,6 +103,10 @@ public function mountFromObject(object $component, array $data, ComponentMetadat } $data[$key] = $value; + + if (!\is_scalar($value) && null !== $value) { + throw new \LogicException(sprintf('A "%s" prop was passed when creating the "%s" component. No matching %s property or mount() argument was found, so we attempted to use this as an HTML attribute. But, the value is not a scalar (it\'s a %s). Did you mean to pass this to your component or is there a typo on its name?', $key, $componentMetadata->getName(), $key, get_debug_type($value))); + } } return new MountedComponent( @@ -121,7 +125,7 @@ public function get(string $name): object return $this->getComponent($name); } - private function mount(object $component, array &$data): void + private function mount(object $component, array &$data, ComponentMetadata $metadata): void { try { $method = (new \ReflectionClass($component))->getMethod('mount'); @@ -131,7 +135,7 @@ private function mount(object $component, array &$data): void } if ($component instanceof AnonymousComponent) { - $component->mount($data); + $component->mount($data, $this->componentTemplateFinder->findComponentProps($metadata->getTemplate())); return; } diff --git a/src/TwigComponent/src/DependencyInjection/TwigComponentExtension.php b/src/TwigComponent/src/DependencyInjection/TwigComponentExtension.php index 59b9789fe64..359586a607d 100644 --- a/src/TwigComponent/src/DependencyInjection/TwigComponentExtension.php +++ b/src/TwigComponent/src/DependencyInjection/TwigComponentExtension.php @@ -17,12 +17,12 @@ use Symfony\Component\DependencyInjection\Exception\LogicException; use Symfony\Component\DependencyInjection\Extension\Extension; use Symfony\Component\DependencyInjection\Reference; +use Symfony\UX\TwigComponent\AnonymousAnonymousComponentTemplateParser; use Symfony\UX\TwigComponent\Attribute\AsTwigComponent; use Symfony\UX\TwigComponent\ComponentFactory; use Symfony\UX\TwigComponent\ComponentRenderer; use Symfony\UX\TwigComponent\ComponentRendererInterface; use Symfony\UX\TwigComponent\ComponentStack; -use Symfony\UX\TwigComponent\ComponentTemplateFinder; use Symfony\UX\TwigComponent\DependencyInjection\Compiler\TwigComponentPass; use Symfony\UX\TwigComponent\Twig\ComponentExtension; use Symfony\UX\TwigComponent\Twig\ComponentLexer; @@ -41,7 +41,7 @@ public function load(array $configs, ContainerBuilder $container): void throw new LogicException('The TwigBundle is not registered in your application. Try running "composer require symfony/twig-bundle".'); } - $container->register('ux.twig_component.component_template_finder', ComponentTemplateFinder::class) + $container->register('ux.twig_component.component_template_finder', AnonymousAnonymousComponentTemplateParser::class) ->setArguments([ new Reference('twig'), ]) @@ -78,7 +78,7 @@ class_exists(AbstractArgument::class) ? new AbstractArgument(sprintf('Added in % ]) ; - $container->register(ComponentTemplateFinder::class, 'ux.twig_component.component_template_finder'); + $container->register(AnonymousAnonymousComponentTemplateParser::class, 'ux.twig_component.component_template_finder'); $container->register('ux.twig_component.twig.component_extension', ComponentExtension::class) ->addTag('twig.extension') diff --git a/src/TwigComponent/tests/Fixtures/templates/components/UserCard.html.twig b/src/TwigComponent/tests/Fixtures/templates/components/UserCard.html.twig index 4a3e94dbaf2..5242c2740ad 100644 --- a/src/TwigComponent/tests/Fixtures/templates/components/UserCard.html.twig +++ b/src/TwigComponent/tests/Fixtures/templates/components/UserCard.html.twig @@ -1,4 +1,6 @@ -
+{% props user %} + +

{{ user.name }}

{{ user.email }}

\ No newline at end of file From 2555d26502457b6dc9ca55c964025eeb0d539b16 Mon Sep 17 00:00:00 2001 From: matheo Date: Thu, 17 Aug 2023 18:13:11 +0200 Subject: [PATCH 3/8] Revert "props tag remove props from attributes" This reverts commit eb6e3a214d6f8b9bab1f5c0f57427f009da8329f. --- src/TwigComponent/src/AnonymousComponent.php | 10 +-------- src/TwigComponent/src/ComponentFactory.php | 12 ++++------- ...Parser.php => ComponentTemplateFinder.php} | 21 +------------------ ...p => ComponentTemplateFinderInterface.php} | 7 +------ .../TwigComponentExtension.php | 6 +++--- .../templates/components/UserCard.html.twig | 4 +--- 6 files changed, 11 insertions(+), 49 deletions(-) rename src/TwigComponent/src/{AnonymousAnonymousComponentTemplateParser.php => ComponentTemplateFinder.php} (60%) rename src/TwigComponent/src/{AnonymousComponentTemplateParserInterface.php => ComponentTemplateFinderInterface.php} (72%) diff --git a/src/TwigComponent/src/AnonymousComponent.php b/src/TwigComponent/src/AnonymousComponent.php index 9d65a9c1a12..59f07bd3f98 100644 --- a/src/TwigComponent/src/AnonymousComponent.php +++ b/src/TwigComponent/src/AnonymousComponent.php @@ -22,16 +22,8 @@ final class AnonymousComponent { private array $props; - public function mount(&$data = [], $propNames = []): void + public function mount($props = []): void { - $props = []; - foreach ($propNames as $propName) { - if (isset($data[$propName])) { - $props[$propName] = $data[$propName]; - unset($data[$propName]); - } - } - $this->props = $props; } diff --git a/src/TwigComponent/src/ComponentFactory.php b/src/TwigComponent/src/ComponentFactory.php index 7a47f6d965f..b75fc32f19f 100644 --- a/src/TwigComponent/src/ComponentFactory.php +++ b/src/TwigComponent/src/ComponentFactory.php @@ -30,7 +30,7 @@ final class ComponentFactory * @param array $classMap */ public function __construct( - private AnonymousComponentTemplateParserInterface $componentTemplateFinder, + private ComponentTemplateFinderInterface $componentTemplateFinder, private ServiceLocator $components, private PropertyAccessorInterface $propertyAccessor, private EventDispatcherInterface $eventDispatcher, @@ -77,7 +77,7 @@ public function mountFromObject(object $component, array $data, ComponentMetadat $originalData = $data; $data = $this->preMount($component, $data); - $this->mount($component, $data, $componentMetadata); + $this->mount($component, $data); // set data that wasn't set in mount on the component directly foreach ($data as $property => $value) { @@ -103,10 +103,6 @@ public function mountFromObject(object $component, array $data, ComponentMetadat } $data[$key] = $value; - - if (!\is_scalar($value) && null !== $value) { - throw new \LogicException(sprintf('A "%s" prop was passed when creating the "%s" component. No matching %s property or mount() argument was found, so we attempted to use this as an HTML attribute. But, the value is not a scalar (it\'s a %s). Did you mean to pass this to your component or is there a typo on its name?', $key, $componentMetadata->getName(), $key, get_debug_type($value))); - } } return new MountedComponent( @@ -125,7 +121,7 @@ public function get(string $name): object return $this->getComponent($name); } - private function mount(object $component, array &$data, ComponentMetadata $metadata): void + private function mount(object $component, array &$data): void { try { $method = (new \ReflectionClass($component))->getMethod('mount'); @@ -135,7 +131,7 @@ private function mount(object $component, array &$data, ComponentMetadata $metad } if ($component instanceof AnonymousComponent) { - $component->mount($data, $this->componentTemplateFinder->findComponentProps($metadata->getTemplate())); + $component->mount($data); return; } diff --git a/src/TwigComponent/src/AnonymousAnonymousComponentTemplateParser.php b/src/TwigComponent/src/ComponentTemplateFinder.php similarity index 60% rename from src/TwigComponent/src/AnonymousAnonymousComponentTemplateParser.php rename to src/TwigComponent/src/ComponentTemplateFinder.php index 07cd6b150bf..452256a43f4 100644 --- a/src/TwigComponent/src/AnonymousAnonymousComponentTemplateParser.php +++ b/src/TwigComponent/src/ComponentTemplateFinder.php @@ -16,7 +16,7 @@ /** * @author Matheo Daninos */ -final class AnonymousAnonymousComponentTemplateParser implements AnonymousComponentTemplateParserInterface +final class ComponentTemplateFinder implements ComponentTemplateFinderInterface { public function __construct( private Environment $environment @@ -46,23 +46,4 @@ public function findAnonymousComponentTemplate(string $name): ?string return null; } - - public function findComponentProps(string $name): array - { - $loader = $this->environment->getLoader(); - $templateContent = $loader->getSourceContext($this->findAnonymousComponentTemplate($name))->getCode(); - - $pattern = '/{%\s*props\s*(.*?)\s*%}/'; - preg_match($pattern, $templateContent, $matches); - - if (isset($matches[1])) { - $props = $matches[1]; - $props = preg_replace('/\s/', '', $props); - $propsArray = explode(',', $props); - - return array_map(fn (string $propName) => strtok($propName, '='), $propsArray); - } - - return []; - } } diff --git a/src/TwigComponent/src/AnonymousComponentTemplateParserInterface.php b/src/TwigComponent/src/ComponentTemplateFinderInterface.php similarity index 72% rename from src/TwigComponent/src/AnonymousComponentTemplateParserInterface.php rename to src/TwigComponent/src/ComponentTemplateFinderInterface.php index f02aea2e7f2..1a76913ea51 100644 --- a/src/TwigComponent/src/AnonymousComponentTemplateParserInterface.php +++ b/src/TwigComponent/src/ComponentTemplateFinderInterface.php @@ -14,12 +14,7 @@ /** * @author Matheo Daninos */ -interface AnonymousComponentTemplateParserInterface +interface ComponentTemplateFinderInterface { public function findAnonymousComponentTemplate(string $name): ?string; - - /** - * @return array - */ - public function findComponentProps(string $name): array; } diff --git a/src/TwigComponent/src/DependencyInjection/TwigComponentExtension.php b/src/TwigComponent/src/DependencyInjection/TwigComponentExtension.php index 359586a607d..59b9789fe64 100644 --- a/src/TwigComponent/src/DependencyInjection/TwigComponentExtension.php +++ b/src/TwigComponent/src/DependencyInjection/TwigComponentExtension.php @@ -17,12 +17,12 @@ use Symfony\Component\DependencyInjection\Exception\LogicException; use Symfony\Component\DependencyInjection\Extension\Extension; use Symfony\Component\DependencyInjection\Reference; -use Symfony\UX\TwigComponent\AnonymousAnonymousComponentTemplateParser; use Symfony\UX\TwigComponent\Attribute\AsTwigComponent; use Symfony\UX\TwigComponent\ComponentFactory; use Symfony\UX\TwigComponent\ComponentRenderer; use Symfony\UX\TwigComponent\ComponentRendererInterface; use Symfony\UX\TwigComponent\ComponentStack; +use Symfony\UX\TwigComponent\ComponentTemplateFinder; use Symfony\UX\TwigComponent\DependencyInjection\Compiler\TwigComponentPass; use Symfony\UX\TwigComponent\Twig\ComponentExtension; use Symfony\UX\TwigComponent\Twig\ComponentLexer; @@ -41,7 +41,7 @@ public function load(array $configs, ContainerBuilder $container): void throw new LogicException('The TwigBundle is not registered in your application. Try running "composer require symfony/twig-bundle".'); } - $container->register('ux.twig_component.component_template_finder', AnonymousAnonymousComponentTemplateParser::class) + $container->register('ux.twig_component.component_template_finder', ComponentTemplateFinder::class) ->setArguments([ new Reference('twig'), ]) @@ -78,7 +78,7 @@ class_exists(AbstractArgument::class) ? new AbstractArgument(sprintf('Added in % ]) ; - $container->register(AnonymousAnonymousComponentTemplateParser::class, 'ux.twig_component.component_template_finder'); + $container->register(ComponentTemplateFinder::class, 'ux.twig_component.component_template_finder'); $container->register('ux.twig_component.twig.component_extension', ComponentExtension::class) ->addTag('twig.extension') diff --git a/src/TwigComponent/tests/Fixtures/templates/components/UserCard.html.twig b/src/TwigComponent/tests/Fixtures/templates/components/UserCard.html.twig index 5242c2740ad..4a3e94dbaf2 100644 --- a/src/TwigComponent/tests/Fixtures/templates/components/UserCard.html.twig +++ b/src/TwigComponent/tests/Fixtures/templates/components/UserCard.html.twig @@ -1,6 +1,4 @@ -{% props user %} - -
+

{{ user.name }}

{{ user.email }}

\ No newline at end of file From 66b7f58e2c685428098a16db8685e72be5cb3b08 Mon Sep 17 00:00:00 2001 From: matheo Date: Thu, 17 Aug 2023 18:26:07 +0200 Subject: [PATCH 4/8] remove props from the attribute in the PropsNode --- src/TwigComponent/src/ComponentAttributes.php | 9 +++++++++ src/TwigComponent/src/Twig/PropsNode.php | 1 + .../Fixtures/templates/components/UserCard.html.twig | 4 +++- 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/TwigComponent/src/ComponentAttributes.php b/src/TwigComponent/src/ComponentAttributes.php index ecd8f97918a..751e324306e 100644 --- a/src/TwigComponent/src/ComponentAttributes.php +++ b/src/TwigComponent/src/ComponentAttributes.php @@ -35,6 +35,10 @@ public function __toString(): string function (string $carry, string $key) { $value = $this->attributes[$key]; + if (!\is_scalar($value) && null !== $value) { + throw new \LogicException(sprintf('A "%s" prop was passed when creating the component. No matching %s property or mount() argument was found, so we attempted to use this as an HTML attribute. But, the value is not a scalar (it\'s a %s). Did you mean to pass this to your component or is there a typo on its name?', $key, $key, get_debug_type($value))); + } + if (null === $value) { trigger_deprecation('symfony/ux-twig-component', '2.8.0', 'Passing "null" as an attribute value is deprecated and will throw an exception in 3.0.'); $value = true; @@ -144,4 +148,9 @@ public function add($stimulusDto): self // add the remaining attributes for values/classes return $clone->defaults($controllersAttributes); } + + public function remove($key): void + { + unset($this->attributes[$key]); + } } diff --git a/src/TwigComponent/src/Twig/PropsNode.php b/src/TwigComponent/src/Twig/PropsNode.php index 91208c5195e..17aebc43ef7 100644 --- a/src/TwigComponent/src/Twig/PropsNode.php +++ b/src/TwigComponent/src/Twig/PropsNode.php @@ -31,6 +31,7 @@ public function compile(Compiler $compiler): void foreach ($this->getAttribute('names') as $name) { $compiler ->addDebugInfo($this) + ->write('$context[\'attributes\']->remove(\''.$name.'\');') ->write('if (!isset($context[\''.$name.'\'])) {') ; diff --git a/src/TwigComponent/tests/Fixtures/templates/components/UserCard.html.twig b/src/TwigComponent/tests/Fixtures/templates/components/UserCard.html.twig index 4a3e94dbaf2..5242c2740ad 100644 --- a/src/TwigComponent/tests/Fixtures/templates/components/UserCard.html.twig +++ b/src/TwigComponent/tests/Fixtures/templates/components/UserCard.html.twig @@ -1,4 +1,6 @@ -
+{% props user %} + +

{{ user.name }}

{{ user.email }}

\ No newline at end of file From 416c46a917b423a14938d57e83c9f1712f7aaa24 Mon Sep 17 00:00:00 2001 From: matheo Date: Fri, 18 Aug 2023 11:19:19 +0200 Subject: [PATCH 5/8] make remove method immutable and test if attributes are still render --- src/TwigComponent/src/ComponentAttributes.php | 8 ++++++-- src/TwigComponent/src/Twig/PropsNode.php | 2 +- .../anonymous_component_none_scalar_prop.html.twig | 2 +- .../Fixtures/templates/components/UserCard.html.twig | 2 +- .../tests/Integration/ComponentExtensionTest.php | 1 + 5 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/TwigComponent/src/ComponentAttributes.php b/src/TwigComponent/src/ComponentAttributes.php index 751e324306e..574a385d69e 100644 --- a/src/TwigComponent/src/ComponentAttributes.php +++ b/src/TwigComponent/src/ComponentAttributes.php @@ -149,8 +149,12 @@ public function add($stimulusDto): self return $clone->defaults($controllersAttributes); } - public function remove($key): void + public function remove($key): self { - unset($this->attributes[$key]); + $attributes = $this->attributes; + + unset($attributes[$key]); + + return new self($attributes); } } diff --git a/src/TwigComponent/src/Twig/PropsNode.php b/src/TwigComponent/src/Twig/PropsNode.php index 17aebc43ef7..250b0ea2be1 100644 --- a/src/TwigComponent/src/Twig/PropsNode.php +++ b/src/TwigComponent/src/Twig/PropsNode.php @@ -31,7 +31,7 @@ public function compile(Compiler $compiler): void foreach ($this->getAttribute('names') as $name) { $compiler ->addDebugInfo($this) - ->write('$context[\'attributes\']->remove(\''.$name.'\');') + ->write('$context[\'attributes\'] = $context[\'attributes\']->remove(\''.$name.'\');') ->write('if (!isset($context[\''.$name.'\'])) {') ; diff --git a/src/TwigComponent/tests/Fixtures/templates/anonymous_component_none_scalar_prop.html.twig b/src/TwigComponent/tests/Fixtures/templates/anonymous_component_none_scalar_prop.html.twig index a1c267e1f1c..4bbccfb3fb7 100644 --- a/src/TwigComponent/tests/Fixtures/templates/anonymous_component_none_scalar_prop.html.twig +++ b/src/TwigComponent/tests/Fixtures/templates/anonymous_component_none_scalar_prop.html.twig @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/TwigComponent/tests/Fixtures/templates/components/UserCard.html.twig b/src/TwigComponent/tests/Fixtures/templates/components/UserCard.html.twig index 5242c2740ad..1a606024828 100644 --- a/src/TwigComponent/tests/Fixtures/templates/components/UserCard.html.twig +++ b/src/TwigComponent/tests/Fixtures/templates/components/UserCard.html.twig @@ -1,6 +1,6 @@ {% props user %} -
+

{{ user.name }}

{{ user.email }}

\ No newline at end of file diff --git a/src/TwigComponent/tests/Integration/ComponentExtensionTest.php b/src/TwigComponent/tests/Integration/ComponentExtensionTest.php index 8d6dbabf455..56c8f12db9e 100644 --- a/src/TwigComponent/tests/Integration/ComponentExtensionTest.php +++ b/src/TwigComponent/tests/Integration/ComponentExtensionTest.php @@ -189,6 +189,7 @@ public function testRenderAnonymousComponentWithNonScalarProps(): void $output = self::getContainer()->get(Environment::class)->render('anonymous_component_none_scalar_prop.html.twig', ['user' => $user]); + $this->assertStringContainsString('class="foo"', $output); $this->assertStringContainsString('Fabien', $output); $this->assertStringContainsString('test@test.com', $output); } From f6ac821296c7b2dff1efa685bb2e2b8e5343613e Mon Sep 17 00:00:00 2001 From: Matheo Daninos Date: Wed, 23 Aug 2023 00:09:25 +0200 Subject: [PATCH 6/8] remove attributes from context --- .idea/.gitignore | 8 +++ .idea/codeStyles/Project.xml | 50 ++++++++++++++ .idea/codeStyles/codeStyleConfig.xml | 5 ++ .idea/inspectionProfiles/Project_Default.xml | 6 ++ .idea/modules.xml | 8 +++ .idea/php-test-framework.xml | 14 ++++ .idea/php.xml | 36 ++++++++++ .idea/ux.iml | 16 +++++ .idea/vcs.xml | 6 ++ src/TwigComponent/.idea/.gitignore | 8 +++ src/TwigComponent/.idea/TwigComponent.iml | 51 ++++++++++++++ src/TwigComponent/.idea/modules.xml | 8 +++ .../.idea/php-test-framework.xml | 14 ++++ src/TwigComponent/.idea/php.xml | 68 +++++++++++++++++++ src/TwigComponent/.idea/phpunit.xml | 10 +++ src/TwigComponent/.idea/vcs.xml | 6 ++ src/TwigComponent/src/ComponentAttributes.php | 2 +- src/TwigComponent/src/Twig/PropsNode.php | 29 ++++++-- .../templates/components/UserCard.html.twig | 1 + .../Integration/ComponentExtensionTest.php | 1 + 20 files changed, 339 insertions(+), 8 deletions(-) create mode 100644 .idea/.gitignore create mode 100644 .idea/codeStyles/Project.xml create mode 100644 .idea/codeStyles/codeStyleConfig.xml create mode 100644 .idea/inspectionProfiles/Project_Default.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/php-test-framework.xml create mode 100644 .idea/php.xml create mode 100644 .idea/ux.iml create mode 100644 .idea/vcs.xml create mode 100644 src/TwigComponent/.idea/.gitignore create mode 100644 src/TwigComponent/.idea/TwigComponent.iml create mode 100644 src/TwigComponent/.idea/modules.xml create mode 100644 src/TwigComponent/.idea/php-test-framework.xml create mode 100644 src/TwigComponent/.idea/php.xml create mode 100644 src/TwigComponent/.idea/phpunit.xml create mode 100644 src/TwigComponent/.idea/vcs.xml diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 00000000000..13566b81b01 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml new file mode 100644 index 00000000000..f814e022c8c --- /dev/null +++ b/.idea/codeStyles/Project.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml new file mode 100644 index 00000000000..79ee123c2b2 --- /dev/null +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 00000000000..03d9549ea8e --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 00000000000..b226e25c22a --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/php-test-framework.xml b/.idea/php-test-framework.xml new file mode 100644 index 00000000000..0ab6de03501 --- /dev/null +++ b/.idea/php-test-framework.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/php.xml b/.idea/php.xml new file mode 100644 index 00000000000..b623dfd0e5a --- /dev/null +++ b/.idea/php.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/ux.iml b/.idea/ux.iml new file mode 100644 index 00000000000..d5ecd7ac514 --- /dev/null +++ b/.idea/ux.iml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 00000000000..35eb1ddfbbc --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/TwigComponent/.idea/.gitignore b/src/TwigComponent/.idea/.gitignore new file mode 100644 index 00000000000..13566b81b01 --- /dev/null +++ b/src/TwigComponent/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/src/TwigComponent/.idea/TwigComponent.iml b/src/TwigComponent/.idea/TwigComponent.iml new file mode 100644 index 00000000000..093f1c4d951 --- /dev/null +++ b/src/TwigComponent/.idea/TwigComponent.iml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/TwigComponent/.idea/modules.xml b/src/TwigComponent/.idea/modules.xml new file mode 100644 index 00000000000..b054278bd89 --- /dev/null +++ b/src/TwigComponent/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/src/TwigComponent/.idea/php-test-framework.xml b/src/TwigComponent/.idea/php-test-framework.xml new file mode 100644 index 00000000000..0ab6de03501 --- /dev/null +++ b/src/TwigComponent/.idea/php-test-framework.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/TwigComponent/.idea/php.xml b/src/TwigComponent/.idea/php.xml new file mode 100644 index 00000000000..e724249a4d6 --- /dev/null +++ b/src/TwigComponent/.idea/php.xml @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/TwigComponent/.idea/phpunit.xml b/src/TwigComponent/.idea/phpunit.xml new file mode 100644 index 00000000000..4f8104cfb16 --- /dev/null +++ b/src/TwigComponent/.idea/phpunit.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/src/TwigComponent/.idea/vcs.xml b/src/TwigComponent/.idea/vcs.xml new file mode 100644 index 00000000000..b2bdec2d71b --- /dev/null +++ b/src/TwigComponent/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/TwigComponent/src/ComponentAttributes.php b/src/TwigComponent/src/ComponentAttributes.php index 574a385d69e..7ac148bb509 100644 --- a/src/TwigComponent/src/ComponentAttributes.php +++ b/src/TwigComponent/src/ComponentAttributes.php @@ -36,7 +36,7 @@ function (string $carry, string $key) { $value = $this->attributes[$key]; if (!\is_scalar($value) && null !== $value) { - throw new \LogicException(sprintf('A "%s" prop was passed when creating the component. No matching %s property or mount() argument was found, so we attempted to use this as an HTML attribute. But, the value is not a scalar (it\'s a %s). Did you mean to pass this to your component or is there a typo on its name?', $key, $key, get_debug_type($value))); + throw new \LogicException(sprintf('A "%s" prop was passed when creating the component. No matching "%s" property or mount() argument was found, so we attempted to use this as an HTML attribute. But, the value is not a scalar (it\'s a %s). Did you mean to pass this to your component or is there a typo on its name?', $key, $key, get_debug_type($value))); } if (null === $value) { diff --git a/src/TwigComponent/src/Twig/PropsNode.php b/src/TwigComponent/src/Twig/PropsNode.php index 250b0ea2be1..92932a5d53f 100644 --- a/src/TwigComponent/src/Twig/PropsNode.php +++ b/src/TwigComponent/src/Twig/PropsNode.php @@ -28,18 +28,21 @@ public function __construct(array $propsNames, array $values, $lineno = 0, strin public function compile(Compiler $compiler): void { + $compiler + ->addDebugInfo($this) + ->write('$propsNames = [];') + ; + foreach ($this->getAttribute('names') as $name) { $compiler - ->addDebugInfo($this) + ->write('$propsNames[] = \''.$name.'\';') ->write('$context[\'attributes\'] = $context[\'attributes\']->remove(\''.$name.'\');') - ->write('if (!isset($context[\''.$name.'\'])) {') - ; + ->write('if (!isset($context[\''.$name.'\'])) {'); if (!$this->hasNode($name)) { $compiler ->write('throw new \Twig\Error\RuntimeError("'.$name.' should be defined for component '.$this->getTemplateName().'");') - ->write('}') - ; + ->write('}'); continue; } @@ -48,8 +51,20 @@ public function compile(Compiler $compiler): void ->write('$context[\''.$name.'\'] = ') ->subcompile($this->getNode($name)) ->raw(";\n") - ->write('}') - ; + ->write('}'); } + + $compiler + ->write('$attributesKeys = array_keys($context[\'attributes\']->all());') + ->raw("\n") + ->write('foreach ($context as $key => $value) {') + ->raw("\n") + ->write('if (in_array($key, $attributesKeys) && !in_array($key, $propsNames)) {') + ->raw("\n") + ->raw('unset($context[$key]);') + ->raw("\n") + ->write('}') + ->write('}') + ; } } diff --git a/src/TwigComponent/tests/Fixtures/templates/components/UserCard.html.twig b/src/TwigComponent/tests/Fixtures/templates/components/UserCard.html.twig index 1a606024828..7acaf4afd51 100644 --- a/src/TwigComponent/tests/Fixtures/templates/components/UserCard.html.twig +++ b/src/TwigComponent/tests/Fixtures/templates/components/UserCard.html.twig @@ -3,4 +3,5 @@

{{ user.name }}

{{ user.email }}

+

class variable defined? {{ class is defined ? 'yes': 'no' }}

\ No newline at end of file diff --git a/src/TwigComponent/tests/Integration/ComponentExtensionTest.php b/src/TwigComponent/tests/Integration/ComponentExtensionTest.php index 56c8f12db9e..1e238f8d694 100644 --- a/src/TwigComponent/tests/Integration/ComponentExtensionTest.php +++ b/src/TwigComponent/tests/Integration/ComponentExtensionTest.php @@ -192,6 +192,7 @@ public function testRenderAnonymousComponentWithNonScalarProps(): void $this->assertStringContainsString('class="foo"', $output); $this->assertStringContainsString('Fabien', $output); $this->assertStringContainsString('test@test.com', $output); + $this->assertStringContainsString('class variable defined? no', $output); } private function renderComponent(string $name, array $data = []): string From 58fce65bc9e1f568b39400064d8a03528c2b95e2 Mon Sep 17 00:00:00 2001 From: matheo Date: Wed, 23 Aug 2023 00:45:36 +0200 Subject: [PATCH 7/8] remove .idea --- src/TwigComponent/.idea/.gitignore | 8 --- src/TwigComponent/.idea/TwigComponent.iml | 51 -------------- src/TwigComponent/.idea/modules.xml | 8 --- .../.idea/php-test-framework.xml | 14 ---- src/TwigComponent/.idea/php.xml | 68 ------------------- src/TwigComponent/.idea/phpunit.xml | 10 --- src/TwigComponent/.idea/vcs.xml | 6 -- 7 files changed, 165 deletions(-) delete mode 100644 src/TwigComponent/.idea/.gitignore delete mode 100644 src/TwigComponent/.idea/TwigComponent.iml delete mode 100644 src/TwigComponent/.idea/modules.xml delete mode 100644 src/TwigComponent/.idea/php-test-framework.xml delete mode 100644 src/TwigComponent/.idea/php.xml delete mode 100644 src/TwigComponent/.idea/phpunit.xml delete mode 100644 src/TwigComponent/.idea/vcs.xml diff --git a/src/TwigComponent/.idea/.gitignore b/src/TwigComponent/.idea/.gitignore deleted file mode 100644 index 13566b81b01..00000000000 --- a/src/TwigComponent/.idea/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml -# Editor-based HTTP Client requests -/httpRequests/ -# Datasource local storage ignored files -/dataSources/ -/dataSources.local.xml diff --git a/src/TwigComponent/.idea/TwigComponent.iml b/src/TwigComponent/.idea/TwigComponent.iml deleted file mode 100644 index 093f1c4d951..00000000000 --- a/src/TwigComponent/.idea/TwigComponent.iml +++ /dev/null @@ -1,51 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/TwigComponent/.idea/modules.xml b/src/TwigComponent/.idea/modules.xml deleted file mode 100644 index b054278bd89..00000000000 --- a/src/TwigComponent/.idea/modules.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/src/TwigComponent/.idea/php-test-framework.xml b/src/TwigComponent/.idea/php-test-framework.xml deleted file mode 100644 index 0ab6de03501..00000000000 --- a/src/TwigComponent/.idea/php-test-framework.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/TwigComponent/.idea/php.xml b/src/TwigComponent/.idea/php.xml deleted file mode 100644 index e724249a4d6..00000000000 --- a/src/TwigComponent/.idea/php.xml +++ /dev/null @@ -1,68 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/TwigComponent/.idea/phpunit.xml b/src/TwigComponent/.idea/phpunit.xml deleted file mode 100644 index 4f8104cfb16..00000000000 --- a/src/TwigComponent/.idea/phpunit.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/src/TwigComponent/.idea/vcs.xml b/src/TwigComponent/.idea/vcs.xml deleted file mode 100644 index b2bdec2d71b..00000000000 --- a/src/TwigComponent/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file From 6f693d6ab9662be8ec3fd37a5ae70e64a96091bf Mon Sep 17 00:00:00 2001 From: matheo Date: Wed, 23 Aug 2023 00:49:01 +0200 Subject: [PATCH 8/8] Remove .idea --- .idea/.gitignore | 8 ---- .idea/codeStyles/Project.xml | 50 -------------------- .idea/codeStyles/codeStyleConfig.xml | 5 -- .idea/inspectionProfiles/Project_Default.xml | 6 --- .idea/modules.xml | 8 ---- .idea/php-test-framework.xml | 14 ------ .idea/php.xml | 36 -------------- .idea/ux.iml | 16 ------- .idea/vcs.xml | 6 --- 9 files changed, 149 deletions(-) delete mode 100644 .idea/.gitignore delete mode 100644 .idea/codeStyles/Project.xml delete mode 100644 .idea/codeStyles/codeStyleConfig.xml delete mode 100644 .idea/inspectionProfiles/Project_Default.xml delete mode 100644 .idea/modules.xml delete mode 100644 .idea/php-test-framework.xml delete mode 100644 .idea/php.xml delete mode 100644 .idea/ux.iml delete mode 100644 .idea/vcs.xml diff --git a/.idea/.gitignore b/.idea/.gitignore deleted file mode 100644 index 13566b81b01..00000000000 --- a/.idea/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml -# Editor-based HTTP Client requests -/httpRequests/ -# Datasource local storage ignored files -/dataSources/ -/dataSources.local.xml diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml deleted file mode 100644 index f814e022c8c..00000000000 --- a/.idea/codeStyles/Project.xml +++ /dev/null @@ -1,50 +0,0 @@ - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml deleted file mode 100644 index 79ee123c2b2..00000000000 --- a/.idea/codeStyles/codeStyleConfig.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml deleted file mode 100644 index 03d9549ea8e..00000000000 --- a/.idea/inspectionProfiles/Project_Default.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml deleted file mode 100644 index b226e25c22a..00000000000 --- a/.idea/modules.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/php-test-framework.xml b/.idea/php-test-framework.xml deleted file mode 100644 index 0ab6de03501..00000000000 --- a/.idea/php-test-framework.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/php.xml b/.idea/php.xml deleted file mode 100644 index b623dfd0e5a..00000000000 --- a/.idea/php.xml +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/ux.iml b/.idea/ux.iml deleted file mode 100644 index d5ecd7ac514..00000000000 --- a/.idea/ux.iml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index 35eb1ddfbbc..00000000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file