Skip to content

Commit a9a466b

Browse files
committed
Initial commit
0 parents  commit a9a466b

File tree

4 files changed

+147
-0
lines changed

4 files changed

+147
-0
lines changed

LICENSE

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
2+
The MIT license
3+
4+
Copyright (c) 2015-present Kévin Dunglas
5+
6+
Permission is hereby granted, free of charge, to any person obtaining a copy
7+
of this software and associated documentation files (the "Software"), to deal
8+
in the Software without restriction, including without limitation the rights
9+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
copies of the Software, and to permit persons to whom the Software is furnished
11+
to do so, subject to the following conditions:
12+
13+
The above copyright notice and this permission notice shall be included in all
14+
copies or substantial portions of the Software.
15+
16+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22+
THE SOFTWARE.

composer.json

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{
2+
"name": "muffe/enum-constraint",
3+
"description": "A Symfony Validator constraint that validates of given strings a valid cases in a given PHP 8 Enum",
4+
"type": "library",
5+
"keywords": ["enum", "validator", "constraint"],
6+
"minimum-stability": "dev",
7+
"license": "MIT",
8+
"autoload": {
9+
"psr-4": {
10+
"Muffe\\EnumConstraint\\": "src/"
11+
}
12+
},
13+
"authors": [
14+
{
15+
"name": "Marc Wustrack",
16+
"email": "[email protected]"
17+
}
18+
],
19+
"require": {
20+
"php": ">=8.1",
21+
"symfony/validator": "^6.1"
22+
}
23+
}

src/Constraints/Enum.php

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Muffe\EnumConstraint\Constraints;
6+
7+
use Symfony\Component\Validator\Constraint;
8+
9+
/**
10+
* @Annotation
11+
* @Target({"PROPERTY", "METHOD", "ANNOTATION"})
12+
*/
13+
#[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
14+
class Enum extends Constraint
15+
{
16+
final public const NO_SUCH_CASE_ERROR = '8e6b7234-171a-4389-a4fc-9324586a6869';
17+
18+
public function __construct(
19+
public string $enumType,
20+
public bool $multiple = false,
21+
public ?string $message = null,
22+
mixed $options = null,
23+
array $groups = null,
24+
mixed $payload = null
25+
) {
26+
parent::__construct($options, $groups, $payload);
27+
}
28+
}

src/Constraints/EnumValidator.php

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Muffe\EnumConstraint\Constraints;
6+
7+
use Symfony\Component\Validator\Constraint;
8+
use Symfony\Component\Validator\ConstraintValidator;
9+
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
10+
11+
class EnumValidator extends ConstraintValidator
12+
{
13+
public function validate($value, Constraint $constraint): void
14+
{
15+
if (!$constraint instanceof Enum) {
16+
throw new UnexpectedTypeException($constraint, Enum::class);
17+
}
18+
19+
$enumType = $constraint->enumType;
20+
21+
if (!\is_a($enumType, \UnitEnum::class, true)) {
22+
throw new \RuntimeException('The "enumType" option of the Enum constraint should be a Enum.');
23+
}
24+
25+
if (null === $value) {
26+
return;
27+
}
28+
29+
$property = 'name';
30+
if (\is_a($enumType, \BackedEnum::class, true)) {
31+
$property = 'value';
32+
}
33+
34+
$enumCases = \array_map(static fn ($item) => $item->{$property}, $enumType::cases());
35+
36+
if (!$constraint->message) {
37+
$constraint->message = 'The enum case "{{ value }}" is not valid. Valid cases are: "{{ choices }}"';
38+
}
39+
40+
if ($constraint->multiple) {
41+
if (!\is_array($value)) {
42+
$value = [$value];
43+
}
44+
45+
foreach ($value as $singleValue) {
46+
if (\in_array($singleValue, $enumCases, true)) {
47+
continue;
48+
}
49+
50+
$this->context->buildViolation($constraint->message)
51+
->setParameter('{{ value }}', $this->formatValue($singleValue))
52+
->setParameter('{{ choices }}', $this->formatValues($enumCases))
53+
->setCode(Enum::NO_SUCH_CASE_ERROR)
54+
->setInvalidValue($value)
55+
->addViolation();
56+
57+
return;
58+
}
59+
60+
return;
61+
}
62+
63+
if (\in_array($value, $enumCases, true)) {
64+
return;
65+
}
66+
67+
$this->context->buildViolation($constraint->message)
68+
->setParameter('{{ value }}', $this->formatValue($value))
69+
->setParameter('{{ choices }}', $this->formatValues($enumCases))
70+
->setCode(Enum::NO_SUCH_CASE_ERROR)
71+
->setInvalidValue($value)
72+
->addViolation();
73+
}
74+
}

0 commit comments

Comments
 (0)