Content-Length: 24450 | pFad | http://github.com/symfony/symfony/pull/58819.diff
thub.com
diff --git a/src/Symfony/Component/Routing/Attribute/DeprecatedAlias.php b/src/Symfony/Component/Routing/Attribute/DeprecatedAlias.php
new file mode 100644
index 0000000000000..ae5a6821b6947
--- /dev/null
+++ b/src/Symfony/Component/Routing/Attribute/DeprecatedAlias.php
@@ -0,0 +1,46 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Routing\Attribute;
+
+/**
+ * This class is meant to be used in {@see Route} to define an alias for a route.
+ */
+class DeprecatedAlias
+{
+ public function __construct(
+ private string $aliasName,
+ private string $package,
+ private string $version,
+ private string $message = '',
+ ) {
+ }
+
+ public function getMessage(): string
+ {
+ return $this->message;
+ }
+
+ public function getAliasName(): string
+ {
+ return $this->aliasName;
+ }
+
+ public function getPackage(): string
+ {
+ return $this->package;
+ }
+
+ public function getVersion(): string
+ {
+ return $this->version;
+ }
+}
diff --git a/src/Symfony/Component/Routing/Attribute/Route.php b/src/Symfony/Component/Routing/Attribute/Route.php
index 07abc556ce893..003bbe64f5a83 100644
--- a/src/Symfony/Component/Routing/Attribute/Route.php
+++ b/src/Symfony/Component/Routing/Attribute/Route.php
@@ -22,23 +22,28 @@ class Route
private array $localizedPaths = [];
private array $methods;
private array $schemes;
+ /**
+ * @var (string|DeprecatedAlias)[]
+ */
+ private array $aliases = [];
/**
- * @param string|array|null $path The route path (i.e. "/user/login")
- * @param string|null $name The route name (i.e. "app_user_login")
- * @param array $requirements Requirements for the route attributes, @see https://symfony.com/doc/current/routing.html#parameters-validation
- * @param array $options Options for the route (i.e. ['prefix' => '/api'])
- * @param array $defaults Default values for the route attributes and query parameters
- * @param string|null $host The host for which this route should be active (i.e. "localhost")
- * @param string|string[] $methods The list of HTTP methods allowed by this route
- * @param string|string[] $schemes The list of schemes allowed by this route (i.e. "https")
- * @param string|null $condition An expression that must evaluate to true for the route to be matched, @see https://symfony.com/doc/current/routing.html#matching-expressions
- * @param int|null $priority The priority of the route if multiple ones are defined for the same path
- * @param string|null $locale The locale accepted by the route
- * @param string|null $format The format returned by the route (i.e. "json", "xml")
- * @param bool|null $utf8 Whether the route accepts UTF-8 in its parameters
- * @param bool|null $stateless Whether the route is defined as stateless or stateful, @see https://symfony.com/doc/current/routing.html#stateless-routes
- * @param string|null $env The env in which the route is defined (i.e. "dev", "test", "prod")
+ * @param string|array|null $path The route path (i.e. "/user/login")
+ * @param string|null $name The route name (i.e. "app_user_login")
+ * @param array $requirements Requirements for the route attributes, @see https://symfony.com/doc/current/routing.html#parameters-validation
+ * @param array $options Options for the route (i.e. ['prefix' => '/api'])
+ * @param array $defaults Default values for the route attributes and query parameters
+ * @param string|null $host The host for which this route should be active (i.e. "localhost")
+ * @param string|string[] $methods The list of HTTP methods allowed by this route
+ * @param string|string[] $schemes The list of schemes allowed by this route (i.e. "https")
+ * @param string|null $condition An expression that must evaluate to true for the route to be matched, @see https://symfony.com/doc/current/routing.html#matching-expressions
+ * @param int|null $priority The priority of the route if multiple ones are defined for the same path
+ * @param string|null $locale The locale accepted by the route
+ * @param string|null $format The format returned by the route (i.e. "json", "xml")
+ * @param bool|null $utf8 Whether the route accepts UTF-8 in its parameters
+ * @param bool|null $stateless Whether the route is defined as stateless or stateful, @see https://symfony.com/doc/current/routing.html#stateless-routes
+ * @param string|null $env The env in which the route is defined (i.e. "dev", "test", "prod")
+ * @param string|DeprecatedAlias|(string|DeprecatedAlias)[] $alias The list of aliases for this route
*/
public function __construct(
string|array|null $path = null,
@@ -56,6 +61,7 @@ public function __construct(
?bool $utf8 = null,
?bool $stateless = null,
private ?string $env = null,
+ string|DeprecatedAlias|array $alias = [],
) {
if (\is_array($path)) {
$this->localizedPaths = $path;
@@ -64,6 +70,7 @@ public function __construct(
}
$this->setMethods($methods);
$this->setSchemes($schemes);
+ $this->setAliases($alias);
if (null !== $locale) {
$this->defaults['_locale'] = $locale;
@@ -201,6 +208,22 @@ public function getEnv(): ?string
{
return $this->env;
}
+
+ /**
+ * @return (string|DeprecatedAlias)[]
+ */
+ public function getAliases(): array
+ {
+ return $this->aliases;
+ }
+
+ /**
+ * @param string|DeprecatedAlias|(string|DeprecatedAlias)[] $aliases
+ */
+ public function setAliases(string|DeprecatedAlias|array $aliases): void
+ {
+ $this->aliases = \is_array($aliases) ? $aliases : [$aliases];
+ }
}
if (!class_exists(\Symfony\Component\Routing\Annotation\Route::class, false)) {
diff --git a/src/Symfony/Component/Routing/CHANGELOG.md b/src/Symfony/Component/Routing/CHANGELOG.md
index 7c4614059fe3d..d66f4526d97c5 100644
--- a/src/Symfony/Component/Routing/CHANGELOG.md
+++ b/src/Symfony/Component/Routing/CHANGELOG.md
@@ -1,6 +1,11 @@
CHANGELOG
=========
+7.3
+---
+
+ * Allow aliases and deprecations in `#[Route]` attribute
+
7.2
---
diff --git a/src/Symfony/Component/Routing/Loader/AttributeClassLoader.php b/src/Symfony/Component/Routing/Loader/AttributeClassLoader.php
index 92471af0f8cbf..254582bf35584 100644
--- a/src/Symfony/Component/Routing/Loader/AttributeClassLoader.php
+++ b/src/Symfony/Component/Routing/Loader/AttributeClassLoader.php
@@ -14,7 +14,9 @@
use Symfony\Component\Config\Loader\LoaderInterface;
use Symfony\Component\Config\Loader\LoaderResolverInterface;
use Symfony\Component\Config\Resource\FileResource;
+use Symfony\Component\Routing\Attribute\DeprecatedAlias;
use Symfony\Component\Routing\Attribute\Route as RouteAttribute;
+use Symfony\Component\Routing\Exception\InvalidArgumentException;
use Symfony\Component\Routing\Exception\LogicException;
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection;
@@ -107,6 +109,15 @@ public function load(mixed $class, ?string $type = null): RouteCollection
return $collection;
}
$fqcnAlias = false;
+
+ if (!$class->hasMethod('__invoke')) {
+ foreach ($this->getAttributes($class) as $attr) {
+ if ($attr->getAliases()) {
+ throw new InvalidArgumentException(\sprintf('Route aliases cannot be used on non-invokable class "%s".', $class->getName()));
+ }
+ }
+ }
+
foreach ($class->getMethods() as $method) {
$this->defaultRouteIndex = 0;
$routeNamesBefore = array_keys($collection->all());
@@ -230,6 +241,19 @@ protected function addRoute(RouteCollection $collection, object $attr, array $gl
} else {
$collection->add($name, $route, $priority);
}
+ foreach ($attr->getAliases() as $aliasAttribute) {
+ if ($aliasAttribute instanceof DeprecatedAlias) {
+ $alias = $collection->addAlias($aliasAttribute->getAliasName(), $name);
+ $alias->setDeprecated(
+ $aliasAttribute->getPackage(),
+ $aliasAttribute->getVersion(),
+ $aliasAttribute->getMessage()
+ );
+ continue;
+ }
+
+ $collection->addAlias($aliasAttribute, $name);
+ }
}
}
diff --git a/src/Symfony/Component/Routing/Tests/Attribute/RouteTest.php b/src/Symfony/Component/Routing/Tests/Attribute/RouteTest.php
index 2696991c404c5..bbaa7563aa33f 100644
--- a/src/Symfony/Component/Routing/Tests/Attribute/RouteTest.php
+++ b/src/Symfony/Component/Routing/Tests/Attribute/RouteTest.php
@@ -9,7 +9,7 @@
* file that was distributed with this source code.
*/
-namespace Symfony\Component\Routing\Tests\Annotation;
+namespace Symfony\Component\Routing\Tests\Attribute;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Routing\Attribute\Route;
@@ -40,6 +40,7 @@ public static function getValidParameters(): iterable
['methods', 'getMethods', ['GET', 'POST']],
['host', 'getHost', '{locale}.example.com'],
['condition', 'getCondition', 'context.getMethod() == \'GET\''],
+ ['alias', 'getAliases', ['alias', 'completely_different_name']],
];
}
}
diff --git a/src/Symfony/Component/Routing/Tests/Fixtures/AttributeFixtures/AliasClassController.php b/src/Symfony/Component/Routing/Tests/Fixtures/AttributeFixtures/AliasClassController.php
new file mode 100644
index 0000000000000..c7e87128b73d3
--- /dev/null
+++ b/src/Symfony/Component/Routing/Tests/Fixtures/AttributeFixtures/AliasClassController.php
@@ -0,0 +1,29 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Routing\Tests\Fixtures\AttributeFixtures;
+
+use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
+use Symfony\Component\Routing\Attribute\Route;
+
+#[Route('/hello', alias: ['alias', 'completely_different_name'])]
+class AliasClassController
+{
+ #[Route('/world')]
+ public function actionWorld()
+ {
+ }
+
+ #[Route('/symfony')]
+ public function actionSymfony()
+ {
+ }
+}
diff --git a/src/Symfony/Component/Routing/Tests/Fixtures/AttributeFixtures/AliasInvokableController.php b/src/Symfony/Component/Routing/Tests/Fixtures/AttributeFixtures/AliasInvokableController.php
new file mode 100644
index 0000000000000..dac27b67141ed
--- /dev/null
+++ b/src/Symfony/Component/Routing/Tests/Fixtures/AttributeFixtures/AliasInvokableController.php
@@ -0,0 +1,23 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Routing\Tests\Fixtures\AttributeFixtures;
+
+use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
+use Symfony\Component\Routing\Attribute\Route;
+
+#[Route('/path', name:'invokable_path', alias: ['alias', 'completely_different_name'])]
+class AliasInvokableController
+{
+ public function __invoke()
+ {
+ }
+}
diff --git a/src/Symfony/Component/Routing/Tests/Fixtures/AttributeFixtures/AliasRouteController.php b/src/Symfony/Component/Routing/Tests/Fixtures/AttributeFixtures/AliasRouteController.php
new file mode 100644
index 0000000000000..0b828576f041c
--- /dev/null
+++ b/src/Symfony/Component/Routing/Tests/Fixtures/AttributeFixtures/AliasRouteController.php
@@ -0,0 +1,22 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Routing\Tests\Fixtures\AttributeFixtures;
+
+use Symfony\Component\Routing\Attribute\Route;
+
+class AliasRouteController
+{
+ #[Route('/path', name: 'action_with_alias', alias: ['alias', 'completely_different_name'])]
+ public function action()
+ {
+ }
+}
diff --git a/src/Symfony/Component/Routing/Tests/Fixtures/AttributeFixtures/DeprecatedAliasCustomMessageRouteController.php b/src/Symfony/Component/Routing/Tests/Fixtures/AttributeFixtures/DeprecatedAliasCustomMessageRouteController.php
new file mode 100644
index 0000000000000..08b1afbdcf108
--- /dev/null
+++ b/src/Symfony/Component/Routing/Tests/Fixtures/AttributeFixtures/DeprecatedAliasCustomMessageRouteController.php
@@ -0,0 +1,24 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Routing\Tests\Fixtures\AttributeFixtures;
+
+use Symfony\Component\Routing\Attribute\DeprecatedAlias;
+use Symfony\Component\Routing\Attribute\Route;
+
+class DeprecatedAliasCustomMessageRouteController
+{
+
+ #[Route('/path', name: 'action_with_deprecated_alias', alias: new DeprecatedAlias('my_other_alias_deprecated', 'MyBundleFixture', '1.0', message: '%alias_id% alias is deprecated.'))]
+ public function action()
+ {
+ }
+}
diff --git a/src/Symfony/Component/Routing/Tests/Fixtures/AttributeFixtures/DeprecatedAliasRouteController.php b/src/Symfony/Component/Routing/Tests/Fixtures/AttributeFixtures/DeprecatedAliasRouteController.php
new file mode 100644
index 0000000000000..06577cd79aa63
--- /dev/null
+++ b/src/Symfony/Component/Routing/Tests/Fixtures/AttributeFixtures/DeprecatedAliasRouteController.php
@@ -0,0 +1,23 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Routing\Tests\Fixtures\AttributeFixtures;
+
+use Symfony\Component\Routing\Attribute\DeprecatedAlias;
+use Symfony\Component\Routing\Attribute\Route;
+
+class DeprecatedAliasRouteController
+{
+ #[Route('/path', name: 'action_with_deprecated_alias', alias: new DeprecatedAlias('my_other_alias_deprecated', 'MyBundleFixture', '1.0'))]
+ public function action()
+ {
+ }
+}
diff --git a/src/Symfony/Component/Routing/Tests/Fixtures/AttributeFixtures/FooController.php b/src/Symfony/Component/Routing/Tests/Fixtures/AttributeFixtures/FooController.php
index adbd038ad9f73..ba82286508076 100644
--- a/src/Symfony/Component/Routing/Tests/Fixtures/AttributeFixtures/FooController.php
+++ b/src/Symfony/Component/Routing/Tests/Fixtures/AttributeFixtures/FooController.php
@@ -55,4 +55,9 @@ public function host()
public function condition()
{
}
+
+ #[Route(alias: ['alias', 'completely_different_name'])]
+ public function alias()
+ {
+ }
}
diff --git a/src/Symfony/Component/Routing/Tests/Fixtures/AttributeFixtures/MultipleDeprecatedAliasRouteController.php b/src/Symfony/Component/Routing/Tests/Fixtures/AttributeFixtures/MultipleDeprecatedAliasRouteController.php
new file mode 100644
index 0000000000000..93662d38f1789
--- /dev/null
+++ b/src/Symfony/Component/Routing/Tests/Fixtures/AttributeFixtures/MultipleDeprecatedAliasRouteController.php
@@ -0,0 +1,27 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Routing\Tests\Fixtures\AttributeFixtures;
+
+use Symfony\Component\Routing\Attribute\DeprecatedAlias;
+use Symfony\Component\Routing\Attribute\Route;
+
+class MultipleDeprecatedAliasRouteController
+{
+ #[Route('/path', name: 'action_with_multiple_deprecated_alias', alias: [
+ new DeprecatedAlias('my_first_alias_deprecated', 'MyFirstBundleFixture', '1.0'),
+ new DeprecatedAlias('my_second_alias_deprecated', 'MySecondBundleFixture', '2.0'),
+ new DeprecatedAlias('my_third_alias_deprecated', 'SurprisedThirdBundleFixture', '3.0'),
+ ])]
+ public function action()
+ {
+ }
+}
diff --git a/src/Symfony/Component/Routing/Tests/Loader/AttributeClassLoaderTest.php b/src/Symfony/Component/Routing/Tests/Loader/AttributeClassLoaderTest.php
index afad8731604f2..50a10a16cac2f 100644
--- a/src/Symfony/Component/Routing/Tests/Loader/AttributeClassLoaderTest.php
+++ b/src/Symfony/Component/Routing/Tests/Loader/AttributeClassLoaderTest.php
@@ -16,8 +16,13 @@
use Symfony\Component\Routing\Exception\LogicException;
use Symfony\Component\Routing\Tests\Fixtures\AttributeFixtures\AbstractClassController;
use Symfony\Component\Routing\Tests\Fixtures\AttributeFixtures\ActionPathController;
+use Symfony\Component\Routing\Tests\Fixtures\AttributeFixtures\AliasClassController;
+use Symfony\Component\Routing\Tests\Fixtures\AttributeFixtures\AliasInvokableController;
+use Symfony\Component\Routing\Tests\Fixtures\AttributeFixtures\AliasRouteController;
use Symfony\Component\Routing\Tests\Fixtures\AttributeFixtures\BazClass;
use Symfony\Component\Routing\Tests\Fixtures\AttributeFixtures\DefaultValueController;
+use Symfony\Component\Routing\Tests\Fixtures\AttributeFixtures\DeprecatedAliasCustomMessageRouteController;
+use Symfony\Component\Routing\Tests\Fixtures\AttributeFixtures\DeprecatedAliasRouteController;
use Symfony\Component\Routing\Tests\Fixtures\AttributeFixtures\EncodingClass;
use Symfony\Component\Routing\Tests\Fixtures\AttributeFixtures\ExplicitLocalizedActionPathController;
use Symfony\Component\Routing\Tests\Fixtures\AttributeFixtures\ExtendedRouteOnClassController;
@@ -35,6 +40,7 @@
use Symfony\Component\Routing\Tests\Fixtures\AttributeFixtures\MethodActionControllers;
use Symfony\Component\Routing\Tests\Fixtures\AttributeFixtures\MethodsAndSchemes;
use Symfony\Component\Routing\Tests\Fixtures\AttributeFixtures\MissingRouteNameController;
+use Symfony\Component\Routing\Tests\Fixtures\AttributeFixtures\MultipleDeprecatedAliasRouteController;
use Symfony\Component\Routing\Tests\Fixtures\AttributeFixtures\NothingButNameController;
use Symfony\Component\Routing\Tests\Fixtures\AttributeFixtures\PrefixedActionLocalizedRouteController;
use Symfony\Component\Routing\Tests\Fixtures\AttributeFixtures\PrefixedActionPathController;
@@ -364,4 +370,98 @@ public function testDefaultRouteName()
$this->assertSame('symfony_component_routing_tests_fixtures_attributefixtures_encodingclass_routeàction', $defaultName);
}
+
+ public function testAliasesOnMethod()
+ {
+ $routes = $this->loader->load(AliasRouteController::class);
+ $route = $routes->get('action_with_alias');
+ $this->assertCount(1, $routes);
+ $this->assertSame('/path', $route->getPath());
+ $this->assertEquals(new Alias('action_with_alias'), $routes->getAlias('alias'));
+ $this->assertEquals(new Alias('action_with_alias'), $routes->getAlias('completely_different_name'));
+ }
+
+ public function testThrowsWithAliasesOnClass()
+ {
+ $this->expectException(\InvalidArgumentException::class);
+ $this->expectExceptionMessage('Route aliases cannot be used on non-invokable class "Symfony\Component\Routing\Tests\Fixtures\AttributeFixtures\AliasClassController".');
+
+ $this->loader->load(AliasClassController::class);
+ }
+
+ public function testAliasesOnInvokableClass()
+ {
+ $routes = $this->loader->load(AliasInvokableController::class);
+ $route = $routes->get('invokable_path');
+ $this->assertCount(1, $routes);
+ $this->assertSame('/path', $route->getPath());
+ $this->assertEquals(new Alias('invokable_path'), $routes->getAlias('alias'));
+ $this->assertEquals(new Alias('invokable_path'), $routes->getAlias('completely_different_name'));
+ }
+
+ public function testDeprecatedAlias()
+ {
+ $routes = $this->loader->load(DeprecatedAliasRouteController::class);
+ $route = $routes->get('action_with_deprecated_alias');
+ $expected = (new Alias('action_with_deprecated_alias'))
+ ->setDeprecated(
+ 'MyBundleFixture',
+ '1.0',
+ 'The "%alias_id%" route alias is deprecated. You should stop using it, as it will be removed in the future.'
+ );
+ $actual = $routes->getAlias('my_other_alias_deprecated');
+ $this->assertCount(1, $routes);
+ $this->assertSame('/path', $route->getPath());
+ $this->assertEquals($expected, $actual);
+ }
+
+ public function testDeprecatedAliasWithCustomMessage()
+ {
+ $routes = $this->loader->load(DeprecatedAliasCustomMessageRouteController::class);
+ $route = $routes->get('action_with_deprecated_alias');
+ $expected = (new Alias('action_with_deprecated_alias'))
+ ->setDeprecated(
+ 'MyBundleFixture',
+ '1.0',
+ '%alias_id% alias is deprecated.'
+ );
+ $actual = $routes->getAlias('my_other_alias_deprecated');
+ $this->assertCount(1, $routes);
+ $this->assertSame('/path', $route->getPath());
+ $this->assertEquals($expected, $actual);
+ }
+
+ public function testMultipleDeprecatedAlias()
+ {
+ $routes = $this->loader->load(MultipleDeprecatedAliasRouteController::class);
+ $route = $routes->get('action_with_multiple_deprecated_alias');
+ $this->assertCount(1, $routes);
+ $this->assertSame('/path', $route->getPath());
+
+ $dataset = [
+ 'my_first_alias_deprecated' => [
+ 'package' => 'MyFirstBundleFixture',
+ 'version' => '1.0',
+ ],
+ 'my_second_alias_deprecated' => [
+ 'package' => 'MySecondBundleFixture',
+ 'version' => '2.0',
+ ],
+ 'my_third_alias_deprecated' => [
+ 'package' => 'SurprisedThirdBundleFixture',
+ 'version' => '3.0',
+ ],
+ ];
+
+ foreach ($dataset as $aliasName => $aliasData) {
+ $expected = (new Alias('action_with_multiple_deprecated_alias'))
+ ->setDeprecated(
+ $aliasData['package'],
+ $aliasData['version'],
+ 'The "%alias_id%" route alias is deprecated. You should stop using it, as it will be removed in the future.'
+ );
+ $actual = $routes->getAlias($aliasName);
+ $this->assertEquals($expected, $actual);
+ }
+ }
}
--- a PPN by Garber Painting Akron. With Image Size Reduction included!Fetched URL: http://github.com/symfony/symfony/pull/58819.diff
Alternative Proxies:
Alternative Proxy
pFad Proxy
pFad v3 Proxy
pFad v4 Proxy