@@ -207,7 +209,9 @@
{% macro render_encoder_cell(item, index, method) %}
{% set nested_encoders_id = 'nested-encoders-' ~ method ~ '-' ~ index %}
+ {% if item.encoder is defined %}
diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/profiler.css.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/profiler.css.twig
index 67e38ab808be1..24ec0863d6117 100644
--- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/profiler.css.twig
+++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/profiler.css.twig
@@ -51,6 +51,7 @@
--highlight-default: #222222;
--highlight-keyword: #a71d5d;
--highlight-string: #183691;
+ --highlight-selected-line: rgba(255, 255, 153, 0.5);
--base-0: #fff;
--base-1: #f5f5f5;
--base-2: #e0e0e0;
@@ -104,6 +105,7 @@
--highlight-default: var(--base-6);
--highlight-keyword: #ff413c;
--highlight-string: #70a6fd;
+ --highlight-selected-line: rgba(14, 14, 14, 0.5);
--base-0: #2e3136;
--base-1: #444;
--base-2: #666;
@@ -1296,15 +1298,15 @@ tr.log-status-silenced {
padding: 0;
}
#collector-content .sf-validator .trace li.selected {
- background: rgba(255, 255, 153, 0.5);
+ background: var(--highlight-selected-line);
}
{# Messenger panel
========================================================================= #}
#collector-content .message-bus .trace {
- border: 1px solid #DDD;
- background: #FFF;
+ border: var(--border);
+ background: var(--base-0);
padding: 10px;
margin: 0.5em 0;
overflow: auto;
@@ -1317,7 +1319,7 @@ tr.log-status-silenced {
padding: 0;
}
#collector-content .message-bus .trace li.selected {
- background: rgba(255, 255, 153, 0.5);
+ background: var(--highlight-selected-line);
}
{# Dump panel
diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar.css.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar.css.twig
index 3b064c27cb63b..75f976bc401b8 100644
--- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar.css.twig
+++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar.css.twig
@@ -336,7 +336,7 @@ div.sf-toolbar .sf-toolbar-block .sf-toolbar-info-piece.sf-toolbar-info-php-ext
.sf-toolbar-block.hover .sf-toolbar-info {
display: block;
padding: 10px;
- max-width: 480px;
+ max-width: 525px;
max-height: 480px;
word-wrap: break-word;
overflow: hidden;
diff --git a/src/Symfony/Component/Cache/Traits/RedisTrait.php b/src/Symfony/Component/Cache/Traits/RedisTrait.php
index f563eab2b2117..5b7352e276d77 100644
--- a/src/Symfony/Component/Cache/Traits/RedisTrait.php
+++ b/src/Symfony/Component/Cache/Traits/RedisTrait.php
@@ -388,7 +388,7 @@ protected function doClear(string $namespace): bool
{
if ($this->redis instanceof \Predis\ClientInterface) {
$prefix = $this->redis->getOptions()->prefix ? $this->redis->getOptions()->prefix->getPrefix() : '';
- $prefixLen = \strlen($prefix);
+ $prefixLen = \strlen($prefix ?? '');
}
$cleared = true;
diff --git a/src/Symfony/Component/Console/DependencyInjection/AddConsoleCommandPass.php b/src/Symfony/Component/Console/DependencyInjection/AddConsoleCommandPass.php
index 4334556663078..62c80c3b06859 100644
--- a/src/Symfony/Component/Console/DependencyInjection/AddConsoleCommandPass.php
+++ b/src/Symfony/Component/Console/DependencyInjection/AddConsoleCommandPass.php
@@ -50,7 +50,7 @@ public function process(ContainerBuilder $container)
if (!$r->isSubclassOf(Command::class)) {
throw new InvalidArgumentException(sprintf('The service "%s" tagged "%s" must be a subclass of "%s".', $id, 'console.command', Command::class));
}
- $aliases = $class::getDefaultName();
+ $aliases = str_replace('%', '%%', $class::getDefaultName() ?? '');
}
$aliases = explode('|', $aliases ?? '');
@@ -107,7 +107,7 @@ public function process(ContainerBuilder $container)
if (!$r->isSubclassOf(Command::class)) {
throw new InvalidArgumentException(sprintf('The service "%s" tagged "%s" must be a subclass of "%s".', $id, 'console.command', Command::class));
}
- $description = $class::getDefaultDescription();
+ $description = str_replace('%', '%%', $class::getDefaultDescription() ?? '');
}
if ($description) {
diff --git a/src/Symfony/Component/Console/Tests/DependencyInjection/AddConsoleCommandPassTest.php b/src/Symfony/Component/Console/Tests/DependencyInjection/AddConsoleCommandPassTest.php
index adbc61e85d1bb..ddda0f47280d0 100644
--- a/src/Symfony/Component/Console/Tests/DependencyInjection/AddConsoleCommandPassTest.php
+++ b/src/Symfony/Component/Console/Tests/DependencyInjection/AddConsoleCommandPassTest.php
@@ -154,6 +154,33 @@ public function testProcessFallsBackToDefaultDescription()
$this->assertSame(1 + $initCounter, DescribedCommand::$initCounter);
}
+ public function testEscapesDefaultFromPhp()
+ {
+ $container = new ContainerBuilder();
+ $container
+ ->register('to-escape', EscapedDefaultsFromPhpCommand::class)
+ ->addTag('console.command')
+ ;
+
+ $pass = new AddConsoleCommandPass();
+ $pass->process($container);
+
+ $commandLoader = $container->getDefinition('console.command_loader');
+ $commandLocator = $container->getDefinition((string) $commandLoader->getArgument(0));
+
+ $this->assertSame(ContainerCommandLoader::class, $commandLoader->getClass());
+ $this->assertSame(['%%cmd%%' => 'to-escape', '%%cmdalias%%' => 'to-escape'], $commandLoader->getArgument(1));
+ $this->assertEquals([['to-escape' => new ServiceClosureArgument(new Reference('.to-escape.lazy'))]], $commandLocator->getArguments());
+ $this->assertSame([], $container->getParameter('console.command.ids'));
+
+ $command = $container->get('console.command_loader')->get('%%cmd%%');
+
+ $this->assertInstanceOf(LazyCommand::class, $command);
+ $this->assertSame('%cmd%', $command->getName());
+ $this->assertSame(['%cmdalias%'], $command->getAliases());
+ $this->assertSame('Creates a 80% discount', $command->getDescription());
+ }
+
public function testProcessThrowAnExceptionIfTheServiceIsAbstract()
{
$this->expectException(\InvalidArgumentException::class);
@@ -287,6 +314,11 @@ class NamedCommand extends Command
{
}
+#[AsCommand(name: '%cmd%|%cmdalias%', description: 'Creates a 80% discount')]
+class EscapedDefaultsFromPhpCommand extends Command
+{
+}
+
#[AsCommand(name: '|cmdname|cmdalias', description: 'Just testing')]
class DescribedCommand extends Command
{
diff --git a/src/Symfony/Component/Form/ChoiceList/Loader/FilterChoiceLoaderDecorator.php b/src/Symfony/Component/Form/ChoiceList/Loader/FilterChoiceLoaderDecorator.php
index 277aba0cf2931..263af68190dd2 100644
--- a/src/Symfony/Component/Form/ChoiceList/Loader/FilterChoiceLoaderDecorator.php
+++ b/src/Symfony/Component/Form/ChoiceList/Loader/FilterChoiceLoaderDecorator.php
@@ -36,10 +36,17 @@ protected function loadChoices(): iterable
}
foreach ($structuredValues as $group => $values) {
- if ($values && $filtered = array_filter($list->getChoicesForValues($values), $this->filter)) {
- $choices[$group] = $filtered;
+ if (is_array($values)) {
+ if ($values && $filtered = array_filter($list->getChoicesForValues($values), $this->filter)) {
+ $choices[$group] = $filtered;
+ }
+ continue;
+ // filter empty groups
+ }
+
+ if ($filtered = array_filter($list->getChoicesForValues([$values]), $this->filter)) {
+ $choices[$group] = $filtered[0];
}
- // filter empty groups
}
return $choices ?? [];
diff --git a/src/Symfony/Component/Form/Resources/translations/validators.de.xlf b/src/Symfony/Component/Form/Resources/translations/validators.de.xlf
index bc8e46d1ec089..7b30839f9183d 100644
--- a/src/Symfony/Component/Form/Resources/translations/validators.de.xlf
+++ b/src/Symfony/Component/Form/Resources/translations/validators.de.xlf
@@ -12,7 +12,7 @@
The CSRF token is invalid. Please try to resubmit the form.
- Der CSRF-Token ist ungültig. Versuchen Sie bitte das Formular erneut zu senden.
+ Der CSRF-Token ist ungültig. Versuchen Sie bitte, das Formular erneut zu senden.
This value is not a valid HTML5 color.
diff --git a/src/Symfony/Component/Form/Tests/ChoiceList/Loader/FilterChoiceLoaderDecoratorTest.php b/src/Symfony/Component/Form/Tests/ChoiceList/Loader/FilterChoiceLoaderDecoratorTest.php
index d4cc3ce72a4d0..1f91a47275a33 100644
--- a/src/Symfony/Component/Form/Tests/ChoiceList/Loader/FilterChoiceLoaderDecoratorTest.php
+++ b/src/Symfony/Component/Form/Tests/ChoiceList/Loader/FilterChoiceLoaderDecoratorTest.php
@@ -47,6 +47,29 @@ public function testLoadChoiceListWithGroupedChoices()
]), $loader->loadChoiceList());
}
+ public function testLoadChoiceListMixedWithGroupedAndNonGroupedChoices()
+ {
+ $filter = function ($choice) {
+ return 0 === $choice % 2;
+ };
+
+ $choices = array_merge(range(1, 9), ['grouped' => range(10, 40, 5)]);
+ $loader = new FilterChoiceLoaderDecorator(new ArrayChoiceLoader($choices), $filter);
+
+ $this->assertEquals(new ArrayChoiceList([
+ 1 => 2,
+ 3 => 4,
+ 5 => 6,
+ 7 => 8,
+ 'grouped' => [
+ 0 => 10,
+ 2 => 20,
+ 4 => 30,
+ 6 => 40,
+ ],
+ ]), $loader->loadChoiceList());
+ }
+
public function testLoadValuesForChoices()
{
$evenValues = [1 => '2', 3 => '4'];
diff --git a/src/Symfony/Component/HttpClient/DataCollector/HttpClientDataCollector.php b/src/Symfony/Component/HttpClient/DataCollector/HttpClientDataCollector.php
index 292cdf3945bcf..dc4b6762b87a7 100644
--- a/src/Symfony/Component/HttpClient/DataCollector/HttpClientDataCollector.php
+++ b/src/Symfony/Component/HttpClient/DataCollector/HttpClientDataCollector.php
@@ -11,6 +11,7 @@
namespace Symfony\Component\HttpClient\DataCollector;
+use Symfony\Component\HttpClient\HttpClientTrait;
use Symfony\Component\HttpClient\TraceableHttpClient;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
@@ -23,6 +24,8 @@
*/
final class HttpClientDataCollector extends DataCollector implements LateDataCollectorInterface
{
+ use HttpClientTrait;
+
/**
* @var TraceableHttpClient[]
*/
@@ -176,7 +179,7 @@ private function getCurlCommand(array $trace): ?string
}
$debug = explode("\n", $trace['info']['debug']);
- $url = $trace['url'];
+ $url = self::mergeQueryString($trace['url'], $trace['options']['query'] ?? [], true);
$command = ['curl', '--compressed'];
if (isset($trace['options']['resolve'])) {
@@ -194,10 +197,15 @@ private function getCurlCommand(array $trace): ?string
$dataArg[] = '--data '.escapeshellarg(json_encode($json, \JSON_PRETTY_PRINT));
} elseif ($body = $trace['options']['body'] ?? null) {
if (\is_string($body)) {
- $dataArg[] = '--data '.escapeshellarg($body);
+ try {
+ $dataArg[] = '--data '.escapeshellarg($body);
+ } catch (\ValueError $e) {
+ return null;
+ }
} elseif (\is_array($body)) {
- foreach ($body as $key => $value) {
- $dataArg[] = '--data '.escapeshellarg("$key=$value");
+ $body = explode('&', self::normalizeBody($body));
+ foreach ($body as $value) {
+ $dataArg[] = '--data '.escapeshellarg(urldecode($value));
}
} else {
return null;
diff --git a/src/Symfony/Component/HttpClient/README.md b/src/Symfony/Component/HttpClient/README.md
index 214489b7e7f76..faffabbac21da 100644
--- a/src/Symfony/Component/HttpClient/README.md
+++ b/src/Symfony/Component/HttpClient/README.md
@@ -3,6 +3,23 @@ HttpClient component
The HttpClient component provides powerful methods to fetch HTTP resources synchronously or asynchronously.
+Sponsor
+-------
+
+The Httpclient component for Symfony 6.1 is [backed][1] by [Prisma Media][2].
+
+Prisma Media has become in 40 years the n°1 French publishing group, on print and
+digitally, with 20 flagship brands of the news magazines : Femme Actuelle, GEO,
+Capital, Gala or Télé-Loisirs… Today, more than 42 million French people are in
+contact with one of our brand each month, either by leafing through a magazine,
+surfing the web, subscribing one our mobile or tablet application or listening to
+our podcasts' series. Prisma Media has successfully transformed one's business
+model : from a historic player in the world of paper, it has become in 5 years
+one of the first publishers of multi-media editorial content, and one of the
+first creators of digital solutions.
+
+Help Symfony by [sponsoring][3] its development!
+
Resources
---------
@@ -11,3 +28,7 @@ Resources
* [Report issues](https://github.com/symfony/symfony/issues) and
[send Pull Requests](https://github.com/symfony/symfony/pulls)
in the [main Symfony repository](https://github.com/symfony/symfony)
+
+[1]: https://symfony.com/backers
+[2]: https://www.prismamedia.com
+[3]: https://symfony.com/sponsor
diff --git a/src/Symfony/Component/HttpClient/Tests/DataCollector/HttpClientDataCollectorTest.php b/src/Symfony/Component/HttpClient/Tests/DataCollector/HttpClientDataCollectorTest.php
index ebe4c2c52569b..863c91d3d68db 100755
--- a/src/Symfony/Component/HttpClient/Tests/DataCollector/HttpClientDataCollectorTest.php
+++ b/src/Symfony/Component/HttpClient/Tests/DataCollector/HttpClientDataCollectorTest.php
@@ -244,6 +244,21 @@ public function provideCurlRequests(): iterable
'foo' => 'fooval',
'bar' => 'barval',
'baz' => 'bazval',
+ 'foobar' => [
+ 'baz' => 'bazval',
+ 'qux' => 'quxval',
+ ],
+ 'bazqux' => ['bazquxval1', 'bazquxval2'],
+ 'object' => (object) [
+ 'fooprop' => 'foopropval',
+ 'barprop' => 'barpropval',
+ ],
+ 'tostring' => new class() {
+ public function __toString(): string
+ {
+ return 'tostringval';
+ }
+ },
],
],
],
@@ -253,14 +268,37 @@ public function provideCurlRequests(): iterable
--url %1$shttp://localhost:8057/json%1$s \\
--header %1$sAccept: */*%1$s \\
--header %1$sContent-Type: application/x-www-form-urlencoded%1$s \\
- --header %1$sContent-Length: 32%1$s \\
+ --header %1$sContent-Length: 211%1$s \\
--header %1$sAccept-Encoding: gzip%1$s \\
--header %1$sUser-Agent: Symfony HttpClient/Native%1$s \\
- --data %1$sfoo=fooval%1$s --data %1$sbar=barval%1$s --data %1$sbaz=bazval%1$s',
+ --data %1$sfoo=fooval%1$s --data %1$sbar=barval%1$s --data %1$sbaz=bazval%1$s --data %1$sfoobar[baz]=bazval%1$s --data %1$sfoobar[qux]=quxval%1$s --data %1$sbazqux[0]=bazquxval1%1$s --data %1$sbazqux[1]=bazquxval2%1$s --data %1$sobject[fooprop]=foopropval%1$s --data %1$sobject[barprop]=barpropval%1$s --data %1$stostring=tostringval%1$s',
];
- // escapeshellarg on Windows replaces double quotes with spaces
+ // escapeshellarg on Windows replaces double quotes & percent signs with spaces
if ('\\' !== \DIRECTORY_SEPARATOR) {
+ yield 'GET with query' => [
+ [
+ 'method' => 'GET',
+ 'url' => 'http://localhost:8057/?foo=fooval&bar=barval',
+ 'options' => [
+ 'query' => [
+ 'bar' => 'newbarval',
+ 'foobar' => [
+ 'baz' => 'bazval',
+ 'qux' => 'quxval',
+ ],
+ 'bazqux' => ['bazquxval1', 'bazquxval2'],
+ ],
+ ],
+ ],
+ 'curl \\
+ --compressed \\
+ --request GET \\
+ --url %1$shttp://localhost:8057/?foo=fooval&bar=newbarval&foobar%%5Bbaz%%5D=bazval&foobar%%5Bqux%%5D=quxval&bazqux%%5B0%%5D=bazquxval1&bazqux%%5B1%%5D=bazquxval2%1$s \\
+ --header %1$sAccept: */*%1$s \\
+ --header %1$sAccept-Encoding: gzip%1$s \\
+ --header %1$sUser-Agent: Symfony HttpClient/Native%1$s',
+ ];
yield 'POST with json' => [
[
'method' => 'POST',
@@ -343,6 +381,28 @@ public function testItDoesNotGeneratesCurlCommandsForUnsupportedBodyType()
self::assertNull($curlCommand);
}
+ /**
+ * @requires extension openssl
+ */
+ public function testItDoesNotGeneratesCurlCommandsForNotEncodableBody()
+ {
+ $sut = new HttpClientDataCollector();
+ $sut->registerClient('http_client', $this->httpClientThatHasTracedRequests([
+ [
+ 'method' => 'POST',
+ 'url' => 'http://localhost:8057/json',
+ 'options' => [
+ 'body' => "\0",
+ ],
+ ],
+ ]));
+ $sut->collect(new Request(), new Response());
+ $collectedData = $sut->getClients();
+ self::assertCount(1, $collectedData['http_client']['traces']);
+ $curlCommand = $collectedData['http_client']['traces'][0]['curlCommand'];
+ self::assertNull($curlCommand);
+ }
+
private function httpClientThatHasTracedRequests($tracedRequests): TraceableHttpClient
{
$httpClient = new TraceableHttpClient(new NativeHttpClient());
diff --git a/src/Symfony/Component/HttpFoundation/CHANGELOG.md b/src/Symfony/Component/HttpFoundation/CHANGELOG.md
index 29bc7507e58b6..fdbd39cead318 100644
--- a/src/Symfony/Component/HttpFoundation/CHANGELOG.md
+++ b/src/Symfony/Component/HttpFoundation/CHANGELOG.md
@@ -6,7 +6,6 @@ CHANGELOG
* Add stale while revalidate and stale if error cache header
* Allow dynamic session "ttl" when using a remote storage
- * Send `Content-Length` when calling `Response::send()` and the content is a non-empty string
6.0
---
diff --git a/src/Symfony/Component/HttpFoundation/Response.php b/src/Symfony/Component/HttpFoundation/Response.php
index da5cec5405226..e452e1a017f37 100644
--- a/src/Symfony/Component/HttpFoundation/Response.php
+++ b/src/Symfony/Component/HttpFoundation/Response.php
@@ -371,10 +371,6 @@ public function sendContent(): static
*/
public function send(): static
{
- if (\is_string($this->content) && '' !== $this->content && !$this->headers->has('Transfer-Encoding')) {
- $this->headers->set('Content-Length', \strlen($this->content));
- }
-
$this->sendHeaders();
$this->sendContent();
diff --git a/src/Symfony/Component/HttpFoundation/Tests/ResponseTest.php b/src/Symfony/Component/HttpFoundation/Tests/ResponseTest.php
index 9d4a32a54269d..d3905c8f9d3aa 100644
--- a/src/Symfony/Component/HttpFoundation/Tests/ResponseTest.php
+++ b/src/Symfony/Component/HttpFoundation/Tests/ResponseTest.php
@@ -57,20 +57,6 @@ public function testSend()
$this->assertObjectHasAttribute('statusCode', $responseSent);
$this->assertObjectHasAttribute('statusText', $responseSent);
$this->assertObjectHasAttribute('charset', $responseSent);
- $this->assertFalse($responseSent->headers->has('Content-Length'));
-
- ob_start();
-
- $response = new Response('foo');
- $responseSent = $response->send();
- $this->assertSame('3', $responseSent->headers->get('Content-Length'));
-
- $response = new Response('bar');
- $response->headers->set('Transfer-Encoding', 'chunked');
- $responseSent = $response->send();
- $this->assertFalse($responseSent->headers->has('Content-Length'));
-
- $this->assertSame('foobar', ob_get_clean());
}
public function testGetCharset()
diff --git a/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/BackedEnumValueResolver.php b/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/BackedEnumValueResolver.php
index 054354963b313..8a84ac130bdca 100644
--- a/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/BackedEnumValueResolver.php
+++ b/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/BackedEnumValueResolver.php
@@ -51,6 +51,12 @@ public function resolve(Request $request, ArgumentMetadata $argument): iterable
return;
}
+ if ($value instanceof \BackedEnum) {
+ yield $value;
+
+ return;
+ }
+
if (!\is_int($value) && !\is_string($value)) {
throw new \LogicException(sprintf('Could not resolve the "%s $%s" controller argument: expecting an int or string, got %s.', $argument->getType(), $argument->getName(), get_debug_type($value)));
}
diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php
index 37a758c7cfaf1..ab882201ee929 100644
--- a/src/Symfony/Component/HttpKernel/Kernel.php
+++ b/src/Symfony/Component/HttpKernel/Kernel.php
@@ -78,11 +78,11 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl
*/
private static array $freshCache = [];
- public const VERSION = '6.1.0';
- public const VERSION_ID = 60100;
+ public const VERSION = '6.1.1';
+ public const VERSION_ID = 60101;
public const MAJOR_VERSION = 6;
public const MINOR_VERSION = 1;
- public const RELEASE_VERSION = 0;
+ public const RELEASE_VERSION = 1;
public const EXTRA_VERSION = '';
public const END_OF_MAINTENANCE = '01/2023';
diff --git a/src/Symfony/Component/HttpKernel/Tests/Controller/ArgumentResolver/BackedEnumValueResolverTest.php b/src/Symfony/Component/HttpKernel/Tests/Controller/ArgumentResolver/BackedEnumValueResolverTest.php
index 41505179f1bf3..03fbe6b7678ea 100644
--- a/src/Symfony/Component/HttpKernel/Tests/Controller/ArgumentResolver/BackedEnumValueResolverTest.php
+++ b/src/Symfony/Component/HttpKernel/Tests/Controller/ArgumentResolver/BackedEnumValueResolverTest.php
@@ -95,6 +95,12 @@ public function provideTestResolveData(): iterable
),
[null],
];
+
+ yield 'already resolved attribute value' => [
+ self::createRequest(['suit' => Suit::Hearts]),
+ self::createArgumentMetadata('suit', Suit::class),
+ [Suit::Hearts],
+ ];
}
public function testResolveThrowsNotFoundOnInvalidValue()
diff --git a/src/Symfony/Component/Lock/Tests/Store/DoctrineDbalStoreTest.php b/src/Symfony/Component/Lock/Tests/Store/DoctrineDbalStoreTest.php
index f44c2fd5cfc6e..3f46311ffe9ce 100644
--- a/src/Symfony/Component/Lock/Tests/Store/DoctrineDbalStoreTest.php
+++ b/src/Symfony/Component/Lock/Tests/Store/DoctrineDbalStoreTest.php
@@ -120,7 +120,7 @@ public function testCreatesTableInTransaction(string $platform)
->willReturn(true);
$platform = $this->createMock($platform);
- $platform->method('getCreateTableSQL')
+ $platform->method(method_exists(AbstractPlatform::class, 'getCreateTablesSQL') ? 'getCreateTablesSQL' : 'getCreateTableSQL')
->willReturn(['create sql stmt']);
$conn->method('getDatabasePlatform')
@@ -165,7 +165,7 @@ public function testTableCreationInTransactionNotSupported()
->willReturn(true);
$platform = $this->createMock(AbstractPlatform::class);
- $platform->method('getCreateTableSQL')
+ $platform->method(method_exists(AbstractPlatform::class, 'getCreateTablesSQL') ? 'getCreateTablesSQL' : 'getCreateTableSQL')
->willReturn(['create sql stmt']);
$conn->expects($this->atLeast(2))
@@ -202,7 +202,7 @@ public function testCreatesTableOutsideTransaction()
->willReturn(false);
$platform = $this->createMock(AbstractPlatform::class);
- $platform->method('getCreateTableSQL')
+ $platform->method(method_exists(AbstractPlatform::class, 'getCreateTablesSQL') ? 'getCreateTablesSQL' : 'getCreateTableSQL')
->willReturn(['create sql stmt']);
$conn->method('getDatabasePlatform')
diff --git a/src/Symfony/Component/Mailer/Exception/UnsupportedSchemeException.php b/src/Symfony/Component/Mailer/Exception/UnsupportedSchemeException.php
index 452b8ba3508a8..e47a129dc7e90 100644
--- a/src/Symfony/Component/Mailer/Exception/UnsupportedSchemeException.php
+++ b/src/Symfony/Component/Mailer/Exception/UnsupportedSchemeException.php
@@ -36,6 +36,10 @@ class UnsupportedSchemeException extends LogicException
'class' => Bridge\Mailchimp\Transport\MandrillTransportFactory::class,
'package' => 'symfony/mailchimp-mailer',
],
+ 'ohmysmtp' => [
+ 'class' => Bridge\OhMySmtp\Transport\OhMySmtpTransportFactory::class,
+ 'package' => 'symfony/oh-my-smtp-mailer',
+ ],
'postmark' => [
'class' => Bridge\Postmark\Transport\PostmarkTransportFactory::class,
'package' => 'symfony/postmark-mailer',
@@ -52,10 +56,6 @@ class UnsupportedSchemeException extends LogicException
'class' => Bridge\Amazon\Transport\SesTransportFactory::class,
'package' => 'symfony/amazon-mailer',
],
- 'ohmysmtp' => [
- 'class' => Bridge\OhMySmtp\Transport\OhMySmtpTransportFactory::class,
- 'package' => 'symfony/oh-my-smtp-mailer',
- ],
];
public function __construct(Dsn $dsn, string $name = null, array $supported = [])
diff --git a/src/Symfony/Component/Mailer/Tests/Exception/UnsupportedSchemeExceptionTest.php b/src/Symfony/Component/Mailer/Tests/Exception/UnsupportedSchemeExceptionTest.php
index 54685da7cb772..54ce6dbe5a38c 100644
--- a/src/Symfony/Component/Mailer/Tests/Exception/UnsupportedSchemeExceptionTest.php
+++ b/src/Symfony/Component/Mailer/Tests/Exception/UnsupportedSchemeExceptionTest.php
@@ -38,10 +38,10 @@ public static function setUpBeforeClass(): void
MailgunTransportFactory::class => false,
MailjetTransportFactory::class => false,
MandrillTransportFactory::class => false,
+ OhMySmtpTransportFactory::class => false,
PostmarkTransportFactory::class => false,
SendgridTransportFactory::class => false,
SendinblueTransportFactory::class => false,
- OhMySmtpTransportFactory::class => false,
SesTransportFactory::class => false,
]);
}
@@ -65,10 +65,10 @@ public function messageWhereSchemeIsPartOfSchemeToPackageMapProvider(): \Generat
yield ['mailgun', 'symfony/mailgun-mailer'];
yield ['mailjet', 'symfony/mailjet-mailer'];
yield ['mandrill', 'symfony/mailchimp-mailer'];
+ yield ['ohmysmtp', 'symfony/oh-my-smtp-mailer'];
yield ['postmark', 'symfony/postmark-mailer'];
yield ['sendgrid', 'symfony/sendgrid-mailer'];
yield ['sendinblue', 'symfony/sendinblue-mailer'];
- yield ['ohmysmtp', 'symfony/oh-my-smtp-mailer'];
yield ['ses', 'symfony/amazon-mailer'];
}
diff --git a/src/Symfony/Component/Mailer/Transport.php b/src/Symfony/Component/Mailer/Transport.php
index 6b7261ab4d0ce..ee65681cede1c 100644
--- a/src/Symfony/Component/Mailer/Transport.php
+++ b/src/Symfony/Component/Mailer/Transport.php
@@ -47,10 +47,10 @@ final class Transport
MailgunTransportFactory::class,
MailjetTransportFactory::class,
MandrillTransportFactory::class,
+ OhMySmtpTransportFactory::class,
PostmarkTransportFactory::class,
SendgridTransportFactory::class,
SendinblueTransportFactory::class,
- OhMySmtpTransportFactory::class,
SesTransportFactory::class,
];
diff --git a/src/Symfony/Component/Messenger/Bridge/Doctrine/Transport/DoctrineReceiver.php b/src/Symfony/Component/Messenger/Bridge/Doctrine/Transport/DoctrineReceiver.php
index 940783730381e..c83efe27cbe23 100644
--- a/src/Symfony/Component/Messenger/Bridge/Doctrine/Transport/DoctrineReceiver.php
+++ b/src/Symfony/Component/Messenger/Bridge/Doctrine/Transport/DoctrineReceiver.php
@@ -20,14 +20,13 @@
use Symfony\Component\Messenger\Stamp\TransportMessageIdStamp;
use Symfony\Component\Messenger\Transport\Receiver\ListableReceiverInterface;
use Symfony\Component\Messenger\Transport\Receiver\MessageCountAwareInterface;
-use Symfony\Component\Messenger\Transport\Receiver\ReceiverInterface;
use Symfony\Component\Messenger\Transport\Serialization\PhpSerializer;
use Symfony\Component\Messenger\Transport\Serialization\SerializerInterface;
/**
* @author Vincent Touzet
*/
-class DoctrineReceiver implements ReceiverInterface, MessageCountAwareInterface, ListableReceiverInterface
+class DoctrineReceiver implements ListableReceiverInterface, MessageCountAwareInterface
{
private const MAX_RETRIES = 3;
private int $retryingSafetyCounter = 0;
diff --git a/src/Symfony/Component/Messenger/Bridge/Doctrine/Transport/PostgreSqlConnection.php b/src/Symfony/Component/Messenger/Bridge/Doctrine/Transport/PostgreSqlConnection.php
index 91892084152d3..ad117a8486296 100644
--- a/src/Symfony/Component/Messenger/Bridge/Doctrine/Transport/PostgreSqlConnection.php
+++ b/src/Symfony/Component/Messenger/Bridge/Doctrine/Transport/PostgreSqlConnection.php
@@ -11,7 +11,6 @@
namespace Symfony\Component\Messenger\Bridge\Doctrine\Transport;
-use Doctrine\DBAL\Driver\PDO\Connection as DoctrinePdoConnection;
use Doctrine\DBAL\Schema\Table;
/**
@@ -73,8 +72,8 @@ public function get(): ?array
if (method_exists($this->driverConnection, 'getNativeConnection')) {
$wrappedConnection = $this->driverConnection->getNativeConnection();
} else {
- $wrappedConnection = $this->driverConnection->getWrappedConnection();
- if (!$wrappedConnection instanceof \PDO && $wrappedConnection instanceof DoctrinePdoConnection) {
+ $wrappedConnection = $this->driverConnection;
+ while (method_exists($wrappedConnection, 'getWrappedConnection')) {
$wrappedConnection = $wrappedConnection->getWrappedConnection();
}
}
diff --git a/src/Symfony/Component/Mime/Part/DataPart.php b/src/Symfony/Component/Mime/Part/DataPart.php
index 38f4689f0df6f..2932fe4c354ed 100644
--- a/src/Symfony/Component/Mime/Part/DataPart.php
+++ b/src/Symfony/Component/Mime/Part/DataPart.php
@@ -61,13 +61,20 @@ public static function fromPath(string $path, string $name = null, string $conte
$contentType = self::$mimeTypes->getMimeTypes($ext)[0] ?? 'application/octet-stream';
}
- if (false === is_readable($path)) {
+ if ((is_file($path) && !is_readable($path)) || is_dir($path)) {
throw new InvalidArgumentException(sprintf('Path "%s" is not readable.', $path));
}
if (false === $handle = @fopen($path, 'r', false)) {
throw new InvalidArgumentException(sprintf('Unable to open path "%s".', $path));
}
+
+ if (!is_file($path)) {
+ $cache = fopen('php://temp', 'r+');
+ stream_copy_to_stream($handle, $cache);
+ $handle = $cache;
+ }
+
$p = new self($handle, $name ?: basename($path), $contentType);
$p->handle = $handle;
diff --git a/src/Symfony/Component/Mime/Tests/Part/DataPartTest.php b/src/Symfony/Component/Mime/Tests/Part/DataPartTest.php
index 5f9569e42157b..99468e4419b72 100644
--- a/src/Symfony/Component/Mime/Tests/Part/DataPartTest.php
+++ b/src/Symfony/Component/Mime/Tests/Part/DataPartTest.php
@@ -12,6 +12,7 @@
namespace Symfony\Component\Mime\Tests\Part;
use PHPUnit\Framework\TestCase;
+use Symfony\Component\Mime\Exception\InvalidArgumentException;
use Symfony\Component\Mime\Header\Headers;
use Symfony\Component\Mime\Header\IdentificationHeader;
use Symfony\Component\Mime\Header\ParameterizedHeader;
@@ -127,6 +128,36 @@ public function testFromPathWithMeta()
), $p->getPreparedHeaders());
}
+ public function testFromPathWithNotAFile()
+ {
+ $this->expectException(InvalidArgumentException::class);
+ DataPart::fromPath(__DIR__.'/../Fixtures/mimetypes/');
+ }
+
+ /**
+ * @group network
+ */
+ public function testFromPathWithUrl()
+ {
+ if (!\in_array('https', stream_get_wrappers())) {
+ $this->markTestSkipped('"https" stream wrapper is not enabled.');
+ }
+
+ $p = DataPart::fromPath($file = 'https://symfony.com/images/common/logo/logo_symfony_header.png');
+ $content = file_get_contents($file);
+ $this->assertEquals($content, $p->getBody());
+ $maxLineLength = 76;
+ $this->assertEquals(substr(base64_encode($content), 0, $maxLineLength), substr($p->bodyToString(), 0, $maxLineLength));
+ $this->assertEquals(substr(base64_encode($content), 0, $maxLineLength), substr(implode('', iterator_to_array($p->bodyToIterable())), 0, $maxLineLength));
+ $this->assertEquals('image', $p->getMediaType());
+ $this->assertEquals('png', $p->getMediaSubType());
+ $this->assertEquals(new Headers(
+ new ParameterizedHeader('Content-Type', 'image/png', ['name' => 'logo_symfony_header.png']),
+ new UnstructuredHeader('Content-Transfer-Encoding', 'base64'),
+ new ParameterizedHeader('Content-Disposition', 'attachment', ['name' => 'logo_symfony_header.png', 'filename' => 'logo_symfony_header.png'])
+ ), $p->getPreparedHeaders());
+ }
+
public function testHasContentId()
{
$p = new DataPart('content');
diff --git a/src/Symfony/Component/PropertyInfo/Tests/Extractor/PhpStanExtractorTestDoc.php b/src/Symfony/Component/PropertyInfo/Tests/Extractor/PhpStanExtractorTestDoc.php
index f57721ca31bdb..7565994ee0c8c 100644
--- a/src/Symfony/Component/PropertyInfo/Tests/Extractor/PhpStanExtractorTestDoc.php
+++ b/src/Symfony/Component/PropertyInfo/Tests/Extractor/PhpStanExtractorTestDoc.php
@@ -437,6 +437,23 @@ public function testDummyNamespaceWithProperty()
$this->assertEquals($phpDocTypes[0]->getClassName(), $phpStanTypes[0]->getClassName());
}
+ /**
+ * @dataProvider intRangeTypeProvider
+ */
+ public function testExtractorIntRangeType(string $property, ?array $types)
+ {
+ $this->assertEquals($types, $this->extractor->getTypes('Symfony\Component\PropertyInfo\Tests\Fixtures\IntRangeDummy', $property));
+ }
+
+ public function intRangeTypeProvider(): array
+ {
+ return [
+ ['a', [new Type(Type::BUILTIN_TYPE_INT)]],
+ ['b', [new Type(Type::BUILTIN_TYPE_INT, true)]],
+ ['c', [new Type(Type::BUILTIN_TYPE_INT)]],
+ ];
+ }
+
/**
* @dataProvider php80TypesProvider
*/
diff --git a/src/Symfony/Component/PropertyInfo/Tests/Fixtures/IntRangeDummy.php b/src/Symfony/Component/PropertyInfo/Tests/Fixtures/IntRangeDummy.php
new file mode 100644
index 0000000000000..12b3784726e82
--- /dev/null
+++ b/src/Symfony/Component/PropertyInfo/Tests/Fixtures/IntRangeDummy.php
@@ -0,0 +1,30 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\PropertyInfo\Tests\Fixtures;
+
+class IntRangeDummy
+{
+ /**
+ * @var int<0, 100>
+ */
+ public $a;
+
+ /**
+ * @var int|null
+ */
+ public $b;
+
+ /**
+ * @var int<50, max>
+ */
+ public $c;
+}
diff --git a/src/Symfony/Component/PropertyInfo/Util/PhpStanTypeHelper.php b/src/Symfony/Component/PropertyInfo/Util/PhpStanTypeHelper.php
index 236eaaf873e17..5c584860f08a2 100644
--- a/src/Symfony/Component/PropertyInfo/Util/PhpStanTypeHelper.php
+++ b/src/Symfony/Component/PropertyInfo/Util/PhpStanTypeHelper.php
@@ -121,6 +121,10 @@ private function extractTypes(TypeNode $node, NameScope $nameScope): array
[$mainType] = $this->extractTypes($node->type, $nameScope);
+ if (Type::BUILTIN_TYPE_INT === $mainType->getBuiltinType()) {
+ return [$mainType];
+ }
+
$collectionKeyTypes = $mainType->getCollectionKeyTypes();
$collectionKeyValues = [];
if (1 === \count($node->genericTypes)) {
diff --git a/src/Symfony/Component/Routing/Annotation/Route.php b/src/Symfony/Component/Routing/Annotation/Route.php
index 1d97be37c1226..a9ab5f3c9fc4a 100644
--- a/src/Symfony/Component/Routing/Annotation/Route.php
+++ b/src/Symfony/Component/Routing/Annotation/Route.php
@@ -30,9 +30,9 @@ class Route
private array $schemes;
/**
- * @param string[] $requirements
- * @param string[]|string $methods
- * @param string[]|string $schemes
+ * @param array $requirements
+ * @param string[]|string $methods
+ * @param string[]|string $schemes
*/
public function __construct(
string|array $path = null,
diff --git a/src/Symfony/Component/Routing/Route.php b/src/Symfony/Component/Routing/Route.php
index 4966a1aa58d16..dc0554be902c1 100644
--- a/src/Symfony/Component/Routing/Route.php
+++ b/src/Symfony/Component/Routing/Route.php
@@ -37,14 +37,14 @@ class Route implements \Serializable
* * compiler_class: A class name able to compile this route instance (RouteCompiler by default)
* * utf8: Whether UTF-8 matching is enforced ot not
*
- * @param string $path The path pattern to match
- * @param array $defaults An array of default parameter values
- * @param array $requirements An array of requirements for parameters (regexes)
- * @param array $options An array of options
- * @param string|null $host The host pattern to match
- * @param string|string[] $schemes A required URI scheme or an array of restricted schemes
- * @param string|string[] $methods A required HTTP method or an array of restricted methods
- * @param string|null $condition A condition that should evaluate to true for the route to match
+ * @param string $path The path pattern to match
+ * @param array $defaults An array of default parameter values
+ * @param array $requirements An array of requirements for parameters (regexes)
+ * @param array $options An array of options
+ * @param string|null $host The host pattern to match
+ * @param string|string[] $schemes A required URI scheme or an array of restricted schemes
+ * @param string|string[] $methods A required HTTP method or an array of restricted methods
+ * @param string|null $condition A condition that should evaluate to true for the route to match
*/
public function __construct(string $path, array $defaults = [], array $requirements = [], array $options = [], ?string $host = '', string|array $schemes = [], string|array $methods = [], ?string $condition = '')
{
diff --git a/src/Symfony/Component/Runtime/README.md b/src/Symfony/Component/Runtime/README.md
index 006e7a22cdf74..070937f575501 100644
--- a/src/Symfony/Component/Runtime/README.md
+++ b/src/Symfony/Component/Runtime/README.md
@@ -3,6 +3,17 @@ Runtime Component
Symfony Runtime enables decoupling applications from global state.
+Sponsor
+-------
+
+The Runtime component for Symfony 6.1 is [backed][1] by [Fulgens][2].
+
+Fulgens is a human-sized company founded in 2018. Specialized in development, we
+provide support, analysis and training. Symfony being at the heart of our work,
+we are committed to contribute to its development at our own scale.
+
+Help Symfony by [sponsoring][3] its development!
+
Resources
---------
@@ -11,3 +22,7 @@ Resources
* [Report issues](https://github.com/symfony/symfony/issues) and
[send Pull Requests](https://github.com/symfony/symfony/pulls)
in the [main Symfony repository](https://github.com/symfony/symfony)
+
+[1]: https://symfony.com/backers
+[2]: https://fulgens.be
+[3]: https://symfony.com/sponsor
diff --git a/src/Symfony/Component/Security/Http/Authenticator/Passport/Credentials/PasswordCredentials.php b/src/Symfony/Component/Security/Http/Authenticator/Passport/Credentials/PasswordCredentials.php
index 3491be107e790..ebaf8eabee4a4 100644
--- a/src/Symfony/Component/Security/Http/Authenticator/Passport/Credentials/PasswordCredentials.php
+++ b/src/Symfony/Component/Security/Http/Authenticator/Passport/Credentials/PasswordCredentials.php
@@ -16,7 +16,7 @@
/**
* Implements password credentials.
*
- * These plaintext passwords are checked by the UserPasswordEncoder during
+ * These plaintext passwords are checked by the UserPasswordHasher during
* authentication.
*
* @author Wouter de Jong
diff --git a/src/Symfony/Component/Security/Http/Authenticator/Passport/Passport.php b/src/Symfony/Component/Security/Http/Authenticator/Passport/Passport.php
index 542d843f0c72d..75feb599c3543 100644
--- a/src/Symfony/Component/Security/Http/Authenticator/Passport/Passport.php
+++ b/src/Symfony/Component/Security/Http/Authenticator/Passport/Passport.php
@@ -17,7 +17,11 @@
use Symfony\Component\Security\Http\Authenticator\Passport\Credentials\CredentialsInterface;
/**
- * The default implementation for passports.
+ * A Passport contains all security-related information that needs to be
+ * validated during authentication.
+ *
+ * A passport badge can be used to add any additional information to the
+ * passport.
*
* @author Wouter de Jong
*/
@@ -55,6 +59,15 @@ public function getUser(): UserInterface
return $this->user;
}
+ /**
+ * Adds a new security badge.
+ *
+ * A passport can hold only one instance of the same security badge.
+ * This method replaces the current badge if it is already set on this
+ * passport.
+ *
+ * @return $this
+ */
public function addBadge(BadgeInterface $badge): static
{
$this->badges[\get_class($badge)] = $badge;
diff --git a/src/Symfony/Component/Serializer/DataCollector/SerializerDataCollector.php b/src/Symfony/Component/Serializer/DataCollector/SerializerDataCollector.php
index cfea563d0119c..2cc4ba7a922e8 100644
--- a/src/Symfony/Component/Serializer/DataCollector/SerializerDataCollector.php
+++ b/src/Symfony/Component/Serializer/DataCollector/SerializerDataCollector.php
@@ -179,6 +179,10 @@ public function lateCollect(): void
];
foreach ($this->collected as $collected) {
+ if (!isset($collected['data'])) {
+ continue;
+ }
+
$data = [
'data' => $this->cloneVar($collected['data']),
'dataType' => get_debug_type($collected['data']),
diff --git a/src/Symfony/Component/Serializer/Debug/TraceableEncoder.php b/src/Symfony/Component/Serializer/Debug/TraceableEncoder.php
index c2927adf914e8..f494cc57cd89f 100644
--- a/src/Symfony/Component/Serializer/Debug/TraceableEncoder.php
+++ b/src/Symfony/Component/Serializer/Debug/TraceableEncoder.php
@@ -55,17 +55,13 @@ public function encode(mixed $data, string $format, array $context = []): string
/**
* {@inheritDoc}
- *
- * @param array $context
*/
- public function supportsEncoding(string $format /*, array $context = [] */): bool
+ public function supportsEncoding(string $format, array $context = []): bool
{
if (!$this->encoder instanceof EncoderInterface) {
return false;
}
- $context = \func_num_args() > 1 ? func_get_arg(1) : [];
-
return $this->encoder->supportsEncoding($format, $context);
}
@@ -91,17 +87,13 @@ public function decode(string $data, string $format, array $context = []): mixed
/**
* {@inheritDoc}
- *
- * @param array $context
*/
- public function supportsDecoding(string $format /*, array $context = [] */): bool
+ public function supportsDecoding(string $format, array $context = []): bool
{
if (!$this->encoder instanceof DecoderInterface) {
return false;
}
- $context = \func_num_args() > 1 ? func_get_arg(1) : [];
-
return $this->encoder->supportsDecoding($format, $context);
}
@@ -121,4 +113,12 @@ public function needsNormalization(): bool
{
return !$this->encoder instanceof NormalizationAwareEncoder;
}
+
+ /**
+ * Proxies all method calls to the original encoder.
+ */
+ public function __call(string $method, array $arguments): mixed
+ {
+ return $this->encoder->{$method}(...$arguments);
+ }
}
diff --git a/src/Symfony/Component/Serializer/Debug/TraceableNormalizer.php b/src/Symfony/Component/Serializer/Debug/TraceableNormalizer.php
index 58a055ecfa90d..8e430e55d4351 100644
--- a/src/Symfony/Component/Serializer/Debug/TraceableNormalizer.php
+++ b/src/Symfony/Component/Serializer/Debug/TraceableNormalizer.php
@@ -57,17 +57,13 @@ public function normalize(mixed $object, string $format = null, array $context =
/**
* {@inheritDoc}
- *
- * @param array $context
*/
- public function supportsNormalization(mixed $data, string $format = null /*, array $context = [] */): bool
+ public function supportsNormalization(mixed $data, string $format = null, array $context = []): bool
{
if (!$this->normalizer instanceof NormalizerInterface) {
return false;
}
- $context = \func_num_args() > 2 ? func_get_arg(2) : [];
-
return $this->normalizer->supportsNormalization($data, $format, $context);
}
@@ -93,17 +89,13 @@ public function denormalize(mixed $data, string $type, string $format = null, ar
/**
* {@inheritDoc}
- *
- * @param array $context
*/
- public function supportsDenormalization(mixed $data, string $type, string $format = null /*, array $context = [] */): bool
+ public function supportsDenormalization(mixed $data, string $type, string $format = null, array $context = []): bool
{
if (!$this->normalizer instanceof DenormalizerInterface) {
return false;
}
- $context = \func_num_args() > 3 ? func_get_arg(3) : [];
-
return $this->normalizer->supportsDenormalization($data, $type, $format, $context);
}
@@ -150,4 +142,12 @@ public function hasCacheableSupportsMethod(): bool
{
return $this->normalizer instanceof CacheableSupportsMethodInterface && $this->normalizer->hasCacheableSupportsMethod();
}
+
+ /**
+ * Proxies all method calls to the original normalizer.
+ */
+ public function __call(string $method, array $arguments): mixed
+ {
+ return $this->normalizer->{$method}(...$arguments);
+ }
}
diff --git a/src/Symfony/Component/Serializer/Debug/TraceableSerializer.php b/src/Symfony/Component/Serializer/Debug/TraceableSerializer.php
index 557bf91286c28..bc16bd7f8d240 100644
--- a/src/Symfony/Component/Serializer/Debug/TraceableSerializer.php
+++ b/src/Symfony/Component/Serializer/Debug/TraceableSerializer.php
@@ -165,4 +165,12 @@ public function supportsDecoding(string $format, array $context = []): bool
{
return $this->serializer->supportsDecoding($format, $context);
}
+
+ /**
+ * Proxies all method calls to the original serializer.
+ */
+ public function __call(string $method, array $arguments): mixed
+ {
+ return $this->serializer->{$method}(...$arguments);
+ }
}
diff --git a/src/Symfony/Component/Serializer/Mapping/Loader/XmlFileLoader.php b/src/Symfony/Component/Serializer/Mapping/Loader/XmlFileLoader.php
index 26aa1f51e0139..8f7e7cf553b33 100644
--- a/src/Symfony/Component/Serializer/Mapping/Loader/XmlFileLoader.php
+++ b/src/Symfony/Component/Serializer/Mapping/Loader/XmlFileLoader.php
@@ -72,7 +72,7 @@ public function loadClassMetadata(ClassMetadataInterface $classMetadata): bool
}
if (isset($attribute['ignore'])) {
- $attributeMetadata->setIgnore((bool) $attribute['ignore']);
+ $attributeMetadata->setIgnore(XmlUtils::phpize($attribute['ignore']));
}
foreach ($attribute->context as $node) {
diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php
index 6409ab213c195..e32533858efc2 100644
--- a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php
+++ b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php
@@ -364,12 +364,12 @@ public function denormalize(mixed $data, string $type, string $format = null, ar
$resolvedClass = $this->objectClassResolver ? ($this->objectClassResolver)($object) : \get_class($object);
foreach ($normalizedData as $attribute => $value) {
- $attributeContext = $this->getAttributeDenormalizationContext($resolvedClass, $attribute, $context);
-
if ($this->nameConverter) {
- $attribute = $this->nameConverter->denormalize($attribute, $resolvedClass, $format, $attributeContext);
+ $attribute = $this->nameConverter->denormalize($attribute, $resolvedClass, $format, $context);
}
+ $attributeContext = $this->getAttributeDenormalizationContext($resolvedClass, $attribute, $context);
+
if ((false !== $allowedAttributes && !\in_array($attribute, $allowedAttributes)) || !$this->isAllowedAttribute($resolvedClass, $attribute, $format, $context)) {
if (!($context[self::ALLOW_EXTRA_ATTRIBUTES] ?? $this->defaultContext[self::ALLOW_EXTRA_ATTRIBUTES])) {
$extraAttributes[] = $attribute;
diff --git a/src/Symfony/Component/Serializer/Tests/DataCollector/SerializerDataCollectorTest.php b/src/Symfony/Component/Serializer/Tests/DataCollector/SerializerDataCollectorTest.php
index 7b22b6064e0d2..b929e262753cc 100644
--- a/src/Symfony/Component/Serializer/Tests/DataCollector/SerializerDataCollectorTest.php
+++ b/src/Symfony/Component/Serializer/Tests/DataCollector/SerializerDataCollectorTest.php
@@ -276,6 +276,27 @@ public function testReset()
$this->assertSame([], $dataCollector->getData());
}
+ public function testDoNotCollectPartialTraces()
+ {
+ $dataCollector = new SerializerDataCollector();
+
+ $dataCollector->collectNormalization('traceIdOne', DateTimeNormalizer::class, 1.0);
+ $dataCollector->collectDenormalization('traceIdTwo', DateTimeNormalizer::class, 1.0);
+ $dataCollector->collectEncoding('traceIdThree', CsvEncoder::class, 10.0);
+ $dataCollector->collectDecoding('traceIdFour', JsonEncoder::class, 1.0);
+
+ $dataCollector->lateCollect();
+
+ $data = $dataCollector->getData();
+
+ $this->assertSame([], $data['serialize']);
+ $this->assertSame([], $data['deserialize']);
+ $this->assertSame([], $data['normalize']);
+ $this->assertSame([], $data['denormalize']);
+ $this->assertSame([], $data['encode']);
+ $this->assertSame([], $data['decode']);
+ }
+
/**
* Cast cloned vars to be able to test nested values.
*/
diff --git a/src/Symfony/Component/Serializer/Tests/Debug/TraceableSerializerTest.php b/src/Symfony/Component/Serializer/Tests/Debug/TraceableSerializerTest.php
index ae8a01623cdbf..00a1ef58a693f 100644
--- a/src/Symfony/Component/Serializer/Tests/Debug/TraceableSerializerTest.php
+++ b/src/Symfony/Component/Serializer/Tests/Debug/TraceableSerializerTest.php
@@ -145,10 +145,7 @@ public function normalize(mixed $object, string $format = null, array $context =
return 'normalized';
}
- /**
- * @param array $context
- */
- public function supportsNormalization(mixed $data, string $format = null /*, array $context = [] */): bool
+ public function supportsNormalization(mixed $data, string $format = null, array $context = []): bool
{
return true;
}
@@ -158,10 +155,7 @@ public function denormalize(mixed $data, string $type, string $format = null, ar
return 'denormalized';
}
- /**
- * @param array $context
- */
- public function supportsDenormalization(mixed $data, string $type, string $format = null /*, array $context = [] */): bool
+ public function supportsDenormalization(mixed $data, string $type, string $format = null, array $context = []): bool
{
return true;
}
@@ -171,10 +165,7 @@ public function encode(mixed $data, string $format, array $context = []): string
return 'encoded';
}
- /**
- * @param array $context
- */
- public function supportsEncoding(string $format /*, array $context = [] */): bool
+ public function supportsEncoding(string $format, array $context = []): bool
{
return true;
}
@@ -184,10 +175,7 @@ public function decode(string $data, string $format, array $context = []): mixed
return 'decoded';
}
- /**
- * @param array $context
- */
- public function supportsDecoding(string $format /*, array $context = [] */): bool
+ public function supportsDecoding(string $format, array $context = []): bool
{
return true;
}
diff --git a/src/Symfony/Component/Serializer/Tests/Fixtures/serialization.xml b/src/Symfony/Component/Serializer/Tests/Fixtures/serialization.xml
index da61e0acce8dc..69243cfddb5ae 100644
--- a/src/Symfony/Component/Serializer/Tests/Fixtures/serialization.xml
+++ b/src/Symfony/Component/Serializer/Tests/Fixtures/serialization.xml
@@ -37,6 +37,7 @@
+
diff --git a/src/Symfony/Component/Serializer/Tests/Mapping/Loader/XmlFileLoaderTest.php b/src/Symfony/Component/Serializer/Tests/Mapping/Loader/XmlFileLoaderTest.php
index 201cb68ba8ff8..47d6305a898f2 100644
--- a/src/Symfony/Component/Serializer/Tests/Mapping/Loader/XmlFileLoaderTest.php
+++ b/src/Symfony/Component/Serializer/Tests/Mapping/Loader/XmlFileLoaderTest.php
@@ -107,6 +107,7 @@ public function testLoadIgnore()
$attributesMetadata = $classMetadata->getAttributesMetadata();
$this->assertTrue($attributesMetadata['ignored1']->isIgnored());
$this->assertTrue($attributesMetadata['ignored2']->isIgnored());
+ $this->assertFalse($attributesMetadata['notIgnored']->isIgnored());
}
protected function getLoaderForContextMapping(): LoaderInterface
diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/Features/ContextMetadataTestTrait.php b/src/Symfony/Component/Serializer/Tests/Normalizer/Features/ContextMetadataTestTrait.php
index 28b1bf7f10cff..e499e5624cbd5 100644
--- a/src/Symfony/Component/Serializer/Tests/Normalizer/Features/ContextMetadataTestTrait.php
+++ b/src/Symfony/Component/Serializer/Tests/Normalizer/Features/ContextMetadataTestTrait.php
@@ -17,6 +17,7 @@
use Symfony\Component\Serializer\Annotation\Groups;
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactory;
use Symfony\Component\Serializer\Mapping\Loader\AnnotationLoader;
+use Symfony\Component\Serializer\NameConverter\CamelCaseToSnakeCaseNameConverter;
use Symfony\Component\Serializer\Normalizer\DateTimeNormalizer;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
use Symfony\Component\Serializer\Serializer;
@@ -84,6 +85,17 @@ public function contextMetadataDummyProvider()
[ContextChildMetadataDummy::class],
];
}
+
+ public function testContextDenormalizeWithNameConverter()
+ {
+ $classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()));
+ $normalizer = new ObjectNormalizer($classMetadataFactory, new CamelCaseToSnakeCaseNameConverter(), null, new PhpDocExtractor());
+ new Serializer([new DateTimeNormalizer(), $normalizer]);
+
+ /** @var ContextMetadataNamingDummy $dummy */
+ $dummy = $normalizer->denormalize(['created_at' => '28/07/2011'], ContextMetadataNamingDummy::class);
+ self::assertEquals('2011-07-28', $dummy->createdAt->format('Y-m-d'));
+ }
}
class ContextMetadataDummy
@@ -123,3 +135,13 @@ class ContextChildMetadataDummy
*/
public $date;
}
+
+class ContextMetadataNamingDummy
+{
+ /**
+ * @var \DateTime
+ *
+ * @Context({ DateTimeNormalizer::FORMAT_KEY = "d/m/Y" })
+ */
+ public $createdAt;
+}
diff --git a/src/Symfony/Component/Validator/Command/DebugCommand.php b/src/Symfony/Component/Validator/Command/DebugCommand.php
index a997a46918dd3..06b8d0b8047dd 100644
--- a/src/Symfony/Component/Validator/Command/DebugCommand.php
+++ b/src/Symfony/Component/Validator/Command/DebugCommand.php
@@ -88,7 +88,19 @@ private function dumpValidatorsForClass(InputInterface $input, OutputInterface $
$rows = [];
$dump = new Dumper($output);
- foreach ($this->getConstrainedPropertiesData($class) as $propertyName => $constraintsData) {
+ /** @var ClassMetadataInterface $classMetadata */
+ $classMetadata = $this->validator->getMetadataFor($class);
+
+ foreach ($this->getClassConstraintsData($classMetadata) as $data) {
+ $rows[] = [
+ '-',
+ $data['class'],
+ implode(', ', $data['groups']),
+ $dump($data['options']),
+ ];
+ }
+
+ foreach ($this->getConstrainedPropertiesData($classMetadata) as $propertyName => $constraintsData) {
foreach ($constraintsData as $data) {
$rows[] = [
$propertyName,
@@ -119,12 +131,20 @@ private function dumpValidatorsForClass(InputInterface $input, OutputInterface $
$table->render();
}
- private function getConstrainedPropertiesData(string $class): array
+ private function getClassConstraintsData(ClassMetadataInterface $classMetadata): iterable
{
- $data = [];
+ foreach ($classMetadata->getConstraints() as $constraint) {
+ yield [
+ 'class' => \get_class($constraint),
+ 'groups' => $constraint->groups,
+ 'options' => $this->getConstraintOptions($constraint),
+ ];
+ }
+ }
- /** @var ClassMetadataInterface $classMetadata */
- $classMetadata = $this->validator->getMetadataFor($class);
+ private function getConstrainedPropertiesData(ClassMetadataInterface $classMetadata): array
+ {
+ $data = [];
foreach ($classMetadata->getConstrainedProperties() as $constrainedProperty) {
$data[$constrainedProperty] = $this->getPropertyData($classMetadata, $constrainedProperty);
diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.de.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.de.xlf
index 00be24fb8ac5f..1c6d0c6c95873 100644
--- a/src/Symfony/Component/Validator/Resources/translations/validators.de.xlf
+++ b/src/Symfony/Component/Validator/Resources/translations/validators.de.xlf
@@ -220,7 +220,7 @@
Unsupported card type or invalid card number.
- Nicht unterstützer Kartentyp oder ungültige Kartennummer.
+ Nicht unterstützter Kartentyp oder ungültige Kartennummer.
This is not a valid International Bank Account Number (IBAN).
@@ -312,7 +312,7 @@
This is not a valid Business Identifier Code (BIC).
- Dieser Wert ist kein gültiger BIC.
+ Dieser Wert ist keine gültige internationale Bankleitzahl (BIC).
Error
diff --git a/src/Symfony/Component/Validator/Tests/Command/DebugCommandTest.php b/src/Symfony/Component/Validator/Tests/Command/DebugCommandTest.php
index f6d1691662e3f..be1691454615d 100644
--- a/src/Symfony/Component/Validator/Tests/Command/DebugCommandTest.php
+++ b/src/Symfony/Component/Validator/Tests/Command/DebugCommandTest.php
@@ -15,6 +15,7 @@
use Symfony\Component\Console\Tester\CommandTester;
use Symfony\Component\Validator\Command\DebugCommand;
use Symfony\Component\Validator\Constraints\Email;
+use Symfony\Component\Validator\Constraints\Expression;
use Symfony\Component\Validator\Constraints\NotBlank;
use Symfony\Component\Validator\Mapping\ClassMetadataInterface;
use Symfony\Component\Validator\Mapping\Factory\MetadataFactoryInterface;
@@ -38,6 +39,11 @@ public function testOutputWithClassArgument()
->with(DummyClassOne::class)
->willReturn($classMetadata);
+ $classMetadata
+ ->expects($this->once())
+ ->method('getConstraints')
+ ->willReturn([new Expression('1 + 1 = 2')]);
+
$classMetadata
->expects($this->once())
->method('getConstrainedProperties')
@@ -68,22 +74,28 @@ public function testOutputWithClassArgument()
Symfony\Component\Validator\Tests\Dummy\DummyClassOne
-----------------------------------------------------
-+---------------+--------------------------------------------------+---------+------------------------------------------------------------+
-| Property | Name | Groups | Options |
-+---------------+--------------------------------------------------+---------+------------------------------------------------------------+
-| firstArgument | Symfony\Component\Validator\Constraints\NotBlank | Default | [ |
-| | | | "allowNull" => false, |
-| | | | "message" => "This value should not be blank.", |
-| | | | "normalizer" => null, |
-| | | | "payload" => null |
-| | | | ] |
-| firstArgument | Symfony\Component\Validator\Constraints\Email | Default | [ |
-| | | | "message" => "This value is not a valid email address.", |
-| | | | "mode" => null, |
-| | | | "normalizer" => null, |
-| | | | "payload" => null |
-| | | | ] |
-+---------------+--------------------------------------------------+---------+------------------------------------------------------------+
++---------------+----------------------------------------------------+---------+------------------------------------------------------------+
+| Property | Name | Groups | Options |
++---------------+----------------------------------------------------+---------+------------------------------------------------------------+
+| - | Symfony\Component\Validator\Constraints\Expression | Default | [ |
+| | | | "expression" => "1 + 1 = 2", |
+| | | | "message" => "This value is not valid.", |
+| | | | "payload" => null, |
+| | | | "values" => [] |
+| | | | ] |
+| firstArgument | Symfony\Component\Validator\Constraints\NotBlank | Default | [ |
+| | | | "allowNull" => false, |
+| | | | "message" => "This value should not be blank.", |
+| | | | "normalizer" => null, |
+| | | | "payload" => null |
+| | | | ] |
+| firstArgument | Symfony\Component\Validator\Constraints\Email | Default | [ |
+| | | | "message" => "This value is not a valid email address.", |
+| | | | "mode" => null, |
+| | | | "normalizer" => null, |
+| | | | "payload" => null |
+| | | | ] |
++---------------+----------------------------------------------------+---------+------------------------------------------------------------+
TXT
, $tester->getDisplay(true)
@@ -108,6 +120,11 @@ public function testOutputWithPathArgument()
'firstArgument',
]);
+ $classMetadata
+ ->expects($this->exactly(2))
+ ->method('getConstraints')
+ ->willReturn([new Expression('1 + 1 = 2')]);
+
$classMetadata
->method('getPropertyMetadata')
->with('firstArgument')
@@ -129,42 +146,54 @@ public function testOutputWithPathArgument()
Symfony\Component\Validator\Tests\Dummy\DummyClassOne
-----------------------------------------------------
-+---------------+--------------------------------------------------+---------+------------------------------------------------------------+
-| Property | Name | Groups | Options |
-+---------------+--------------------------------------------------+---------+------------------------------------------------------------+
-| firstArgument | Symfony\Component\Validator\Constraints\NotBlank | Default | [ |
-| | | | "allowNull" => false, |
-| | | | "message" => "This value should not be blank.", |
-| | | | "normalizer" => null, |
-| | | | "payload" => null |
-| | | | ] |
-| firstArgument | Symfony\Component\Validator\Constraints\Email | Default | [ |
-| | | | "message" => "This value is not a valid email address.", |
-| | | | "mode" => null, |
-| | | | "normalizer" => null, |
-| | | | "payload" => null |
-| | | | ] |
-+---------------+--------------------------------------------------+---------+------------------------------------------------------------+
++---------------+----------------------------------------------------+---------+------------------------------------------------------------+
+| Property | Name | Groups | Options |
++---------------+----------------------------------------------------+---------+------------------------------------------------------------+
+| - | Symfony\Component\Validator\Constraints\Expression | Default | [ |
+| | | | "expression" => "1 + 1 = 2", |
+| | | | "message" => "This value is not valid.", |
+| | | | "payload" => null, |
+| | | | "values" => [] |
+| | | | ] |
+| firstArgument | Symfony\Component\Validator\Constraints\NotBlank | Default | [ |
+| | | | "allowNull" => false, |
+| | | | "message" => "This value should not be blank.", |
+| | | | "normalizer" => null, |
+| | | | "payload" => null |
+| | | | ] |
+| firstArgument | Symfony\Component\Validator\Constraints\Email | Default | [ |
+| | | | "message" => "This value is not a valid email address.", |
+| | | | "mode" => null, |
+| | | | "normalizer" => null, |
+| | | | "payload" => null |
+| | | | ] |
++---------------+----------------------------------------------------+---------+------------------------------------------------------------+
Symfony\Component\Validator\Tests\Dummy\DummyClassTwo
-----------------------------------------------------
-+---------------+--------------------------------------------------+---------+------------------------------------------------------------+
-| Property | Name | Groups | Options |
-+---------------+--------------------------------------------------+---------+------------------------------------------------------------+
-| firstArgument | Symfony\Component\Validator\Constraints\NotBlank | Default | [ |
-| | | | "allowNull" => false, |
-| | | | "message" => "This value should not be blank.", |
-| | | | "normalizer" => null, |
-| | | | "payload" => null |
-| | | | ] |
-| firstArgument | Symfony\Component\Validator\Constraints\Email | Default | [ |
-| | | | "message" => "This value is not a valid email address.", |
-| | | | "mode" => null, |
-| | | | "normalizer" => null, |
-| | | | "payload" => null |
-| | | | ] |
-+---------------+--------------------------------------------------+---------+------------------------------------------------------------+
++---------------+----------------------------------------------------+---------+------------------------------------------------------------+
+| Property | Name | Groups | Options |
++---------------+----------------------------------------------------+---------+------------------------------------------------------------+
+| - | Symfony\Component\Validator\Constraints\Expression | Default | [ |
+| | | | "expression" => "1 + 1 = 2", |
+| | | | "message" => "This value is not valid.", |
+| | | | "payload" => null, |
+| | | | "values" => [] |
+| | | | ] |
+| firstArgument | Symfony\Component\Validator\Constraints\NotBlank | Default | [ |
+| | | | "allowNull" => false, |
+| | | | "message" => "This value should not be blank.", |
+| | | | "normalizer" => null, |
+| | | | "payload" => null |
+| | | | ] |
+| firstArgument | Symfony\Component\Validator\Constraints\Email | Default | [ |
+| | | | "message" => "This value is not a valid email address.", |
+| | | | "mode" => null, |
+| | | | "normalizer" => null, |
+| | | | "payload" => null |
+| | | | ] |
++---------------+----------------------------------------------------+---------+------------------------------------------------------------+
TXT
, $tester->getDisplay(true)
diff --git a/src/Symfony/Component/VarExporter/Instantiator.php b/src/Symfony/Component/VarExporter/Instantiator.php
index 4a9c1c6deac73..38fce27b69587 100644
--- a/src/Symfony/Component/VarExporter/Instantiator.php
+++ b/src/Symfony/Component/VarExporter/Instantiator.php
@@ -39,10 +39,10 @@ final class Instantiator
* Bar::class => ['privateBarProperty' => $propertyValue],
* ]);
*
- * Instances of ArrayObject, ArrayIterator and SplObjectHash can be created
+ * Instances of ArrayObject, ArrayIterator and SplObjectStorage can be created
* by using the special "\0" property name to define their internal value:
*
- * // creates an SplObjectHash where $info1 is attached to $obj1, etc.
+ * // creates an SplObjectStorage where $info1 is attached to $obj1, etc.
* Instantiator::instantiate(SplObjectStorage::class, ["\0" => [$obj1, $info1, $obj2, $info2...]]);
*
* // creates an ArrayObject populated with $inputArray
diff --git a/src/Symfony/Component/VarExporter/Internal/Exporter.php b/src/Symfony/Component/VarExporter/Internal/Exporter.php
index 0a620ababfeae..4e13e12f6124c 100644
--- a/src/Symfony/Component/VarExporter/Internal/Exporter.php
+++ b/src/Symfony/Component/VarExporter/Internal/Exporter.php
@@ -108,7 +108,7 @@ public static function prepare($values, $objectsPool, &$refsPool, &$objectsCount
$arrayValue = (array) $value;
} elseif ($value instanceof \Serializable
|| $value instanceof \__PHP_Incomplete_Class
- || PHP_VERSION_ID < 80200 && $value instanceof \DatePeriod
+ || \PHP_VERSION_ID < 80200 && $value instanceof \DatePeriod
) {
++$objectsCount;
$objectsPool[$value] = [$id = \count($objectsPool), serialize($value), [], 0];
diff --git a/src/Symfony/Component/Workflow/README.md b/src/Symfony/Component/Workflow/README.md
index 66fb6013a59ed..8fd69221addb5 100644
--- a/src/Symfony/Component/Workflow/README.md
+++ b/src/Symfony/Component/Workflow/README.md
@@ -4,6 +4,19 @@ Workflow Component
The Workflow component provides tools for managing a workflow or finite state
machine.
+Sponsor
+-------
+
+The Workflow component for Symfony 6.1 is [backed][1] by [ZEturf][2].
+
+The ZEturf group is an online gaming operator licensed in 6 markets in Europe and
+Africa. It operates two brands, ZEturf on horse racing betting, and ZEbet on
+sports betting. In parallel with the development of its betting activities, the
+group is also investing in Entertainment / gaming with Free-to-Play and
+Play-to-Earn projects.
+
+Help Symfony by [sponsoring][3] its development!
+
Resources
---------
@@ -12,3 +25,7 @@ Resources
* [Report issues](https://github.com/symfony/symfony/issues) and
[send Pull Requests](https://github.com/symfony/symfony/pulls)
in the [main Symfony repository](https://github.com/symfony/symfony)
+
+[1]: https://symfony.com/backers
+[2]: https://zeturf.com
+[3]: https://symfony.com/sponsor
diff --git a/src/Symfony/Contracts/Service/ServiceSubscriberTrait.php b/src/Symfony/Contracts/Service/ServiceSubscriberTrait.php
index 98e2008926c4d..e8c9b17042bdb 100644
--- a/src/Symfony/Contracts/Service/ServiceSubscriberTrait.php
+++ b/src/Symfony/Contracts/Service/ServiceSubscriberTrait.php
@@ -31,12 +31,6 @@ trait ServiceSubscriberTrait
*/
public static function getSubscribedServices(): array
{
- static $services;
-
- if (null !== $services) {
- return $services;
- }
-
$services = method_exists(get_parent_class(self::class) ?: '', __FUNCTION__) ? parent::getSubscribedServices() : [];
foreach ((new \ReflectionClass(self::class))->getMethods() as $method) {