8000 [HttpKernel] Handle multi-attribute controller arguments · symfony/symfony@cf03bcb · GitHub
[go: up one dir, main page]

Skip to content

Commit cf03bcb

Browse files
committed
[HttpKernel] Handle multi-attribute controller arguments
1 parent adbb341 commit cf03bcb

File tree

10 files changed

+81
-13
lines changed

10 files changed

+81
-13
lines changed

UPGRADE-5.3.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ HttpFoundation
4444
HttpKernel
4545
----------
4646

47+
* Deprecate `ArgumentInterface`
48+
* Deprecate `ArgumentMetadata::getAttribute()`, use `getAttributes()` and `hasAttributes()` instead
4749
* Marked the class `Symfony\Component\HttpKernel\EventListener\DebugHandlersListener` as internal
4850

4951
Messenger

UPGRADE-6.0.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,8 @@ HttpFoundation
9292
HttpKernel
9393
----------
9494

95+
* Remove `ArgumentInterface`
96+
* Remove `ArgumentMetadata::getAttribute()`, use `getAttributes()` and `hasAttribute()` instead
9597
* Made `WarmableInterface::warmUp()` return a list of classes or files to preload on PHP 7.4+
9698
* Removed support for `service:action` syntax to reference controllers. Use `serviceOrFqcn::method` instead.
9799

src/Symfony/Component/HttpKernel/Attribute/ArgumentInterface.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,12 @@
1111

1212
namespace Symfony\Component\HttpKernel\Attribute;
1313

14+
trigger_deprecation('symfony/http-kernel', '5.3', 'The "%s" interface is deprecated.', ArgumentInterface::class);
15+
1416
/**
1517
* Marker interface for controller argument attributes.
18+
*
19+
* @deprecated since Symfony 5.3
1620
*/
1721
interface ArgumentInterface
1822
{

src/Symfony/Component/HttpKernel/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ CHANGELOG
44
5.3
55
---
66

7+
* Deprecate `ArgumentInterface`
8+
* Deprecate `ArgumentMetadata::getAttribute()`, use `getAttributes()` and `hasAttribute()` instead
79
* marked the class `Symfony\Component\HttpKernel\EventListener\DebugHandlersListener` as internal
810

911
5.2.0

src/Symfony/Component/HttpKernel/ControllerMetadata/ArgumentMetadata.php

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,17 +26,26 @@ class ArgumentMetadata
2626
private $hasDefaultValue;
2727
private $defaultValue;
2828
private $isNullable;
29-
private $attribute;
29+
private $attributes;
3030

31-
public function __construct(string $name, ?string $type, bool $isVariadic, bool $hasDefaultValue, $defaultValue, bool $isNullable = false, ?ArgumentInterface $attribute = null)
31+
/**
32+
* @param object[] $attributes A list of PHP Attribute instances
33+
*/
34+
public function __construct(string $name, ?string $type, bool $isVariadic, bool $hasDefaultValue, $defaultValue, bool $isNullable = false, $attributes = [])
3235
{
3336
$this->name = $name;
3437
$this->type = $type;
3538
$this->isVariadic = $isVariadic;
3639
$this->hasDefaultValue = $hasDefaultValue;
3740
$this->defaultValue = $defaultValue;
3841
$this->isNullable = $isNullable || null === $type || ($hasDefaultValue && null === $defaultValue);
39-
$this->attribute = $attribute;
42+
43+
if (null === $attributes || $attributes instanceof ArgumentInterface) {
44+
trigger_deprecation('symfony/http-kernel', '5.3', 'The "%s" constructor expects an array of PHP attributes as last argument, %s given.', __CLASS__, get_debug_type($attributes));
45+
$attributes = $attributes ? [$attributes] : [];
46+
}
47+
48+
$this->attributes = $attributes;
4049
}
4150

4251
/**
@@ -114,6 +123,28 @@ public function getDefaultValue()
114123
*/
115124
public function getAttribute(): ?ArgumentInterface
116125
{
117-
return $this->attribute;
126+
trigger_deprecation('symfony/http-kernel', '5.3', 'Method "%s()" is deprecated, use "getAttributes()" instead.', __METHOD__);
127+
128+
if (!$this->attributes) {
129+
return null;
130+
}
131+
132+
return $this->attributes[0] instanceof ArgumentInterface ? $this->attributes[0] : null;
133+
}
134+
135+
public function hasAttribute(string $type): bool
136+
{
137+
foreach ($this->attributes as $attribute) {
138+
if ($attribute instanceof $type) {
139+
return true;
140+
}
141+
}
142+
143+
return false;
144+
}
145+
146+
public function getAttributes(): array
147+
{
148+
return $this->attributes;
118149
}
119150
}

src/Symfony/Component/HttpKernel/ControllerMetadata/ArgumentMetadataFactory.php

Lines changed: 5 additions & 6 deletions
if (\count($reflectionAttributes) > 1) {
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111

1212
namespace Symfony\Component\HttpKernel\ControllerMetadata;
1313

14-
use Symfony\Component\HttpKernel\Attribute\ArgumentInterface;
1514
use Symfony\Component\HttpKernel\Exception\InvalidMetadataException;
1615

1716
/**
@@ -37,9 +36,9 @@ public function createArgumentMetadata($controller): array
3736
}
3837

3938
foreach ($reflection->getParameters() as $param) {
40-
$attribute = null;
39+
$attributes = [];
4140
if (\PHP_VERSION_ID >= 80000) {
42-
$reflectionAttributes = $param->getAttributes(ArgumentInterface::class, \ReflectionAttribute::IS_INSTANCEOF);
41+
$reflectionAttributes = $param->getAttributes();
4342

4443
4544
$representative = $controller;
@@ -53,12 +52,12 @@ public function createArgumentMetadata($controller): array
5352
throw new InvalidMetadataException(sprintf('Controller "%s" has more than one attribute for "$%s" argument.', $representative, $param->getName()));
5453
}
5554

56-
if (isset($reflectionAttributes[0])) {
57-
$attribute = $reflectionAttributes[0]->newInstance();
55+
foreach ($reflectionAttributes as $reflectionAttribute) {
56+
$attributes[] = $reflectionAttribute->newInstance();
5857
}
5958
}
6059

61-
$arguments[] = new ArgumentMetadata($param->getName(), $this->getType($param, $reflection), $param->isVariadic(), $param->isDefaultValueAvailable(), $param->isDefaultValueAvailable() ? $param->getDefaultValue() : null, $param->allowsNull(), $attribute);
60+
$arguments[] = new ArgumentMetadata($param->getName(), $this->getType($param, $reflection), $param->isVariadic(), $param->isDefaultValueAvailable(), $param->isDefaultValueAvailable() ? $param->getDefaultValue() : null, $param->allowsNull(), $attributes);
6261
}
6362

6463
return $arguments;

src/Symfony/Component/HttpKernel/Tests/ControllerMetadata/ArgumentMetadataFactoryTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ public function testAttributeSignature()
128128
$arguments = $this->factory->createArgumentMetadata([new AttributeController(), 'action']);
129129

130130
$this->assertEquals([
131-
new ArgumentMetadata('baz', 'string', false, false, null, false, new Foo('bar')),
131+
new ArgumentMetadata('baz', 'string', false, false, null, false, [new Foo('bar')]),
132132
], $arguments);
133133
}
134134

src/Symfony/Component/HttpKernel/Tests/ControllerMetadata/ArgumentMetadataTest.php

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,15 @@
1212
namespace Symfony\Component\HttpKernel\Tests\ControllerMetadata;
1313

1414
use PHPUnit\Framework\TestCase;
15+
use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait;
16+
use Symfony\Component\HttpKernel\Attribute\ArgumentInterface;
1517
use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata;
18+
use Symfony\Component\HttpKernel\Tests\Fixtures\Attribute\Foo;
1619

1720
class ArgumentMetadataTest extends TestCase
1821
{
22+
use ExpectDeprecationTrait;
23+
1924
public function testWithBcLayerWithDefault()
2025
{
2126
$argument = new ArgumentMetadata('foo', 'string', false, true, 'default value');
@@ -41,4 +46,27 @@ public function testDefaultValueUnavailable()
4146
$this->assertFalse($argument->hasDefaultValue());
4247
$argument->getDefaultValue();
4348
}
49+
50+
/**
51+
* @group legacy
52+
*/
53+
public function testLegacyAttribute()
54+
{
55+
$attribute = $this->createMock(ArgumentInterface::class);
56+
57+
$this->expectDeprecation('Since symfony/http-kernel 5.3: The "Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata" constructor expects an array of PHP attributes as last argument, %s given.');
58+
$this->expectDeprecation('Since symfony/http-kernel 5.3: Method "Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata::getAttribute()" is deprecated, use "getAttributes()" instead.');
59+
60+
$argument = new ArgumentMetadata('foo', 'string', false, true, 'default value', true, $attribute);
61+
$this->assertSame($attribute, $argument->getAttribute());
62+
}
63+
64+
public function testGetAttributes()
65+
{
66+
$attribute = new Foo('bar');
67+
68+
$argument = new ArgumentMetadata('foo', 'string', false, true, 'default value', true, [$attribute]);
69+
$this->assertTrue($argument->hasAttribute(Foo::class));
70+
$this->assertSame([$attribute], $argument->getAttributes());
71+
}
4472
}

src/Symfony/Component/HttpKernel/Tests/Fixtures/Attribute/Foo.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
use Symfony\Component\HttpKernel\Attribute\ArgumentInterface;
1515

1616
#[\Attribute(\Attribute::TARGET_PARAMETER)]
17-
class Foo implements< 9BC3 span class="x x-last"> ArgumentInterface
17+
class Foo
1818
{
1919
private $foo;
2020

src/Symfony/Component/Security/Http/Controller/UserValueResolver.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ public function supports(Request $request, ArgumentMetadata $argument): bool
3737
{
3838
// with the attribute, the type can be any UserInterface implementation
3939
// otherwise, the type must be UserInterface
40-
if (UserInterface::class !== $argument->getType() && !$argument->getAttribute() instanceof CurrentUser) {
40+
if (UserInterface::class !== $argument->getType() && !$argument->hasAttribute(CurrentUser::class)) {
4141
return false;
4242
}
4343

0 commit comments

Comments
 (0)
0