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

Skip to content

Commit f74a31c

Browse files
[Validator] Add the format option to the Uuid constraint to allow accepting different ULID formats
1 parent 9ed27d0 commit f74a31c

File tree

5 files changed

+77
-11
lines changed

5 files changed

+77
-11
lines changed

src/Symfony/Component/Validator/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ CHANGELOG
66

77
* Make `PasswordStrengthValidator::estimateStrength()` public
88
* Add the `Yaml` constraint for validating YAML content
9+
* Add the `format` option to the `Uuid` constraint to allow accepting different ULID formats
910

1011
7.1
1112
---

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,20 +35,31 @@ class Ulid extends Constraint
3535
self::TOO_LARGE_ERROR => 'TOO_LARGE_ERROR',
3636
];
3737

38+
public const FORMAT_BASE_32 = 'base32';
39+
public const FORMAT_BASE_58 = 'base58';
40+
3841
public string $message = 'This is not a valid ULID.';
42+
public string $format = self::FORMAT_BASE_32;
3943

4044
/**
4145
* @param array<string,mixed>|null $options
4246
* @param string[]|null $groups
47+
* @param self::FORMAT_*|null $format
4348
*/
4449
public function __construct(
4550
?array $options = null,
4651
?string $message = null,
4752
?array $groups = null,
4853
mixed $payload = null,
54+
?string $format = null,
4955
) {
5056
parent::__construct($options, $groups, $payload);
5157

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

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

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

4141
$value = (string) $value;
4242

43-
if (26 !== \strlen($value)) {
43+
switch ($constraint->format) {
44+
case Ulid::FORMAT_BASE_32:
45+
$requiredLength = 26;
46+
$requiredCharset = '0123456789ABCDEFGHJKMNPQRSTVWXYZabcdefghjkmnpqrstvwxyz';
47+
break;
48+
case Ulid::FORMAT_BASE_58:
49+
$requiredLength = 22;
50+
$requiredCharset = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';
51+
break;
52+
// no default, the constraint constructor already ensures a valid format
53+
}
54+
55+
if ($requiredLength !== \strlen($value)) {
4456
$this->context->buildViolation($constraint->message)
4557
->setParameter('{{ value }}', $this->formatValue($value))
46-
->setCode(26 > \strlen($value) ? Ulid::TOO_SHORT_ERROR : Ulid::TOO_LONG_ERROR)
58+
->setCode($requiredLength > \strlen($value) ? Ulid::TOO_SHORT_ERROR : Ulid::TOO_LONG_ERROR)
4759
->addViolation();
4860

4961
return;
5062
}
5163

52-
if (\strlen($value) !== strspn($value, '0123456789ABCDEFGHJKMNPQRSTVWXYZabcdefghjkmnpqrstvwxyz')) {
64+
if (\strlen($value) !== strspn($value, $requiredCharset)) {
5365
$this->context->buildViolation($constraint->message)
5466
->setParameter('{{ value }}', $this->formatValue($value))
5567
->setCode(Ulid::INVALID_CHARACTERS_ERROR)
@@ -58,13 +70,15 @@ public function validate(mixed $value, Constraint $constraint): void
5870
return;
5971
}
6072

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();
73+
if (Ulid::FORMAT_BASE_32 === $constraint->format) {
74+
// Largest valid ULID is '7ZZZZZZZZZZZZZZZZZZZZZZZZZ'
75+
// Cf https://github.com/ulid/spec#overflow-errors-when-parsing-base32-strings
76+
if ($value[0] > '7') {
77+
$this->context->buildViolation($constraint->message)
78+
->setParameter('{{ value }}', $this->formatValue($value))
79+
->setCode(Ulid::TOO_LARGE_ERROR)
80+
->addViolation();
81+
}
6882
}
6983
}
7084
}

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,14 @@ public function testAttributes()
3232
self::assertSame(['my_group'], $cConstraint->groups);
3333
self::assertSame('some attached data', $cConstraint->payload);
3434
}
35+
36+
public function testUnexpectedValidationFormat()
37+
{
38+
$this->expectException(\InvalidArgumentException::class);
39+
$this->expectExceptionMessage('The "invalid" validation format is not supported.');
40+
41+
new Ulid(format: 'invalid');
42+
}
3543
}
3644

3745
class UlidDummy

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

Lines changed: 33 additions & 1 deletion
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
*/
@@ -70,7 +77,7 @@ public function testInvalidUlid(string $ulid, string $code)
7077
->assertRaised();
7178
}
7279

73-
public static function getInvalidUlids()
80+
public static function getInvalidUlids(): array
7481
{
7582
return [
7683
['01ARZ3NDEKTSV4RRFFQ69G5FA', Ulid::TOO_SHORT_ERROR],
@@ -81,6 +88,31 @@ public static function getInvalidUlids()
8188
];
8289
}
8390

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

0 commit comments

Comments
 (0)
0