feature #40266 [Routing] Construct Route annotations using named arguments (derrabus)
This PR was merged into the 5.3-dev branch.
Discussion
----------
[Routing] Construct Route annotations using named arguments
| Q | A
| ------------- | ---
| Branch? | 5.x
| Bug fix? | no
| New feature? | no
| Deprecations? | yes
| Tickets | N/A
| License | MIT
| Doc PR | Not needed
This PR proposes to bump the `doctrine/annotations` library to 1.12 to gain access to its emulation layer for named arguments. Furthermore, constructing a `Route` annotation the old way by passing an array of parameters is deprecated.
### Reasons for this change
The constructors of our annotation classes have become unnecessarily complicated because we have to support two ways of calling them:
* An array of parameters, passed as first argument, because that's the default behavior `doctrine/annotations`.
* A set of named arguments because that's how PHP 8 attributes work.
Since we can now tell the Doctrine annotation reader to use named arguments as well, we can simplify the constructors of our annotations significantly.
### Drawback
After this change, there is no easy way anymore to construct instances of the `Route` annotation class directly on PHP 7. The PR has been built under the assumption that instances of this class are usually created using either Doctrine annotations or a PHP 8 attribute. Thus, most applications should be unaffected by this change.
Commits
-------
29b0f96046
[Routing] Construct Route annotations using named arguments
This commit is contained in:
commit
d54a1223f7
@ -69,6 +69,11 @@ PropertyInfo
|
|||||||
|
|
||||||
* Deprecated the `Type::getCollectionKeyType()` and `Type::getCollectionValueType()` methods, use `Type::getCollectionKeyTypes()` and `Type::getCollectionValueTypes()` instead
|
* Deprecated the `Type::getCollectionKeyType()` and `Type::getCollectionValueType()` methods, use `Type::getCollectionKeyTypes()` and `Type::getCollectionValueTypes()` instead
|
||||||
|
|
||||||
|
Routing
|
||||||
|
-------
|
||||||
|
|
||||||
|
* Deprecated creating instances of the `Route` annotation class by passing an array of parameters, use named arguments instead
|
||||||
|
|
||||||
Security
|
Security
|
||||||
--------
|
--------
|
||||||
|
|
||||||
|
@ -164,6 +164,7 @@ Routing
|
|||||||
* Removed `RouteCollectionBuilder`.
|
* Removed `RouteCollectionBuilder`.
|
||||||
* Added argument `$priority` to `RouteCollection::add()`
|
* Added argument `$priority` to `RouteCollection::add()`
|
||||||
* Removed the `RouteCompiler::REGEX_DELIMITER` constant
|
* Removed the `RouteCompiler::REGEX_DELIMITER` constant
|
||||||
|
* Removed the `$data` parameter from the constructor of the `Route` annotation class
|
||||||
|
|
||||||
Security
|
Security
|
||||||
--------
|
--------
|
||||||
|
@ -123,7 +123,7 @@
|
|||||||
"async-aws/sqs": "^1.0",
|
"async-aws/sqs": "^1.0",
|
||||||
"cache/integration-tests": "dev-master",
|
"cache/integration-tests": "dev-master",
|
||||||
"composer/package-versions-deprecated": "^1.8",
|
"composer/package-versions-deprecated": "^1.8",
|
||||||
"doctrine/annotations": "^1.10.4",
|
"doctrine/annotations": "^1.12",
|
||||||
"doctrine/cache": "~1.6",
|
"doctrine/cache": "~1.6",
|
||||||
"doctrine/collections": "~1.0",
|
"doctrine/collections": "~1.0",
|
||||||
"doctrine/data-fixtures": "^1.1",
|
"doctrine/data-fixtures": "^1.1",
|
||||||
@ -151,6 +151,7 @@
|
|||||||
},
|
},
|
||||||
"conflict": {
|
"conflict": {
|
||||||
"async-aws/core": "<1.5",
|
"async-aws/core": "<1.5",
|
||||||
|
"doctrine/annotations": "<1.12",
|
||||||
"doctrine/dbal": "<2.10",
|
"doctrine/dbal": "<2.10",
|
||||||
"masterminds/html5": "<2.6",
|
"masterminds/html5": "<2.6",
|
||||||
"phpdocumentor/reflection-docblock": "<3.2.2",
|
"phpdocumentor/reflection-docblock": "<3.2.2",
|
||||||
|
@ -15,6 +15,7 @@ namespace Symfony\Component\Routing\Annotation;
|
|||||||
* Annotation class for @Route().
|
* Annotation class for @Route().
|
||||||
*
|
*
|
||||||
* @Annotation
|
* @Annotation
|
||||||
|
* @NamedArgumentConstructor
|
||||||
* @Target({"CLASS", "METHOD"})
|
* @Target({"CLASS", "METHOD"})
|
||||||
*
|
*
|
||||||
* @author Fabien Potencier <fabien@symfony.com>
|
* @author Fabien Potencier <fabien@symfony.com>
|
||||||
@ -67,6 +68,8 @@ class Route
|
|||||||
$data = ['path' => $data];
|
$data = ['path' => $data];
|
||||||
} elseif (!\is_array($data)) {
|
} elseif (!\is_array($data)) {
|
||||||
throw new \TypeError(sprintf('"%s": Argument $data is expected to be a string or array, got "%s".', __METHOD__, get_debug_type($data)));
|
throw new \TypeError(sprintf('"%s": Argument $data is expected to be a string or array, got "%s".', __METHOD__, get_debug_type($data)));
|
||||||
|
} elseif ([] !== $data) {
|
||||||
|
trigger_deprecation('symfony/routing', '5.3', 'Passing an array as first argument to "%s" is deprecated. Use named arguments instead.', __METHOD__);
|
||||||
}
|
}
|
||||||
if (null !== $path && !\is_string($path) && !\is_array($path)) {
|
if (null !== $path && !\is_string($path) && !\is_array($path)) {
|
||||||
throw new \TypeError(sprintf('"%s": Argument $path is expected to be a string, array or null, got "%s".', __METHOD__, get_debug_type($path)));
|
throw new \TypeError(sprintf('"%s": Argument $path is expected to be a string, array or null, got "%s".', __METHOD__, get_debug_type($path)));
|
||||||
|
@ -6,6 +6,7 @@ CHANGELOG
|
|||||||
|
|
||||||
* Already encoded slashes are not decoded nor double-encoded anymore when generating URLs
|
* Already encoded slashes are not decoded nor double-encoded anymore when generating URLs
|
||||||
* Add support for per-env configuration in loaders
|
* Add support for per-env configuration in loaders
|
||||||
|
* Deprecate creating instances of the `Route` annotation class by passing an array of parameters
|
||||||
|
|
||||||
5.2.0
|
5.2.0
|
||||||
-----
|
-----
|
||||||
|
@ -12,16 +12,25 @@
|
|||||||
namespace Symfony\Component\Routing\Tests\Annotation;
|
namespace Symfony\Component\Routing\Tests\Annotation;
|
||||||
|
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait;
|
||||||
use Symfony\Component\Routing\Annotation\Route;
|
use Symfony\Component\Routing\Annotation\Route;
|
||||||
|
|
||||||
class RouteTest extends TestCase
|
class RouteTest extends TestCase
|
||||||
{
|
{
|
||||||
|
use ExpectDeprecationTrait;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @group legacy
|
||||||
|
*/
|
||||||
public function testInvalidRouteParameter()
|
public function testInvalidRouteParameter()
|
||||||
{
|
{
|
||||||
$this->expectException(\BadMethodCallException::class);
|
$this->expectException(\BadMethodCallException::class);
|
||||||
new Route(['foo' => 'bar']);
|
new Route(['foo' => 'bar']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @group legacy
|
||||||
|
*/
|
||||||
public function testTryingToSetLocalesDirectly()
|
public function testTryingToSetLocalesDirectly()
|
||||||
{
|
{
|
||||||
$this->expectException(\BadMethodCallException::class);
|
$this->expectException(\BadMethodCallException::class);
|
||||||
@ -29,18 +38,30 @@ class RouteTest extends TestCase
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @requires PHP 8
|
||||||
* @dataProvider getValidParameters
|
* @dataProvider getValidParameters
|
||||||
*/
|
*/
|
||||||
public function testRouteParameters($parameter, $value, $getter)
|
public function testRouteParameters(string $parameter, $value, string $getter)
|
||||||
{
|
{
|
||||||
|
$route = new Route(...[$parameter => $value]);
|
||||||
|
$this->assertEquals($route->$getter(), $value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @group legacy
|
||||||
|
* @dataProvider getLegacyValidParameters
|
||||||
|
*/
|
||||||
|
public function testLegacyRouteParameters(string $parameter, $value, string $getter)
|
||||||
|
{
|
||||||
|
$this->expectDeprecation('Since symfony/routing 5.3: Passing an array as first argument to "Symfony\Component\Routing\Annotation\Route::__construct" is deprecated. Use named arguments instead.');
|
||||||
|
|
||||||
$route = new Route([$parameter => $value]);
|
$route = new Route([$parameter => $value]);
|
||||||
$this->assertEquals($route->$getter(), $value);
|
$this->assertEquals($route->$getter(), $value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getValidParameters()
|
public function getValidParameters(): iterable
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
['value', '/Blog', 'getPath'],
|
|
||||||
['requirements', ['locale' => 'en'], 'getRequirements'],
|
['requirements', ['locale' => 'en'], 'getRequirements'],
|
||||||
['options', ['compiler_class' => 'RouteCompiler'], 'getOptions'],
|
['options', ['compiler_class' => 'RouteCompiler'], 'getOptions'],
|
||||||
['name', 'blog_index', 'getName'],
|
['name', 'blog_index', 'getName'],
|
||||||
@ -49,7 +70,14 @@ class RouteTest extends TestCase
|
|||||||
['methods', ['GET', 'POST'], 'getMethods'],
|
['methods', ['GET', 'POST'], 'getMethods'],
|
||||||
['host', '{locale}.example.com', 'getHost'],
|
['host', '{locale}.example.com', 'getHost'],
|
||||||
['condition', 'context.getMethod() == "GET"', 'getCondition'],
|
['condition', 'context.getMethod() == "GET"', 'getCondition'],
|
||||||
['value', ['nl' => '/hier', 'en' => '/here'], 'getLocalizedPaths'],
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getLegacyValidParameters(): iterable
|
||||||
|
{
|
||||||
|
yield from $this->getValidParameters();
|
||||||
|
|
||||||
|
yield ['value', '/Blog', 'getPath'];
|
||||||
|
yield ['value', ['nl' => '/hier', 'en' => '/here'], 'getLocalizedPaths'];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -51,7 +51,7 @@ class AnnotationFileLoaderTest extends AbstractAnnotationLoaderTest
|
|||||||
|
|
||||||
public function testLoadVariadic()
|
public function testLoadVariadic()
|
||||||
{
|
{
|
||||||
$route = new Route(['path' => '/path/to/{id}']);
|
$route = new Route('/path/to/{id}');
|
||||||
$this->reader->expects($this->once())->method('getClassAnnotation');
|
$this->reader->expects($this->once())->method('getClassAnnotation');
|
||||||
$this->reader->expects($this->once())->method('getMethodAnnotations')
|
$this->reader->expects($this->once())->method('getMethodAnnotations')
|
||||||
->willReturn([$route]);
|
->willReturn([$route]);
|
||||||
|
@ -26,10 +26,11 @@
|
|||||||
"symfony/yaml": "^4.4|^5.0",
|
"symfony/yaml": "^4.4|^5.0",
|
||||||
"symfony/expression-language": "^4.4|^5.0",
|
"symfony/expression-language": "^4.4|^5.0",
|
||||||
"symfony/dependency-injection": "^4.4|^5.0",
|
"symfony/dependency-injection": "^4.4|^5.0",
|
||||||
"doctrine/annotations": "^1.10.4",
|
"doctrine/annotations": "^1.12",
|
||||||
"psr/log": "~1.0"
|
"psr/log": "~1.0"
|
||||||
},
|
},
|
||||||
"conflict": {
|
"conflict": {
|
||||||
|
"doctrine/annotations": "<1.12",
|
||||||
"symfony/config": "<5.3",
|
"symfony/config": "<5.3",
|
||||||
"symfony/dependency-injection": "<4.4",
|
"symfony/dependency-injection": "<4.4",
|
||||||
"symfony/yaml": "<4.4"
|
"symfony/yaml": "<4.4"
|
||||||
|
Reference in New Issue
Block a user