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 cead49a

Browse filesBrowse files
committed
Added new CssColor constraint
1 parent 5010ebd commit cead49a
Copy full SHA for cead49a

File tree

8 files changed

+586
-1
lines changed
Filter options

8 files changed

+586
-1
lines changed

‎src/Symfony/Component/Validator/CHANGELOG.md

Copy file name to clipboardExpand all lines: src/Symfony/Component/Validator/CHANGELOG.md
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ CHANGELOG
44
5.4
55
---
66

7+
* Add a `HexaColor` constraint to validate hexadecimal colors
78
* Add support for `ConstraintViolationList::createFromMessage()`
89

910
5.3
+72Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
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\Exception\InvalidArgumentException;
16+
17+
/**
18+
* @Annotation
19+
* @Target({"PROPERTY", "METHOD", "ANNOTATION"})
20+
*
21+
* @author Mathieu Santostefano <msantostefano@protonmail.com>
22+
*/
23+
#[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
24+
class CssColor extends Constraint
25+
{
26+
public const VALIDATION_MODE_HTML5 = 'html5';
27+
public const VALIDATION_MODE_LONG = 'css_long';
28+
public const VALIDATION_MODE_SHORT = 'css_short';
29+
public const VALIDATION_MODE_NAMED_COLORS = 'named_colors';
30+
public const INVALID_FORMAT_ERROR = '454ab47b-aacf-4059-8f26-184b2dc9d48d';
31+
32+
protected static $errorNames = [
33+
self::INVALID_FORMAT_ERROR => 'STRICT_CHECK_FAILED_ERROR',
34+
];
35+
36+
/**
37+
* @var string[]
38+
*/
39+
private static $validationModes = [
40+
self::VALIDATION_MODE_HTML5,
41+
self::VALIDATION_MODE_LONG,
42+
self::VALIDATION_MODE_SHORT,
43+
self::VALIDATION_MODE_NAMED_COLORS,
44+
];
45+
46+
public $message = 'This value is not a valid hexadecimal color.';
47+
public $mode;
48+
public $normalizer;
49+
50+
public function __construct(
51+
array $options = null,
52+
string $message = null,
53+
string $mode = null,
54+
callable $normalizer = null,
55+
array $groups = null,
56+
$payload = null
57+
) {
58+
if (\is_array($options) && \array_key_exists('mode', $options) && !\in_array($options['mode'], self::$validationModes, true)) {
59+
throw new InvalidArgumentException('The "mode" parameter value is not valid.');
60+
}
61+
62+
parent::__construct($options, $groups, $payload);
63+
64+
$this->message = $message ?? $this->message;
65+
$this->mode = $mode ?? $this->mode;
66+
$this->normalizer = $normalizer ?? $this->normalizer;
67+
68+
if (null !== $this->normalizer && !\is_callable($this->normalizer)) {
69+
throw new InvalidArgumentException(sprintf('The "normalizer" option must be a valid callable ("%s" given).', get_debug_type($this->normalizer)));
70+
}
71+
}
72+
}
+88Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
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 Mathieu Santostefano <msantostefano@protonmail.com>
21+
*/
22+
class CssColorValidator extends ConstraintValidator
23+
{
24+
private const PATTERN_HTML5 = '/^#[0-9a-f]{6}$/i';
25+
private const PATTERN_LONG = '/^#[0-9a-f]{8}$/i';
26+
private const PATTERN_SHORT = '/^#[0-9a-f]{3,4}$/i';
27+
private const PATTERN_NAMED_COLORS = '/^(black|red|green|yellow|blue|magenta|cyan|white)/i';
28+
29+
private const COLOR_PATTERNS = [
30+
CssColor::VALIDATION_MODE_HTML5 => self::PATTERN_HTML5,
31+
CssColor::VALIDATION_MODE_LONG => self::PATTERN_LONG,
32+
CssColor::VALIDATION_MODE_SHORT => self::PATTERN_SHORT,
33+
CssColor::VALIDATION_MODE_NAMED_COLORS => self::PATTERN_NAMED_COLORS,
34+
];
35+
36+
private $defaultMode;
37+
38+
public function __construct(string $defaultMode = CssColor::VALIDATION_MODE_HTML5)
39+
{
40+
if (!isset(self::COLOR_PATTERNS[$defaultMode])) {
41+
throw new \InvalidArgumentException('The "defaultMode" parameter value is not valid.');
42+
}
43+
44+
$this->defaultMode = $defaultMode;
45+
}
46+
47+
/**
48+
* {@inheritdoc}
49+
*/
50+
public function validate($value, Constraint $constraint): void
51+
{
52+
if (!$constraint instanceof CssColor) {
53+
throw new UnexpectedTypeException($constraint, CssColor::class);
54+
}
55+
56+
if (null === $value || '' === $value) {
57+
return;
58+
}
59+
60+
if (!is_scalar($value) && !(\is_object($value) && method_exists($value, '__toString'))) {
61+
throw new UnexpectedValueException($value, 'string');
62+
}
63+
64+
$value = (string) $value;
65+
if ('' === $value) {
66+
return;
67+
}
68+
69+
if (null !== $constraint->normalizer) {
70+
$value = ($constraint->normalizer)($value);
71+
}
72+
73+
if (null === $constraint->mode) {
74+
$constraint->mode = $this->defaultMode;
75+
}
76+
77+
if (!isset(self::COLOR_PATTERNS[$constraint->mode])) {
78+
throw new \InvalidArgumentException(sprintf('The "%s::$mode" parameter value is not valid.', get_debug_type($constraint)));
79+
}
80+
81+
if (!preg_match(self::COLOR_PATTERNS[$constraint->mode], $value)) {
82+
$this->context->buildViolation($constraint->message)
83+
->setParameter('{{ value }}', $this->formatValue($value))
84+
->setCode(CssColor::INVALID_FORMAT_ERROR)
85+
->addViolation();
86+
}
87+
}
88+
}

‎src/Symfony/Component/Validator/Resources/translations/validators.en.xlf

Copy file name to clipboardExpand all lines: src/Symfony/Component/Validator/Resources/translations/validators.en.xlf
+4Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,10 @@
390390
<source>This value should be a valid expression.</source>
391391
<target>This value should be a valid expression.</target>
392392
</trans-unit>
393+
<trans-unit id="101">
394+
<source>This value is not a valid hexadecimal color.</source>
395+
<target>This value is not a valid hexadecimal color.</target>
396+
</trans-unit>
393397
</body>
394398
</file>
395399
</xliff>

‎src/Symfony/Component/Validator/Resources/translations/validators.fr.xlf

Copy file name to clipboardExpand all lines: src/Symfony/Component/Validator/Resources/translations/validators.fr.xlf
+4Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,10 @@
390390
<source>This value should be a valid expression.</source>
391391
<target>Cette valeur doit être une expression valide.</target>
392392
</trans-unit>
393+
<trans-unit id="101">
394+
<source>This value is not a valid hexadecimal color.</source>
395+
<target>Cette valeur n'est pas une couleur hexadécimale valide.</target>
396+
</trans-unit>
393397
</body>
394398
</file>
395399
</xliff>

‎src/Symfony/Component/Validator/Resources/translations/validators.it.xlf

Copy file name to clipboardExpand all lines: src/Symfony/Component/Validator/Resources/translations/validators.it.xlf
+5-1Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@
5656
</trans-unit>
5757
<trans-unit id="14">
5858
<source>The file could not be found.</source>
59-
<target>Non è stato possibile trovare il file.</target>
59+
<target>Non è stato possible trovare il file.</target>
6060
</trans-unit>
6161
<trans-unit id="15">
6262
<source>The file is not readable.</source>
@@ -390,6 +390,10 @@
390390
<source>This value should be a valid expression.</source>
391391
<target>Questo valore dovrebbe essere un'espressione valida.</target>
392392
</trans-unit>
393+
<trans-unit id="101">
394+
<source>This value is not a valid hexadecimal color.</source>
395+
<target>Questo valore non è un colore esadecimale valido.</target>
396+
</trans-unit>
393397
</body>
394398
</file>
395399
</xliff>
+93Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
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 PHPUnit\Framework\TestCase;
15+
use Symfony\Component\Validator\Constraints\CssColor;
16+
use Symfony\Component\Validator\Exception\InvalidArgumentException;
17+
use Symfony\Component\Validator\Mapping\ClassMetadata;
18+
use Symfony\Component\Validator\Mapping\Loader\AnnotationLoader;
19+
20+
/**
21+
* @author Mathieu Santostefano <msantostefano@protonmail.com>
22+
*/
23+
class CssColorTest extends TestCase
24+
{
25+
public function testConstructorStrict()
26+
{
27+
$subject = new CssColor(['mode' => CssColor::VALIDATION_MODE_SHORT]);
28+
29+
$this->assertEquals(CssColor::VALIDATION_MODE_SHORT, $subject->mode);
30+
}
31+
32+
public function testUnknownModesTriggerException()
33+
{
34+
$this->expectException(InvalidArgumentException::class);
35+
$this->expectExceptionMessage('The "mode" parameter value is not valid.');
36+
new CssColor(['mode' => 'Unknown Mode']);
37+
}
38+
39+
public function testNormalizerCanBeSet()
40+
{
41+
$hexaColor = new CssColor(['normalizer' => 'trim']);
42+
43+
$this->assertEquals('trim', $hexaColor->normalizer);
44+
}
45+
46+
public function testInvalidNormalizerThrowsException()
47+
{
48+
$this->expectException(InvalidArgumentException::class);
49+
$this->expectExceptionMessage('The "normalizer" option must be a valid callable ("string" given).');
50+
new CssColor(['normalizer' => 'Unknown Callable']);
51+
}
52+
53+
public function testInvalidNormalizerObjectThrowsException()
54+
{
55+
$this->expectException(InvalidArgumentException::class);
56+
$this->expectExceptionMessage('The "normalizer" option must be a valid callable ("stdClass" given).');
57+
new CssColor(['normalizer' => new \stdClass()]);
58+
}
59+
60+
/**
61+
* @requires PHP 8
62+
*/
63+
public function testAttribute()
64+
{
65+
$metadata = new ClassMetadata(HexaColorDummy::class);
66+
(new AnnotationLoader())->loadClassMetadata($metadata);
67+
68+
[$aConstraint] = $metadata->properties['a']->constraints;
69+
self::assertNull($aConstraint->mode);
70+
self::assertNull($aConstraint->normalizer);
71+
72+
[$bConstraint] = $metadata->properties['b']->constraints;
73+
self::assertSame('myMessage', $bConstraint->message);
74+
self::assertSame(CssColor::VALIDATION_MODE_HTML5, $bConstraint->mode);
75+
self::assertSame('trim', $bConstraint->normalizer);
76+
77+
[$cConstraint] = $metadata->properties['c']->getConstraints();
78+
self::assertSame(['my_group'], $cConstraint->groups);
79+
self::assertSame('some attached data', $cConstraint->payload);
80+
}
81+
}
82+
83+
class HexaColorDummy
84+
{
85+
#[CssColor]
86+
private $a;
87+
88+
#[CssColor(message: 'myMessage', mode: CssColor::VALIDATION_MODE_HTML5, normalizer: 'trim')]
89+
private $b;
90+
91+
#[CssColor(groups: ['my_group'], payload: 'some attached data')]
92+
private $c;
93+
}

0 commit comments

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