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 ce87606

Browse filesBrowse files
committed
[Routing] Add EnumRequirement to help generate route requirements from a \BackedEnum
1 parent 8252414 commit ce87606
Copy full SHA for ce87606

File tree

7 files changed

+201
-0
lines changed
Filter options

7 files changed

+201
-0
lines changed

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/Routing/CHANGELOG.md
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ CHANGELOG
88
* Allow using UTF-8 parameter names
99
* Support the `attribute` type (alias of `annotation`) in annotation loaders
1010
* Already encoded slashes are not decoded nor double-encoded anymore when generating URLs (query parameters)
11+
* Add `EnumRequirement` to help generate route requirements from a `\BackedEnum`
1112

1213
5.3
1314
---
+50Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
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\Routing\Requirement;
13+
14+
use Symfony\Component\Routing\Exception\InvalidArgumentException;
15+
16+
final class EnumRequirement implements \Stringable
17+
{
18+
/**
19+
* @var string[]
20+
*/
21+
private readonly array $values;
22+
23+
/**
24+
* @template T of \BackedEnum
25+
* @param class-string<T> $enum
26+
* @param T ...$cases
27+
*/
28+
public function __construct(string $enum, \BackedEnum ...$cases)
29+
{
30+
if (!\is_subclass_of($enum, \BackedEnum::class, true)) {
31+
throw new InvalidArgumentException(sprintf('"%s" is not a \BackedEnum class.', $enum));
32+
}
33+
34+
foreach ($cases as $case) {
35+
if (!$case instanceof $enum) {
36+
throw new InvalidArgumentException(sprintf('"%s::%s" is not a case of "%s".', \get_class($case), $case->name, $enum));
37+
}
38+
}
39+
40+
$this->values = array_unique(array_map(
41+
static fn (\BackedEnum $e): string => $e->value,
42+
$cases ?: $enum::cases(),
43+
));
44+
}
45+
46+
public function __toString(): string
47+
{
48+
return implode('|', array_map(preg_quote(...), $this->values));
49+
}
50+
}
+20Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
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\Routing\Tests\Fixtures\Enum;
13+
14+
enum TestIntBackedEnum: int
15+
{
16+
case Hearts = 10;
17+
case Diamonds = 20;
18+
case Clubs = 30;
19+
case Spades = 40;
20+
}
+20Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
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\Routing\Tests\Fixtures\Enum;
13+
14+
enum TestStringBackedEnum: string
15+
{
16+
case Hearts = 'hearts';
17+
case Diamonds = 'diamonds';
18+
case Clubs = 'clubs';
19+
case Spades = 'spades';
20+
}
+20Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
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\Routing\Tests\Fixtures\Enum;
13+
14+
enum TestStringBackedEnum2: string
15+
{
16+
case Hearts = 'hearts';
17+
case Diamonds = 'diamonds';
18+
case Clubs = 'clubs';
19+
case Spades = 'spa|des';
20+
}
+20Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
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\Routing\Tests\Fixtures\Enum;
13+
14+
enum TestUnitEnum
15+
{
16+
case Hearts;
17+
case Diamonds;
18+
case Clubs;
19+
case Spades;
20+
}
+70Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
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\Routing\Tests\Requirement;
13+
14+
use PHPUnit\Framework\TestCase;
15+
use Symfony\Component\Routing\Exception\InvalidArgumentException;
16+
use Symfony\Component\Routing\Requirement\EnumRequirement;
17+
use Symfony\Component\Routing\Route;
18+
use Symfony\Component\Routing\Tests\Fixtures\Enum\TestIntBackedEnum;
19+
use Symfony\Component\Routing\Tests\Fixtures\Enum\TestStringBackedEnum;
20+
use Symfony\Component\Routing\Tests\Fixtures\Enum\TestStringBackedEnum2;
21+
use Symfony\Component\Routing\Tests\Fixtures\Enum\TestUnitEnum;
22+
23+
class EnumRequirementTest extends TestCase
24+
{
25+
public function testNotABackedEnum()
26+
{
27+
$this->expectException(InvalidArgumentException::class);
28+
$this->expectExceptionMessage('"Symfony\Component\Routing\Tests\Fixtures\Enum\TestUnitEnum" is not a \BackedEnum class.');
29+
30+
new EnumRequirement(TestUnitEnum::class);
31+
}
32+
33+
public function testCaseFromAnotherEnum()
34+
{
35+
$this->expectException(InvalidArgumentException::class);
36+
$this->expectExceptionMessage('"Symfony\Component\Routing\Tests\Fixtures\Enum\TestStringBackedEnum2::Spades" is not a case of "Symfony\Component\Routing\Tests\Fixtures\Enum\TestStringBackedEnum".');
37+
38+
new EnumRequirement(TestStringBackedEnum::class, TestStringBackedEnum::Diamonds, TestStringBackedEnum2::Spades);
39+
}
40+
41+
/**
42+
* @dataProvider provideToString
43+
*/
44+
public function testToString(string $expected, string $enum, \BackedEnum ...$cases)
45+
{
46+
$this->assertSame($expected, (string) new EnumRequirement($enum, ...$cases));
47+
}
48+
49+
public function provideToString()
50+
{
51+
return [
52+
['hearts|diamonds|clubs|spades', TestStringBackedEnum::class],
53+
['10|20|30|40', TestIntBackedEnum::class],
54+
['diamonds|spades', TestStringBackedEnum::class, TestStringBackedEnum::Diamonds, TestStringBackedEnum::Spades],
55+
['hearts|diamonds|clubs|spa\|des', TestStringBackedEnum2::class],
56+
];
57+
}
58+
59+
public function testInRoute()
60+
{
61+
$this->assertSame([
62+
'bar' => 'hearts|diamonds|clubs|spades',
63+
], (new Route(
64+
path: '/foo/{bar}',
65+
requirements: [
66+
'bar' => new EnumRequirement(TestStringBackedEnum::class),
67+
],
68+
))->getRequirements());
69+
}
70+
}

0 commit comments

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