Skip to content

Commit 526e05d

Browse files
committed
Merge branch '7.4' into 8.0
* 7.4: [FrameworkBundle] Auto-generate `config/reference.php` to assist in writing and discovering app's configuration Conflicts: src/Symfony/Bundle/FrameworkBundle/composer.json src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services_dump_load.xml src/Symfony/Component/DependencyInjection/Tests/Loader/PhpFileLoaderTest.php src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php
2 parents 645e093 + b76bdfd commit 526e05d

File tree

8 files changed

+56
-124
lines changed

8 files changed

+56
-124
lines changed

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ CHANGELOG
1919
* Add support of multiple env names in the `Symfony\Component\Routing\Attribute\Route` attribute
2020
* Add argument `$parameters` to `RequestContext`'s constructor
2121
* Handle declaring routes using PHP arrays that follow the same shape as corresponding yaml files
22-
* Add `RoutesConfig` to help writing PHP configs using yaml-like array-shapes
22+
* Add `RoutesReference` to help writing PHP configs using yaml-like array-shapes
2323
* Deprecate class aliases in the `Annotation` namespace, use attributes instead
2424
* Deprecate getters and setters in attribute classes in favor of public properties
2525
* Deprecate accessing the internal scope of the loader in PHP config files, use only its public API instead

Loader/Config/RoutesConfig.php renamed to Loader/Configurator/RoutesReference.php

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,30 @@
99
* file that was distributed with this source code.
1010
*/
1111

12-
namespace Symfony\Config;
12+
namespace Symfony\Component\Routing\Loader\Configurator;
13+
14+
// For the phpdoc to remain compatible with the generation of per-app Routes class,
15+
// this file should have no "use" statements: all symbols referenced by
16+
// the phpdoc need to be in the current namespace or be root-scoped.
1317

1418
/**
15-
* @psalm-type Route = array{
19+
* This class provides array-shapes for configuring the routes of an application.
20+
*
21+
* Example:
22+
*
23+
* ```php
24+
* // config/routes.php
25+
* namespace Symfony\Component\Routing\Loader\Configurator;
26+
*
27+
* return Routes::config([
28+
* 'controllers' => [
29+
* 'resource' => 'attributes',
30+
* 'type' => 'tagged_services',
31+
* ],
32+
* ]);
33+
* ```
34+
*
35+
* @psalm-type RouteConfig = array{
1636
* path: string|array<string,string>,
1737
* controller?: string,
1838
* methods?: string|list<string>,
@@ -27,7 +47,7 @@
2747
* utf8?: bool,
2848
* stateless?: bool,
2949
* }
30-
* @psalm-type Import = array{
50+
* @psalm-type ImportConfig = array{
3151
* resource: string,
3252
* type?: string,
3353
* exclude?: string|list<string>,
@@ -47,19 +67,21 @@
4767
* utf8?: bool,
4868
* stateless?: bool,
4969
* }
50-
* @psalm-type Alias = array{
70+
* @psalm-type AliasConfig = array{
5171
* alias: string,
5272
* deprecated?: array{package:string, version:string, message?:string},
5373
* }
54-
* @psalm-type Routes = array<string, Route|Import|Alias|RoutesConfig|array<string, Route|Import|Alias>>
74+
* @psalm-type RoutesConfig = array<string, RouteConfig|ImportConfig|AliasConfig|array<string, RouteConfig|ImportConfig|AliasConfig>>
5575
*/
56-
class RoutesConfig
76+
class RoutesReference
5777
{
5878
/**
59-
* @param Routes $routes
79+
* @param RoutesConfig $config
80+
*
81+
* @psalm-return RoutesConfig
6082
*/
61-
public function __construct(
62-
public readonly array $routes,
63-
) {
83+
public static function config(array $config): array
84+
{
85+
return $config;
6486
}
6587
}

Loader/PhpFileLoader.php

Lines changed: 14 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,10 @@
1515
use Symfony\Component\Config\Loader\LoaderResolver;
1616
use Symfony\Component\Config\Resource\FileResource;
1717
use Symfony\Component\Routing\Exception\InvalidArgumentException;
18-
use Symfony\Component\Routing\Loader\Configurator\AliasConfigurator;
19-
use Symfony\Component\Routing\Loader\Configurator\CollectionConfigurator;
20-
use Symfony\Component\Routing\Loader\Configurator\ImportConfigurator;
21-
use Symfony\Component\Routing\Loader\Configurator\RouteConfigurator;
18+
use Symfony\Component\Routing\Loader\Configurator\Routes;
19+
use Symfony\Component\Routing\Loader\Configurator\RoutesReference;
2220
use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator;
2321
use Symfony\Component\Routing\RouteCollection;
24-
use Symfony\Config\RoutesConfig;
2522

2623
/**
2724
* PhpFileLoader loads routes from a PHP file.
@@ -42,6 +39,11 @@ public function load(mixed $file, ?string $type = null): RouteCollection
4239
$path = $this->locator->locate($file);
4340
$this->setCurrentDir(\dirname($path));
4441

42+
// Expose RoutesReference::config() as Routes::config()
43+
if (!class_exists(Routes::class)) {
44+
class_alias(RoutesReference::class, Routes::class);
45+
}
46+
4547
// the closure forbids access to the private scope in the included file
4648
$loader = $this;
4749
$load = \Closure::bind(static function ($file) use ($loader) {
@@ -54,9 +56,13 @@ public function load(mixed $file, ?string $type = null): RouteCollection
5456

5557
if (\is_object($result) && \is_callable($result)) {
5658
$collection = $this->callConfigurator($result, $path, $file);
57-
} else {
59+
} elseif (\is_array($result)) {
5860
$collection = new RouteCollection();
59-
$this->loadRoutes($collection, $result, $path, $file);
61+
$loader = new YamlFileLoader($this->locator, $this->env);
62+
$loader->setResolver($this->resolver ?? new LoaderResolver([$this]));
63+
(new \ReflectionMethod(YamlFileLoader::class, 'loadContent'))->invoke($loader, $collection, $result, $path, $file);
64+
} elseif (!($collection = $result) instanceof RouteCollection) {
65+
throw new InvalidArgumentException(\sprintf('The return value in config file "%s" is expected to be a RouteCollection, an array or a configurator callable, but got "%s".', $path, get_debug_type($result)));
6066
}
6167

6268
$collection->addResource(new FileResource($path));
@@ -73,61 +79,10 @@ protected function callConfigurator(callable $callback, string $path, string $fi
7379
{
7480
$collection = new RouteCollection();
7581

76-
$result = $callback(new RoutingConfigurator($collection, $this, $path, $file, $this->env));
77-
$this->loadRoutes($collection, $result, $path, $file);
82+
$callback(new RoutingConfigurator($collection, $this, $path, $file, $this->env));
7883

7984
return $collection;
8085
}
81-
82-
private function loadRoutes(RouteCollection $collection, mixed $routes, string $path, string $file): void
83-
{
84-
if (null === $routes
85-
|| $routes instanceof RouteCollection
86-
|| $routes instanceof AliasConfigurator
87-
|| $routes instanceof CollectionConfigurator
88-
|| $routes instanceof ImportConfigurator
89-
|| $routes instanceof RouteConfigurator
90-
|| $routes instanceof RoutingConfigurator
91-
) {
92-
if ($routes instanceof RouteCollection && $collection !== $routes) {
93-
$collection->addCollection($routes);
94-
}
95-
96-
return;
97-
}
98-
99-
if ($routes instanceof RoutesConfig) {
100-
$routes = $routes->routes;
101-
} elseif (!is_iterable($routes)) {
102-
throw new InvalidArgumentException(\sprintf('The return value in config file "%s" is invalid: "%s" given.', $path, get_debug_type($routes)));
103-
}
104-
105-
$loader = new YamlFileLoader($this->locator, $this->env);
106-
$loader->setResolver(new LoaderResolver([$this]));
107-
108-
\Closure::bind(function () use ($collection, $routes, $path, $file) {
109-
foreach ($routes as $name => $config) {
110-
if (str_starts_with($when = $name, 'when@')) {
111-
if (!$this->env || 'when@'.$this->env !== $name) {
112-
continue;
113-
}
114-
$when .= '" when "@'.$this->env;
115-
} elseif (!$config instanceof RoutesConfig) {
116-
$config = [$name => $config];
117-
} elseif (!\is_int($name)) {
118-
throw new InvalidArgumentException(\sprintf('Invalid key "%s" returned for the "%s" config builder; none or "when@%%env%%" expected in file "%s".', $name, get_debug_type($config), $path));
119-
}
120-
121-
if ($config instanceof RoutesConfig) {
122-
$config = $config->routes;
123-
} elseif (!\is_array($config)) {
124-
throw new InvalidArgumentException(\sprintf('The "%s" key should contain an array in "%s".', $name, $path));
125-
}
126-
127-
$this->loadContent($collection, $config, $path, $file);
128-
}
129-
}, $loader, YamlFileLoader::class)();
130-
}
13186
}
13287

13388
/**

Tests/Fixtures/array_routes.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
<?php
22

3-
return [
3+
use Symfony\Component\Routing\Loader\Configurator\Routes;
4+
5+
return Routes::config([
46
'a' => ['path' => '/a'],
57
'b' => ['path' => '/b', 'methods' => ['GET']],
6-
];
8+
]);

Tests/Fixtures/array_when_env.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
<?php
22

3-
return [
3+
use Symfony\Component\Routing\Loader\Configurator\Routes;
4+
5+
return Routes::config([
46
'when@some-env' => [
57
'x' => ['path' => '/x'],
68
],
79
'a' => ['path' => '/a'],
8-
];
10+
]);

Tests/Fixtures/routes_object.php

Lines changed: 0 additions & 23 deletions
This file was deleted.

Tests/Loader/PhpFileLoaderTest.php

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -358,29 +358,6 @@ public function testLoadsArrayRoutes()
358358
$this->assertSame(['GET'], $routes->get('b')->getMethods());
359359
}
360360

361-
public function testLoadsObjectRoutes()
362-
{
363-
$loader = new PhpFileLoader(new FileLocator([__DIR__.'/../Fixtures']));
364-
$routes = $loader->load('routes_object.php');
365-
$this->assertSame('/a', $routes->get('a')->getPath());
366-
$this->assertSame('/b', $routes->get('b')->getPath());
367-
$this->assertSame(['GET'], $routes->get('b')->getMethods());
368-
$this->assertNull($routes->get('c'));
369-
$this->assertNull($routes->get('d'));
370-
371-
$loader = new PhpFileLoader(new FileLocator([__DIR__.'/../Fixtures']), 'dev');
372-
$routes = $loader->load('routes_object.php');
373-
$this->assertSame('/a', $routes->get('a')->getPath());
374-
$this->assertSame('/b', $routes->get('b')->getPath());
375-
$this->assertSame('/c', $routes->get('c')->getPath());
376-
$this->assertNull($routes->get('d'));
377-
378-
$loader = new PhpFileLoader(new FileLocator([__DIR__.'/../Fixtures']), 'test');
379-
$routes = $loader->load('routes_object.php');
380-
$this->assertNull($routes->get('c'));
381-
$this->assertSame('/d', $routes->get('d')->getPath());
382-
}
383-
384361
public function testWhenEnvWithArray()
385362
{
386363
$locator = new FileLocator([__DIR__.'/../Fixtures']);

composer.json

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,7 @@
2828
"symfony/yaml": "^7.4|^8.0"
2929
},
3030
"autoload": {
31-
"psr-4": {
32-
"Symfony\\Component\\Routing\\": "",
33-
"Symfony\\Config\\": "Loader/Config/"
34-
},
31+
"psr-4": { "Symfony\\Component\\Routing\\": "" },
3532
"exclude-from-classmap": [
3633
"/Tests/"
3734
]

0 commit comments

Comments
 (0)