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

Skip to content

[Validator] Add MacAddress constraint for validating MAC address #51862

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/Symfony/Component/Validator/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ CHANGELOG
7.1
---

* Add `MacAddress` constraint
* Add `list` and `associative_array` types to `Type` constraint
* Add the `Charset` constraint

Expand Down
52 changes: 52 additions & 0 deletions src/Symfony/Component/Validator/Constraints/MacAddress.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\Validator\Constraints;

use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\Exception\InvalidArgumentException;

/**
* Validates that a value is a valid MAC address.
*
* @author Ninos Ego <[email protected]>
*/
#[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
class MacAddress extends Constraint
{
public const INVALID_MAC_ERROR = 'a183fbff-6968-43b4-82a2-cc5cf7150036';

protected const ERROR_NAMES = [
self::INVALID_MAC_ERROR => 'INVALID_MAC_ERROR',
];

public string $message = 'This is not a valid MAC address.';

/** @var callable|null */
public $normalizer;

public function __construct(
array $options = null,
string $message = null,
callable $normalizer = null,
array $groups = null,
mixed $payload = null,
) {
parent::__construct($options, $groups, $payload);

$this->message = $message ?? $this->message;
$this->normalizer = $normalizer ?? $this->normalizer;

if (null !== $this->normalizer && !\is_callable($this->normalizer)) {
throw new InvalidArgumentException(sprintf('The "normalizer" option must be a valid callable ("%s" given).', get_debug_type($this->normalizer)));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\Validator\Constraints;

use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
use Symfony\Component\Validator\Exception\UnexpectedValueException;

/**
* Validates whether a value is a valid MAC address.
*
* @author Ninos Ego <[email protected]>
*/
class MacAddressValidator extends ConstraintValidator
{
public function validate(mixed $value, Constraint $constraint): void
{
if (!$constraint instanceof MacAddress) {
throw new UnexpectedTypeException($constraint, MacAddress::class);
}

if (null === $value || '' === $value) {
return;
}

if (!\is_scalar($value) && !$value instanceof \Stringable) {
throw new UnexpectedValueException($value, 'string');
}

$value = (string) $value;

if (null !== $constraint->normalizer) {
$value = ($constraint->normalizer)($value);
}

if (!filter_var($value, \FILTER_VALIDATE_MAC)) {
$this->context->buildViolation($constraint->message)
->setParameter('{{ value }}', $this->formatValue($value))
->setCode(MacAddress::INVALID_MAC_ERROR)
->addViolation();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -434,6 +434,10 @@
<source>The detected character encoding is invalid ({{ detected }}). Allowed encodings are {{ encodings }}.</source>
<target>Der erkannte Zeichensatz ist nicht gültig ({{ detected }}). Gültige Zeichensätze sind "{{ encodings }}".</target>
</trans-unit>
<trans-unit id="112">
<source>This is not a valid MAC address.</source>
<target>Dies ist keine gültige MAC-Adresse.</target>
</trans-unit>
</body>
</file>
</xliff>
Original file line number Diff line number Diff line change
Expand Up @@ -434,6 +434,10 @@
<source>The detected character encoding is invalid ({{ detected }}). Allowed encodings are {{ encodings }}.</source>
<target>The detected character encoding is invalid ({{ detected }}). Allowed encodings are {{ encodings }}.</target>
</trans-unit>
<trans-unit id="112">
<source>This is not a valid MAC address.</source>
<target>This is not a valid MAC address.</target>
</trans-unit>
</body>
</file>
</xliff>
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\Validator\Tests\Constraints;

use PHPUnit\Framework\TestCase;
use Symfony\Component\Validator\Constraints\MacAddress;
use Symfony\Component\Validator\Exception\InvalidArgumentException;
use Symfony\Component\Validator\Mapping\ClassMetadata;
use Symfony\Component\Validator\Mapping\Loader\AttributeLoader;

/**
* @author Ninos Ego <[email protected]>
*/
class MacAddressTest extends TestCase
{
public function testNormalizerCanBeSet()
{
$mac = new MacAddress(['normalizer' => 'trim']);

$this->assertEquals('trim', $mac->normalizer);
}

public function testInvalidNormalizerThrowsException()
{
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('The "normalizer" option must be a valid callable ("string" given).');
new MacAddress(['normalizer' => 'Unknown Callable']);
}

public function testInvalidNormalizerObjectThrowsException()
{
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('The "normalizer" option must be a valid callable ("stdClass" given).');
new MacAddress(['normalizer' => new \stdClass()]);
}

public function testAttributes()
{
$metadata = new ClassMetadata(MacAddressDummy::class);
$loader = new AttributeLoader();
self::assertTrue($loader->loadClassMetadata($metadata));

[$aConstraint] = $metadata->properties['a']->getConstraints();
self::assertSame('myMessage', $aConstraint->message);
self::assertSame('trim', $aConstraint->normalizer);
self::assertSame(['Default', 'MacAddressDummy'], $aConstraint->groups);

[$bConstraint] = $metadata->properties['b']->getConstraints();
self::assertSame(['my_group'], $bConstraint->groups);
self::assertSame('some attached data', $bConstraint->payload);
}
}

class MacAddressDummy
{
#[MacAddress(message: 'myMessage', normalizer: 'trim')]
private $a;

#[MacAddress(groups: ['my_group'], payload: 'some attached data')]
private $b;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\Validator\Tests\Constraints;

use Symfony\Component\Validator\Constraints\MacAddress;
use Symfony\Component\Validator\Constraints\MacAddressValidator;
use Symfony\Component\Validator\Exception\UnexpectedValueException;
use Symfony\Component\Validator\Test\ConstraintValidatorTestCase;

/**
* @author Ninos Ego <[email protected]>
*/
class MacAddressValidatorTest extends ConstraintValidatorTestCase
{
protected function createValidator(): MacAddressValidator
{
return new MacAddressValidator();
}

public function testNullIsValid()
{
$this->validator->validate(null, new MacAddress());

$this->assertNoViolation();
}

public function testEmptyStringIsValid()
{
$this->validator->validate('', new MacAddress());

$this->assertNoViolation();
}

public function testExpectsStringCompatibleType()
{
$this->expectException(UnexpectedValueException::class);
$this->validator->validate(new \stdClass(), new MacAddress());
}

/**
* @dataProvider getValidMacs
*/
public function testValidMac($mac)
{
$this->validator->validate($mac, new MacAddress());

$this->assertNoViolation();
}

public static function getValidMacs(): array
{
return [
['00:00:00:00:00:00'],
['00-00-00-00-00-00'],
['ff:ff:ff:ff:ff:ff'],
['ff-ff-ff-ff-ff-ff'],
['FF:FF:FF:FF:FF:FF'],
['FF-FF-FF-FF-FF-FF'],
];
}

/**
* @dataProvider getValidMacsWithWhitespaces
*/
public function testValidMacsWithWhitespaces($mac)
{
$this->validator->validate($mac, new MacAddress([
'normalizer' => 'trim',
]));

$this->assertNoViolation();
}

public static function getValidMacsWithWhitespaces(): array
{
return [
["\x2000:00:00:00:00:00"],
["\x09\x0900-00-00-00-00-00"],
["ff:ff:ff:ff:ff:ff\x0A"],
["ff-ff-ff-ff-ff-ff\x0D\x0D"],
["\x00FF:FF:FF:FF:FF:FF\x00"],
["\x0B\x0BFF-FF-FF-FF-FF-FF\x0B\x0B"],
];
}

/**
* @dataProvider getInvalidMacs
*/
public function testInvalidMacs($mac)
{
$constraint = new MacAddress([
'message' => 'myMessage',
]);

$this->validator->validate($mac, $constraint);

$this->buildViolation('myMessage')
->setParameter('{{ value }}', '"'.$mac.'"')
->setCode(MacAddress::INVALID_MAC_ERROR)
->assertRaised();
}

public static function getInvalidMacs(): array
{
return [
['0'],
['00:00'],
['00:00:00'],
['00:00:00:00'],
['00:00:00:00:00'],
['00:00:00:00:00:000'],
['-00:00:00:00:00:00'],
['foobar'],
];
}
}