From 9ae364e63c4d8a8ce3839502854e7b790124b4da Mon Sep 17 00:00:00 2001 From: Julien Tattevin Date: Tue, 2 Apr 2024 12:48:34 +0200 Subject: [PATCH 1/3] [HttpKernel] Add `#[MapSessionParameter]` to pass a session parameter to a controller argument This attribute allow to pass an object from the session. The read/write to the public properties are proxied to the session using the property name as a key, or the value defined by the attribute SessionKey This allows to type property and have ide autocompletion, static validation and runtime validation of type. The default value of the argument is also used as a default value when not defined in session. In case of interfaces, the user will be required to provide a default value or make the parameter nullable. This check is done even if the session may have a value already to notify quickly of the potential issue --- .../FrameworkBundle/Resources/config/web.php | 4 + .../Attribute/MapSessionParameter.php | 33 +++ src/Symfony/Component/HttpKernel/CHANGELOG.md | 3 +- .../SessionParameterValueResolver.php | 55 +++++ .../SessionParameterValueResolverTest.php | 225 ++++++++++++++++++ 5 files changed, 319 insertions(+), 1 deletion(-) create mode 100644 src/Symfony/Component/HttpKernel/Attribute/MapSessionParameter.php create mode 100644 src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/SessionParameterValueResolver.php create mode 100644 src/Symfony/Component/HttpKernel/Tests/Controller/ArgumentResolver/SessionParameterValueResolverTest.php diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/web.php b/src/Symfony/Bundle/FrameworkBundle/Resources/config/web.php index a4e975dac8749..0d526b03e1d55 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/web.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/web.php @@ -23,6 +23,7 @@ use Symfony\Component\HttpKernel\Controller\ArgumentResolver\RequestPayloadValueResolver; use Symfony\Component\HttpKernel\Controller\ArgumentResolver\RequestValueResolver; use Symfony\Component\HttpKernel\Controller\ArgumentResolver\ServiceValueResolver; +use Symfony\Component\HttpKernel\Controller\ArgumentResolver\SessionParameterValueResolver; use Symfony\Component\HttpKernel\Controller\ArgumentResolver\SessionValueResolver; use Symfony\Component\HttpKernel\Controller\ArgumentResolver\UidValueResolver; use Symfony\Component\HttpKernel\Controller\ArgumentResolver\VariadicValueResolver; @@ -101,6 +102,9 @@ ->set('argument_resolver.query_parameter_value_resolver', QueryParameterValueResolver::class) ->tag('controller.targeted_value_resolver', ['name' => QueryParameterValueResolver::class]) + ->set('argument_resolver.session_parameter_value_resolver', SessionParameterValueResolver::class) + ->tag('controller.targeted_value_resolver', ['name' => SessionParameterValueResolver::class]) + ->set('response_listener', ResponseListener::class) ->args([ param('kernel.charset'), diff --git a/src/Symfony/Component/HttpKernel/Attribute/MapSessionParameter.php b/src/Symfony/Component/HttpKernel/Attribute/MapSessionParameter.php new file mode 100644 index 0000000000000..af6d2cdc28998 --- /dev/null +++ b/src/Symfony/Component/HttpKernel/Attribute/MapSessionParameter.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Attribute; + +use Symfony\Component\HttpKernel\Controller\ArgumentResolver\SessionParameterValueResolver; +use Symfony\Component\HttpKernel\Controller\ValueResolverInterface; + +/** + * Can be used to pass a session parameter to a controller argument. + */ +#[\Attribute(\Attribute::TARGET_PARAMETER)] +class MapSessionParameter extends ValueResolver +{ + /** + * @param string|null $name The name of the session parameter; if null, the name of the argument in the controller will be used + * @param class-string|string $resolver The name of the resolver to use + */ + public function __construct( + public ?string $name = null, + string $resolver = SessionParameterValueResolver::class, + ) { + parent::__construct($resolver); + } +} diff --git a/src/Symfony/Component/HttpKernel/CHANGELOG.md b/src/Symfony/Component/HttpKernel/CHANGELOG.md index 6bf1a60ebc6e2..f6362010cceeb 100644 --- a/src/Symfony/Component/HttpKernel/CHANGELOG.md +++ b/src/Symfony/Component/HttpKernel/CHANGELOG.md @@ -8,7 +8,8 @@ CHANGELOG * Support `Uid` in `#[MapQueryParameter]` * Add `ServicesResetterInterface`, implemented by `ServicesResetter` * Allow configuring the logging channel per type of exceptions in ErrorListener - + * Add `#[MapSessionParameter]` to pass a session parameter to a controller argument + 7.2 --- diff --git a/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/SessionParameterValueResolver.php b/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/SessionParameterValueResolver.php new file mode 100644 index 0000000000000..8be394ab08d56 --- /dev/null +++ b/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/SessionParameterValueResolver.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Controller\ArgumentResolver; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\Attribute\MapSessionParameter; +use Symfony\Component\HttpKernel\Controller\ValueResolverInterface; +use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata; + +final class SessionParameterValueResolver implements ValueResolverInterface +{ + public function resolve(Request $request, ArgumentMetadata $argument): array + { + if (!$attribute = $argument->getAttributesOfType(MapSessionParameter::class, ArgumentMetadata::IS_INSTANCEOF)[0] ?? null) { + return []; + } + + if (!$request->hasSession()) { + return []; + } + + if ((!$type = $argument->getType()) || (!class_exists($type) && !interface_exists($type, false))) { + throw new \LogicException(\sprintf('#[MapSessionParameter] cannot be used on controller argument "$%s": "%s" is not a class or interface name.', $argument->getName(), $type)); + } + + if (interface_exists($type, false) && !$argument->hasDefaultValue() && !$argument->isNullable()) { + throw new \LogicException(\sprintf('#[MapSessionParameter] cannot be used on controller argument "$%s": "%s" is an interface, you need to make the parameter nullable or provide a default value.', $argument->getName(), $type)); + } + + $name = $attribute->name ?? $argument->getName(); + if ($request->getSession()->has($name)) { + $value = $request->getSession()->get($name); + if (!$value instanceof $type) { + throw new \LogicException(\sprintf('#[MapSessionParameter] cannot be used to map controller argument "$%s": the session contains a value of type "%s" which is not an instance of "%s".', $argument->getName(), get_debug_type($value), $type)); + } + + return [$value]; + } + + if (\is_object($value = $argument->hasDefaultValue() ? $argument->getDefaultValue() : new $type())) { + $request->getSession()->set($name, $value); + } + + return [$value]; + } +} diff --git a/src/Symfony/Component/HttpKernel/Tests/Controller/ArgumentResolver/SessionParameterValueResolverTest.php b/src/Symfony/Component/HttpKernel/Tests/Controller/ArgumentResolver/SessionParameterValueResolverTest.php new file mode 100644 index 0000000000000..ab3d620852a14 --- /dev/null +++ b/src/Symfony/Component/HttpKernel/Tests/Controller/ArgumentResolver/SessionParameterValueResolverTest.php @@ -0,0 +1,225 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\Controller\ArgumentResolver; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Session\Session; +use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage; +use Symfony\Component\HttpKernel\Attribute\MapSessionParameter; +use Symfony\Component\HttpKernel\Controller\ArgumentResolver\SessionParameterValueResolver; +use Symfony\Component\HttpKernel\Controller\ValueResolverInterface; +use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata; + +class SessionParameterValueResolverTest extends TestCase +{ + private ValueResolverInterface $resolver; + + private Request $request; + + protected function setUp(): void + { + $this->resolver = new SessionParameterValueResolver(); + + $session = new Session(new MockArraySessionStorage()); + $this->request = Request::create('/'); + $this->request->setSession($session); + } + + public function testSkipWhenNoAttribute() + { + $metadata = new ArgumentMetadata('browsingContext', 'string', false, true, false); + + $this->assertSame([], $this->resolver->resolve($this->request, $metadata)); + } + + public function testSkipWhenNoSession() + { + $metadata = new ArgumentMetadata('MySessionObject', BasicSessionParameter::class, false, false, false, attributes: [new MapSessionParameter()]); + + $this->assertSame([], $this->resolver->resolve(Request::create('/'), $metadata)); + } + + /** + * @dataProvider invalidArgumentTypeProvider + */ + public function testResolvingWithInvalidArgumentType(ArgumentMetadata $metadata, string $exceptionMessage) + { + $this->expectException(\LogicException::class); + $this->expectExceptionMessage($exceptionMessage); + + $this->resolver->resolve($this->request, $metadata); + } + + /** + * @return iterable + */ + public static function invalidArgumentTypeProvider(): iterable + { + yield 'untyped parameter' => [ + new ArgumentMetadata('MySessionObject', null, false, false, false, attributes: [new MapSessionParameter()]), + '#[MapSessionParameter] cannot be used on controller argument "$MySessionObject": "" is not a class or interface name.', + ]; + + yield 'scalar parameter' => [ + new ArgumentMetadata('MySessionObject', 'string', false, false, false, attributes: [new MapSessionParameter()]), + '#[MapSessionParameter] cannot be used on controller argument "$MySessionObject": "string" is not a class or interface name.', + ]; + + yield 'variadic scalar parameter' => [ + new ArgumentMetadata('MySessionObject', 'string', true, false, false, true, attributes: [new MapSessionParameter()]), + '#[MapSessionParameter] cannot be used on controller argument "$MySessionObject": "string" is not a class or interface name.', + ]; + + yield 'interface without default value and not nullable' => [ + new ArgumentMetadata('MySessionObject', SessionParameterInterface::class, false, false, false, attributes: [new MapSessionParameter()]), + '#[MapSessionParameter] cannot be used on controller argument "$MySessionObject": "Symfony\Component\HttpKernel\Tests\Controller\ArgumentResolver\SessionParameterInterface" is an interface, you need to make the parameter nullable or provide a default value.', + ]; + } + + /** + * @dataProvider validDataProvider + */ + public function testResolvingSuccessfully(ArgumentMetadata $metadata, ?string $expectedType) + { + $result = $this->resolver->resolve($this->request, $metadata); + $this->assertCount(1, $result); + + if (null === $expectedType) { + $this->assertNull($result[0]); + } else { + $this->assertInstanceOf($expectedType, $result[0]); + } + } + + /** + * @return iterable + */ + public static function validDataProvider(): iterable + { + yield 'typed parameter with properties' => [ + new ArgumentMetadata('MySessionObject', BasicSessionParameter::class, false, false, false, attributes: [new MapSessionParameter()]), + BasicSessionParameter::class, + ]; + yield 'typed parameter without properties' => [ + new ArgumentMetadata('MySessionObject', EmptySessionParameter::class, false, false, false, attributes: [new MapSessionParameter()]), + EmptySessionParameter::class, + ]; + yield 'stdClass parameter' => [ + new ArgumentMetadata('MySessionObject', \stdClass::class, false, false, false, attributes: [new MapSessionParameter()]), + \stdClass::class, + ]; + yield 'variadic parameter' => [ + new ArgumentMetadata('MySessionObject', BasicSessionParameter::class, true, false, false, attributes: [new MapSessionParameter()]), + BasicSessionParameter::class, + ]; + yield 'nullable parameter' => [ + new ArgumentMetadata('MySessionObject', BasicSessionParameter::class, false, false, false, true, attributes: [new MapSessionParameter()]), + BasicSessionParameter::class, + ]; + yield 'default to null parameter' => [ + new ArgumentMetadata('MySessionObject', BasicSessionParameter::class, false, true, null, true, attributes: [new MapSessionParameter()]), + null, + ]; + yield 'nullable interface without default value' => [ + new ArgumentMetadata('MySessionObject', SessionParameterInterface::class, false, true, null, true, attributes: [new MapSessionParameter()]), + null, + ]; + yield 'interface with default value' => [ + new ArgumentMetadata('MySessionObject', SessionParameterInterface::class, false, true, new BasicSessionParameter(), false, attributes: [new MapSessionParameter()]), + BasicSessionParameter::class, + ]; + } + + public function testWithoutNameParameter() + { + $metadata = new ArgumentMetadata('MySessionObject', BasicSessionParameter::class, false, false, false, attributes: [new MapSessionParameter()]); + $this->resolver->resolve($this->request, $metadata); + $this->assertEquals(['MySessionObject'], array_keys($this->request->getSession()->all())); + } + + /** + * @dataProvider sessionNameProvider + */ + public function testNameParameter(?string $name, string $sessionKey) + { + $metadata = new ArgumentMetadata('MySessionObject', BasicSessionParameter::class, false, false, false, attributes: [ + new MapSessionParameter($name), + ]); + $this->resolver->resolve($this->request, $metadata); + $this->assertEquals([$sessionKey], array_keys($this->request->getSession()->all())); + } + + /** + * @return iterable + */ + public static function sessionNameProvider(): iterable + { + yield 'no value' => [null, 'MySessionObject']; + yield 'same as class' => ['MySessionObject', 'MySessionObject']; + yield 'empty' => ['', '']; + yield 'other' => ['other', 'other']; + } + + public function testResolvingCorrectTypeSuccessfully() + { + $this->request->getSession()->set('MySessionObject', new ExtendingEmptySessionParameter()); + $result = $this->resolver->resolve($this->request, new ArgumentMetadata('MySessionObject', EmptySessionParameter::class, false, false, false, attributes: [new MapSessionParameter()])); + + $this->assertCount(1, $result); + $this->assertInstanceOf(EmptySessionParameter::class, $result[0]); + } + + public function testResolvingCorrectInterfaceSuccessfully() + { + $this->request->getSession()->set('MySessionObject', new BasicSessionParameter()); + $result = $this->resolver->resolve($this->request, new ArgumentMetadata('MySessionObject', SessionParameterInterface::class, false, false, false, isNullable: true, attributes: [new MapSessionParameter()])); + + $this->assertCount(1, $result); + $this->assertInstanceOf(SessionParameterInterface::class, $result[0]); + } + + public function testResolvingIncorrectTypeFailure() + { + $this->expectException(\LogicException::class); + $this->expectExceptionMessage('#[MapSessionParameter] cannot be used to map controller argument "$MySessionObject": the session contains a value of type "Symfony\Component\HttpKernel\Tests\Controller\ArgumentResolver\BasicSessionParameter" which is not an instance of "Symfony\Component\HttpKernel\Tests\Controller\ArgumentResolver\EmptySessionParameter".'); + + $this->request->getSession()->set('MySessionObject', new BasicSessionParameter()); + $this->resolver->resolve($this->request, new ArgumentMetadata('MySessionObject', EmptySessionParameter::class, false, false, false, attributes: [new MapSessionParameter()])); + } + + public function testResolvingIncorrectInterfaceFailure() + { + $this->expectException(\LogicException::class); + $this->expectExceptionMessage('#[MapSessionParameter] cannot be used to map controller argument "$MySessionObject": the session contains a value of type "Symfony\Component\HttpKernel\Tests\Controller\ArgumentResolver\EmptySessionParameter" which is not an instance of "Symfony\Component\HttpKernel\Tests\Controller\ArgumentResolver\SessionParameterInterface".'); + + $this->request->getSession()->set('MySessionObject', new EmptySessionParameter()); + $this->resolver->resolve($this->request, new ArgumentMetadata('MySessionObject', SessionParameterInterface::class, false, false, false, isNullable: true, attributes: [new MapSessionParameter()])); + } +} + +class BasicSessionParameter implements SessionParameterInterface +{ + public $locale; +} + +class EmptySessionParameter +{ +} + +class ExtendingEmptySessionParameter extends EmptySessionParameter +{ +} + +interface SessionParameterInterface +{ +} From 7f28a8d0c2009890264ad6d9c4b78769b8a19ecd Mon Sep 17 00:00:00 2001 From: Julien Tattevin Date: Fri, 23 Aug 2024 10:14:51 +0200 Subject: [PATCH 2/3] Add support for union type --- .../SessionParameterValueResolver.php | 19 +++++-- .../SessionParameterValueResolverTest.php | 52 +++++++++++++++++++ 2 files changed, 68 insertions(+), 3 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/SessionParameterValueResolver.php b/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/SessionParameterValueResolver.php index 8be394ab08d56..3e8106509ff99 100644 --- a/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/SessionParameterValueResolver.php +++ b/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/SessionParameterValueResolver.php @@ -29,7 +29,13 @@ public function resolve(Request $request, ArgumentMetadata $argument): array } if ((!$type = $argument->getType()) || (!class_exists($type) && !interface_exists($type, false))) { - throw new \LogicException(\sprintf('#[MapSessionParameter] cannot be used on controller argument "$%s": "%s" is not a class or interface name.', $argument->getName(), $type)); + if ($type && (str_contains($type, '|') || str_contains($type, '&'))) { + if (!$argument->hasDefaultValue() && !$argument->isNullable()) { + throw new \LogicException(\sprintf('#[MapSessionParameter] cannot be used on controller argument "$%s": "%s" is an union or intersection type, you need to make the parameter nullable or provide a default value..', $argument->getName(), $type)); + } + } else { + throw new \LogicException(\sprintf('#[MapSessionParameter] cannot be used on controller argument "$%s": "%s" is not a class or interface name.', $argument->getName(), $type)); + } } if (interface_exists($type, false) && !$argument->hasDefaultValue() && !$argument->isNullable()) { @@ -39,14 +45,21 @@ public function resolve(Request $request, ArgumentMetadata $argument): array $name = $attribute->name ?? $argument->getName(); if ($request->getSession()->has($name)) { $value = $request->getSession()->get($name); - if (!$value instanceof $type) { + if (!$value instanceof $type && !str_contains($type, '|') && !str_contains($type, '&')) { throw new \LogicException(\sprintf('#[MapSessionParameter] cannot be used to map controller argument "$%s": the session contains a value of type "%s" which is not an instance of "%s".', $argument->getName(), get_debug_type($value), $type)); } return [$value]; } - if (\is_object($value = $argument->hasDefaultValue() ? $argument->getDefaultValue() : new $type())) { + if ($argument->hasDefaultValue()) { + $value = $argument->getDefaultValue(); + } else { + // handle the type SessionInterface|null $param, which doesn't have a default value and can't be instantiated. + $value = class_exists($type, false) ? new $type() : null; + } + + if (\is_object($value)) { $request->getSession()->set($name, $value); } diff --git a/src/Symfony/Component/HttpKernel/Tests/Controller/ArgumentResolver/SessionParameterValueResolverTest.php b/src/Symfony/Component/HttpKernel/Tests/Controller/ArgumentResolver/SessionParameterValueResolverTest.php index ab3d620852a14..70d5e137ae59b 100644 --- a/src/Symfony/Component/HttpKernel/Tests/Controller/ArgumentResolver/SessionParameterValueResolverTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/Controller/ArgumentResolver/SessionParameterValueResolverTest.php @@ -84,6 +84,16 @@ public static function invalidArgumentTypeProvider(): iterable new ArgumentMetadata('MySessionObject', SessionParameterInterface::class, false, false, false, attributes: [new MapSessionParameter()]), '#[MapSessionParameter] cannot be used on controller argument "$MySessionObject": "Symfony\Component\HttpKernel\Tests\Controller\ArgumentResolver\SessionParameterInterface" is an interface, you need to make the parameter nullable or provide a default value.', ]; + + yield 'union type' => [ + new ArgumentMetadata('MySessionObject', BasicSessionParameter::class.'|'.EmptySessionParameter::class, false, false, false, attributes: [new MapSessionParameter()]), + '#[MapSessionParameter] cannot be used on controller argument "$MySessionObject": "Symfony\Component\HttpKernel\Tests\Controller\ArgumentResolver\BasicSessionParameter|Symfony\Component\HttpKernel\Tests\Controller\ArgumentResolver\EmptySessionParameter" is an union or intersection type, you need to make the parameter nullable or provide a default value.', + ]; + + yield 'intersection type' => [ + new ArgumentMetadata('MySessionObject', BasicSessionParameter::class.'&'.EmptySessionParameter::class, false, false, false, attributes: [new MapSessionParameter()]), + '#[MapSessionParameter] cannot be used on controller argument "$MySessionObject": "Symfony\Component\HttpKernel\Tests\Controller\ArgumentResolver\BasicSessionParameter&Symfony\Component\HttpKernel\Tests\Controller\ArgumentResolver\EmptySessionParameter" is an union or intersection type, you need to make the parameter nullable or provide a default value.', + ]; } /** @@ -131,6 +141,10 @@ public static function validDataProvider(): iterable null, ]; yield 'nullable interface without default value' => [ + new ArgumentMetadata('MySessionObject', SessionParameterInterface::class, false, false, null, true, attributes: [new MapSessionParameter()]), + null, + ]; + yield 'nullable interface defaulting to null' => [ new ArgumentMetadata('MySessionObject', SessionParameterInterface::class, false, true, null, true, attributes: [new MapSessionParameter()]), null, ]; @@ -138,6 +152,26 @@ public static function validDataProvider(): iterable new ArgumentMetadata('MySessionObject', SessionParameterInterface::class, false, true, new BasicSessionParameter(), false, attributes: [new MapSessionParameter()]), BasicSessionParameter::class, ]; + + yield 'nullable union type without default value' => [ + new ArgumentMetadata('MySessionObject', BasicSessionParameter::class.'|'.EmptySessionParameter::class, false, true, null, attributes: [new MapSessionParameter()]), + null, + ]; + + yield 'nullable intersection type without default value' => [ + new ArgumentMetadata('MySessionObject', BasicSessionParameter::class.'&'.EmptySessionParameter::class, false, true, null, attributes: [new MapSessionParameter()]), + null, + ]; + + yield 'union type with default value' => [ + new ArgumentMetadata('MySessionObject', BasicSessionParameter::class.'|'.EmptySessionParameter::class, false, true, new BasicSessionParameter(), attributes: [new MapSessionParameter()]), + BasicSessionParameter::class, + ]; + + yield 'intersection type with default value' => [ + new ArgumentMetadata('MySessionObject', BasicSessionParameter::class.'&'.EmptySessionParameter::class, false, true, new BasicSessionParameter(), attributes: [new MapSessionParameter()]), + BasicSessionParameter::class, + ]; } public function testWithoutNameParameter() @@ -188,6 +222,24 @@ public function testResolvingCorrectInterfaceSuccessfully() $this->assertInstanceOf(SessionParameterInterface::class, $result[0]); } + public function testResolvingCorrectUnionSuccessfully() + { + $this->request->getSession()->set('MySessionObject', new BasicSessionParameter()); + $result = $this->resolver->resolve($this->request, new ArgumentMetadata('MySessionObject', SessionParameterInterface::class.'|'.EmptySessionParameter::class, false, false, false, isNullable: true, attributes: [new MapSessionParameter()])); + + $this->assertCount(1, $result); + $this->assertInstanceOf(BasicSessionParameter::class, $result[0]); + } + + public function testResolvingCorrectIntersectionSuccessfully() + { + $this->request->getSession()->set('MySessionObject', new BasicSessionParameter()); + $result = $this->resolver->resolve($this->request, new ArgumentMetadata('MySessionObject', SessionParameterInterface::class.'&'.BasicSessionParameter::class, false, false, false, isNullable: true, attributes: [new MapSessionParameter()])); + + $this->assertCount(1, $result); + $this->assertInstanceOf(BasicSessionParameter::class, $result[0]); + } + public function testResolvingIncorrectTypeFailure() { $this->expectException(\LogicException::class); From 8ad905c2f0ecbcc97e0833e0810d64862359181d Mon Sep 17 00:00:00 2001 From: Julien Tattevin Date: Tue, 17 Dec 2024 11:47:34 +0100 Subject: [PATCH 3/3] Set FrameworkBundle dependency to HttpKernel 7.3 --- src/Symfony/Bundle/FrameworkBundle/composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/composer.json b/src/Symfony/Bundle/FrameworkBundle/composer.json index 2ecedbc45660e..95ebcd3cc4eef 100644 --- a/src/Symfony/Bundle/FrameworkBundle/composer.json +++ b/src/Symfony/Bundle/FrameworkBundle/composer.json @@ -26,7 +26,7 @@ "symfony/error-handler": "^7.3", "symfony/event-dispatcher": "^6.4|^7.0", "symfony/http-foundation": "^7.3", - "symfony/http-kernel": "^7.2", + "symfony/http-kernel": "^7.3", "symfony/polyfill-mbstring": "~1.0", "symfony/filesystem": "^7.1", "symfony/finder": "^6.4|^7.0",