Skip to content

Commit eb90d79

Browse files
Merge branch 'symfony:7.4' into feature/redis-receiver-listable-interface
2 parents fafcad3 + 5d3f8f4 commit eb90d79

File tree

193 files changed

+2413
-842
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

193 files changed

+2413
-842
lines changed

.github/expected-missing-return-types.diff

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,23 @@ diff --git a/src/Symfony/Component/DependencyInjection/Extension/PrependExtensio
187187
- public function prepend(ContainerBuilder $container);
188188
+ public function prepend(ContainerBuilder $container): void;
189189
}
190+
diff --git a/src/Symfony/Component/DomCrawler/Crawler.php b/src/Symfony/Component/DomCrawler/Crawler.php
191+
--- a/src/Symfony/Component/DomCrawler/Crawler.php
192+
+++ b/src/Symfony/Component/DomCrawler/Crawler.php
193+
@@ -405,5 +405,5 @@ class Crawler implements \Countable, \IteratorAggregate
194+
* @throws \InvalidArgumentException When current node is empty
195+
*/
196+
- public function closest(string $selector): ?self
197+
+ public function closest(string $selector): ?static
198+
{
199+
if (!$this->nodes) {
200+
@@ -646,5 +646,5 @@ class Crawler implements \Countable, \IteratorAggregate
201+
* @return array|static
202+
*/
203+
- public function evaluate(string $xpath): array|self
204+
+ public function evaluate(string $xpath): array|static
205+
{
206+
if (null === $this->document) {
190207
diff --git a/src/Symfony/Component/Emoji/EmojiTransliterator.php b/src/Symfony/Component/Emoji/EmojiTransliterator.php
191208
--- a/src/Symfony/Component/Emoji/EmojiTransliterator.php
192209
+++ b/src/Symfony/Component/Emoji/EmojiTransliterator.php

UPGRADE-7.4.md

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@ Read more about this in the [Symfony documentation](https://symfony.com/doc/7.4/
88

99
If you're upgrading from a version below 7.3, follow the [7.3 upgrade guide](UPGRADE-7.3.md) first.
1010

11+
BrowserKit
12+
----------
13+
14+
* Deprecate `AbstractBrowser::useHtml5Parser()`; Symfony 8 will unconditionally use the native HTML5 parser
15+
1116
Cache
1217
-----
1318

@@ -29,6 +34,11 @@ DoctrineBridge
2934

3035
* Deprecate `UniqueEntity::getRequiredOptions()` and `UniqueEntity::getDefaultOption()`
3136

37+
DomCrawler
38+
----------
39+
40+
* Disabling HTML5 parsing is deprecated; Symfony 8 will unconditionally use the native HTML5 parser
41+
3242
FrameworkBundle
3343
---------------
3444

@@ -67,7 +77,8 @@ Mime
6777
Routing
6878
-------
6979

70-
* Deprecate `getEnv()` and `setEnv()` methods of the `Symfony\Component\Routing\Attribute\Route` class in favor of the plurialized `getEnvs()` and `setEnvs()` methods
80+
* Deprecate class aliases in the `Annotation` namespace, use attributes instead
81+
* Deprecate getters and setters in attribute classes in favor of public properties
7182

7283
Security
7384
--------
@@ -80,6 +91,8 @@ Serializer
8091
----------
8192

8293
* Make `AttributeMetadata` and `ClassMetadata` final
94+
* Deprecate class aliases in the `Annotation` namespace, use attributes instead
95+
* Deprecate getters in attribute classes in favor of public properties
8396

8497
String
8598
------

composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
"symfony/polyfill-intl-normalizer": "~1.0",
5656
"symfony/polyfill-mbstring": "~1.0",
5757
"symfony/polyfill-php83": "^1.28",
58+
"symfony/polyfill-php84": "^1.30",
5859
"symfony/polyfill-php85": "^1.32",
5960
"symfony/polyfill-uuid": "^1.15"
6061
},

src/Symfony/Bridge/PhpUnit/bin/simple-phpunit.php

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -270,13 +270,6 @@
270270
exit($exit);
271271
}
272272

273-
// Mutate PhptTestCase code
274-
$alteredFile = defined('GLOB_BRACE') ? glob('./src/Runner/{Phpt/,PHPT/Phpt,Phpt}TestCase.php', GLOB_BRACE) : false;
275-
if ($alteredFile && str_contains($alteredCode = file_get_contents($alteredFile[0]), " 'report_memleaks=0',\n")) {
276-
$alteredCode = str_replace(" 'report_memleaks=0',\n", '', $alteredCode);
277-
file_put_contents($alteredFile[0], $alteredCode);
278-
}
279-
280273
// Mutate TestCase code
281274
if (version_compare($PHPUNIT_VERSION, '11.0', '<')) {
282275
$alteredCode = file_get_contents($alteredFile = './src/Framework/TestCase.php');

src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@ CHANGELOG
44
7.4
55
---
66

7+
* Auto-register routes from attributes found on controller services
78
* Add `ControllerHelper`; the helpers from AbstractController as a standalone service
89
* Allow using their name without added suffix when using `#[Target]` for custom services
910
* Deprecate `Symfony\Bundle\FrameworkBundle\Console\Application::add()` in favor of `Symfony\Bundle\FrameworkBundle\Console\Application::addCommand()`
1011
* Add `assertEmailAddressNotContains()` to the `MailerAssertionsTrait`
12+
* Add `framework.type_info.aliases` option
1113

1214
7.3
1315
---

src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php

Lines changed: 69 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -38,47 +38,83 @@
3838
*/
3939
class TextDescriptor extends Descriptor
4040
{
41+
private const VERB_COLORS = [
42+
'ANY' => 'default',
43+
'GET' => 'blue',
44+
'QUERY' => 'blue',
45+
'HEAD' => 'magenta',
46+
'OPTIONS' => 'blue',
47+
'POST' => 'green',
48+
'PUT' => 'yellow',
49+
'PATCH' => 'yellow',
50+
'DELETE' => 'red',
51+
];
52+
4153
public function __construct(
4254
private ?FileLinkFormatter $fileLinkFormatter = null,
4355
) {
4456
}
4557

4658
protected function describeRouteCollection(RouteCollection $routes, array $options = []): void
4759
{
48-
$showControllers = isset($options['show_controllers']) && $options['show_controllers'];
49-
50-
$tableHeaders = ['Name', 'Method', 'Scheme', 'Host', 'Path'];
51-
if ($showControllers) {
52-
$tableHeaders[] = 'Controller';
53-
}
54-
55-
if ($showAliases = $options['show_aliases'] ?? false) {
56-
$tableHeaders[] = 'Aliases';
57-
}
60+
$showAliases = $options['show_aliases'] ?? false;
61+
$showControllers = $options['show_controllers'] ?? false;
5862

5963
$tableRows = [];
64+
$shouldShowScheme = false;
65+
$shouldShowHost = false;
6066
foreach ($routes->all() as $name => $route) {
6167
$controller = $route->getDefault('_controller');
6268

69+
$scheme = $route->getSchemes() ? implode('|', $route->getSchemes()) : 'ANY';
70+
$shouldShowScheme = $shouldShowScheme || 'ANY' !== $scheme;
71+
72+
$host = '' !== $route->getHost() ? $route->getHost() : 'ANY';
73+
$shouldShowHost = $shouldShowHost || 'ANY' !== $host;
74+
6375
$row = [
64-
$name,
65-
$route->getMethods() ? implode('|', $route->getMethods()) : 'ANY',
66-
$route->getSchemes() ? implode('|', $route->getSchemes()) : 'ANY',
67-
'' !== $route->getHost() ? $route->getHost() : 'ANY',
68-
$this->formatControllerLink($controller, $route->getPath(), $options['container'] ?? null),
76+
'Name' => $name,
77+
'Methods' => $this->formatMethods($route->getMethods()),
78+
'Scheme' => $scheme,
79+
'Host' => $host,
80+
'Path' => $route->getPath(),
6981
];
7082

7183
if ($showControllers) {
72-
$row[] = $controller ? $this->formatControllerLink($controller, $this->formatCallable($controller), $options['container'] ?? null) : '';
84+
$row['Controller'] = $controller ? $this->formatControllerLink($controller, $this->formatCallable($controller), $options['container'] ?? null) : '';
7385
}
7486

7587
if ($showAliases) {
76-
$row[] = implode('|', ($reverseAliases ??= $this->getReverseAliases($routes))[$name] ?? []);
88+
$row['Aliases'] = implode('|', $this->getReverseAliases($routes)[$name] ?? []);
7789
}
7890

7991
$tableRows[] = $row;
8092
}
8193

94+
$tableHeaders = ['Name', 'Method'];
95+
96+
if ($shouldShowScheme) {
97+
$tableHeaders[] = 'Scheme';
98+
} else {
99+
array_walk($tableRows, function (&$row) { unset($row['Scheme']); });
100+
}
101+
102+
if ($shouldShowHost) {
103+
$tableHeaders[] = 'Host';
104+
} else {
105+
array_walk($tableRows, function (&$row) { unset($row['Host']); });
106+
}
107+
108+
$tableHeaders[] = 'Path';
109+
110+
if ($showControllers) {
111+
$tableHeaders[] = 'Controller';
112+
}
113+
114+
if ($showAliases) {
115+
$tableHeaders[] = 'Aliases';
116+
}
117+
82118
if (isset($options['output'])) {
83119
$options['output']->table($tableHeaders, $tableRows);
84120
} else {
@@ -103,7 +139,7 @@ protected function describeRoute(Route $route, array $options = []): void
103139
['Host', '' !== $route->getHost() ? $route->getHost() : 'ANY'],
104140
['Host Regex', '' !== $route->getHost() ? $route->compile()->getHostRegex() : ''],
105141
['Scheme', $route->getSchemes() ? implode('|', $route->getSchemes()) : 'ANY'],
106-
['Method', $route->getMethods() ? implode('|', $route->getMethods()) : 'ANY'],
142+
['Method', $this->formatMethods($route->getMethods())],
107143
['Requirements', $route->getRequirements() ? $this->formatRouterConfig($route->getRequirements()) : 'NO CUSTOM'],
108144
['Class', $route::class],
109145
['Defaults', $this->formatRouterConfig($defaults)],
@@ -576,6 +612,21 @@ private function formatRouterConfig(array $config): string
576612
return trim($configAsString);
577613
}
578614

615+
/**
616+
* @param array<string> $methods
617+
*/
618+
private function formatMethods(array $methods): string
619+
{
620+
if ([] === $methods) {
621+
$methods = ['ANY'];
622+
}
623+
624+
return implode('|', array_map(
625+
fn (string $method): string => \sprintf('<fg=%s>%s</>', self::VERB_COLORS[$method] ?? 'default', $method),
626+
$methods
627+
));
628+
}
629+
579630
/**
580631
* @param (callable():ContainerBuilder)|null $getContainer
581632
*/

src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1304,6 +1304,17 @@ private function addTypeInfoSection(ArrayNodeDefinition $rootNode, callable $ena
13041304
->arrayNode('type_info')
13051305
->info('Type info configuration')
13061306
->{$enableIfStandalone('symfony/type-info', Type::class)}()
1307+
->addDefaultsIfNotSet()
1308+
->fixXmlConfig('alias', 'aliases')
1309+
->children()
1310+
->arrayNode('aliases')
1311+
->info('Additional type aliases to be used during type context creation.')
1312+
->defaultValue([])
1313+
->normalizeKeys(false)
1314+
->useAttributeAsKey('name')
1315+
->scalarPrototype()->end()
1316+
->end()
1317+
->end()
13071318
->end()
13081319
->end()
13091320
;

src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@
172172
use Symfony\Component\RemoteEvent\Attribute\AsRemoteEventConsumer;
173173
use Symfony\Component\RemoteEvent\RemoteEvent;
174174
use Symfony\Component\Routing\Attribute\Route;
175+
use Symfony\Component\Routing\Loader\AttributeServicesLoader;
175176
use Symfony\Component\Scheduler\Attribute\AsCronTask;
176177
use Symfony\Component\Scheduler\Attribute\AsPeriodicTask;
177178
use Symfony\Component\Scheduler\Attribute\AsSchedule;
@@ -469,7 +470,7 @@ public function load(array $configs, ContainerBuilder $container): void
469470
}
470471

471472
if ($typeInfoEnabled = $this->readConfigEnabled('type_info', $container, $config['type_info'])) {
472-
$this->registerTypeInfoConfiguration($container, $loader);
473+
$this->registerTypeInfoConfiguration($config['type_info'], $container, $loader);
473474
}
474475

475476
if ($propertyInfoEnabled) {
@@ -768,7 +769,7 @@ public function load(array $configs, ContainerBuilder $container): void
768769
$definition->addTag('controller.service_arguments');
769770
});
770771
$container->registerAttributeForAutoconfiguration(Route::class, static function (ChildDefinition $definition, Route $attribute, \ReflectionClass|\ReflectionMethod $reflection): void {
771-
$definition->addTag('controller.service_arguments');
772+
$definition->addTag('controller.service_arguments')->addTag('routing.controller');
772773
});
773774
$container->registerAttributeForAutoconfiguration(AsRemoteEventConsumer::class, static function (ChildDefinition $definition, AsRemoteEventConsumer $attribute): void {
774775
$definition->addTag('remote_event.consumer', ['consumer' => $attribute->name]);
@@ -1307,6 +1308,10 @@ private function registerRouterConfiguration(array $config, ContainerBuilder $co
13071308

13081309
$loader->load('routing.php');
13091310

1311+
if (!class_exists(AttributeServicesLoader::class)) {
1312+
$container->removeDefinition('routing.loader.attribute.services');
1313+
}
1314+
13101315
if ($config['utf8']) {
13111316
$container->getDefinition('routing.loader')->replaceArgument(1, ['utf8' => true]);
13121317
}
@@ -2170,7 +2175,7 @@ private function registerPropertyInfoConfiguration(array $config, ContainerBuild
21702175
}
21712176
}
21722177

2173-
private function registerTypeInfoConfiguration(ContainerBuilder $container, PhpFileLoader $loader): void
2178+
private function registerTypeInfoConfiguration(array $config, ContainerBuilder $container, PhpFileLoader $loader): void
21742179
{
21752180
if (!class_exists(Type::class)) {
21762181
throw new LogicException('TypeInfo support cannot be enabled as the TypeInfo component is not installed. Try running "composer require symfony/type-info".');
@@ -2179,7 +2184,8 @@ private function registerTypeInfoConfiguration(ContainerBuilder $container, PhpF
21792184
$loader->load('type_info.php');
21802185

21812186
if (ContainerBuilder::willBeAvailable('phpstan/phpdoc-parser', PhpDocParser::class, ['symfony/framework-bundle', 'symfony/type-info'])) {
2182-
$container->register('type_info.resolver.string', StringTypeResolver::class);
2187+
$container->register('type_info.resolver.string', StringTypeResolver::class)
2188+
->setArguments([null, null, $config['aliases']]);
21832189

21842190
$container->register('type_info.resolver.reflection_parameter.phpdoc_aware', PhpDocAwareReflectionTypeResolver::class)
21852191
->setArguments([new Reference('type_info.resolver.reflection_parameter'), new Reference('type_info.resolver.string'), new Reference('type_info.type_context_factory')]);
@@ -2196,6 +2202,8 @@ private function registerTypeInfoConfiguration(ContainerBuilder $container, PhpF
21962202
\ReflectionProperty::class => new Reference('type_info.resolver.reflection_property.phpdoc_aware'),
21972203
\ReflectionFunctionAbstract::class => new Reference('type_info.resolver.reflection_return.phpdoc_aware'),
21982204
] + $resolversLocator->getValues());
2205+
2206+
$container->getDefinition('type_info.type_context_factory')->replaceArgument(1, $config['aliases']);
21992207
}
22002208
}
22012209

src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161
use Symfony\Component\PropertyInfo\DependencyInjection\PropertyInfoConstructorPass;
6262
use Symfony\Component\PropertyInfo\DependencyInjection\PropertyInfoPass;
6363
use Symfony\Component\Routing\DependencyInjection\AddExpressionLanguageProvidersPass;
64+
use Symfony\Component\Routing\DependencyInjection\RoutingControllerPass;
6465
use Symfony\Component\Routing\DependencyInjection\RoutingResolverPass;
6566
use Symfony\Component\Runtime\SymfonyRuntime;
6667
use Symfony\Component\Scheduler\DependencyInjection\AddScheduleMessengerPass;
@@ -146,6 +147,7 @@ public function build(ContainerBuilder $container): void
146147
$container->addCompilerPass(new RegisterControllerArgumentLocatorsPass());
147148
$container->addCompilerPass(new RemoveEmptyControllerArgumentLocatorsPass(), PassConfig::TYPE_BEFORE_REMOVING);
148149
$container->addCompilerPass(new RoutingResolverPass());
150+
$this->addCompilerPassIfExists($container, RoutingControllerPass::class);
149151
$this->addCompilerPassIfExists($container, DataCollectorTranslatorPass::class);
150152
$container->addCompilerPass(new ProfilerPass());
151153
// must be registered before removing private services as some might be listeners/subscribers

src/Symfony/Bundle/FrameworkBundle/Resources/config/routing.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
2727
use Symfony\Component\Routing\Loader\AttributeDirectoryLoader;
2828
use Symfony\Component\Routing\Loader\AttributeFileLoader;
29+
use Symfony\Component\Routing\Loader\AttributeServicesLoader;
2930
use Symfony\Component\Routing\Loader\ContainerLoader;
3031
use Symfony\Component\Routing\Loader\DirectoryLoader;
3132
use Symfony\Component\Routing\Loader\GlobFileLoader;
@@ -98,6 +99,12 @@
9899
])
99100
->tag('routing.loader', ['priority' => -10])
100101

102+
->set('routing.loader.attribute.services', AttributeServicesLoader::class)
103+
->args([
104+
abstract_arg('classes tagged with "routing.controller"'),
105+
])
106+
->tag('routing.loader', ['priority' => -10])
107+
101108
->set('routing.loader.attribute.directory', AttributeDirectoryLoader::class)
102109
->args([
103110
service('file_locator'),

0 commit comments

Comments
 (0)