8000 [Validator] Add the `format` option to the `Uuid` constraint to allow… · symfony/symfony@880e8fa · GitHub
[go: up one dir, main page]

Skip to content

Commit 880e8fa

Browse files
[Validator] Add the format option to the Uuid constraint to allow accepting different ULID formats
1 parent c0e30bb commit 880e8fa

File tree

5 files changed

+94
-15
lines changed

5 files changed

+94
-15
lines changed

src/Symfony/Component/Validator/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ CHANGELOG
88
* Make `PasswordStrengthValidator::estimateStrength()` public
99
* Add the `Yaml` constraint for validating YAML content
1010
* Add `errorPath` to Unique constraint
11+
* Add the `format` option to the `Ulid` constraint to allow accepting different ULID formats
1112

1213
7.1
1314
---

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

Lines changed: 12 additions & 0 deletions
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 Universally Unique Lexicographically Sortable Identifier (ULID).
@@ -35,20 +36,31 @@ class Ulid extends Constraint
3536
self::TOO_LARGE_ERROR => 'TOO_LARGE_ERROR',
3637
];
3738

39+
public const FORMAT_BASE_32 = 'base32';
40+
public const FORMAT_BASE_58 = 'base58';
41+
3842
public string $message = 'This is not a valid ULID.';
43+
public string $format = self::FORMAT_BASE_32;
3944

4045
/**
4146
* @param array<string,mixed>|null $options
4247
* @param string[]|null $groups
48+
* @param self::FORMAT_*|null $format
4349
*/
4450
public function __construct(
4551
?array $options = null,
4652
?string $message = null,
4753
?array $groups = null,
4854
mixed $payload = null,
55+
?string $format = null,
4956
) {
5057
parent::__construct($options, $groups, $payload);
5158

5259
$this->message = $message ?? $this->message;
60+
$this->format = $format ?? $this->format;
61+
62+
if (!\in_array($this->format, [self::FORMAT_BASE_32, self::FORMAT_BASE_58], true)) {
63+
throw new ConstraintDefinitionException(sprintf('The "%s" validation format is not supported.', $format));
64+
}
5365
}
5466
}

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

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -40,31 +40,47 @@ public function validate(mixed $value, Constraint $constraint): void
4040

4141
$value = (string) $value;
4242

43-
if (26 !== \strlen($value)) {
43+
[$requiredLength, $requiredCharset] = match ($constraint->format) {
44+
Ulid::FORMAT_BASE_32 => [26, '0123456789ABCDEFGHJKMNPQRSTVWXYZabcdefghjkmnpqrstvwxyz'],
45+
Ulid::FORMAT_BASE_58 => [22, '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'],
46+
};
47+
48+
if ($requiredLength !== \strlen($value)) {
4449
$this->context->buildViolation($constraint->message)
45-
->setParameter('{{ value }}', $this->formatValue($value))
46-
->setCode(26 > \strlen($value) ? Ulid::TOO_SHORT_ERROR : Ulid::TOO_LONG_ERROR)
50+
->setParameters([
51+
'{{ value }}' => $this->formatValue($value),
52+
'{{ format }}' => $constraint->format,
53+
])
54+
->setCode($requiredLength > \strlen($value) ? Ulid::TOO_SHORT_ERROR : Ulid::TOO_LONG_ERROR)
4755
->addViolation();
4856

4957
return;
5058
}
5159

52-
if (\strlen($value< 2364 /span>) !== strspn($value, '0123456789ABCDEFGHJKMNPQRSTVWXYZabcdefghjkmnpqrstvwxyz')) {
60+
if (\strlen($value) !== strspn($value, $requiredCharset)) {
5361
$this->context->buildViolation($constraint->message)
54-
->setParameter('{{ value }}', $this->formatValue($value))
62+
->setParameters([
63+
'{{ value }}' => $this->formatValue($value),
64+
'{{ format }}' => $constraint->format,
65+
])
5566
->setCode(Ulid::INVALID_CHARACTERS_ERROR)
5667
->addViolation();
5768

5869
return;
5970
}
6071

61-
// Largest valid ULID is '7ZZZZZZZZZZZZZZZZZZZZZZZZZ'
62-
// Cf https://github.com/ulid/spec#overflow-errors-when-parsing-base32-strings
63-
if ($value[0] > '7') {
64-
$this->context->buildViolation($constraint->message)
65-
->setParameter('{{ value }}', $this->formatValue($value))
66-
->setCode(Ulid::TOO_LARGE_ERROR)
67-
->addViolation();
72+
if (Ulid::FORMAT_BASE_32 === $constraint->format) {
73+
// Largest valid ULID is '7ZZZZZZZZZZZZZZZZZZZZZZZZZ'
74+
// Cf https://github.com/ulid/spec#overflow-errors-when-parsing-base32-strings
75+
if ($value[0] > '7') {
76+
$this->context->buildViolation($constraint->message)
77+
->setParameters([
78+
'{{ value }}' => $this->formatValue($value),
79+
'{{ format }}' => $constraint->format,
80+
])
81+
->setCode(Ulid::TOO_LARGE_ERROR)
82+
->addViolation();
83+
}
6884
}
6985
}
7086
}

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
use PHPUnit\Framework\TestCase;
1515
use Symfony\Component\Validator\Constraints\Ulid;
16+
use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
1617
use Symfony\Component\Validator\Mapping\ClassMetadata;
1718
use Symfony\Component\Validator\Mapping\Loader\AttributeLoader;
1819

@@ -32,6 +33,14 @@ public function testAttributes()
3233
self::assertSame(['my_group'], $cConstraint->groups);
3334
self::assertSame('some attached data', $cConstraint->payload);
3435
}
36+
37+
public function testUnexpectedValidationFormat()
38+
{
39+
$this->expectException(ConstraintDefinitionException::class);
40+
$this->expectExceptionMessage('The "invalid" validation format is not supported.');
41+
42+
new Ulid(format: 'invalid');
43+
}
3544
}
3645

3746
class UlidDummy

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

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,13 @@ public function testValidUlid()
5353
$this->assertNoViolation();
5454
}
5555

56+
public function testValidUlidAsBase58()
57+
{
58+
$this->validator->validate('1CCD2w4mK2m455S2BAXFht', new Ulid(format: Ulid::FORMAT_BASE_58));
59+
60+
$this->assertNoViolation();
61+
}
62+
5663
/**
5764
* @dataProvider getInvalidUlids
5865
*/
@@ -65,12 +72,15 @@ public function testInvalidUlid(string $ulid, string $code)
6572
$this->validator->validate($ulid, $constraint);
6673

6774
$this->buildViolation('testMessage')
68-
->setParameter('{{ value }}', '"'.$ulid.'"')
75+
->setParameters([
76+
'{{ value }}' => '"'.$ulid.'"',
77+
'{{ format }}' => Ulid::FORMAT_BASE_32,
78+
])
6979
->setCode($code)
7080
->assertRaised();
7181
}
7282

73-
public static function getInvalidUlids()
83+
public static function getInvalidUlids(): array
7484
{
7585
return [
7686
['01ARZ3NDEKTSV4RRFFQ69G5FA', Ulid::TOO_SHORT_ERROR],
@@ -81,14 +91,45 @@ public static function getInvalidUlids()
8191
];
8292
}
8393

94+
/**
95+
* @dataProvider getInvalidBase58Ulids
96+
*/
97+
public function testInvalidBase58Ulid(string $ulid, string $code)
98+
{
99+
$constraint = new Ulid(message: 'testMessage', format: Ulid::FORMAT_BASE_58);
100+
101+
$this->validator->validate($ulid, $constraint);
102+
103+
$this->buildViolation('testMessage')
104+
->setParameters([
105+
'{{ value }}' => '"'.$ulid.'"',
106+
'{{ format }}' => Ulid::FORMAT_BASE_58,
107+
])
108+
->setCode($code)
109+
->assertRaised();
110+
}
111+
112+
public static function getInvalidBase58Ulids(): array
113+
{
114+
return [
115+
['1CCD2w4mK2m455S2BAXFh', Ulid::TOO_SHORT_ERROR],
116+
['1CCD2w4mK2m455S2BAXFhttt', Ulid::TOO_LONG_ERROR],
117+
['1CCD2w4mK2m455S2BAXFhO', Ulid::INVALID_CHARACTERS_ERROR],
118+
['not-even-ulid-like', Ulid::TOO_SHORT_ERROR],
119+
];
120+
}
121+
84122
public function testInvalidUlidNamed()
85123
{
86124
$constraint = new Ulid(message: 'testMessage');
87125

88126
$this->validator->validate('01ARZ3NDEKTSV4RRFFQ69G5FA', $constraint);
89127

90128
$this->buildViolation('testMessage')
91-
->setParameter('{{ value }}', '"01ARZ3NDEKTSV4RRFFQ69G5FA"')
129+
->setParameters([
130+
'{{ value }}' => '"01ARZ3NDEKTSV4RRFFQ69G5FA"',
131+
'{{ format }}' => Ulid::FORMAT_BASE_32,
132+
])
92133
->setCode(Ulid::TOO_SHORT_ERROR)
93134
->assertRaised();
94135
}

0 commit comments

Comments
 (0)
0