diff --git a/src/Symfony/Component/HttpFoundation/CHANGELOG.md b/src/Symfony/Component/HttpFoundation/CHANGELOG.md index 1aaf6ed0f8507..208f466d749b8 100644 --- a/src/Symfony/Component/HttpFoundation/CHANGELOG.md +++ b/src/Symfony/Component/HttpFoundation/CHANGELOG.md @@ -4,6 +4,7 @@ CHANGELOG 6.3 --- + * Add `ParameterBag::getEnum()` * Create migration for session table when pdo handler is used 6.2 diff --git a/src/Symfony/Component/HttpFoundation/InputBag.php b/src/Symfony/Component/HttpFoundation/InputBag.php index 877ac60f3aefd..446b82132b140 100644 --- a/src/Symfony/Component/HttpFoundation/InputBag.php +++ b/src/Symfony/Component/HttpFoundation/InputBag.php @@ -73,6 +73,25 @@ public function set(string $key, mixed $value) $this->parameters[$key] = $value; } + /** + * Returns the parameter value converted to an enum. + * + * @template T of \BackedEnum + * + * @param class-string $class + * @param ?T $default + * + * @return ?T + */ + public function getEnum(string $key, string $class, \BackedEnum $default = null): ?\BackedEnum + { + try { + return parent::getEnum($key, $class, $default); + } catch (\UnexpectedValueException $e) { + throw new BadRequestException($e->getMessage(), $e->getCode(), $e); + } + } + public function filter(string $key, mixed $default = null, int $filter = \FILTER_DEFAULT, mixed $options = []): mixed { $value = $this->has($key) ? $this->all()[$key] : $default; diff --git a/src/Symfony/Component/HttpFoundation/ParameterBag.php b/src/Symfony/Component/HttpFoundation/ParameterBag.php index 72c8f0949c5d4..9df9604e6c0ef 100644 --- a/src/Symfony/Component/HttpFoundation/ParameterBag.php +++ b/src/Symfony/Component/HttpFoundation/ParameterBag.php @@ -141,6 +141,31 @@ public function getBoolean(string $key, bool $default = false): bool return $this->filter($key, $default, \FILTER_VALIDATE_BOOL); } + /** + * Returns the parameter value converted to an enum. + * + * @template T of \BackedEnum + * + * @param class-string $class + * @param ?T $default + * + * @return ?T + */ + public function getEnum(string $key, string $class, \BackedEnum $default = null): ?\BackedEnum + { + $value = $this->get($key); + + if (null === $value) { + return $default; + } + + try { + return $class::from($value); + } catch (\ValueError|\TypeError $e) { + throw new \UnexpectedValueException(sprintf('Parameter "%s" cannot be converted to enum: %s.', $key, $e->getMessage()), $e->getCode(), $e); + } + } + /** * Filter key. * diff --git a/src/Symfony/Component/HttpFoundation/Tests/InputBagTest.php b/src/Symfony/Component/HttpFoundation/Tests/InputBagTest.php index 696318e91ea98..ccb4779ef35dc 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/InputBagTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/InputBagTest.php @@ -106,4 +106,21 @@ public function testFilterArrayWithoutArrayFlag() $bag = new InputBag(['foo' => ['bar', 'baz']]); $bag->filter('foo', \FILTER_VALIDATE_INT); } + + public function testGetEnum() + { + $bag = new InputBag(['valid-value' => 1]); + + $this->assertSame(Foo::Bar, $bag->getEnum('valid-value', Foo::class)); + } + + public function testGetEnumThrowsExceptionWithInvalidValue() + { + $bag = new InputBag(['invalid-value' => 2]); + + $this->expectException(BadRequestException::class); + $this->expectExceptionMessage('Parameter "invalid-value" cannot be converted to enum: 2 is not a valid backing value for enum "Symfony\Component\HttpFoundation\Tests\Foo".'); + + $this->assertNull($bag->getEnum('invalid-value', Foo::class)); + } } diff --git a/src/Symfony/Component/HttpFoundation/Tests/ParameterBagTest.php b/src/Symfony/Component/HttpFoundation/Tests/ParameterBagTest.php index 1b60fb2418008..43aaade7efa16 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/ParameterBagTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/ParameterBagTest.php @@ -226,4 +226,39 @@ public function testGetBoolean() $this->assertFalse($bag->getBoolean('string_false'), '->getBoolean() gets the string false as boolean false'); $this->assertFalse($bag->getBoolean('unknown'), '->getBoolean() returns false if a parameter is not defined'); } + + public function testGetEnum() + { + $bag = new ParameterBag(['valid-value' => 1]); + + $this->assertSame(Foo::Bar, $bag->getEnum('valid-value', Foo::class)); + + $this->assertNull($bag->getEnum('invalid-key', Foo::class)); + $this->assertSame(Foo::Bar, $bag->getEnum('invalid-key', Foo::class, Foo::Bar)); + } + + public function testGetEnumThrowsExceptionWithNotBackingValue() + { + $bag = new ParameterBag(['invalid-value' => 2]); + + $this->expectException(\UnexpectedValueException::class); + $this->expectExceptionMessage('Parameter "invalid-value" cannot be converted to enum: 2 is not a valid backing value for enum "Symfony\Component\HttpFoundation\Tests\Foo".'); + + $this->assertNull($bag->getEnum('invalid-value', Foo::class)); + } + + public function testGetEnumThrowsExceptionWithInvalidValueType() + { + $bag = new ParameterBag(['invalid-value' => ['foo']]); + + $this->expectException(\UnexpectedValueException::class); + $this->expectExceptionMessage('Parameter "invalid-value" cannot be converted to enum: Symfony\Component\HttpFoundation\Tests\Foo::from(): Argument #1 ($value) must be of type int, array given.'); + + $this->assertNull($bag->getEnum('invalid-value', Foo::class)); + } +} + +enum Foo: int +{ + case Bar = 1; }