Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 8101e63

Browse filesBrowse files
committed
Added DomainValidator
1 parent 519ba3c commit 8101e63
Copy full SHA for 8101e63

File tree

3 files changed

+269
-0
lines changed
Filter options

3 files changed

+269
-0
lines changed
+29Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
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\Validator\Constraints;
13+
14+
use Symfony\Component\Validator\Constraint;
15+
16+
/**
17+
* @Annotation
18+
* @Target({"PROPERTY", "METHOD", "ANNOTATION"})
19+
*
20+
* @author Dmitrii Poddubnyi <dpoddubny@gmail.com>
21+
*/
22+
class Domain extends Constraint
23+
{
24+
const INVALID_FORMAT_ERROR = '7057ffdb-0af4-4f7e-bd5e-e9acfa6d7a2d';
25+
26+
public $message = 'This value is not a valid domain';
27+
28+
public $requireTld = true;
29+
}
+75Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
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\Validator\Constraints;
13+
14+
use Symfony\Component\Validator\Constraint;
15+
use Symfony\Component\Validator\ConstraintValidator;
16+
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
17+
use Symfony\Component\Validator\Exception\UnexpectedValueException;
18+
19+
/**
20+
* @author Dmitrii Poddubnyi <dpoddubny@gmail.com>
21+
*/
22+
class DomainValidator extends ConstraintValidator
23+
{
24+
private const RESERVED = [
25+
'example',
26+
'invalid',
27+
'localhost',
28+
'test',
29+
];
30+
31+
public function validate($value, Constraint $constraint)
32+
{
33+
if (!$constraint instanceof Domain) {
34+
throw new UnexpectedTypeException($constraint, __NAMESPACE__.'\Domain');
35+
}
36+
37+
if (null === $value || '' === $value) {
38+
return;
39+
}
40+
41+
if (!is_scalar($value) && !(\is_object($value) && method_exists($value, '__toString'))) {
42+
throw new UnexpectedValueException($value, 'string');
43+
}
44+
45+
$value = (string) $value;
46+
if ('' === $value) {
47+
return;
48+
}
49+
if (!$this->isValid($value) || ($constraint->requireTld && !$this->isTld($value))) {
50+
$this->context->buildViolation($constraint->message)
51+
->setParameter('{{ value }}', $this->formatValue($value))
52+
->setCode(Domain::INVALID_FORMAT_ERROR)
53+
->addViolation();
54+
}
55+
}
56+
57+
private function isValid(string $domain): bool
58+
{
59+
return false !== filter_var($domain, FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME);
60+
}
61+
62+
private function isTld(string $domain): bool
63+
{
64+
return (false !== strpos($domain, '.')) && (!$this->isReserved($domain));
65+
}
66+
67+
private function isReserved(string $domain): bool
68+
{
69+
$parts = explode('.', $domain);
70+
$domain = end($parts);
71+
$domain = strtolower($domain);
72+
73+
return \in_array($domain, self::RESERVED);
74+
}
75+
}
+165Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
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\Validator\Tests\Constraints;
13+
14+
use Symfony\Component\Validator\Constraints\Domain;
15+
use Symfony\Component\Validator\Constraints\DomainValidator;
16+
use Symfony\Component\Validator\Test\ConstraintValidatorTestCase;
17+
18+
/**
19+
* @author Dmitrii Poddubnyi <dpoddubny@gmail.com>
20+
*/
21+
class DomainValidatorTest extends ConstraintValidatorTestCase
22+
{
23+
protected function createValidator()
24+
{
25+
return new DomainValidator();
26+
}
27+
28+
public function testNullIsValid()
29+
{
30+
$this->validator->validate(null, new Domain());
31+
32+
$this->assertNoViolation();
33+
}
34+
35+
public function testEmptyStringIsValid()
36+
{
37+
$this->validator->validate('', new Domain());
38+
39+
$this->assertNoViolation();
40+
}
41+
42+
/**
43+
* @expectedException \Symfony\Component\Validator\Exception\UnexpectedValueException
44+
*/
45+
public function testExpectsStringCompatibleType()
46+
{
47+
$this->validator->validate(new \stdClass(), new Domain());
48+
}
49+
50+
/**
51+
* @dataProvider getValidTldDomains
52+
*/
53+
public function testValidTldDomains_passValidation_ifTldRequired($domain)
54+
{
55+
$this->validator->validate($domain, new Domain());
56+
57+
$this->assertNoViolation();
58+
}
59+
60+
/**
61+
* @dataProvider getValidTldDomains
62+
*/
63+
public function testValidTldDomains_passValidation_ifTldNotRequired($domain)
64+
{
65+
$this->validator->validate($domain, new Domain(['requireTld' => false]));
66+
67+
$this->assertNoViolation();
68+
}
69+
70+
public function getValidTldDomains()
71+
{
72+
return [
73+
['symfony.com'],
74+
['example.co.uk'],
75+
['example.fr'],
76+
['example.com'],
77+
['xn--diseolatinoamericano-66b.com'],
78+
['xn--ggle-0nda.com'],
79+
['www.xn--simulateur-prt-2kb.fr'],
80+
[sprintf('%s.com', str_repeat('a', 20))],
81+
];
82+
}
83+
84+
/**
85+
* @dataProvider getInvalidDomains
86+
*/
87+
public function testInvalidDomains_throwException_ifTldRequired($domain)
88+
{
89+
$this->validator->validate($domain, new Domain([
90+
'message' => 'myMessage',
91+
]));
92+
93+
$this->buildViolation('myMessage')
94+
->setParameter('{{ value }}', '"'.$domain.'"')
95+
->setCode(Domain::INVALID_FORMAT_ERROR)
96+
->assertRaised();
97+
}
98+
99+
/**
100+
* @dataProvider getInvalidDomains
101+
*/
102+
public function testInvalidDomains_throwException_ifTldNotRequired($domain)
103+
{
104+
$this->validator->validate($domain, new Domain([
105+
'message' => 'myMessage',
106+
'requireTld' => false,
107+
]));
108+
109+
$this->buildViolation('myMessage')
110+
->setParameter('{{ value }}', '"'.$domain.'"')
111+
->setCode(Domain::INVALID_FORMAT_ERROR)
112+
->assertRaised();
113+
}
114+
115+
public function getInvalidDomains()
116+
{
117+
return [
118+
['acme..com'],
119+
['qq--.com'],
120+
['-example.com'],
121+
['example-.com'],
122+
[sprintf('%s.com', str_repeat('a', 300))],
123+
];
124+
}
125+
126+
/**
127+
* @dataProvider getReservedDomains
128+
*/
129+
public function testReservedDomains_passValidation_ifTldNotRequired($domain)
130+
{
131+
$this->validator->validate($domain, new Domain(['requireTld' => false]));
132+
133+
$this->assertNoViolation();
134+
}
135+
136+
/**
137+
* @dataProvider getReservedDomains
138+
*/
139+
public function testReservedDomains_throwException_ifTldRequired($domain)
140+
{
141+
$this->validator->validate($domain, new Domain([
142+
'message' => 'myMessage',
143+
'requireTld' => true,
144+
]));
145+
146+
$this->buildViolation('myMessage')
147+
->setParameter('{{ value }}', '"'.$domain.'"')
148+
->setCode(Domain::INVALID_FORMAT_ERROR)
149+
->assertRaised();
150+
}
151+
152+
public function getReservedDomains()
153+
{
154+
return [
155+
['example'],
156+
['foo.example'],
157+
['invalid'],
158+
['bar.invalid'],
159+
['localhost'],
160+
['lol.localhost'],
161+
['test'],
162+
['abc.test'],
163+
];
164+
}
165+
}

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.