Skip to content

Commit 0020235

Browse files
[FrameworkBundle][TwigBundle][Form] Add Twig filter, form-type extension and improve service definitions for HtmlSanitizer
1 parent 346eaa1 commit 0020235

File tree

4 files changed

+166
-0
lines changed

4 files changed

+166
-0
lines changed
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Form\Extension\HtmlSanitizer;
13+
14+
use Psr\Container\ContainerInterface;
15+
use Symfony\Component\Form\AbstractExtension;
16+
17+
/**
18+
* Integrates the HtmlSanitizer component with the Form library.
19+
*
20+
* @author Nicolas Grekas <[email protected]>
21+
*/
22+
class HtmlSanitizerExtension extends AbstractExtension
23+
{
24+
public function __construct(
25+
private ContainerInterface $sanitizers,
26+
private string $defaultSanitizer = 'default',
27+
) {
28+
}
29+
30+
protected function loadTypeExtensions(): array
31+
{
32+
return [
33+
new Type\TextTypeHtmlSanitizerExtension($this->sanitizers, $this->defaultSanitizer),
34+
];
35+
}
36+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Form\Extension\HtmlSanitizer\Type;
13+
14+
use Psr\Container\ContainerInterface;
15+
use Symfony\Component\Form\AbstractTypeExtension;
16+
use Symfony\Component\Form\Extension\Core\Type\TextType;
17+
use Symfony\Component\Form\FormBuilderInterface;
18+
use Symfony\Component\Form\FormEvent;
19+
use Symfony\Component\Form\FormEvents;
20+
use Symfony\Component\OptionsResolver\OptionsResolver;
21+
22+
/**
23+
* @author Titouan Galopin <[email protected]>
24+
*/
25+
class TextTypeHtmlSanitizerExtension extends AbstractTypeExtension
26+
{
27+
public function __construct(
28+
private ContainerInterface $sanitizers,
29+
private string $defaultSanitizer = 'default',
30+
) {
31+
}
32+
33+
public static function getExtendedTypes(): iterable
34+
{
35+
return [TextType::class];
36+
}
37+
38+
public function configureOptions(OptionsResolver $resolver)
39+
{
40+
$resolver
41+
->setDefaults(['sanitize_html' => false, 'sanitizer' => null])
42+
->setAllowedTypes('sanitize_html', 'bool')
43+
->setAllowedTypes('sanitizer', ['string', 'null'])
44+
;
45+
}
46+
47+
public function buildForm(FormBuilderInterface $builder, array $options)
48+
{
49+
if (!$options['sanitize_html']) {
50+
return;
51+
}
52+
53+
$sanitizers = $this->sanitizers;
54+
$sanitizer = $options['sanitizer'] ?? $this->defaultSanitizer;
55+
56+
$builder->addEventListener(
57+
FormEvents::PRE_SUBMIT,
58+
static function (FormEvent $event) use ($sanitizers, $sanitizer) {
59+
if (is_scalar($data = $event->getData()) && '' !== trim($data)) {
60+
$event->setData($sanitizers->get($sanitizer)->sanitize($data));
61+
}
62+
},
63+
10000 /* as soon as possible */
64+
);
65+
}
66+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Form\Tests\Extension\Csrf\Type;
13+
14+
use Symfony\Component\DependencyInjection\ServiceLocator;
15+
use Symfony\Component\Form\Extension\Core\Type\FormType;
16+
use Symfony\Component\Form\Extension\Core\Type\TextType;
17+
use Symfony\Component\Form\Extension\HtmlSanitizer\HtmlSanitizerExtension;
18+
use Symfony\Component\Form\Test\TypeTestCase;
19+
use Symfony\Component\HtmlSanitizer\HtmlSanitizerInterface;
20+
21+
class TextTypeHtmlSanitizerExtensionTest extends TypeTestCase
22+
{
23+
protected function getExtensions()
24+
{
25+
$fooSanitizer = $this->createMock(HtmlSanitizerInterface::class);
26+
$fooSanitizer->expects($this->once())
27+
->method('sanitize')
28+
->with('foobar')
29+
->willReturn('foo');
30+
31+
$barSanitizer = $this->createMock(HtmlSanitizerInterface::class);
32+
$barSanitizer->expects($this->once())
33+
->method('sanitize')
34+
->with('foobar')
35+
->willReturn('bar');
36+
37+
return array_merge(parent::getExtensions(), [
38+
new HtmlSanitizerExtension(new ServiceLocator([
39+
'foo' => fn () => $fooSanitizer,
40+
'bar' => fn () => $barSanitizer,
41+
]), 'foo'),
42+
]);
43+
}
44+
45+
public function testSanitizer()
46+
{
47+
$form = $this->factory->createBuilder(FormType::class, ['data' => null])
48+
->add('data', TextType::class, ['sanitize_html' => true])
49+
->getForm()
50+
;
51+
$form->submit(['data' => 'foobar']);
52+
53+
$this->assertSame(['data' => 'foo'], $form->getData());
54+
55+
$form = $this->factory->createBuilder(FormType::class, ['data' => null])
56+
->add('data', TextType::class, ['sanitize_html' => true, 'sanitizer' => 'bar'])
57+
->getForm()
58+
;
59+
$form->submit(['data' => 'foobar']);
60+
61+
$this->assertSame(['data' => 'bar'], $form->getData());
62+
}
63+
}

composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
"symfony/expression-language": "^5.4|^6.0",
3434
"symfony/config": "^5.4|^6.0",
3535
"symfony/console": "^5.4|^6.0",
36+
"symfony/html-sanitizer": "^6.1",
3637
"symfony/http-foundation": "^5.4|^6.0",
3738
"symfony/http-kernel": "^5.4|^6.0",
3839
"symfony/intl": "^5.4|^6.0",

0 commit comments

Comments
 (0)