Skip to content

Commit 6087c60

Browse files
committed
Add support for union type
1 parent 8e8826f commit 6087c60

File tree

2 files changed

+68
-3
lines changed

2 files changed

+68
-3
lines changed

src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/SessionParameterValueResolver.php

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,13 @@ public function resolve(Request $request, ArgumentMetadata $argument): array
2929
}
3030

3131
if ((!$type = $argument->getType()) || (!class_exists($type) && !interface_exists($type, false))) {
32-
throw new \LogicException(\sprintf('#[MapSessionParameter] cannot be used on controller argument "$%s": "%s" is not a class or interface name.', $argument->getName(), $type));
32+
if ($type && (str_contains($type, '|') || str_contains($type, '&'))) {
33+
if (!$argument->hasDefaultValue() && !$argument->isNullable()) {
34+
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));
35+
}
36+
} else {
37+
throw new \LogicException(\sprintf('#[MapSessionParameter] cannot be used on controller argument "$%s": "%s" is not a class or interface name.', $argument->getName(), $type));
38+
}
3339
}
3440

3541
if (interface_exists($type, false) && !$argument->hasDefaultValue() && !$argument->isNullable()) {
@@ -39,14 +45,21 @@ public function resolve(Request $request, ArgumentMetadata $argument): array
3945
$name = $attribute->name ?? $argument->getName();
4046
if ($request->getSession()->has($name)) {
4147
$value = $request->getSession()->get($name);
42-
if (!$value instanceof $type) {
48+
if (!$value instanceof $type && !str_contains($type, '|') && !str_contains($type, '&')) {
4349
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));
4450
}
4551

4652
return [$value];
4753
}
4854

49-
if (\is_object($value = $argument->hasDefaultValue() ? $argument->getDefaultValue() : new $type())) {
55+
if ($argument->hasDefaultValue()) {
56+
$value = $argument->getDefaultValue();
57+
} else {
58+
// handle the type SessionInterface|null $param, which doesn't have a default value and can't be instantiated.
59+
$value = class_exists($type, false) ? new $type() : null;
60+
}
61+
62+
if (\is_object($value)) {
5063
$request->getSession()->set($name, $value);
5164
}
5265

src/Symfony/Component/HttpKernel/Tests/Controller/ArgumentResolver/SessionParameterValueResolverTest.php

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,16 @@ public static function invalidArgumentTypeProvider(): iterable
8484
new ArgumentMetadata('MySessionObject', SessionParameterInterface::class, false, false, false, attributes: [new MapSessionParameter()]),
8585
'#[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.',
8686
];
87+
88+
yield 'union type' => [
89+
new ArgumentMetadata('MySessionObject', BasicSessionParameter::class.'|'.EmptySessionParameter::class, false, false, false, attributes: [new MapSessionParameter()]),
90+
'#[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.',
91+
];
92+
93+
yield 'intersection type' => [
94+
new ArgumentMetadata('MySessionObject', BasicSessionParameter::class.'&'.EmptySessionParameter::class, false, false, false, attributes: [new MapSessionParameter()]),
95+
'#[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.',
96+
];
8797
}
8898

8999
/**
@@ -131,13 +141,37 @@ public static function validDataProvider(): iterable
131141
null,
132142
];
133143
yield 'nullable interface without default value' => [
144+
new ArgumentMetadata('MySessionObject', SessionParameterInterface::class, false, false, null, true, attributes: [new MapSessionParameter()]),
145+
null,
146+
];
147+
yield 'nullable interface defaulting to null' => [
134148
new ArgumentMetadata('MySessionObject', SessionParameterInterface::class, false, true, null, true, attributes: [new MapSessionParameter()]),
135149
null,
136150
];
137151
yield 'interface with default value' => [
138152
new ArgumentMetadata('MySessionObject', SessionParameterInterface::class, false, true, new BasicSessionParameter(), false, attributes: [new MapSessionParameter()]),
139153
BasicSessionParameter::class,
140154
];
155+
156+
yield 'nullable union type without default value' => [
157+
new ArgumentMetadata('MySessionObject', BasicSessionParameter::class.'|'.EmptySessionParameter::class, false, true, null, attributes: [new MapSessionParameter()]),
158+
null,
159+
];
160+
161+
yield 'nullable intersection type without default value' => [
162+
new ArgumentMetadata('MySessionObject', BasicSessionParameter::class.'&'.EmptySessionParameter::class, false, true, null, attributes: [new MapSessionParameter()]),
163+
null,
164+
];
165+
166+
yield 'union type with default value' => [
167+
new ArgumentMetadata('MySessionObject', BasicSessionParameter::class.'|'.EmptySessionParameter::class, false, true, new BasicSessionParameter(), attributes: [new MapSessionParameter()]),
168+
BasicSessionParameter::class,
169+
];
170+
171+
yield 'intersection type with default value' => [
172+
new ArgumentMetadata('MySessionObject', BasicSessionParameter::class.'&'.EmptySessionParameter::class, false, true, new BasicSessionParameter(), attributes: [new MapSessionParameter()]),
173+
BasicSessionParameter::class,
174+
];
141175
}
142176

143177
public function testWithoutNameParameter()
@@ -188,6 +222,24 @@ public function testResolvingCorrectInterfaceSuccessfully()
188222
$this->assertInstanceOf(SessionParameterInterface::class, $result[0]);
189223
}
190224

225+
public function testResolvingCorrectUnionSuccessfully()
226+
{
227+
$this->request->getSession()->set('MySessionObject', new BasicSessionParameter());
228+
$result = $this->resolver->resolve($this->request, new ArgumentMetadata('MySessionObject', SessionParameterInterface::class.'|'.EmptySessionParameter::class, false, false, false, isNullable: true, attributes: [new MapSessionParameter()]));
229+
230+
$this->assertCount(1, $result);
231+
$this->assertInstanceOf(BasicSessionParameter::class, $result[0]);
232+
}
233+
234+
public function testResolvingCorrectIntersectionSuccessfully()
235+
{
236+
$this->request->getSession()->set('MySessionObject', new BasicSessionParameter());
237+
$result = $this->resolver->resolve($this->request, new ArgumentMetadata('MySessionObject', SessionParameterInterface::class.'&'.BasicSessionParameter::class, false, false, false, isNullable: true, attributes: [new MapSessionParameter()]));
238+
239+
$this->assertCount(1, $result);
240+
$this->assertInstanceOf(BasicSessionParameter::class, $result[0]);
241+
}
242+
191243
public function testResolvingIncorrectTypeFailure()
192244
{
193245
$this->expectException(\LogicException::class);

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy