Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 06d9fdd

Browse files
committed
[Validator] Add support for versions (PUBLIC, PRIVATE, ALL) in MacAddress constraint
1 parent 09437dc commit 06d9fdd

File tree

5 files changed

+133
-5
lines changed

5 files changed

+133
-5
lines changed

src/Symfony/Component/Validator/CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ CHANGELOG
66

77
* Add support for `Stringable` values when using the `Cidr`, `CssColor`, `ExpressionSyntax` and `PasswordStrength` constraints
88
* Add `MacAddress` constraint
9+
* Add support for versions (`PUBLIC`, `PRIVATE`, `ALL`) in `MacAddress` constraint
910
* Add `*_NO_PUBLIC`, `*_ONLY_PRIVATE` and `*_ONLY_RESERVED` versions to `Ip` constraint
1011
* Possibility to use all `Ip` constraint versions for `Cidr` constraint
1112
* Add `list` and `associative_array` types to `Type` constraint

src/Symfony/Component/Validator/Constraints/MacAddress.php

+16
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
namespace Symfony\Component\Validator\Constraints;
1313

1414
use Symfony\Component\Validator\Constraint;
15+
use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
1516

1617
/**
1718
* Validates that a value is a valid MAC address.
@@ -21,8 +22,18 @@
2122
#[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
2223
class MacAddress extends Constraint
2324
{
25+
public const PUBLIC = 'public';
26+
public const PRIVATE = 'private';
27+
public const ALL = 'all';
28+
2429
public const INVALID_MAC_ERROR = 'a183fbff-6968-43b4-82a2-cc5cf7150036';
2530

31+
protected const VERSIONS = [
32+
self::PUBLIC,
33+
self::PRIVATE,
34+
self::ALL,
35+
];
36+
2637
protected const ERROR_NAMES = [
2738
self::INVALID_MAC_ERROR => 'INVALID_MAC_ERROR',
2839
];
@@ -31,12 +42,17 @@ class MacAddress extends Constraint
3142

3243
public function __construct(
3344
public string $message = 'This value is not a valid MAC address.',
45+
public string $version = self::ALL,
3446
?callable $normalizer = null,
3547
?array $groups = null,
3648
mixed $payload = null,
3749
) {
3850
parent::__construct(null, $groups, $payload);
3951

52+
if (!\in_array($this->version, static::VERSIONS, true)) {
53+
throw new ConstraintDefinitionException(sprintf('The option "version" must be one of "%s".', implode('", "', static::VERSIONS)));
54+
}
55+
4056
$this->normalizer = null !== $normalizer ? $normalizer(...) : null;
4157
}
4258
}

src/Symfony/Component/Validator/Constraints/MacAddressValidator.php

+33-2
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,37 @@
2323
*/
2424
class MacAddressValidator extends ConstraintValidator
2525
{
26+
/**
27+
* Checks whether an MAC address is valid.
28+
*
29+
* @internal
30+
*/
31+
public static function checkMac(string $mac, mixed $version): bool
32+
{
33+
if (!filter_var($mac, \FILTER_VALIDATE_MAC)) {
34+
return false;
35+
}
36+
37+
return match ($version) {
38+
MacAddress::PUBLIC => !self::isPrivate($mac),
39+
MacAddress::PRIVATE => self::isPrivate($mac),
40+
default => true,
41+
};
42+
}
43+
44+
/**
45+
* Checks whether an MAC address is private.
46+
*
47+
* @internal
48+
*/
49+
public static function isPrivate(string $mac): bool
50+
{
51+
return match (substr(strtolower($mac), 1, 1)) {
52+
'2', '6', 'a', 'e' => true,
53+
default => false,
54+
};
55+
}
56+
2657
public function validate(mixed $value, Constraint $constraint): void
2758
{
2859
if (!$constraint instanceof MacAddress) {
@@ -37,13 +68,13 @@ public function validate(mixed $value, Constraint $constraint): void
3768
throw new UnexpectedValueException($value, 'string');
3869
}
3970

40-
$value = (string) $value;
71+
$value = (string)$value;
4172

4273
if (null !== $constraint->normalizer) {
4374
$value = ($constraint->normalizer)($value);
4475
}
4576

46-
if (!filter_var($value, \FILTER_VALIDATE_MAC)) {
77+
if (!self::checkMac($value, $constraint->version)) {
4778
$this->context->buildViolation($constraint->message)
4879
->setParameter('{{ value }}', $this->formatValue($value))
4980
->setCode(MacAddress::INVALID_MAC_ERROR)

src/Symfony/Component/Validator/Tests/Constraints/MacAddressTest.php

+11-3
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,16 @@ public function testAttributes()
3737
[$aConstraint] = $metadata->properties['a']->getConstraints();
3838
self::assertSame('myMessage', $aConstraint->message);
3939
self::assertEquals(trim(...), $aConstraint->normalizer);
40+
self::assertSame(MacAddress::ALL, $aConstraint->version);
4041
self::assertSame(['Default', 'MacAddressDummy'], $aConstraint->groups);
4142

4243
[$bConstraint] = $metadata->properties['b']->getConstraints();
43-
self::assertSame(['my_group'], $bConstraint->groups);
44-
self::assertSame('some attached data', $bConstraint->payload);
44+
self::assertSame(MacAddress::PRIVATE, $bConstraint->version);
45+
self::assertSame(['Default', 'MacAddressDummy'], $bConstraint->groups);
46+
47+
[$cConstraint] = $metadata->properties['c']->getConstraints();
48+
self::assertSame(['my_group'], $cConstraint->groups);
49+
self::assertSame('some attached data', $cConstraint->payload);
4550
}
4651
}
4752

@@ -50,6 +55,9 @@ class MacAddressDummy
5055
#[MacAddress(message: 'myMessage', normalizer: 'trim')]
5156
private $a;
5257

53-
#[MacAddress(groups: ['my_group'], payload: 'some attached data')]
58+
#[MacAddress(version: MacAddress::PRIVATE)]
5459
private $b;
60+
61+
#[MacAddress(groups: ['my_group'], payload: 'some attached data')]
62+
private $c;
5563
}

src/Symfony/Component/Validator/Tests/Constraints/MacAddressValidatorTest.php

+72
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,78 @@ public static function getValidMacs(): array
6868
];
6969
}
7070

71+
/**
72+
* @dataProvider getValidPublicMacs
73+
*/
74+
public function testValidPublicMacs($mac)
75+
{
76+
$this->validator->validate($mac, new MacAddress(version: MacAddress::PUBLIC));
77+
78+
$this->assertNoViolation();
79+
}
80+
81+
/**
82+
* @dataProvider getValidPrivateMacs
83+
*/
84+
public function testInvalidPublicMacs($mac)
85+
{
86+
$constraint = new MacAddress('myMessage', version: MacAddress::PUBLIC);
87+
88+
$this->validator->validate($mac, $constraint);
89+
90+
$this->buildViolation('myMessage')
91+
->setParameter('{{ value }}', '"'.$mac.'"')
92+
->setCode(MacAddress::INVALID_MAC_ERROR)
93+
->assertRaised();
94+
}
95+
96+
public static function getValidPublicMacs(): array
97+
{
98+
return [
99+
['01:00:00:00:00:00'],
100+
['03-00-00-00-00-00'],
101+
['04-00-00-00-00-00'],
102+
['a5-00-00-00-00-00'],
103+
['fb:ff:ff:ff:ff:ff'],
104+
['fc-ff-ff-ff-ff-ff'],
105+
];
106+
}
107+
108+
/**
109+
* @dataProvider getValidPrivateMacs
110+
*/
111+
public function testValidPrivateMacs($mac)
112+
{
113+
$this->validator->validate($mac, new MacAddress(version: MacAddress::PRIVATE));
114+
115+
$this->assertNoViolation();
116+
}
117+
118+
/**
119+
* @dataProvider getValidPublicMacs
120+
*/
121+
public function testInvalidPrivateMacs($mac)
122+
{
123+
$constraint = new MacAddress('myMessage', version: MacAddress::PRIVATE);
124+
125+
$this->validator->validate($mac, $constraint);
126+
127+
$this->buildViolation('myMessage')
128+
->setParameter('{{ value }}', '"'.$mac.'"')
129+
->setCode(MacAddress::INVALID_MAC_ERROR)
130+
->assertRaised();
131+
}
132+
133+
public static function getValidPrivateMacs(): array
134+
{
135+
return [
136+
['02:00:00:00:00:00'],
137+
['06-00-00-00-00-00'],
138+
['fa:ff:ff:ff:ff:ff'],
139+
['fe-ff-ff-ff-ff-ff'],
140+
];
141+
}
142+
71143
/**
72144
* @dataProvider getValidMacsWithWhitespaces
73145
*/

0 commit comments

Comments
 (0)