Skip to content

Commit b5d4e77

Browse files
smnandrefabpot
authored andcommitted
Remove ComponentAttributeFactory and inject EscaperRuntime directly
1 parent 442debb commit b5d4e77

12 files changed

Lines changed: 111 additions & 332 deletions

CHANGELOG.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
11
# CHANGELOG
22

3+
## 2.25.1
4+
5+
- [SECURITY] `ComponentAttributes` now requires a `Twig\Runtime\EscaperRuntime`
6+
instance as second argument
7+
- Remove `HtmlAttributeEscaperInterface`, `TwigHtmlAttributeEscaper` and `ComponentAttributesFactory`
8+
39
## 2.25.0
410

511
- [SECURITY] Make `ComponentAttributes` responsible for attribute escaping ensuring
6-
consistent and secure HTML output across all rendering contexts.
12+
consistent and secure HTML output across all rendering contexts
713
- Deprecate not passing an `HtmlAttributeEscaperInterface` to the `ComponentAttributes`
8-
constructor.
14+
constructor
915

1016
## 2.20.0
1117

src/ComponentAttributes.php

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@
1212
namespace Symfony\UX\TwigComponent;
1313

1414
use Symfony\UX\StimulusBundle\Dto\StimulusAttributes;
15-
use Symfony\UX\TwigComponent\Escaper\HtmlAttributeEscaperInterface;
1615
use Symfony\WebpackEncoreBundle\Dto\AbstractStimulusDto;
16+
use Twig\Runtime\EscaperRuntime;
1717

1818
/**
1919
* @author Kevin Bond <kevinbond@gmail.com>
@@ -29,19 +29,13 @@ final class ComponentAttributes implements \Stringable, \IteratorAggregate, \Cou
2929
/** @var array<string,true> */
3030
private array $rendered = [];
3131

32-
private readonly ?HtmlAttributeEscaperInterface $escaper;
33-
3432
/**
3533
* @param array<string, string|bool> $attributes
3634
*/
3735
public function __construct(
3836
private array $attributes,
39-
?HtmlAttributeEscaperInterface $escaper = null,
37+
private readonly EscaperRuntime $escaper,
4038
) {
41-
// Third argument used as internal flag to prevent multiple deprecations
42-
if ((null === $this->escaper = $escaper) && 3 > func_num_args()) {
43-
trigger_deprecation('symfony/ux-twig-component', '2.24', 'Not passing an "%s" to "%s" is deprecated and will throw in 3.0.', HtmlAttributeEscaperInterface::class, self::class);
44-
}
4539
}
4640

4741
public function __toString(): string
@@ -87,13 +81,17 @@ public function __toString(): string
8781
// - special syntax names (Vue.js, Svelte, Alpine.js, ...)
8882
// v-*, x-*, @*, :*
8983
if (!ctype_alpha(str_replace(['-', '_', ':', '@', '.'], '', $key))) {
90-
$key = $this->escaper?->escapeName($key) ?? $key;
84+
$key = (string) $this->escaper->escape($key, 'html_attr');
9185
}
9286

9387
if (true === $value) {
9488
$attributes .= ' '.$key;
9589
} else {
96-
$attributes .= ' '.\sprintf('%s="%s"', $key, $this->escaper?->escapeValue($value) ?? $value);
90+
if (!ctype_alnum(str_replace(['-', '_'], '', $value))) {
91+
$value = $this->escaper->escape($value, 'html');
92+
}
93+
94+
$attributes .= ' '.\sprintf('%s="%s"', $key, $value);
9795
}
9896
}
9997

@@ -167,7 +165,7 @@ public function defaults(iterable $attributes): self
167165
unset($attributes[$attribute]);
168166
}
169167

170-
return new self($attributes, $this->escaper, true);
168+
return new self($attributes, $this->escaper);
171169
}
172170

173171
/**
@@ -183,7 +181,7 @@ public function only(string ...$keys): self
183181
}
184182
}
185183

186-
return new self($attributes, $this->escaper, true);
184+
return new self($attributes, $this->escaper);
187185
}
188186

189187
/**
@@ -221,7 +219,7 @@ public function add($stimulusDto): self
221219
)));
222220
unset($controllersAttributes['data-controller']);
223221

224-
$clone = new self($attributes, $this->escaper, true);
222+
$clone = new self($attributes, $this->escaper);
225223

226224
// add the remaining attributes for values/classes
227225
return $clone->defaults($controllersAttributes);
@@ -233,7 +231,7 @@ public function remove($key): self
233231

234232
unset($attributes[$key]);
235233

236-
return new self($attributes, $this->escaper, true);
234+
return new self($attributes, $this->escaper);
237235
}
238236

239237
public function nested(string $namespace): self
@@ -249,7 +247,7 @@ public function nested(string $namespace): self
249247
}
250248
}
251249

252-
return new self($attributes, $this->escaper, true);
250+
return new self($attributes, $this->escaper);
253251
}
254252

255253
public function getIterator(): \Traversable

src/ComponentAttributesFactory.php

Lines changed: 0 additions & 42 deletions
This file was deleted.

src/ComponentFactory.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
use Symfony\Contracts\Service\ResetInterface;
1818
use Symfony\UX\TwigComponent\Event\PostMountEvent;
1919
use Symfony\UX\TwigComponent\Event\PreMountEvent;
20+
use Twig\Environment;
21+
use Twig\Runtime\EscaperRuntime;
2022

2123
/**
2224
* @author Kevin Bond <kevinbond@gmail.com>
@@ -38,7 +40,7 @@ public function __construct(
3840
private EventDispatcherInterface $eventDispatcher,
3941
private array $config,
4042
private readonly array $classMap,
41-
private ComponentAttributesFactory $componentAttributesFactory,
43+
private readonly Environment $twig,
4244
) {
4345
}
4446

@@ -120,7 +122,7 @@ public function mountFromObject(object $component, array $data, ComponentMetadat
120122
return new MountedComponent(
121123
$componentMetadata->getName(),
122124
$component,
123-
$this->componentAttributesFactory->create([...$attributes, ...$data]),
125+
new ComponentAttributes([...$attributes, ...$data], $this->twig->getRuntime(EscaperRuntime::class)),
124126
$originalData,
125127
$postMount->getExtraMetadata(),
126128
);

src/DependencyInjection/TwigComponentExtension.php

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@
3030
use Symfony\UX\TwigComponent\Attribute\AsTwigComponent;
3131
use Symfony\UX\TwigComponent\CacheWarmer\TwigComponentCacheWarmer;
3232
use Symfony\UX\TwigComponent\Command\TwigComponentDebugCommand;
33-
use Symfony\UX\TwigComponent\ComponentAttributesFactory;
3433
use Symfony\UX\TwigComponent\ComponentFactory;
3534
use Symfony\UX\TwigComponent\ComponentProperties;
3635
use Symfony\UX\TwigComponent\ComponentRenderer;
@@ -93,7 +92,7 @@ static function (ChildDefinition $definition, AsTwigComponent $attribute) {
9392
new Reference('event_dispatcher'),
9493
new AbstractArgument(\sprintf('Added in %s.', TwigComponentPass::class)),
9594
new AbstractArgument(\sprintf('Added in %s.', TwigComponentPass::class)),
96-
new Reference('ux.twig_component.component_attributes_factory'),
95+
new Reference('twig'),
9796
])
9897
->addTag('kernel.reset', ['method' => 'reset'])
9998
;
@@ -108,12 +107,6 @@ static function (ChildDefinition $definition, AsTwigComponent $attribute) {
108107
])
109108
;
110109

111-
$container->register('ux.twig_component.component_attributes_factory', ComponentAttributesFactory::class)
112-
->setArguments([
113-
new Reference('twig'),
114-
])
115-
;
116-
117110
$container->register('ux.twig_component.component_renderer', ComponentRenderer::class)
118111
->setArguments([
119112
new Reference('twig'),

src/Escaper/HtmlAttributeEscaperInterface.php

Lines changed: 0 additions & 33 deletions
This file was deleted.

src/Escaper/TwigHtmlAttributeEscaper.php

Lines changed: 0 additions & 54 deletions
This file was deleted.

0 commit comments

Comments
 (0)