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

Skip to content

Commit 589cd04

Browse files
committed
[Validator] Add support for versions (ALL, LOCAL_*, UNIVERSAL_*, UNICAST_*, MULTICAST_*) in MacAddress constraint
1 parent 09437dc commit 589cd04

File tree

5 files changed

+352
-5
lines changed

5 files changed

+352
-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 (`ALL`, `LOCAL_*`, `UNIVERSAL_*`, `UNICAST_*`, `MULTICAST_*`) 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

+31
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,22 +22,52 @@
2122
#[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
2223
class MacAddress extends Constraint
2324
{
25+
public const ALL = 'all';
26+
public const LOCAL_ALL = 'local_all';
27+
public const LOCAL_UNICAST = 'local_unicast';
28+
public const LOCAL_MULTICAST = 'local_multicast';
29+
public const UNIVERSAL_ALL = 'universal_all';
30+
public const UNIVERSAL_UNICAST = 'universal_unicast';
31+
public const UNIVERSAL_MULTICAST = 'universal_multicast';
32+
public const UNICAST_ALL = 'unicast_all';
33+
public const MULTICAST_ALL = 'multicast_all';
34+
2435
public const INVALID_MAC_ERROR = 'a183fbff-6968-43b4-82a2-cc5cf7150036';
2536

37+
private const VERSIONS = [
38+
self::ALL,
39+
self::LOCAL_ALL,
40+
self::LOCAL_UNICAST,
41+
self::LOCAL_MULTICAST,
42+
self::UNIVERSAL_ALL,
43+
self::UNIVERSAL_UNICAST,
44+
self::UNIVERSAL_MULTICAST,
45+
self::UNICAST_ALL,
46+
self::MULTICAST_ALL,
47+
];
48+
2649
protected const ERROR_NAMES = [
2750
self::INVALID_MAC_ERROR => 'INVALID_MAC_ERROR',
2851
];
2952

3053
public ?\Closure $normalizer;
3154

55+
/**
56+
* @param self::ALL|self::LOCAL_*|self::UNIVERSAL_*|self::UNICAST_*|self::MULTICAST_* $version A mac address version to validate (defaults to {@see self::ALL})
57+
*/
3258
public function __construct(
3359
public string $message = 'This value is not a valid MAC address.',
60+
public string $version = self::ALL,
3461
?callable $normalizer = null,
3562
?array $groups = null,
3663
mixed $payload = null,
3764
) {
3865
parent::__construct(null, $groups, $payload);
3966

67+
if (!\in_array($this->version, self::VERSIONS, true)) {
68+
throw new ConstraintDefinitionException(sprintf('The option "version" must be one of "%s".', implode('", "', self::VERSIONS)));
69+
}
70+
4071
$this->normalizer = null !== $normalizer ? $normalizer(...) : null;
4172
}
4273
}

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

+45-1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,50 @@
2323
*/
2424
class MacAddressValidator extends ConstraintValidator
2525
{
26+
/**
27+
* Checks whether an MAC address is valid.
28+
*/
29+
private static function checkMac(string $mac, mixed $version): bool
30+
{
31+
if (!filter_var($mac, \FILTER_VALIDATE_MAC)) {
32+
return false;
33+
}
34+
35+
return match ($version) {
36+
MacAddress::LOCAL_ALL => self::isLocal($mac),
37+
MacAddress::LOCAL_UNICAST => self::isLocal($mac) && self::isUnicast($mac),
38+
MacAddress::LOCAL_MULTICAST => self::isLocal($mac) && !self::isUnicast($mac),
39+
MacAddress::UNIVERSAL_ALL => !self::isLocal($mac),
40+
MacAddress::UNIVERSAL_UNICAST => !self::isLocal($mac) && self::isUnicast($mac),
41+
MacAddress::UNIVERSAL_MULTICAST => !self::isLocal($mac) && !self::isUnicast($mac),
42+
MacAddress::UNICAST_ALL => self::isUnicast($mac),
43+
MacAddress::MULTICAST_ALL => !self::isUnicast($mac),
44+
default => true,
45+
};
46+
}
47+
48+
/**
49+
* Checks whether an MAC address is unicast or multicast.
50+
*/
51+
private static function isUnicast(string $mac): bool
52+
{
53+
return match (substr(strtolower($mac), 1, 1)) {
54+
'0', '4', '8', 'c', '2', '6', 'a', 'e' => true,
55+
default => false,
56+
};
57+
}
58+
59+
/**
60+
* Checks whether an MAC address is local or universal.
61+
*/
62+
private static function isLocal(string $mac): bool
63+
{
64+
return match (substr(strtolower($mac), 1, 1)) {
65+
'2', '6', 'a', 'e', '3', '7', 'b', 'f' => true,
66+
default => false,
67+
};
68+
}
69+
2670
public function validate(mixed $value, Constraint $constraint): void
2771
{
2872
if (!$constraint instanceof MacAddress) {
@@ -43,7 +87,7 @@ public function validate(mixed $value, Constraint $constraint): void
4387
$value = ($constraint->normalizer)($value);
4488
}
4589

46-
if (!filter_var($value, \FILTER_VALIDATE_MAC)) {
90+
if (!self::checkMac($value, $constraint->version)) {
4791
$this->context->buildViolation($constraint->message)
4892
->setParameter('{{ value }}', $this->formatValue($value))
4993
->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::LOCAL_UNICAST, $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::LOCAL_UNICAST)]
5459
private $b;
60+
61+
#[MacAddress(groups: ['my_group'], payload: 'some attached data')]
62+
private $c;
5563
}

0 commit comments

Comments
 (0)