8000 [Form] UrlType should not add protocol to emails · symfony/symfony@01931e1 · GitHub
[go: up one dir, main page]

Skip to content

Commit 01931e1

Browse files
committed
[Form] UrlType should not add protocol to emails
1 parent ff70bd1 commit 01931e1

File tree

8 files changed

+119
-28
lines changed

8 files changed

+119
-28
lines changed

UPGRADE-5.4.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ Form
2222
------
2323

2424
* Deprecate calling `FormErrorIterator::children()` if the current element is not iterable.
25+
* Add `'default_protocol_skip_email' => true` to `UrlType` options.
2526

2627
FrameworkBundle
2728
---------------

src/Symfony/Component/Form/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ CHANGELOG
77
* Deprecate calling `FormErrorIterator::children()` if the current element is not iterable.
88
* Allow to pass `TranslatableMessage` objects to the `help` option
99
* Add the `EnumType`
10+
* Deprecate usage of `UrlType` without option `'default_protocol_skip_email' => true`, added to prevent emails from being converted to valid URLs.
1011

1112
5.3
1213
---

src/Symfony/Component/Form/Extension/Core/EventListener/FixUrlProtocolListener.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
class FixUrlProtocolListener implements EventSubscriberInterface
2424
{
2525
private $defaultProtocol;
26+
private $skipEmail = false;
2627

2728
/**
2829
* @param string|null $defaultProtocol The URL scheme to add when there is none or null to not modify the data
@@ -32,11 +33,25 @@ public function __construct(?string $defaultProtocol = 'http')
3233
$this->defaultProtocol = $defaultProtocol;
3334
}
3435

36+
/**
37+
* @param boolean $skipEmail The URL scheme is not added to values that match an email pattern.
38+
*/
39+
public function skipEmail(): void
40+
{
41+
$this->skipEmail = true;
42+
}
43+
3544
public function onSubmit(FormEvent $event)
3645
{
3746
$data = $event->getData();
3847

3948
if ($this->defaultProtocol && $data && \is_string($data) && !preg_match('~^[\w+.-]+://~', $data)) {
49+
if (preg_match('~^[^:/]+@[A-Za-z0-9-.]+\.[A-Za-z0-9]+$~', $data)) {
50+
if ($this->skipEmail) {
51+
return;
52+
}
53+
trigger_deprecation('symfony/form', '5.4', 'Class "%s", will add a scheme to urls that looks like emails in 6.0. Call "setIgnoreEmail(true)"', __CLASS__);
54+
}
4055
$event->setData($this->defaultProtocol.'://'.$data);
4156
}
4257
}

src/Symfony/Component/Form/Extension/Core/Type/UrlType.php

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,13 @@ class UrlType extends AbstractType
2727
public function buildForm(FormBuilderInterface $builder, array $options)
2828
{
2929
if (null !== $options['default_protocol']) {
30-
$builder->addEventSubscriber(new FixUrlProtocolListener($options['default_protocol']));
30+
$subscriber = new FixUrlProtocolListener($options['default_protocol']);
31+
if ($options['default_protocol_skip_email']) {
32+
$subscriber->skipEmail();
33+
} else {
34+
trigger_deprecation('symfony/form', '5.4', 'Type "%s" option "default_protocol_skip_email" will be "true" in 6.0.', static::class);
35+
}
36+
$builder->addEventSubscriber($subscriber);
3137
}
3238
}
3339

@@ -54,9 +60,11 @@ public function configureOptions(OptionsResolver $resolver)
5460
? $previousValue
5561
: 'Please enter a valid URL.';
5662
},
63+
'default_protocol_skip_email' => false,
5764
]);
5865

5966
$resolver->setAllowedTypes('default_protocol', ['null', 'string']);
67+
$resolver->setAllowedTypes('default_protocol_skip_email', 'bool');
6068
}
6169

6270
/**

src/Symfony/Component/Form/Tests/Extension/Core/EventListener/FixUrlProtocolListenerTest.php

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
namespace Symfony\Component\Form\Tests\Extension\Core\EventListener;
1313

1414
use PHPUnit\Framework\TestCase;
15+
use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait;
1516
use Symfony\Component\EventDispatcher\EventDispatcher;
1617
use Symfony\Component\Form\Extension\Core\EventListener\FixUrlProtocolListener;
1718
use Symfony\Component\Form\Form;
@@ -20,25 +21,16 @@
2021

2122
class FixUrlProtocolListenerTest extends TestCase
2223
{
24+
use ExpectDeprecationTrait;
25+
2326
public function testFixHttpUrl()
2427
{
2528
$data = 'www.symfony.com';
2629
$form = new Form(new FormConfigBuilder('name', null, new EventDispatcher()));
2730
$event = new FormEvent($form, $data);
2831

2932
$filter = new FixUrlProtocolListener('http');
30-
$filter->onSubmit($event);
31-
32-
$this->assertEquals('http://www.symfony.com', $event->getData());
33-
}
34-
35-
public function testSkipKnownUrl()
36-
{
37-
$data = 'http://www.symfony.com';
38-
$form = new Form(new FormConfigBuilder('name', null, new EventDispatcher()));
39-
$event = new FormEvent($form, $data);
40-
41-
$filter = new FixUrlProtocolListener('http');
33+
$filter->skipEmail();
4234
$filter->onSubmit($event);
4335

4436
$this->assertEquals('http://www.symfony.com', $event->getData());
@@ -47,11 +39,14 @@ public function testSkipKnownUrl()
4739
public function provideUrlsWithSupportedProtocols()
4840
{
4941
return [
42+
['http://www.symfony.com'],
5043
['ftp://www.symfony.com'],
5144
['chrome-extension://foo'],
5245
['h323://foo'],
5346
['iris.beep://foo'],
5447
['foo+bar://foo'],
48+
['fabien@symfony.com'],
49+
['Contact+42@subdomain.example.com'],
5550
];
5651
}
5752

@@ -64,8 +59,26 @@ public function testSkipOtherProtocol($url)
6459
$event = new FormEvent($form, $url);
6560

6661
$filter = new FixUrlProtocolListener('http');
62+
$filter->skipEmail();
6763
$filter->onSubmit($event);
6864

6965
$this->assertEquals($url, $event->getData());
7066
}
67+
68+
/**
69+
* @group legacy
70+
*/
71+
public function testDeprecatedFixEmail()
72+
{
73+
$this->expectDeprecation('Since symfony/form 5.4: Class "Symfony\Component\Form\Extension\Core\EventListener\FixUrlProtocolListener", will add a scheme to urls that looks like emails in 6.0. Call "setIgnoreEmail(true)"');
74+
75+
$data = 'fabien@symfony.com';
76+
$form = new Form(new FormConfigBuilder('name', null, new EventDispatcher()));
77+
$event = new FormEvent($form, $data);
78+
79+
$filter = new FixUrlProtocolListener('http');
80+
$filter->onSubmit($event);
81+
82+
$this->assertEquals('http://fabien@symfony.com', $event->getData());
83+
}
7184
}

src/Symfony/Component/Form/Tests/Extension/Core/Type/TextTypeTest.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,9 @@ public function testSubmitNull($expected = null, $norm = null, $view = null)
2222

2323
public function testSubmitNullReturnsNullWithEmptyDataAsString()
2424
{
25-
$form = $this->factory->create(static::TESTED_TYPE, 'name', [
25+
$form = $this->factory->create(static::TESTED_TYPE, 'name', array_merge($this->getTestOptions(), [
2626
'empty_data' => '',
27-
]);
27+
]));
2828

2929
$form->submit(null);
3030
$this->assertSame('', $form->getData());
@@ -48,9 +48,9 @@ public function provideZeros()
4848
*/
4949
public function testSetDataThroughParamsWithZero($data, $dataAsString)
5050
{
51-
$form = $this->factory->create(static::TESTED_TYPE, null, [
51+
$form = $this->factory->create(static::TESTED_TYPE, null, array_merge($this->getTestOptions(), [
5252
'data' => $data,
53-
]);
53+
]));
5454
$view = $form->createView();
5555

5656
$this->assertFalse($form->isEmpty());
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 <fabien@symfony.com>
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\Core\Type;
13+
14+
/**
15+
* @group legacy
16+
*/
17+
class UrlTypeLegacyTest extends UrlTypeTest
18+
{
19+
/**
20+
* Legacy behavior. Replace test in parent class.
21+
*/
22+
public function testSubmitAddsNoDefaultProtocolToEmail()
23+
{
24+
$form = $this->factory->create(static::TESTED_TYPE, 'name', $this->getTestOptions());
25+
26+
$form->submit('contact@domain.com');
27+
28+
$this->assertSame('http://contact@domain.com', $form->getData());
29+
$this->assertSame('http://contact@domain.com', $form->getViewData());
30+
}
31+
32+
protected function getTestOptions(): array
33+
{
34+
return [];
35+
}
36+
}

src/Symfony/Component/Form/Tests/Extension/Core/Type/UrlTypeTest.php

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,19 +19,29 @@ class UrlTypeTest extends TextTypeTest
1919

2020
public function testSubmitAddsDefaultProtocolIfNoneIsIncluded()
2121
{
22-
$form = $this->factory->create(static::TESTED_TYPE, 'name');
22+
$form = $this->factory->create(static::TESTED_TYPE, 'name', $this->getTestOptions());
2323

2424
$form->submit('www.domain.com');
2525

2626
$this->assertSame('http://www.domain.com', $form->getData());
2727
$this->assertSame('http://www.domain.com', $form->getViewData());
2828
}
2929

30+
public function testSubmitAddsNoDefaultProtocolToEmail()
31+
{
32+
$form = $this->factory->create(static::TESTED_TYPE, 'name', $this->getTestOptions());
33+
34+
$form->submit('contact@domain.com');
35+
36+
$this->assertSame('contact@domain.com', $form->getData());
37+
$this->assertSame('contact@domain.com', $form->getViewData());
38+
}
39+
3040
public function testSubmitAddsNoDefaultProtocolIfAlreadyIncluded()
3141
{
32-
$form = $this->factory->create(static::TESTED_TYPE, null, [
42+
$form = $this->factory->create(static::TESTED_TYPE, null, array_merge($this->getTestOptions(), [
3343
'default_protocol' => 'http',
34-
]);
44+
]));
3545

3646
$form->submit('ftp://www.domain.com');
3747

@@ -41,9 +51,9 @@ public function testSubmitAddsNoDefaultProtocolIfAlreadyIncluded()
4151

4252
public function testSubmitAddsNoDefaultProtocolIfEmpty()
4353
{
44-
$form = $this->factory->create(static::TESTED_TYPE, null, [
54+
$form = $this->factory->create(static::TESTED_TYPE, null, array_merge($this->getTestOptions(), [
4555
'default_protocol' => 'http',
46-
]);
56+
]));
4757

4858
$form->submit('');
4959

@@ -53,9 +63,9 @@ public function testSubmitAddsNoDefaultProtocolIfEmpty()
5363

5464
public function testSubmitAddsNoDefaultProtocolIfNull()
5565
{
56-
$form = $this->factory->create(static::TESTED_TYPE, null, [
66+
$form = $this->factory->create(static::TESTED_TYPE, null, array_merge($this->getTestOptions(), [
5767
'default_protocol' => 'http',
58-
]);
68+
]));
5969

6070
$form->submit(null);
6171

@@ -65,9 +75,9 @@ public function testSubmitAddsNoDefaultProtocolIfNull()
6575

6676
public function testSubmitAddsNoDefaultProtocolIfSetToNull()
6777
{
68-
$form = $this->factory->create(static::TESTED_TYPE, null, [
78+
$form = $this->factory->create(static::TESTED_TYPE, null, array_merge($this->getTestOptions(), [
6979
'default_protocol' => null,
70-
]);
80+
]));
7181

7282
$form->submit('www.domain.com');
7383

@@ -85,14 +95,21 @@ public function testThrowExceptionIfDefaultProtocolIsInvalid()
8595

8696
public function testSubmitNullUsesDefaultEmptyData($emptyData = 'empty', $expectedData = 'http://empty')
8797
{
88-
$form = $this->factory->create(static::TESTED_TYPE, null, [
98+
$form = $this->factory->create(static::TESTED_TYPE, null, array_merge($this->getTestOptions(), [
8999
'empty_data' => $emptyData,
90-
]);
100+
]));
91101
$form->submit(null);
92102

93103
// listener normalizes data on submit
94104
$this->assertSame($expectedData, $form->getViewData());
95105
$this->assertSame($expectedData, $form->getNormData());
96106
$this->assertSame($expectedData, $form->getData());
97107
}
108+
109+
protected function getTestOptions(): array
110+
{
111+
return [
112+
'default_protocol_skip_email' => true,
113+
];
114+
}
98115
}

0 commit comments

Comments
 (0)
0