From e31aeebbba2bae807522e7247d1ec6529228d2e9 Mon Sep 17 00:00:00 2001 From: Mathias Arlaud Date: Thu, 23 Nov 2023 08:57:08 +0100 Subject: [PATCH 01/79] [PropertyAccessor] Fix unexpected collection when generics --- .../Tests/Extractor/PhpDocExtractorTest.php | 5 +++++ .../Tests/Extractor/PhpStanExtractorTest.php | 7 ++++++- .../Tests/Extractor/ReflectionExtractorTest.php | 3 +++ .../Component/PropertyInfo/Tests/Fixtures/Dummy.php | 5 +++++ .../PropertyInfo/Tests/Fixtures/DummyUnionType.php | 2 +- .../PropertyInfo/Util/PhpDocTypeHelper.php | 13 ++++++++++--- .../PropertyInfo/Util/PhpStanTypeHelper.php | 9 ++++++++- .../Tests/DeserializeNestedArrayOfObjectsTest.php | 2 +- 8 files changed, 39 insertions(+), 7 deletions(-) diff --git a/src/Symfony/Component/PropertyInfo/Tests/Extractor/PhpDocExtractorTest.php b/src/Symfony/Component/PropertyInfo/Tests/Extractor/PhpDocExtractorTest.php index f71664d5a3547..57869e40819bb 100644 --- a/src/Symfony/Component/PropertyInfo/Tests/Extractor/PhpDocExtractorTest.php +++ b/src/Symfony/Component/PropertyInfo/Tests/Extractor/PhpDocExtractorTest.php @@ -428,6 +428,11 @@ public function testUnknownPseudoType() $this->assertEquals([new Type(Type::BUILTIN_TYPE_OBJECT, false, 'scalar')], $this->extractor->getTypes(PseudoTypeDummy::class, 'unknownPseudoType')); } + public function testGenericInterface() + { + $this->assertNull($this->extractor->getTypes(Dummy::class, 'genericInterface')); + } + protected static function isPhpDocumentorV5() { if (class_exists(InvalidTag::class)) { diff --git a/src/Symfony/Component/PropertyInfo/Tests/Extractor/PhpStanExtractorTest.php b/src/Symfony/Component/PropertyInfo/Tests/Extractor/PhpStanExtractorTest.php index d8fa9b9192c51..51e28ed4b8373 100644 --- a/src/Symfony/Component/PropertyInfo/Tests/Extractor/PhpStanExtractorTest.php +++ b/src/Symfony/Component/PropertyInfo/Tests/Extractor/PhpStanExtractorTest.php @@ -388,7 +388,7 @@ public static function unionTypesProvider(): array ['b', [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, [new Type(Type::BUILTIN_TYPE_INT)], [new Type(Type::BUILTIN_TYPE_STRING), new Type(Type::BUILTIN_TYPE_INT)])]], ['c', [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, [], [new Type(Type::BUILTIN_TYPE_STRING), new Type(Type::BUILTIN_TYPE_INT)])]], ['d', [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, [new Type(Type::BUILTIN_TYPE_STRING), new Type(Type::BUILTIN_TYPE_INT)], [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, [], [new Type(Type::BUILTIN_TYPE_STRING)])])]], - ['e', [new Type(Type::BUILTIN_TYPE_OBJECT, true, Dummy::class, true, [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, [], [new Type(Type::BUILTIN_TYPE_STRING)])], [new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, [new Type(Type::BUILTIN_TYPE_INT)], [new Type(Type::BUILTIN_TYPE_STRING, false, null, true, [], [new Type(Type::BUILTIN_TYPE_OBJECT, false, DefaultValue::class)])])]), new Type(Type::BUILTIN_TYPE_OBJECT, false, ParentDummy::class)]], + ['e', [new Type(Type::BUILTIN_TYPE_OBJECT, true, Dummy::class, false, [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, [], [new Type(Type::BUILTIN_TYPE_STRING)])], [new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, [new Type(Type::BUILTIN_TYPE_INT)], [new Type(Type::BUILTIN_TYPE_OBJECT, false, \Traversable::class, true, [], [new Type(Type::BUILTIN_TYPE_OBJECT, false, DefaultValue::class)])])]), new Type(Type::BUILTIN_TYPE_OBJECT, false, ParentDummy::class)]], ['f', null], ['g', [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, [], [new Type(Type::BUILTIN_TYPE_STRING), new Type(Type::BUILTIN_TYPE_INT)])]], ]; @@ -427,6 +427,11 @@ public static function intRangeTypeProvider(): array ['c', [new Type(Type::BUILTIN_TYPE_INT)]], ]; } + + public function testGenericInterface() + { + $this->assertNull($this->extractor->getTypes(Dummy::class, 'genericInterface')); + } } class PhpStanOmittedParamTagTypeDocBlock diff --git a/src/Symfony/Component/PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php b/src/Symfony/Component/PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php index d3d57514a02c9..06e8bc53f87b5 100644 --- a/src/Symfony/Component/PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php +++ b/src/Symfony/Component/PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php @@ -73,6 +73,7 @@ public function testGetProperties() 'arrayOfMixed', 'listOfStrings', 'parentAnnotation', + 'genericInterface', 'foo', 'foo2', 'foo3', @@ -137,6 +138,7 @@ public function testGetPropertiesWithCustomPrefixes() 'arrayOfMixed', 'listOfStrings', 'parentAnnotation', + 'genericInterface', 'foo', 'foo2', 'foo3', @@ -190,6 +192,7 @@ public function testGetPropertiesWithNoPrefixes() 'arrayOfMixed', 'listOfStrings', 'parentAnnotation', + 'genericInterface', 'foo', 'foo2', 'foo3', diff --git a/src/Symfony/Component/PropertyInfo/Tests/Fixtures/Dummy.php b/src/Symfony/Component/PropertyInfo/Tests/Fixtures/Dummy.php index 06c0783a3c8f8..cf0c791784695 100644 --- a/src/Symfony/Component/PropertyInfo/Tests/Fixtures/Dummy.php +++ b/src/Symfony/Component/PropertyInfo/Tests/Fixtures/Dummy.php @@ -165,6 +165,11 @@ class Dummy extends ParentDummy */ public $parentAnnotation; + /** + * @var \BackedEnum + */ + public $genericInterface; + public static function getStatic() { } diff --git a/src/Symfony/Component/PropertyInfo/Tests/Fixtures/DummyUnionType.php b/src/Symfony/Component/PropertyInfo/Tests/Fixtures/DummyUnionType.php index 86ddb8a1650eb..7e2e1aa3ec8f7 100644 --- a/src/Symfony/Component/PropertyInfo/Tests/Fixtures/DummyUnionType.php +++ b/src/Symfony/Component/PropertyInfo/Tests/Fixtures/DummyUnionType.php @@ -40,7 +40,7 @@ class DummyUnionType public $d; /** - * @var (Dummy, (int | (string)[])> | ParentDummy | null) + * @var (Dummy, (int | (\Traversable)[])> | ParentDummy | null) */ public $e; diff --git a/src/Symfony/Component/PropertyInfo/Util/PhpDocTypeHelper.php b/src/Symfony/Component/PropertyInfo/Util/PhpDocTypeHelper.php index 6020be0b80a3c..dc8a941b5e7fc 100644 --- a/src/Symfony/Component/PropertyInfo/Util/PhpDocTypeHelper.php +++ b/src/Symfony/Component/PropertyInfo/Util/PhpDocTypeHelper.php @@ -102,9 +102,9 @@ public function getTypes(DocType $varType): array /** * Creates a {@see Type} from a PHPDoc type. */ - private function createType(DocType $type, bool $nullable, ?string $docType = null): ?Type + private function createType(DocType $type, bool $nullable): ?Type { - $docType = $docType ?? (string) $type; + $docType = (string) $type; if ($type instanceof Collection) { $fqsen = $type->getFqsen(); @@ -115,10 +115,17 @@ private function createType(DocType $type, bool $nullable, ?string $docType = nu [$phpType, $class] = $this->getPhpTypeAndClass((string) $fqsen); + $collection = \is_a($class, \Traversable::class, true) || \is_a($class, \ArrayAccess::class, true); + + // it's safer to fall back to other extractors if the generic type is too abstract + if (!$collection && !class_exists($class)) { + return null; + } + $keys = $this->getTypes($type->getKeyType()); $values = $this->getTypes($type->getValueType()); - return new Type($phpType, $nullable, $class, true, $keys, $values); + return new Type($phpType, $nullable, $class, $collection, $keys, $values); } // Cannot guess diff --git a/src/Symfony/Component/PropertyInfo/Util/PhpStanTypeHelper.php b/src/Symfony/Component/PropertyInfo/Util/PhpStanTypeHelper.php index 256122af759b7..6171530abadc7 100644 --- a/src/Symfony/Component/PropertyInfo/Util/PhpStanTypeHelper.php +++ b/src/Symfony/Component/PropertyInfo/Util/PhpStanTypeHelper.php @@ -121,6 +121,13 @@ private function extractTypes(TypeNode $node, NameScope $nameScope): array return [$mainType]; } + $collection = $mainType->isCollection() || \in_array($mainType->getClassName(), [\Traversable::class, \Iterator::class, \IteratorAggregate::class, \ArrayAccess::class, \Generator::class], true); + + // it's safer to fall back to other extractors if the generic type is too abstract + if (!$collection && !class_exists($mainType->getClassName())) { + return []; + } + $collectionKeyTypes = $mainType->getCollectionKeyTypes(); $collectionKeyValues = []; if (1 === \count($node->genericTypes)) { @@ -136,7 +143,7 @@ private function extractTypes(TypeNode $node, NameScope $nameScope): array } } - return [new Type($mainType->getBuiltinType(), $mainType->isNullable(), $mainType->getClassName(), true, $collectionKeyTypes, $collectionKeyValues)]; + return [new Type($mainType->getBuiltinType(), $mainType->isNullable(), $mainType->getClassName(), $collection, $collectionKeyTypes, $collectionKeyValues)]; } if ($node instanceof ArrayShapeNode) { return [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true)]; diff --git a/src/Symfony/Component/Serializer/Tests/DeserializeNestedArrayOfObjectsTest.php b/src/Symfony/Component/Serializer/Tests/DeserializeNestedArrayOfObjectsTest.php index 8da1b471bd567..57f2b568ef44e 100644 --- a/src/Symfony/Component/Serializer/Tests/DeserializeNestedArrayOfObjectsTest.php +++ b/src/Symfony/Component/Serializer/Tests/DeserializeNestedArrayOfObjectsTest.php @@ -156,7 +156,7 @@ class ZooWithKeyTypes public $animalsString = []; /** @var array */ public $animalsUnion = []; - /** @var \stdClass */ + /** @var \Traversable */ public $animalsGenerics = []; } From e10aa0ea934f05d5b792681d17a89425c7a3bf41 Mon Sep 17 00:00:00 2001 From: "Jonathan H. Wage" Date: Tue, 5 Mar 2024 14:09:36 -0600 Subject: [PATCH 02/79] [Messenger] [Amqp] Handle AMQPConnectionException when publishing a message. --- .../Amqp/Tests/Transport/ConnectionTest.php | 67 +++++++++++++++++++ .../Bridge/Amqp/Transport/Connection.php | 56 +++++++++++----- 2 files changed, 108 insertions(+), 15 deletions(-) diff --git a/src/Symfony/Component/Messenger/Bridge/Amqp/Tests/Transport/ConnectionTest.php b/src/Symfony/Component/Messenger/Bridge/Amqp/Tests/Transport/ConnectionTest.php index 9de6fa8ca0919..322bf6f4df84b 100644 --- a/src/Symfony/Component/Messenger/Bridge/Amqp/Tests/Transport/ConnectionTest.php +++ b/src/Symfony/Component/Messenger/Bridge/Amqp/Tests/Transport/ConnectionTest.php @@ -813,6 +813,73 @@ public function testItCanBeConstructedWithTLSOptionsAndNonTLSDsn() ); } + public function testItCanRetryPublishWhenAMQPConnectionExceptionIsThrown() + { + $factory = new TestAmqpFactory( + $amqpConnection = $this->createMock(\AMQPConnection::class), + $amqpChannel = $this->createMock(\AMQPChannel::class), + $amqpQueue = $this->createMock(\AMQPQueue::class), + $amqpExchange = $this->createMock(\AMQPExchange::class) + ); + + $amqpExchange->expects($this->exactly(2)) + ->method('publish') + ->willReturnOnConsecutiveCalls( + $this->throwException(new \AMQPConnectionException('a socket error occurred')), + null + ); + + $connection = Connection::fromDsn('amqp://localhost', [], $factory); + $connection->publish('body'); + } + + public function testItCanRetryPublishWithDelayWhenAMQPConnectionExceptionIsThrown() + { + $factory = new TestAmqpFactory( + $amqpConnection = $this->createMock(\AMQPConnection::class), + $amqpChannel = $this->createMock(\AMQPChannel::class), + $amqpQueue = $this->createMock(\AMQPQueue::class), + $amqpExchange = $this->createMock(\AMQPExchange::class) + ); + + $amqpExchange->expects($this->exactly(2)) + ->method('publish') + ->willReturnOnConsecutiveCalls( + $this->throwException(new \AMQPConnectionException('a socket error occurred')), + null + ); + + $connection = Connection::fromDsn('amqp://localhost', [], $factory); + $connection->publish('body', [], 5000); + } + + public function testItWillRetryMaxThreeTimesWhenAMQPConnectionExceptionIsThrown() + { + $factory = new TestAmqpFactory( + $amqpConnection = $this->createMock(\AMQPConnection::class), + $amqpChannel = $this->createMock(\AMQPChannel::class), + $amqpQueue = $this->createMock(\AMQPQueue::class), + $amqpExchange = $this->createMock(\AMQPExchange::class) + ); + + $exception = new \AMQPConnectionException('a socket error occurred'); + + $amqpExchange->expects($this->exactly(4)) + ->method('publish') + ->willReturnOnConsecutiveCalls( + $this->throwException($exception), + $this->throwException($exception), + $this->throwException($exception), + $this->throwException($exception) + ); + + self::expectException(get_class($exception)); + self::expectExceptionMessage($exception->getMessage()); + + $connection = Connection::fromDsn('amqp://localhost', [], $factory); + $connection->publish('body'); + } + private function createDelayOrRetryConnection(\AMQPExchange $delayExchange, string $deadLetterExchangeName, string $delayQueueName): Connection { $amqpConnection = $this->createMock(\AMQPConnection::class); diff --git a/src/Symfony/Component/Messenger/Bridge/Amqp/Transport/Connection.php b/src/Symfony/Component/Messenger/Bridge/Amqp/Transport/Connection.php index 3ea7784d862fd..8689b8ee306cc 100644 --- a/src/Symfony/Component/Messenger/Bridge/Amqp/Transport/Connection.php +++ b/src/Symfony/Component/Messenger/Bridge/Amqp/Transport/Connection.php @@ -306,19 +306,21 @@ public function publish(string $body, array $headers = [], int $delayInMs = 0, ? $this->setupExchangeAndQueues(); // also setup normal exchange for delayed messages so delay queue can DLX messages to it } - if (0 !== $delayInMs) { - $this->publishWithDelay($body, $headers, $delayInMs, $amqpStamp); + $this->withConnectionExceptionRetry(function () use ($body, $headers, $delayInMs, $amqpStamp) { + if (0 !== $delayInMs) { + $this->publishWithDelay($body, $headers, $delayInMs, $amqpStamp); - return; - } + return; + } - $this->publishOnExchange( - $this->exchange(), - $body, - $this->getRoutingKeyForMessage($amqpStamp), - $headers, - $amqpStamp - ); + $this->publishOnExchange( + $this->exchange(), + $body, + $this->getRoutingKeyForMessage($amqpStamp), + $headers, + $amqpStamp + ); + }); } /** @@ -570,13 +572,18 @@ public function exchange(): \AMQPExchange private function clearWhenDisconnected(): void { if (!$this->channel()->isConnected()) { - $this->amqpChannel = null; - $this->amqpQueues = []; - $this->amqpExchange = null; - $this->amqpDelayExchange = null; + $this->clear(); } } + private function clear(): void + { + $this->amqpChannel = null; + $this->amqpQueues = []; + $this->amqpExchange = null; + $this->amqpDelayExchange = null; + } + private function getDefaultPublishRoutingKey(): ?string { return $this->exchangeOptions['default_publish_routing_key'] ?? null; @@ -593,6 +600,25 @@ private function getRoutingKeyForMessage(?AmqpStamp $amqpStamp): ?string { return (null !== $amqpStamp ? $amqpStamp->getRoutingKey() : null) ?? $this->getDefaultPublishRoutingKey(); } + + private function withConnectionExceptionRetry(callable $callable): void + { + $maxRetries = 3; + $retries = 0; + + retry: + try { + $callable(); + } catch (\AMQPConnectionException $e) { + if (++$retries <= $maxRetries) { + $this->clear(); + + goto retry; + } + + throw $e; + } + } } if (!class_exists(\Symfony\Component\Messenger\Transport\AmqpExt\Connection::class, false)) { From 87b4904ba7945beacf60b07b09cdbc64e7dbacc1 Mon Sep 17 00:00:00 2001 From: Michael Hirschler Date: Tue, 4 Jun 2024 09:06:48 +0200 Subject: [PATCH 03/79] add space in error message --- .../Bundle/SecurityBundle/Security/FirewallAwareTrait.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Bundle/SecurityBundle/Security/FirewallAwareTrait.php b/src/Symfony/Bundle/SecurityBundle/Security/FirewallAwareTrait.php index d79d0b7a1df53..a04101626c916 100644 --- a/src/Symfony/Bundle/SecurityBundle/Security/FirewallAwareTrait.php +++ b/src/Symfony/Bundle/SecurityBundle/Security/FirewallAwareTrait.php @@ -41,7 +41,7 @@ private function getForFirewall(): object if (!$this->locator->has($firewallName)) { $message = 'No '.$serviceIdentifier.' found for this firewall.'; if (\defined(static::class.'::FIREWALL_OPTION')) { - $message .= sprintf('Did you forget to add a "'.static::FIREWALL_OPTION.'" key under your "%s" firewall?', $firewallName); + $message .= sprintf(' Did you forget to add a "'.static::FIREWALL_OPTION.'" key under your "%s" firewall?', $firewallName); } throw new \LogicException($message); From fb64f33b4c7279c30c958dc58220b1ce82516645 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Tue, 4 Jun 2024 09:34:02 +0200 Subject: [PATCH 04/79] Bump Symfony version to 7.1.2 --- src/Symfony/Component/HttpKernel/Kernel.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index 0d96519f5dd0d..693a13da96861 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -73,12 +73,12 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl */ private static array $freshCache = []; - public const VERSION = '7.1.1'; - public const VERSION_ID = 70101; + public const VERSION = '7.1.2-DEV'; + public const VERSION_ID = 70102; public const MAJOR_VERSION = 7; public const MINOR_VERSION = 1; - public const RELEASE_VERSION = 1; - public const EXTRA_VERSION = ''; + public const RELEASE_VERSION = 2; + public const EXTRA_VERSION = 'DEV'; public const END_OF_MAINTENANCE = '01/2025'; public const END_OF_LIFE = '01/2025'; From 10132c921b4d8e353d8a3f402744a7e5f3f4bb2a Mon Sep 17 00:00:00 2001 From: Antoine M Date: Tue, 4 Jun 2024 23:40:07 +0200 Subject: [PATCH 05/79] chore: upgrade class doc --- src/Symfony/Component/HttpKernel/Event/KernelEvent.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/HttpKernel/Event/KernelEvent.php b/src/Symfony/Component/HttpKernel/Event/KernelEvent.php index d9d425e114b93..87933187a32c7 100644 --- a/src/Symfony/Component/HttpKernel/Event/KernelEvent.php +++ b/src/Symfony/Component/HttpKernel/Event/KernelEvent.php @@ -16,7 +16,7 @@ use Symfony\Contracts\EventDispatcher\Event; /** - * Base class for events thrown in the HttpKernel component. + * Base class for events dispatched in the HttpKernel component. * * @author Bernhard Schussek */ From 62c2d8d250fd29523649e5ce18987ab792fe40ad Mon Sep 17 00:00:00 2001 From: "hubert.lenoir" Date: Wed, 5 Jun 2024 09:54:36 +0200 Subject: [PATCH 06/79] [AssetMapper] fix npm version constraint conversion --- .../ImportMap/ImportMapVersionChecker.php | 2 +- .../ImportMap/ImportMapVersionCheckerTest.php | 30 +++++++++++++++---- 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/src/Symfony/Component/AssetMapper/ImportMap/ImportMapVersionChecker.php b/src/Symfony/Component/AssetMapper/ImportMap/ImportMapVersionChecker.php index b0af5736eb821..6a2cf579956bd 100644 --- a/src/Symfony/Component/AssetMapper/ImportMap/ImportMapVersionChecker.php +++ b/src/Symfony/Component/AssetMapper/ImportMap/ImportMapVersionChecker.php @@ -137,7 +137,7 @@ public static function convertNpmConstraint(string $versionConstraint): ?string if (str_contains($segment, '-') && !preg_match('/-(alpha|beta|rc)\./', $segment)) { // This is a range [$start, $end] = explode('-', $segment); - $processedSegments[] = '>='.self::cleanVersionSegment(trim($start)).' <='.self::cleanVersionSegment(trim($end)); + $processedSegments[] = self::cleanVersionSegment(trim($start)).' - '.self::cleanVersionSegment(trim($end)); } elseif (preg_match('/^~(\d+\.\d+)$/', $segment, $matches)) { // Handle the tilde when only major.minor specified $baseVersion = $matches[1]; diff --git a/src/Symfony/Component/AssetMapper/Tests/ImportMap/ImportMapVersionCheckerTest.php b/src/Symfony/Component/AssetMapper/Tests/ImportMap/ImportMapVersionCheckerTest.php index 43346d3de33aa..2d6582c1d6cc4 100644 --- a/src/Symfony/Component/AssetMapper/Tests/ImportMap/ImportMapVersionCheckerTest.php +++ b/src/Symfony/Component/AssetMapper/Tests/ImportMap/ImportMapVersionCheckerTest.php @@ -261,6 +261,26 @@ public static function getCheckVersionsTests() new PackageVersionProblem('foo', 'bar', 'some/repo', '1.5.0'), ], ]; + + yield 'single with range constraint but no problem' => [ + [ + self::createRemoteEntry('foo', version: '1.0'), + self::createRemoteEntry('bar', version: '2.0.3'), + ], + [ + 'foo' => ['bar'], + 'bar' => [], + ], + [ + [ + 'url' => '/foo/1.0', + 'response' => [ + 'dependencies' => ['bar' => '1.11 - 2'], + ], + ], + ], + [], + ]; } /** @@ -297,22 +317,22 @@ public static function getNpmSpecificVersionConstraints() // Hyphen Ranges yield 'hyphen range simple' => [ '1.0.0 - 2.0.0', - '>=1.0.0 <=2.0.0', + '1.0.0 - 2.0.0', ]; yield 'hyphen range with v prefix' => [ 'v1.0.0 - 2.0.0', - '>=1.0.0 <=2.0.0', + '1.0.0 - 2.0.0', ]; yield 'hyphen range without patch' => [ '1.0 - 2.0', - '>=1.0 <=2.0', + '1.0 - 2.0', ]; yield 'hyphen range with no spaces' => [ '1.0-v2.0', - '>=1.0 <=2.0', + '1.0 - 2.0', ]; // .x Wildcards @@ -386,7 +406,7 @@ public static function getNpmSpecificVersionConstraints() yield 'multiple constraints with space and or operator' => [ '1.2.7 || 1.2.9- v2.0.0', - '1.2.7 || >=1.2.9 <=2.0.0', + '1.2.7 || 1.2.9 - 2.0.0', ]; yield 'tilde constraint with patch version no change' => [ From e0a65879eac305908f9c7c5e56094dc2e8b0fabd Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Wed, 5 Jun 2024 09:30:29 +0200 Subject: [PATCH 07/79] avoid calling undefined built-in is_*() functions --- .../Normalizer/AbstractObjectNormalizer.php | 23 +++++++++++++++---- .../AbstractObjectNormalizerTest.php | 22 ++++++++++++++---- 2 files changed, 36 insertions(+), 9 deletions(-) diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php index df3d693f21fc9..27c383177c09a 100644 --- a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php @@ -773,11 +773,24 @@ private function validateAndDenormalize(Type $type, string $currentClass, string return (float) $data; } - if ((TypeIdentifier::FALSE === $typeIdentifier && false === $data) || (TypeIdentifier::TRUE === $typeIdentifier && true === $data)) { - return $data; - } - - if (('is_'.$typeIdentifier->value)($data)) { + $dataMatchesExpectedType = match ($typeIdentifier) { + TypeIdentifier::ARRAY => \is_array($data), + TypeIdentifier::BOOL => \is_bool($data), + TypeIdentifier::CALLABLE => \is_callable($data), + TypeIdentifier::FALSE => false === $data, + TypeIdentifier::FLOAT => \is_float($data), + TypeIdentifier::INT => \is_int($data), + TypeIdentifier::ITERABLE => is_iterable($data), + TypeIdentifier::MIXED => true, + TypeIdentifier::NULL => null === $data, + TypeIdentifier::OBJECT => \is_object($data), + TypeIdentifier::RESOURCE => \is_resource($data), + TypeIdentifier::STRING => \is_string($data), + TypeIdentifier::TRUE => true === $data, + default => false, + }; + + if ($dataMatchesExpectedType) { return $data; } } catch (NotNormalizableValueException|InvalidArgumentException $e) { diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php index f41c0fdf3956b..21ce5d7684c9d 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php @@ -1132,7 +1132,7 @@ public function testNormalizationWithMaxDepthOnStdclassObjectDoesNotThrowWarning public function testDenormalizeCollectionOfScalarTypesPropertyWithPhpDocExtractor() { - $normalizer = new AbstractObjectNormalizerWithMetadataAndPhpDocExtractor(); + $normalizer = new AbstractObjectNormalizerWithMetadataAndPropertyTypeExtractors(); $data = [ 'type' => 'foo', 'values' => [ @@ -1150,7 +1150,7 @@ public function testDenormalizeCollectionOfScalarTypesPropertyWithPhpDocExtracto public function testDenormalizeCollectionOfUnionTypesPropertyWithPhpDocExtractor() { - $normalizer = new AbstractObjectNormalizerWithMetadataAndPhpDocExtractor(); + $normalizer = new AbstractObjectNormalizerWithMetadataAndPropertyTypeExtractors(); $data = [ 'values1' => [ 'foo' => 'foo', @@ -1166,6 +1166,15 @@ public function testDenormalizeCollectionOfUnionTypesPropertyWithPhpDocExtractor $this->assertEquals($expected, $normalizer->denormalize($data, UnionCollectionDocBlockDummy::class)); } + + public function testDenormalizeMixedProperty() + { + $normalizer = new AbstractObjectNormalizerWithMetadataAndPropertyTypeExtractors(); + $expected = new MixedPropertyDummy(); + $expected->foo = 'bar'; + + $this->assertEquals($expected, $normalizer->denormalize(['foo' => 'bar'], MixedPropertyDummy::class)); + } } class AbstractObjectNormalizerDummy extends AbstractObjectNormalizer @@ -1268,6 +1277,11 @@ class SnakeCaseNestedDummy public $fooBar; } +class MixedPropertyDummy +{ + public mixed $foo; +} + #[DiscriminatorMap(typeProperty: 'type', mapping: [ 'first' => FirstNestedDummyWithConstructorAndDiscriminator::class, 'second' => SecondNestedDummyWithConstructorAndDiscriminator::class, @@ -1612,11 +1626,11 @@ public function __construct( public array $values2; } -class AbstractObjectNormalizerWithMetadataAndPhpDocExtractor extends AbstractObjectNormalizer +class AbstractObjectNormalizerWithMetadataAndPropertyTypeExtractors extends AbstractObjectNormalizer { public function __construct() { - parent::__construct(new ClassMetadataFactory(new AttributeLoader()), null, new PropertyInfoExtractor([], [new PhpDocExtractor()])); + parent::__construct(new ClassMetadataFactory(new AttributeLoader()), null, new PropertyInfoExtractor([], [new PhpDocExtractor(), new ReflectionExtractor()])); } protected function extractAttributes(object $object, ?string $format = null, array $context = []): array From d35d4a337be7f838520d89abe80788d21e43b35e Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Fri, 7 Jun 2024 09:46:25 +0200 Subject: [PATCH 08/79] properly handle invalid data for false/true types --- .../Normalizer/AbstractObjectNormalizer.php | 32 +++++++++-- .../AbstractObjectNormalizerTest.php | 57 +++++++++++++++++++ 2 files changed, 84 insertions(+), 5 deletions(-) diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php index 5a51bde39b7ab..63b519b701305 100644 --- a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php @@ -621,12 +621,34 @@ private function validateAndDenormalize(array $types, string $currentClass, stri return (float) $data; } - if (Type::BUILTIN_TYPE_FALSE === $builtinType && false === $data) { - return $data; - } + switch ($builtinType) { + case Type::BUILTIN_TYPE_ARRAY: + case Type::BUILTIN_TYPE_BOOL: + case Type::BUILTIN_TYPE_CALLABLE: + case Type::BUILTIN_TYPE_FLOAT: + case Type::BUILTIN_TYPE_INT: + case Type::BUILTIN_TYPE_ITERABLE: + case Type::BUILTIN_TYPE_NULL: + case Type::BUILTIN_TYPE_OBJECT: + case Type::BUILTIN_TYPE_RESOURCE: + case Type::BUILTIN_TYPE_STRING: + if (('is_'.$builtinType)($data)) { + return $data; + } + + break; + case Type::BUILTIN_TYPE_FALSE: + if (false === $data) { + return $data; + } + + break; + case Type::BUILTIN_TYPE_TRUE: + if (true === $data) { + return $data; + } - if (('is_'.$builtinType)($data)) { - return $data; + break; } } catch (NotNormalizableValueException $e) { if (!$isUnionType && !$isNullable) { diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php index a6477e97ad331..afaf57ea06b52 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php @@ -687,6 +687,26 @@ protected function setAttributeValue(object $object, string $attribute, $value, $this->assertSame('scalar', $normalizer->denormalize('scalar', XmlScalarDummy::class, 'xml')->value); } + + /** + * @dataProvider provideBooleanTypesData + */ + public function testDenormalizeBooleanTypesWithNotMatchingData(array $data, string $type) + { + $normalizer = new AbstractObjectNormalizerWithMetadataAndPropertyTypeExtractors(); + + $this->expectException(NotNormalizableValueException::class); + + $normalizer->denormalize($data, $type); + } + + public function provideBooleanTypesData() + { + return [ + [['foo' => true], FalsePropertyDummy::class], + [['foo' => false], TruePropertyDummy::class], + ]; + } } class AbstractObjectNormalizerDummy extends AbstractObjectNormalizer @@ -816,6 +836,18 @@ class XmlScalarDummy public $value; } +class FalsePropertyDummy +{ + /** @var false */ + public $foo; +} + +class TruePropertyDummy +{ + /** @var true */ + public $foo; +} + class SerializerCollectionDummy implements SerializerInterface, DenormalizerInterface { private $normalizers; @@ -936,3 +968,28 @@ public function __sleep(): array throw new \Error('not serializable'); } } + +class AbstractObjectNormalizerWithMetadataAndPropertyTypeExtractors extends AbstractObjectNormalizer +{ + public function __construct() + { + parent::__construct(new ClassMetadataFactory(new AnnotationLoader()), null, new PropertyInfoExtractor([], [new PhpDocExtractor(), new ReflectionExtractor()])); + } + + protected function extractAttributes(object $object, ?string $format = null, array $context = []): array + { + return []; + } + + protected function getAttributeValue(object $object, string $attribute, ?string $format = null, array $context = []) + { + return null; + } + + protected function setAttributeValue(object $object, string $attribute, $value, ?string $format = null, array $context = []): void + { + if (property_exists($object, $attribute)) { + $object->$attribute = $value; + } + } +} From be348f284701fb110b1ccb94744a47458ca91271 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Fri, 7 Jun 2024 13:36:04 +0200 Subject: [PATCH 09/79] change notifier type for brevo from chatter to texter --- .../FrameworkBundle/Resources/config/notifier_transports.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/notifier_transports.php b/src/Symfony/Bundle/FrameworkBundle/Resources/config/notifier_transports.php index df9be94ed5e32..5ddc9ae240ea1 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/notifier_transports.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/notifier_transports.php @@ -27,7 +27,6 @@ $chatterFactories = [ 'bluesky' => Bridge\Bluesky\BlueskyTransportFactory::class, - 'brevo' => Bridge\Brevo\BrevoTransportFactory::class, 'chatwork' => Bridge\Chatwork\ChatworkTransportFactory::class, 'discord' => Bridge\Discord\DiscordTransportFactory::class, 'fake-chat' => Bridge\FakeChat\FakeChatTransportFactory::class, @@ -59,6 +58,7 @@ $texterFactories = [ 'all-my-sms' => Bridge\AllMySms\AllMySmsTransportFactory::class, 'bandwidth' => Bridge\Bandwidth\BandwidthTransportFactory::class, + 'brevo' => Bridge\Brevo\BrevoTransportFactory::class, 'click-send' => Bridge\ClickSend\ClickSendTransportFactory::class, 'clickatell' => Bridge\Clickatell\ClickatellTransportFactory::class, 'contact-everyone' => Bridge\ContactEveryone\ContactEveryoneTransportFactory::class, From 6fb83abcb3a0a89bcb2b0668a8414e7e52a2495a Mon Sep 17 00:00:00 2001 From: Mokhtar Tlili Date: Sat, 8 Jun 2024 16:25:46 +0200 Subject: [PATCH 10/79] fix cssColor HSLA test dataProvider --- .../Validator/Tests/Constraints/CssColorValidatorTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/Validator/Tests/Constraints/CssColorValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/CssColorValidatorTest.php index 5c7904a8001af..6c298f8236791 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/CssColorValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/CssColorValidatorTest.php @@ -396,7 +396,7 @@ public static function getInvalidHSL(): array } /** - * @dataProvider getInvalidHSL + * @dataProvider getInvalidHSLA */ public function testInvalidHSLA($cssColor) { From 0d441bf62aa318419ed951c60bd0e32f6339b1c2 Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Tue, 7 May 2024 11:00:46 +0200 Subject: [PATCH 11/79] [Messenger] Comply with Amazon SQS requirements for message body --- .../Tests/Transport/AmazonSqsSenderTest.php | 15 +++++++++++++++ .../AmazonSqs/Transport/AmazonSqsSender.php | 17 +++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/src/Symfony/Component/Messenger/Bridge/AmazonSqs/Tests/Transport/AmazonSqsSenderTest.php b/src/Symfony/Component/Messenger/Bridge/AmazonSqs/Tests/Transport/AmazonSqsSenderTest.php index 80840c859cb05..d11a5d8037b27 100644 --- a/src/Symfony/Component/Messenger/Bridge/AmazonSqs/Tests/Transport/AmazonSqsSenderTest.php +++ b/src/Symfony/Component/Messenger/Bridge/AmazonSqs/Tests/Transport/AmazonSqsSenderTest.php @@ -72,4 +72,19 @@ public function testSendWithAmazonSqsXrayTraceHeaderStamp() $sender = new AmazonSqsSender($connection, $serializer); $sender->send($envelope); } + + public function testSendEncodeBodyToRespectAmazonRequirements() + { + $envelope = new Envelope(new DummyMessage('Oy')); + $encoded = ['body' => "\x7", 'headers' => ['type' => DummyMessage::class]]; + + $connection = $this->createMock(Connection::class); + $connection->expects($this->once())->method('send')->with(base64_encode($encoded['body']), $encoded['headers']); + + $serializer = $this->createMock(SerializerInterface::class); + $serializer->method('encode')->with($envelope)->willReturn($encoded); + + $sender = new AmazonSqsSender($connection, $serializer); + $sender->send($envelope); + } } diff --git a/src/Symfony/Component/Messenger/Bridge/AmazonSqs/Transport/AmazonSqsSender.php b/src/Symfony/Component/Messenger/Bridge/AmazonSqs/Transport/AmazonSqsSender.php index 1994313720e0d..b253c82e97e30 100644 --- a/src/Symfony/Component/Messenger/Bridge/AmazonSqs/Transport/AmazonSqsSender.php +++ b/src/Symfony/Component/Messenger/Bridge/AmazonSqs/Transport/AmazonSqsSender.php @@ -38,6 +38,7 @@ public function __construct(Connection $connection, SerializerInterface $seriali public function send(Envelope $envelope): Envelope { $encodedMessage = $this->serializer->encode($envelope); + $encodedMessage = $this->complyWithAmazonSqsRequirements($encodedMessage); /** @var DelayStamp|null $delayStamp */ $delayStamp = $envelope->last(DelayStamp::class); @@ -75,4 +76,20 @@ public function send(Envelope $envelope): Envelope return $envelope; } + + /** + * @see https://docs.aws.amazon.com/AWSSimpleQueueService/latest/APIReference/API_SendMessage.html + * + * @param array{body: string, headers?: array} $encodedMessage + * + * @return array{body: string, headers?: array} + */ + private function complyWithAmazonSqsRequirements(array $encodedMessage): array + { + if (preg_match('/[^\x20-\x{D7FF}\xA\xD\x9\x{E000}-\x{FFFD}\x{10000}-\x{10FFFF}]/u', $encodedMessage['body'])) { + $encodedMessage['body'] = base64_encode($encodedMessage['body']); + } + + return $encodedMessage; + } } From 8ef75c2c012210d7ed88be5953a074fc428b6608 Mon Sep 17 00:00:00 2001 From: seho-nl <65092701+seho-nl@users.noreply.github.com> Date: Tue, 28 May 2024 19:55:02 +0200 Subject: [PATCH 12/79] [Validator] [UniqueValidator] Use correct variable as parameter in (custom) error message --- .../Validator/Constraints/UniqueValidator.php | 2 +- .../Tests/Constraints/UniqueValidatorTest.php | 22 +++++++++---------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/Symfony/Component/Validator/Constraints/UniqueValidator.php b/src/Symfony/Component/Validator/Constraints/UniqueValidator.php index 2758a3faa11f6..95dc48c632186 100644 --- a/src/Symfony/Component/Validator/Constraints/UniqueValidator.php +++ b/src/Symfony/Component/Validator/Constraints/UniqueValidator.php @@ -45,7 +45,7 @@ public function validate($value, Constraint $constraint) if (\in_array($element, $collectionElements, true)) { $this->context->buildViolation($constraint->message) - ->setParameter('{{ value }}', $this->formatValue($value)) + ->setParameter('{{ value }}', $this->formatValue($element)) ->setCode(Unique::IS_NOT_UNIQUE) ->addViolation(); diff --git a/src/Symfony/Component/Validator/Tests/Constraints/UniqueValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/UniqueValidatorTest.php index 417050bd8e67d..de0b47280190a 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/UniqueValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/UniqueValidatorTest.php @@ -59,7 +59,7 @@ public static function getValidValues() /** * @dataProvider getInvalidValues */ - public function testInvalidValues($value) + public function testInvalidValues($value, $expectedMessageParam) { $constraint = new Unique([ 'message' => 'myMessage', @@ -67,7 +67,7 @@ public function testInvalidValues($value) $this->validator->validate($value, $constraint); $this->buildViolation('myMessage') - ->setParameter('{{ value }}', 'array') + ->setParameter('{{ value }}', $expectedMessageParam) ->setCode(Unique::IS_NOT_UNIQUE) ->assertRaised(); } @@ -77,12 +77,12 @@ public static function getInvalidValues() $object = new \stdClass(); return [ - yield 'not unique booleans' => [[true, true]], - yield 'not unique integers' => [[1, 2, 3, 3]], - yield 'not unique floats' => [[0.1, 0.2, 0.1]], - yield 'not unique string' => [['a', 'b', 'a']], - yield 'not unique arrays' => [[[1, 1], [2, 3], [1, 1]]], - yield 'not unique objects' => [[$object, $object]], + yield 'not unique booleans' => [[true, true], 'true'], + yield 'not unique integers' => [[1, 2, 3, 3], 3], + yield 'not unique floats' => [[0.1, 0.2, 0.1], 0.1], + yield 'not unique string' => [['a', 'b', 'a'], '"a"'], + yield 'not unique arrays' => [[[1, 1], [2, 3], [1, 1]], 'array'], + yield 'not unique objects' => [[$object, $object], 'object'], ]; } @@ -95,7 +95,7 @@ public function testInvalidValueNamed() $this->validator->validate([1, 2, 3, 3], $constraint); $this->buildViolation('myMessage') - ->setParameter('{{ value }}', 'array') + ->setParameter('{{ value }}', '3') ->setCode(Unique::IS_NOT_UNIQUE) ->assertRaised(); } @@ -176,7 +176,7 @@ public function testExpectsInvalidNonStrictComparison() ])); $this->buildViolation('myMessage') - ->setParameter('{{ value }}', 'array') + ->setParameter('{{ value }}', '1') ->setCode(Unique::IS_NOT_UNIQUE) ->assertRaised(); } @@ -206,7 +206,7 @@ public function testExpectsInvalidCaseInsensitiveComparison() ])); $this->buildViolation('myMessage') - ->setParameter('{{ value }}', 'array') + ->setParameter('{{ value }}', '"hello"') ->setCode(Unique::IS_NOT_UNIQUE) ->assertRaised(); } From 91325ea6dd75730874f0c4dce0f2f30ade55b193 Mon Sep 17 00:00:00 2001 From: Geordie Date: Wed, 17 Apr 2024 12:33:44 +0200 Subject: [PATCH 13/79] [String] Fix #54611 pluralization of -on ending words + singularization of -a ending foreign words --- .../Inflector/Tests/InflectorTest.php | 27 ++++++--- src/Symfony/Component/Inflector/composer.json | 2 +- .../String/Inflector/EnglishInflector.php | 59 +++++++++++-------- .../Tests/Inflector/EnglishInflectorTest.php | 28 ++++++--- 4 files changed, 76 insertions(+), 40 deletions(-) diff --git a/src/Symfony/Component/Inflector/Tests/InflectorTest.php b/src/Symfony/Component/Inflector/Tests/InflectorTest.php index 0702c717e3495..d637e3d72d1eb 100644 --- a/src/Symfony/Component/Inflector/Tests/InflectorTest.php +++ b/src/Symfony/Component/Inflector/Tests/InflectorTest.php @@ -37,7 +37,7 @@ public static function singularizeProvider() ['atlases', ['atlas', 'atlase', 'atlasis']], ['axes', ['ax', 'axe', 'axis']], ['babies', 'baby'], - ['bacteria', ['bacterion', 'bacterium']], + ['bacteria', 'bacterium'], ['bases', ['bas', 'base', 'basis']], ['batches', ['batch', 'batche']], ['beaux', 'beau'], @@ -48,6 +48,7 @@ public static function singularizeProvider() ['bureaux', 'bureau'], ['buses', ['bus', 'buse', 'busis']], ['bushes', ['bush', 'bushe']], + ['buttons', 'button'], ['calves', ['calf', 'calve', 'calff']], ['cars', 'car'], ['cassettes', ['cassett', 'cassette']], @@ -58,10 +59,12 @@ public static function singularizeProvider() ['circuses', ['circus', 'circuse', 'circusis']], ['cliffs', 'cliff'], ['committee', 'committee'], + ['corpora', 'corpus'], + ['coupons', 'coupon'], ['crises', ['cris', 'crise', 'crisis']], - ['criteria', ['criterion', 'criterium']], + ['criteria', 'criterion'], ['cups', 'cup'], - ['coupons', 'coupon'], + ['curricula', 'curriculum'], ['data', 'data'], ['days', 'day'], ['discos', 'disco'], @@ -87,6 +90,7 @@ public static function singularizeProvider() ['funguses', ['fungus', 'funguse', 'fungusis']], ['garages', ['garag', 'garage']], ['geese', 'goose'], + ['genera', 'genus'], ['halves', ['half', 'halve', 'halff']], ['hats', 'hat'], ['heroes', ['hero', 'heroe']], @@ -107,6 +111,8 @@ public static function singularizeProvider() ['lives', 'life'], ['matrices', ['matrex', 'matrix', 'matrice']], ['matrixes', 'matrix'], + ['media', 'medium'], + ['memoranda', 'memorandum'], ['men', 'man'], ['mice', 'mouse'], ['moves', 'move'], @@ -120,7 +126,7 @@ public static function singularizeProvider() ['parties', 'party'], ['people', 'person'], ['persons', 'person'], - ['phenomena', ['phenomenon', 'phenomenum']], + ['phenomena', 'phenomenon'], ['photos', 'photo'], ['pianos', 'piano'], ['plateaux', 'plateau'], @@ -144,7 +150,7 @@ public static function singularizeProvider() ['spies', 'spy'], ['staves', ['staf', 'stave', 'staff']], ['stories', 'story'], - ['strata', ['straton', 'stratum']], + ['strata', 'stratum'], ['suitcases', ['suitcas', 'suitcase', 'suitcasis']], ['syllabi', 'syllabus'], ['tags', 'tag'], @@ -195,7 +201,9 @@ public static function pluralizeProvider() ['bureau', ['bureaus', 'bureaux']], ['bus', 'buses'], ['bush', 'bushes'], + ['button', 'buttons'], ['calf', ['calfs', 'calves']], + ['campus', 'campuses'], ['car', 'cars'], ['cassette', 'cassettes'], ['cave', 'caves'], @@ -205,10 +213,11 @@ public static function pluralizeProvider() ['circus', 'circuses'], ['cliff', 'cliffs'], ['committee', 'committees'], + ['coupon', 'coupons'], ['crisis', 'crises'], - ['criteria', 'criterion'], + ['criterion', 'criteria'], ['cup', 'cups'], - ['coupon', 'coupons'], + ['curriculum', 'curricula'], ['data', 'data'], ['day', 'days'], ['disco', 'discos'], @@ -232,10 +241,12 @@ public static function pluralizeProvider() ['half', ['halfs', 'halves']], ['hat', 'hats'], ['hero', 'heroes'], + ['hippocampus', 'hippocampi'], ['hippopotamus', 'hippopotami'], // hippopotamuses ['hoax', 'hoaxes'], ['hoof', ['hoofs', 'hooves']], ['house', 'houses'], + ['icon', 'icons'], ['index', ['indicies', 'indexes']], ['ion', 'ions'], ['iris', 'irises'], @@ -248,6 +259,8 @@ public static function pluralizeProvider() ['louse', 'lice'], ['man', 'men'], ['matrix', ['matricies', 'matrixes']], + ['medium', 'media'], + ['memorandum', 'memoranda'], ['mouse', 'mice'], ['move', 'moves'], ['movie', 'movies'], diff --git a/src/Symfony/Component/Inflector/composer.json b/src/Symfony/Component/Inflector/composer.json index 5b7280c1f42ce..6b46f7cb918b1 100644 --- a/src/Symfony/Component/Inflector/composer.json +++ b/src/Symfony/Component/Inflector/composer.json @@ -26,7 +26,7 @@ "php": ">=7.2.5", "symfony/deprecation-contracts": "^2.1|^3", "symfony/polyfill-php80": "^1.16", - "symfony/string": "^5.3.10|^6.0" + "symfony/string": "^5.4.41|^6.4.9" }, "autoload": { "psr-4": { "Symfony\\Component\\Inflector\\": "" }, diff --git a/src/Symfony/Component/String/Inflector/EnglishInflector.php b/src/Symfony/Component/String/Inflector/EnglishInflector.php index 4739f07c7be1b..e068fcbcd6d98 100644 --- a/src/Symfony/Component/String/Inflector/EnglishInflector.php +++ b/src/Symfony/Component/String/Inflector/EnglishInflector.php @@ -25,8 +25,32 @@ final class EnglishInflector implements InflectorInterface // Fourth entry: Whether the suffix may succeed a consonant // Fifth entry: singular suffix, normal - // bacteria (bacterium), criteria (criterion), phenomena (phenomenon) - ['a', 1, true, true, ['on', 'um']], + // bacteria (bacterium) + ['airetcab', 8, true, true, 'bacterium'], + + // corpora (corpus) + ['aroproc', 7, true, true, 'corpus'], + + // criteria (criterion) + ['airetirc', 8, true, true, 'criterion'], + + // curricula (curriculum) + ['alucirruc', 9, true, true, 'curriculum'], + + // genera (genus) + ['areneg', 6, true, true, 'genus'], + + // media (medium) + ['aidem', 5, true, true, 'medium'], + + // memoranda (memorandum) + ['adnaromem', 9, true, true, 'memorandum'], + + // phenomena (phenomenon) + ['anemonehp', 9, true, true, 'phenomenon'], + + // strata (stratum) + ['atarts', 6, true, true, 'stratum'], // nebulae (nebula) ['ea', 2, true, true, 'a'], @@ -141,7 +165,7 @@ final class EnglishInflector implements InflectorInterface // shoes (shoe) ['se', 2, true, true, ['', 'e']], - // status (status) + // status (status) ['sutats', 6, true, true, 'status'], // tags (tag) @@ -241,7 +265,7 @@ final class EnglishInflector implements InflectorInterface // albums (album) ['mubla', 5, true, true, 'albums'], - // bacteria (bacterium), criteria (criterion), phenomena (phenomenon) + // bacteria (bacterium), curricula (curriculum), media (medium), memoranda (memorandum), phenomena (phenomenon), strata (stratum) ['mu', 2, true, true, 'a'], // men (man), women (woman) @@ -250,20 +274,11 @@ final class EnglishInflector implements InflectorInterface // people (person) ['nosrep', 6, true, true, ['persons', 'people']], - // bacteria (bacterium), criteria (criterion), phenomena (phenomenon) - ['noi', 3, true, true, 'ions'], - - // coupon (coupons) - ['nop', 3, true, true, 'pons'], - - // seasons (season), treasons (treason), poisons (poison), lessons (lesson) - ['nos', 3, true, true, 'sons'], - - // icons (icon) - ['noc', 3, true, true, 'cons'], + // criteria (criterion) + ['noiretirc', 9, true, true, 'criteria'], - // bacteria (bacterium), criteria (criterion), phenomena (phenomenon) - ['no', 2, true, true, 'a'], + // phenomena (phenomenon) + ['nonemonehp', 10, true, true, 'phenomena'], // echoes (echo) ['ohce', 4, true, true, 'echoes'], @@ -404,9 +419,6 @@ final class EnglishInflector implements InflectorInterface 'erawdrah', ]; - /** - * {@inheritdoc} - */ public function singularize(string $plural): array { $pluralRev = strrev($plural); @@ -438,7 +450,7 @@ public function singularize(string $plural): array if ($j === $suffixLength) { // Is there any character preceding the suffix in the plural string? if ($j < $pluralLength) { - $nextIsVowel = false !== strpos('aeiou', $lowerPluralRev[$j]); + $nextIsVowel = str_contains('aeiou', $lowerPluralRev[$j]); if (!$map[2] && $nextIsVowel) { // suffix may not succeed a vowel but next char is one @@ -483,9 +495,6 @@ public function singularize(string $plural): array return [$plural]; } - /** - * {@inheritdoc} - */ public function pluralize(string $singular): array { $singularRev = strrev($singular); @@ -518,7 +527,7 @@ public function pluralize(string $singular): array if ($j === $suffixLength) { // Is there any character preceding the suffix in the plural string? if ($j < $singularLength) { - $nextIsVowel = false !== strpos('aeiou', $lowerSingularRev[$j]); + $nextIsVowel = str_contains('aeiou', $lowerSingularRev[$j]); if (!$map[2] && $nextIsVowel) { // suffix may not succeed a vowel but next char is one diff --git a/src/Symfony/Component/String/Tests/Inflector/EnglishInflectorTest.php b/src/Symfony/Component/String/Tests/Inflector/EnglishInflectorTest.php index 89f4966a40c1f..6744814b66603 100644 --- a/src/Symfony/Component/String/Tests/Inflector/EnglishInflectorTest.php +++ b/src/Symfony/Component/String/Tests/Inflector/EnglishInflectorTest.php @@ -35,7 +35,7 @@ public static function singularizeProvider() ['atlases', ['atlas', 'atlase', 'atlasis']], ['axes', ['ax', 'axe', 'axis']], ['babies', 'baby'], - ['bacteria', ['bacterion', 'bacterium']], + ['bacteria', 'bacterium'], ['bases', ['bas', 'base', 'basis']], ['batches', ['batch', 'batche']], ['beaux', 'beau'], @@ -46,6 +46,7 @@ public static function singularizeProvider() ['bureaux', 'bureau'], ['buses', ['bus', 'buse', 'busis']], ['bushes', ['bush', 'bushe']], + ['buttons', 'button'], ['calves', ['calf', 'calve', 'calff']], ['cars', 'car'], ['cassettes', ['cassett', 'cassette']], @@ -57,10 +58,12 @@ public static function singularizeProvider() ['cliffs', 'cliff'], ['codes', 'code'], ['committee', 'committee'], + ['corpora', 'corpus'], + ['coupons', 'coupon'], ['crises', ['cris', 'crise', 'crisis']], - ['criteria', ['criterion', 'criterium']], + ['criteria', 'criterion'], ['cups', 'cup'], - ['coupons', 'coupon'], + ['curricula', 'curriculum'], ['data', 'data'], ['days', 'day'], ['discos', 'disco'], @@ -86,6 +89,7 @@ public static function singularizeProvider() ['funguses', ['fungus', 'funguse', 'fungusis']], ['garages', ['garag', 'garage']], ['geese', 'goose'], + ['genera', 'genus'], ['halves', ['half', 'halve', 'halff']], ['hats', 'hat'], ['heroes', ['hero', 'heroe']], @@ -106,6 +110,8 @@ public static function singularizeProvider() ['lives', 'life'], ['matrices', ['matrex', 'matrix', 'matrice']], ['matrixes', 'matrix'], + ['media', 'medium'], + ['memoranda', 'memorandum'], ['men', 'man'], ['mice', 'mouse'], ['moves', 'move'], @@ -120,7 +126,7 @@ public static function singularizeProvider() ['parties', 'party'], ['people', 'person'], ['persons', 'person'], - ['phenomena', ['phenomenon', 'phenomenum']], + ['phenomena', 'phenomenon'], ['photos', 'photo'], ['pianos', 'piano'], ['plateaux', 'plateau'], @@ -146,7 +152,7 @@ public static function singularizeProvider() ['status', 'status'], ['statuses', 'status'], ['stories', 'story'], - ['strata', ['straton', 'stratum']], + ['strata', 'stratum'], ['suitcases', ['suitcas', 'suitcase', 'suitcasis']], ['syllabi', 'syllabus'], ['tags', 'tag'], @@ -200,7 +206,9 @@ public static function pluralizeProvider() ['bureau', ['bureaus', 'bureaux']], ['bus', 'buses'], ['bush', 'bushes'], + ['button', 'buttons'], ['calf', ['calfs', 'calves']], + ['campus', 'campuses'], ['car', 'cars'], ['cassette', 'cassettes'], ['cave', 'caves'], @@ -210,10 +218,11 @@ public static function pluralizeProvider() ['circus', 'circuses'], ['cliff', 'cliffs'], ['committee', 'committees'], + ['coupon', 'coupons'], ['crisis', 'crises'], - ['criteria', 'criterion'], + ['criterion', 'criteria'], ['cup', 'cups'], - ['coupon', 'coupons'], + ['curriculum', 'curricula'], ['data', 'data'], ['day', 'days'], ['disco', 'discos'], @@ -237,10 +246,12 @@ public static function pluralizeProvider() ['half', ['halfs', 'halves']], ['hat', 'hats'], ['hero', 'heroes'], + ['hippocampus', 'hippocampi'], ['hippopotamus', 'hippopotami'], // hippopotamuses ['hoax', 'hoaxes'], ['hoof', ['hoofs', 'hooves']], ['house', 'houses'], + ['icon', 'icons'], ['index', ['indicies', 'indexes']], ['ion', 'ions'], ['iris', 'irises'], @@ -253,6 +264,8 @@ public static function pluralizeProvider() ['louse', 'lice'], ['man', 'men'], ['matrix', ['matricies', 'matrixes']], + ['medium', 'media'], + ['memorandum', 'memoranda'], ['mouse', 'mice'], ['move', 'moves'], ['movie', 'movies'], @@ -286,6 +299,7 @@ public static function pluralizeProvider() ['shoe', 'shoes'], ['species', 'species'], ['status', ['status', 'statuses']], + ['stratum', 'strata'], ['spy', 'spies'], ['staff', 'staves'], ['story', 'stories'], From fcad14256a86e9c569eb6dcdbb035c15c36e6e7b Mon Sep 17 00:00:00 2001 From: Christoph Kappestein Date: Thu, 9 May 2024 19:48:37 +0200 Subject: [PATCH 14/79] [Messenger] Added postgres asset filter integration test --- ...octrinePostgreSqlFilterIntegrationTest.php | 119 ++++++++++++++++++ 1 file changed, 119 insertions(+) create mode 100644 src/Symfony/Component/Messenger/Bridge/Doctrine/Tests/Transport/DoctrinePostgreSqlFilterIntegrationTest.php diff --git a/src/Symfony/Component/Messenger/Bridge/Doctrine/Tests/Transport/DoctrinePostgreSqlFilterIntegrationTest.php b/src/Symfony/Component/Messenger/Bridge/Doctrine/Tests/Transport/DoctrinePostgreSqlFilterIntegrationTest.php new file mode 100644 index 0000000000000..59e653e4f9e9b --- /dev/null +++ b/src/Symfony/Component/Messenger/Bridge/Doctrine/Tests/Transport/DoctrinePostgreSqlFilterIntegrationTest.php @@ -0,0 +1,119 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Messenger\Bridge\Doctrine\Tests\Transport; + +use Doctrine\DBAL\Configuration; +use Doctrine\DBAL\Connection; +use Doctrine\DBAL\DriverManager; +use Doctrine\DBAL\Schema\Column; +use Doctrine\DBAL\Schema\DefaultSchemaManagerFactory; +use Doctrine\DBAL\Schema\Sequence; +use Doctrine\DBAL\Schema\Table; +use Doctrine\DBAL\Tools\DsnParser; +use Doctrine\DBAL\Types\Type; +use PHPUnit\Framework\TestCase; +use Symfony\Component\Messenger\Bridge\Doctrine\Transport\PostgreSqlConnection; + +/** + * This test checks on a postgres connection whether the doctrine asset filter works as expected + * + * @requires extension pdo_pgsql + * + * @group integration + */ +class DoctrinePostgreSqlFilterIntegrationTest extends TestCase +{ + private Connection $driverConnection; + + protected function setUp(): void + { + if (!$host = getenv('POSTGRES_HOST')) { + $this->markTestSkipped('Missing POSTGRES_HOST env variable'); + } + + $url = "pdo-pgsql://postgres:password@$host"; + $params = (new DsnParser())->parse($url); + $config = new Configuration(); + if (class_exists(DefaultSchemaManagerFactory::class)) { + $config->setSchemaManagerFactory(new DefaultSchemaManagerFactory()); + } + + $this->driverConnection = DriverManager::getConnection($params, $config); + + $this->createAssets(); + } + + protected function tearDown(): void + { + $this->removeAssets(); + + $this->driverConnection->close(); + } + + public function testFilterAssets(): void + { + $schemaManager = $this->driverConnection->createSchemaManager(); + + $this->assertFalse($schemaManager->tableExists('queue_table')); + $this->assertTrue($schemaManager->tableExists('app_table')); + $this->assertTrue($this->hasSequence('app_table_id')); + + $connection = new PostgreSqlConnection(['table_name' => 'queue_table'], $this->driverConnection); + $connection->setup(); + + $schemaManager = $this->driverConnection->createSchemaManager(); + + $this->assertTrue($schemaManager->tableExists('queue_table')); + $this->assertTrue($schemaManager->tableExists('app_table')); + $this->assertTrue($this->hasSequence('app_table_id')); + } + + private function createAssets(): void + { + $this->removeAssets(); + + $schemaManager = $this->driverConnection->createSchemaManager(); + $schemaManager->createTable(new Table('app_table', [new Column('id', Type::getType('integer'))])); + $schemaManager->createSequence(new Sequence('app_table_id')); + } + + private function removeAssets(): void + { + $schemaManager = $this->driverConnection->createSchemaManager(); + + if ($schemaManager->tableExists('queue_table')) { + $schemaManager->dropTable('queue_table'); + } + + if ($schemaManager->tableExists('app_table')) { + $schemaManager->dropTable('app_table'); + } + + if ($this->hasSequence('app_table_id')) { + $schemaManager->dropSequence('app_table_id'); + } + } + + private function hasSequence(string $name): bool + { + $schemaManager = $this->driverConnection->createSchemaManager(); + + $sequences = $schemaManager->listSequences(); + foreach ($sequences as $sequence) { + if ($sequence->getName() === $name) { + return true; + } + } + + return false; + } +} From 7389b350090246cf148645136013c5610ebf86e3 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Sun, 9 Jun 2024 09:20:37 +0200 Subject: [PATCH 15/79] Fix CS --- .../Tests/Transport/DoctrinePostgreSqlFilterIntegrationTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/Messenger/Bridge/Doctrine/Tests/Transport/DoctrinePostgreSqlFilterIntegrationTest.php b/src/Symfony/Component/Messenger/Bridge/Doctrine/Tests/Transport/DoctrinePostgreSqlFilterIntegrationTest.php index 59e653e4f9e9b..99e216905a024 100644 --- a/src/Symfony/Component/Messenger/Bridge/Doctrine/Tests/Transport/DoctrinePostgreSqlFilterIntegrationTest.php +++ b/src/Symfony/Component/Messenger/Bridge/Doctrine/Tests/Transport/DoctrinePostgreSqlFilterIntegrationTest.php @@ -59,7 +59,7 @@ protected function tearDown(): void $this->driverConnection->close(); } - public function testFilterAssets(): void + public function testFilterAssets() { $schemaManager = $this->driverConnection->createSchemaManager(); From 2b46e2a6527bc1348d684a29335d6a75f0006eae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tinjo=20Sch=C3=B6ni?= <32767367+tscni@users.noreply.github.com> Date: Sun, 9 Jun 2024 20:42:34 +0200 Subject: [PATCH 16/79] [ErrorHandler] Fix rendered exception code highlighting on PHP 8.3 --- src/Symfony/Bridge/Twig/Extension/CodeExtension.php | 8 +++----- .../ErrorHandler/ErrorRenderer/HtmlErrorRenderer.php | 8 +++----- .../ErrorHandler/Resources/assets/css/exception.css | 2 +- 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/src/Symfony/Bridge/Twig/Extension/CodeExtension.php b/src/Symfony/Bridge/Twig/Extension/CodeExtension.php index d76924633efe0..e7a3329478d98 100644 --- a/src/Symfony/Bridge/Twig/Extension/CodeExtension.php +++ b/src/Symfony/Bridge/Twig/Extension/CodeExtension.php @@ -129,12 +129,10 @@ public function fileExcerpt(string $file, int $line, int $srcContext = 3): ?stri if (\PHP_VERSION_ID >= 80300) { // remove main pre/code tags $code = preg_replace('#^\s*(.*)\s*#s', '\\1', $code); - // split multiline code tags - $code = preg_replace_callback('#]++)>((?:[^<]*+\\n)++[^<]*+)#', function ($m) { - return "".str_replace("\n", "\n", $m[2]).''; + // split multiline span tags + $code = preg_replace_callback('#]++)>((?:[^<\\n]*+\\n)++[^<]*+)#', function ($m) { + return "".str_replace("\n", "\n", $m[2]).''; }, $code); - // Convert spaces to html entities to preserve indentation when rendered - $code = str_replace(' ', ' ', $code); $content = explode("\n", $code); } else { // remove main code/span tags diff --git a/src/Symfony/Component/ErrorHandler/ErrorRenderer/HtmlErrorRenderer.php b/src/Symfony/Component/ErrorHandler/ErrorRenderer/HtmlErrorRenderer.php index 5b264fa5a7e90..05cbeec166b6e 100644 --- a/src/Symfony/Component/ErrorHandler/ErrorRenderer/HtmlErrorRenderer.php +++ b/src/Symfony/Component/ErrorHandler/ErrorRenderer/HtmlErrorRenderer.php @@ -274,12 +274,10 @@ private function fileExcerpt(string $file, int $line, int $srcContext = 3): stri if (\PHP_VERSION_ID >= 80300) { // remove main pre/code tags $code = preg_replace('#^\s*(.*)\s*#s', '\\1', $code); - // split multiline code tags - $code = preg_replace_callback('#]++)>((?:[^<]*+\\n)++[^<]*+)#', function ($m) { - return "".str_replace("\n", "\n", $m[2]).''; + // split multiline span tags + $code = preg_replace_callback('#]++)>((?:[^<\\n]*+\\n)++[^<]*+)#', function ($m) { + return "".str_replace("\n", "\n", $m[2]).''; }, $code); - // Convert spaces to html entities to preserve indentation when rendered - $code = str_replace(' ', ' ', $code); $content = explode("\n", $code); } else { // remove main code/span tags diff --git a/src/Symfony/Component/ErrorHandler/Resources/assets/css/exception.css b/src/Symfony/Component/ErrorHandler/Resources/assets/css/exception.css index 7cb3206da2055..2d05a5e6a6620 100644 --- a/src/Symfony/Component/ErrorHandler/Resources/assets/css/exception.css +++ b/src/Symfony/Component/ErrorHandler/Resources/assets/css/exception.css @@ -242,7 +242,7 @@ header .container { display: flex; justify-content: space-between; } .trace-code li { color: #969896; margin: 0; padding-left: 10px; float: left; width: 100%; } .trace-code li + li { margin-top: 5px; } .trace-code li.selected { background: var(--trace-selected-background); margin-top: 2px; } -.trace-code li code { color: var(--base-6); white-space: nowrap; } +.trace-code li code { color: var(--base-6); white-space: pre; } .trace-as-text .stacktrace { line-height: 1.8; margin: 0 0 15px; white-space: pre-wrap; } From 871647ae2a3a1e5d94c80303c0e9befd7d12309c Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Mon, 10 Jun 2024 08:53:49 +0200 Subject: [PATCH 17/79] fix test --- .../DoctrinePostgreSqlFilterIntegrationTest.php | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Symfony/Component/Messenger/Bridge/Doctrine/Tests/Transport/DoctrinePostgreSqlFilterIntegrationTest.php b/src/Symfony/Component/Messenger/Bridge/Doctrine/Tests/Transport/DoctrinePostgreSqlFilterIntegrationTest.php index 99e216905a024..9a4738be2ed97 100644 --- a/src/Symfony/Component/Messenger/Bridge/Doctrine/Tests/Transport/DoctrinePostgreSqlFilterIntegrationTest.php +++ b/src/Symfony/Component/Messenger/Bridge/Doctrine/Tests/Transport/DoctrinePostgreSqlFilterIntegrationTest.php @@ -24,7 +24,7 @@ use Symfony\Component\Messenger\Bridge\Doctrine\Transport\PostgreSqlConnection; /** - * This test checks on a postgres connection whether the doctrine asset filter works as expected + * This test checks on a postgres connection whether the doctrine asset filter works as expected. * * @requires extension pdo_pgsql * @@ -63,8 +63,8 @@ public function testFilterAssets() { $schemaManager = $this->driverConnection->createSchemaManager(); - $this->assertFalse($schemaManager->tableExists('queue_table')); - $this->assertTrue($schemaManager->tableExists('app_table')); + $this->assertFalse($schemaManager->tablesExist(['queue_table'])); + $this->assertTrue($schemaManager->tablesExist(['app_table'])); $this->assertTrue($this->hasSequence('app_table_id')); $connection = new PostgreSqlConnection(['table_name' => 'queue_table'], $this->driverConnection); @@ -72,8 +72,8 @@ public function testFilterAssets() $schemaManager = $this->driverConnection->createSchemaManager(); - $this->assertTrue($schemaManager->tableExists('queue_table')); - $this->assertTrue($schemaManager->tableExists('app_table')); + $this->assertTrue($schemaManager->tablesExist(['queue_table'])); + $this->assertTrue($schemaManager->tablesExist(['app_table'])); $this->assertTrue($this->hasSequence('app_table_id')); } @@ -90,11 +90,11 @@ private function removeAssets(): void { $schemaManager = $this->driverConnection->createSchemaManager(); - if ($schemaManager->tableExists('queue_table')) { + if ($schemaManager->tablesExist(['queue_table'])) { $schemaManager->dropTable('queue_table'); } - if ($schemaManager->tableExists('app_table')) { + if ($schemaManager->tablesExist(['app_table'])) { $schemaManager->dropTable('app_table'); } From 69d3d7593e585c39a7d2a087586dd8bab98928b0 Mon Sep 17 00:00:00 2001 From: Cosmin Sandu Date: Mon, 10 Jun 2024 12:18:21 +0300 Subject: [PATCH 18/79] [57251] Missing translations for Romanian (ro) --- .../Validator/Resources/translations/validators.ro.xlf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.ro.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.ro.xlf index 3d0b819a95441..3c0ace5490efd 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.ro.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.ro.xlf @@ -440,7 +440,7 @@ This URL is missing a top-level domain. - Acest URL îi lipsește un domeniu de nivel superior. + Acestui URL îi lipsește un domeniu de nivel superior. From f536f3c473a9a49e3ac042c8a3d621396e4ddf05 Mon Sep 17 00:00:00 2001 From: Michael Babker Date: Mon, 3 Jun 2024 11:35:02 -0400 Subject: [PATCH 19/79] Add a note about the change in the default cache namespace generation to the upgrade guide --- UPGRADE-7.1.md | 1 + src/Symfony/Component/Cache/CHANGELOG.md | 1 + 2 files changed, 2 insertions(+) diff --git a/UPGRADE-7.1.md b/UPGRADE-7.1.md index 204b6f1c75f23..f5ba66f7c48ce 100644 --- a/UPGRADE-7.1.md +++ b/UPGRADE-7.1.md @@ -45,6 +45,7 @@ Cache ----- * Deprecate `CouchbaseBucketAdapter`, use `CouchbaseCollectionAdapter` with Couchbase 3 instead + * The algorithm for the default cache namespace changed from SHA256 to XXH128 DependencyInjection ------------------- diff --git a/src/Symfony/Component/Cache/CHANGELOG.md b/src/Symfony/Component/Cache/CHANGELOG.md index a6b391de1f25a..cab9bf61c88cb 100644 --- a/src/Symfony/Component/Cache/CHANGELOG.md +++ b/src/Symfony/Component/Cache/CHANGELOG.md @@ -8,6 +8,7 @@ CHANGELOG * Deprecate `CouchbaseBucketAdapter`, use `CouchbaseCollectionAdapter` * Add support for URL encoded characters in Couchbase DSN * Add support for using DSN with PDOAdapter + * The algorithm for the default cache namespace changed from SHA256 to XXH128 7.0 --- From 2ab91bb5162d45fa6b579e02c7148639e1c1776e Mon Sep 17 00:00:00 2001 From: llupa Date: Wed, 12 Jun 2024 12:32:51 +0200 Subject: [PATCH 20/79] [Security] Change to `BadCredentialsException` when empty username / password --- .../Security/Http/Authenticator/FormLoginAuthenticator.php | 5 +++-- .../Http/Tests/Authenticator/FormLoginAuthenticatorTest.php | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/Symfony/Component/Security/Http/Authenticator/FormLoginAuthenticator.php b/src/Symfony/Component/Security/Http/Authenticator/FormLoginAuthenticator.php index 4cb990934a549..7109ff244a79f 100644 --- a/src/Symfony/Component/Security/Http/Authenticator/FormLoginAuthenticator.php +++ b/src/Symfony/Component/Security/Http/Authenticator/FormLoginAuthenticator.php @@ -18,6 +18,7 @@ use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; use Symfony\Component\Security\Core\Exception\AuthenticationException; +use Symfony\Component\Security\Core\Exception\BadCredentialsException; use Symfony\Component\Security\Core\User\PasswordUpgraderInterface; use Symfony\Component\Security\Core\User\UserProviderInterface; use Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface; @@ -130,7 +131,7 @@ private function getCredentials(Request $request): array $credentials['username'] = trim($credentials['username']); if ('' === $credentials['username']) { - throw new BadRequestHttpException(sprintf('The key "%s" must be a non-empty string.', $this->options['username_parameter'])); + throw new BadCredentialsException(sprintf('The key "%s" must be a non-empty string.', $this->options['username_parameter'])); } $request->getSession()->set(SecurityRequestAttributes::LAST_USERNAME, $credentials['username']); @@ -140,7 +141,7 @@ private function getCredentials(Request $request): array } if ('' === (string) $credentials['password']) { - throw new BadRequestHttpException(sprintf('The key "%s" must be a non-empty string.', $this->options['password_parameter'])); + throw new BadCredentialsException(sprintf('The key "%s" must be a non-empty string.', $this->options['password_parameter'])); } if (!\is_string($credentials['csrf_token'] ?? '') && (!\is_object($credentials['csrf_token']) || !method_exists($credentials['csrf_token'], '__toString'))) { diff --git a/src/Symfony/Component/Security/Http/Tests/Authenticator/FormLoginAuthenticatorTest.php b/src/Symfony/Component/Security/Http/Tests/Authenticator/FormLoginAuthenticatorTest.php index e58f5020e3a7a..9469eab7c2a94 100644 --- a/src/Symfony/Component/Security/Http/Tests/Authenticator/FormLoginAuthenticatorTest.php +++ b/src/Symfony/Component/Security/Http/Tests/Authenticator/FormLoginAuthenticatorTest.php @@ -44,7 +44,7 @@ protected function setUp(): void public function testHandleWhenUsernameEmpty() { - $this->expectException(BadRequestHttpException::class); + $this->expectException(BadCredentialsException::class); $this->expectExceptionMessage('The key "_username" must be a non-empty string.'); $request = Request::create('/login_check', 'POST', ['_username' => '', '_password' => 's$cr$t']); @@ -56,7 +56,7 @@ public function testHandleWhenUsernameEmpty() public function testHandleWhenPasswordEmpty() { - $this->expectException(BadRequestHttpException::class); + $this->expectException(BadCredentialsException::class); $this->expectExceptionMessage('The key "_password" must be a non-empty string.'); $request = Request::create('/login_check', 'POST', ['_username' => 'foo', '_password' => '']); From f4792bcbf014264d234ae82a4d6c849fe8297b45 Mon Sep 17 00:00:00 2001 From: Antoine Lamirault Date: Thu, 13 Jun 2024 19:55:40 +0200 Subject: [PATCH 21/79] [DependencyInjection] Fix ternary in AutowireCallable attribute --- .../DependencyInjection/Attribute/AutowireCallable.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/DependencyInjection/Attribute/AutowireCallable.php b/src/Symfony/Component/DependencyInjection/Attribute/AutowireCallable.php index f14d9066d33d0..da9ac0d0014d9 100644 --- a/src/Symfony/Component/DependencyInjection/Attribute/AutowireCallable.php +++ b/src/Symfony/Component/DependencyInjection/Attribute/AutowireCallable.php @@ -42,7 +42,7 @@ public function __construct( public function buildDefinition(mixed $value, ?string $type, \ReflectionParameter $parameter): Definition { - return (new Definition($type = \is_string($this->lazy) ? $this->lazy : ($type ?: 'Closure'))) + return (new Definition($type = \is_array($this->lazy) ? current($this->lazy) : ($type ?: 'Closure'))) ->setFactory(['Closure', 'fromCallable']) ->setArguments([\is_array($value) ? $value + [1 => '__invoke'] : $value]) ->setLazy($this->lazy || 'Closure' !== $type && 'callable' !== (string) $parameter->getType()); From 86480fe485bac7ae7e185be1522f14e66c8b8462 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Fri, 14 Jun 2024 11:49:55 +0200 Subject: [PATCH 22/79] send the recipient phone number as an array --- .../Notifier/Bridge/Clickatell/ClickatellTransport.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/Notifier/Bridge/Clickatell/ClickatellTransport.php b/src/Symfony/Component/Notifier/Bridge/Clickatell/ClickatellTransport.php index 65321f79ca793..75621994d2c2e 100644 --- a/src/Symfony/Component/Notifier/Bridge/Clickatell/ClickatellTransport.php +++ b/src/Symfony/Component/Notifier/Bridge/Clickatell/ClickatellTransport.php @@ -63,7 +63,7 @@ protected function doSend(MessageInterface $message): SentMessage $options = []; $options['from'] = $message->getFrom() ?: $this->from; - $options['to'] = $message->getPhone(); + $options['to'] = [$message->getPhone()]; $options['text'] = $message->getSubject(); $response = $this->client->request('POST', $endpoint, [ From f903893dff5a0f18c9069ee4fc90466dfd60a9af Mon Sep 17 00:00:00 2001 From: HypeMC Date: Fri, 31 May 2024 18:43:25 +0200 Subject: [PATCH 23/79] [FrameworkBundle] Fix setting default context for certain normalizers --- .../FrameworkExtension.php | 8 ++- .../Resources/config/serializer.php | 1 - .../Tests/Functional/AbstractWebTestCase.php | 2 +- .../Tests/Functional/SerializerTest.php | 70 ++++++++++++------- .../Tests/Functional/app/AppKernel.php | 5 ++ .../Functional/app/Serializer/config.yml | 45 ------------ .../app/Serializer/default_context.yaml | 59 ++++++++++++++++ 7 files changed, 115 insertions(+), 75 deletions(-) create mode 100644 src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Serializer/default_context.yaml diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index ebaed7c2b2b76..71505f2519340 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -1859,18 +1859,20 @@ private function registerSerializerConfiguration(array $config, ContainerBuilder } $arguments = $container->getDefinition('serializer.normalizer.object')->getArguments(); - $context = []; + $context = $arguments[6] ?? $defaultContext; if (isset($config['circular_reference_handler']) && $config['circular_reference_handler']) { - $context += ($arguments[6] ?? $defaultContext) + ['circular_reference_handler' => new Reference($config['circular_reference_handler'])]; + $context += ['circular_reference_handler' => new Reference($config['circular_reference_handler'])]; $container->getDefinition('serializer.normalizer.object')->setArgument(5, null); } if ($config['max_depth_handler'] ?? false) { - $context += ($arguments[6] ?? $defaultContext) + ['max_depth_handler' => new Reference($config['max_depth_handler'])]; + $context += ['max_depth_handler' => new Reference($config['max_depth_handler'])]; } $container->getDefinition('serializer.normalizer.object')->setArgument(6, $context); + + $container->getDefinition('serializer.normalizer.property')->setArgument(5, $defaultContext); } private function registerPropertyInfoConfiguration(ContainerBuilder $container, PhpFileLoader $loader) diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/serializer.php b/src/Symfony/Bundle/FrameworkBundle/Resources/config/serializer.php index 7762e5a64ca86..2fb42027fd61e 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/serializer.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/serializer.php @@ -139,7 +139,6 @@ service('property_info')->ignoreOnInvalid(), service('serializer.mapping.class_discriminator_resolver')->ignoreOnInvalid(), null, - [], ]) ->alias(PropertyNormalizer::class, 'serializer.normalizer.property') diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/AbstractWebTestCase.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/AbstractWebTestCase.php index bce53b8668251..30ca91d1ee5b7 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/AbstractWebTestCase.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/AbstractWebTestCase.php @@ -52,7 +52,7 @@ protected static function getKernelClass(): string protected static function createKernel(array $options = []): KernelInterface { - $class = self::getKernelClass(); + $class = static::getKernelClass(); if (!isset($options['test_case'])) { throw new \InvalidArgumentException('The option "test_case" must be set.'); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/SerializerTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/SerializerTest.php index 9a6527b14dd62..7ce9b0735e134 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/SerializerTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/SerializerTest.php @@ -11,6 +11,10 @@ namespace Symfony\Bundle\FrameworkBundle\Tests\Functional; +use Symfony\Bundle\FrameworkBundle\Tests\Functional\app\AppKernel; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; + /** * @author Kévin Dunglas */ @@ -33,39 +37,55 @@ public function testDeserializeArrayOfObject() $this->assertEquals($expected, $result); } - /** - * @dataProvider provideNormalizersAndEncodersWithDefaultContextOption - */ - public function testNormalizersAndEncodersUseDefaultContextConfigOption(string $normalizerId) + public function testNormalizersAndEncodersUseDefaultContextConfigOption() { - static::bootKernel(['test_case' => 'Serializer']); + /** @var SerializerKernel $kernel */ + $kernel = static::bootKernel(['test_case' => 'Serializer', 'root_config' => 'default_context.yaml']); + + foreach ($kernel->normalizersAndEncoders as $normalizerOrEncoderId) { + $normalizerOrEncoder = static::getContainer()->get($normalizerOrEncoderId); - $normalizer = static::getContainer()->get($normalizerId); + $reflectionObject = new \ReflectionObject($normalizerOrEncoder); + $property = $reflectionObject->getProperty('defaultContext'); + $property->setAccessible(true); - $reflectionObject = new \ReflectionObject($normalizer); - $property = $reflectionObject->getProperty('defaultContext'); - $property->setAccessible(true); + $defaultContext = $property->getValue($normalizerOrEncoder); - $defaultContext = $property->getValue($normalizer); + self::assertArrayHasKey('fake_context_option', $defaultContext); + self::assertEquals('foo', $defaultContext['fake_context_option']); + } + } - self::assertArrayHasKey('fake_context_option', $defaultContext); - self::assertEquals('foo', $defaultContext['fake_context_option']); + protected static function getKernelClass(): string + { + return SerializerKernel::class; } +} + +class SerializerKernel extends AppKernel implements CompilerPassInterface +{ + public $normalizersAndEncoders = [ + 'serializer.normalizer.property.alias', // Special case as this normalizer isn't tagged + ]; - public static function provideNormalizersAndEncodersWithDefaultContextOption(): array + public function process(ContainerBuilder $container) { - return [ - ['serializer.normalizer.constraint_violation_list.alias'], - ['serializer.normalizer.dateinterval.alias'], - ['serializer.normalizer.datetime.alias'], - ['serializer.normalizer.json_serializable.alias'], - ['serializer.normalizer.problem.alias'], - ['serializer.normalizer.uid.alias'], - ['serializer.normalizer.object.alias'], - ['serializer.encoder.xml.alias'], - ['serializer.encoder.yaml.alias'], - ['serializer.encoder.csv.alias'], - ]; + $services = array_merge( + $container->findTaggedServiceIds('serializer.normalizer'), + $container->findTaggedServiceIds('serializer.encoder') + ); + foreach ($services as $serviceId => $attributes) { + $class = $container->getDefinition($serviceId)->getClass(); + if (null === $reflectionConstructor = (new \ReflectionClass($class))->getConstructor()) { + continue; + } + foreach ($reflectionConstructor->getParameters() as $reflectionParam) { + if ('array $defaultContext' === $reflectionParam->getType()->getName().' $'.$reflectionParam->getName()) { + $this->normalizersAndEncoders[] = $serviceId.'.alias'; + break; + } + } + } } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/AppKernel.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/AppKernel.php index 49fb0ca2e6f8d..0c3c9cc3564e4 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/AppKernel.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/AppKernel.php @@ -49,6 +49,11 @@ public function __construct($varDir, $testCase, $rootConfig, $environment, $debu parent::__construct($environment, $debug); } + protected function getContainerClass(): string + { + return parent::getContainerClass().substr(md5($this->rootConfig), -16); + } + public function registerBundles(): iterable { if (!file_exists($filename = $this->getProjectDir().'/'.$this->testCase.'/bundles.php')) { diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Serializer/config.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Serializer/config.yml index f023f6341970d..c22edfccef331 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Serializer/config.yml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Serializer/config.yml @@ -8,7 +8,6 @@ framework: max_depth_handler: Symfony\Bundle\FrameworkBundle\Tests\Fixtures\Serializer\MaxDepthHandler default_context: enable_max_depth: true - fake_context_option: foo property_info: { enabled: true } services: @@ -16,50 +15,6 @@ services: alias: serializer public: true - serializer.normalizer.constraint_violation_list.alias: - alias: serializer.normalizer.constraint_violation_list - public: true - - serializer.normalizer.dateinterval.alias: - alias: serializer.normalizer.dateinterval - public: true - - serializer.normalizer.datetime.alias: - alias: serializer.normalizer.datetime - public: true - - serializer.normalizer.json_serializable.alias: - alias: serializer.normalizer.json_serializable - public: true - - serializer.normalizer.problem.alias: - alias: serializer.normalizer.problem - public: true - - serializer.normalizer.uid.alias: - alias: serializer.normalizer.uid - public: true - - serializer.normalizer.property.alias: - alias: serializer.normalizer.property - public: true - - serializer.normalizer.object.alias: - alias: serializer.normalizer.object - public: true - - serializer.encoder.xml.alias: - alias: serializer.encoder.xml - public: true - - serializer.encoder.yaml.alias: - alias: serializer.encoder.yaml - public: true - - serializer.encoder.csv.alias: - alias: serializer.encoder.csv - public: true - Symfony\Bundle\FrameworkBundle\Tests\Fixtures\Serializer\CircularReferenceHandler: ~ Symfony\Bundle\FrameworkBundle\Tests\Fixtures\Serializer\MaxDepthHandler: ~ diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Serializer/default_context.yaml b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Serializer/default_context.yaml new file mode 100644 index 0000000000000..de6114c5d4bb8 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Serializer/default_context.yaml @@ -0,0 +1,59 @@ +imports: + - { resource: ../config/default.yml } + +framework: + serializer: + enabled: true + circular_reference_handler: ~ # This must be null + max_depth_handler: ~ # This must be null + default_context: + fake_context_option: foo + +services: + serializer.normalizer.constraint_violation_list.alias: + alias: serializer.normalizer.constraint_violation_list + public: true + + serializer.normalizer.dateinterval.alias: + alias: serializer.normalizer.dateinterval + public: true + + serializer.normalizer.datetime.alias: + alias: serializer.normalizer.datetime + public: true + + serializer.normalizer.json_serializable.alias: + alias: serializer.normalizer.json_serializable + public: true + + serializer.normalizer.object.alias: + alias: serializer.normalizer.object + public: true + + serializer.normalizer.problem.alias: + alias: serializer.normalizer.problem + public: true + + serializer.normalizer.property.alias: + alias: serializer.normalizer.property + public: true + + serializer.normalizer.uid.alias: + alias: serializer.normalizer.uid + public: true + + serializer.encoder.csv.alias: + alias: serializer.encoder.csv + public: true + + serializer.encoder.json.alias: + alias: serializer.encoder.json + public: true + + serializer.encoder.xml.alias: + alias: serializer.encoder.xml + public: true + + serializer.encoder.yaml.alias: + alias: serializer.encoder.yaml + public: true From 661e79a581f3c4cf564676c27fdce7ae81f08a6a Mon Sep 17 00:00:00 2001 From: HypeMC Date: Fri, 14 Jun 2024 21:09:16 +0200 Subject: [PATCH 24/79] [PhpUnitBridge] Add missing import --- src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler.php b/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler.php index 95312e2b3ce80..2821d92e358f4 100644 --- a/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler.php +++ b/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler.php @@ -11,6 +11,7 @@ namespace Symfony\Bridge\PhpUnit; +use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestResult; use PHPUnit\Runner\ErrorHandler; use PHPUnit\Util\Error\Handler; From c2f71af48161e36cd3231174d9081e742a2a7474 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Sat, 15 Jun 2024 00:34:59 +0200 Subject: [PATCH 25/79] fix handling of special "value" constraint option --- .../Tests/Validator/Constraints/UniqueEntityTest.php | 7 +++++++ .../Bridge/Doctrine/Validator/Constraints/UniqueEntity.php | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Bridge/Doctrine/Tests/Validator/Constraints/UniqueEntityTest.php b/src/Symfony/Bridge/Doctrine/Tests/Validator/Constraints/UniqueEntityTest.php index 4380bba494bba..fbfc2cb39b4ed 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Validator/Constraints/UniqueEntityTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Validator/Constraints/UniqueEntityTest.php @@ -60,6 +60,13 @@ public function testAttributeWithGroupsAndPaylod() self::assertSame('some attached data', $constraint->payload); self::assertSame(['some_group'], $constraint->groups); } + + public function testValueOptionConfiguresFields() + { + $constraint = new UniqueEntity(['value' => 'email']); + + $this->assertSame('email', $constraint->fields); + } } #[UniqueEntity(['email'], message: 'myMessage')] diff --git a/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntity.php b/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntity.php index edc9011b27ea1..34a16df0efce8 100644 --- a/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntity.php +++ b/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntity.php @@ -60,7 +60,7 @@ public function __construct( $payload = null, array $options = [], ) { - if (\is_array($fields) && \is_string(key($fields)) && [] === array_diff(array_keys($fields), array_keys(get_class_vars(static::class)))) { + if (\is_array($fields) && \is_string(key($fields)) && [] === array_diff(array_keys($fields), array_merge(array_keys(get_class_vars(static::class)), ['value']))) { $options = array_merge($fields, $options); } else { $options['fields'] = $fields; From 5f68cf780fcd4013c2cacc760a5368dabdc1f543 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Sat, 15 Jun 2024 00:34:59 +0200 Subject: [PATCH 26/79] test handling of special "value" constraint option --- .../Tests/Validator/Constraints/UniqueEntityTest.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/Symfony/Bridge/Doctrine/Tests/Validator/Constraints/UniqueEntityTest.php b/src/Symfony/Bridge/Doctrine/Tests/Validator/Constraints/UniqueEntityTest.php index 2c9c3815654ba..5d9edce2408c2 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Validator/Constraints/UniqueEntityTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Validator/Constraints/UniqueEntityTest.php @@ -63,6 +63,13 @@ public function testAttributeWithGroupsAndPaylod() self::assertSame('some attached data', $constraint->payload); self::assertSame(['some_group'], $constraint->groups); } + + public function testValueOptionConfiguresFields() + { + $constraint = new UniqueEntity(['value' => 'email']); + + $this->assertSame('email', $constraint->fields); + } } #[UniqueEntity(['email'], message: 'myMessage')] From 3857545d33cd97f4eb85d9fae9ea8681e4201f81 Mon Sep 17 00:00:00 2001 From: HypeMC Date: Mon, 27 May 2024 01:34:50 +0200 Subject: [PATCH 27/79] [Serializer] Fix `ObjectNormalizer` with property path --- .../Normalizer/ObjectNormalizer.php | 6 +++- .../Tests/Fixtures/property-path-mapping.yaml | 5 +++ .../Tests/Normalizer/ObjectNormalizerTest.php | 35 +++++++++++++++++++ 3 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 src/Symfony/Component/Serializer/Tests/Fixtures/property-path-mapping.yaml diff --git a/src/Symfony/Component/Serializer/Normalizer/ObjectNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/ObjectNormalizer.php index 6a5413f69d317..f4a234981e6fb 100644 --- a/src/Symfony/Component/Serializer/Normalizer/ObjectNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/ObjectNormalizer.php @@ -194,7 +194,11 @@ protected function isAllowedAttribute($classOrObject, string $attribute, ?string $class = \is_object($classOrObject) ? \get_class($classOrObject) : $classOrObject; if ($context['_read_attributes'] ?? true) { - return $this->propertyInfoExtractor->isReadable($class, $attribute) || $this->hasAttributeAccessorMethod($class, $attribute); + return (\is_object($classOrObject) && $this->propertyAccessor->isReadable($classOrObject, $attribute)) || $this->propertyInfoExtractor->isReadable($class, $attribute) || $this->hasAttributeAccessorMethod($class, $attribute); + } + + if (str_contains($attribute, '.')) { + return true; } if ($this->propertyInfoExtractor->isWritable($class, $attribute)) { diff --git a/src/Symfony/Component/Serializer/Tests/Fixtures/property-path-mapping.yaml b/src/Symfony/Component/Serializer/Tests/Fixtures/property-path-mapping.yaml new file mode 100644 index 0000000000000..834b39150fe89 --- /dev/null +++ b/src/Symfony/Component/Serializer/Tests/Fixtures/property-path-mapping.yaml @@ -0,0 +1,5 @@ +Symfony\Component\Serializer\Tests\Normalizer\ObjectOuter: + attributes: + inner.foo: + serialized_name: inner_foo + groups: [ 'read' ] diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php index 5f88844974cd9..4ff8c114db058 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php @@ -25,6 +25,7 @@ use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactory; use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface; use Symfony\Component\Serializer\Mapping\Loader\AnnotationLoader; +use Symfony\Component\Serializer\Mapping\Loader\YamlFileLoader; use Symfony\Component\Serializer\NameConverter\AdvancedNameConverterInterface; use Symfony\Component\Serializer\NameConverter\CamelCaseToSnakeCaseNameConverter; use Symfony\Component\Serializer\NameConverter\MetadataAwareNameConverter; @@ -911,6 +912,40 @@ public function testDenormalizeWithIgnoreAnnotationAndPrivateProperties() $this->assertEquals($expected, $obj); } + + public function testNormalizeWithPropertyPath() + { + $classMetadataFactory = new ClassMetadataFactory(new YamlFileLoader(__DIR__.'/../Fixtures/property-path-mapping.yaml')); + $normalizer = new ObjectNormalizer($classMetadataFactory, new MetadataAwareNameConverter($classMetadataFactory)); + + $dummyInner = new ObjectInner(); + $dummyInner->foo = 'foo'; + $dummy = new ObjectOuter(); + $dummy->setInner($dummyInner); + + $this->assertSame(['inner_foo' => 'foo'], $normalizer->normalize($dummy, 'json', ['groups' => 'read'])); + } + + public function testDenormalizeWithPropertyPath() + { + $classMetadataFactory = new ClassMetadataFactory(new YamlFileLoader(__DIR__.'/../Fixtures/property-path-mapping.yaml')); + $normalizer = new ObjectNormalizer($classMetadataFactory, new MetadataAwareNameConverter($classMetadataFactory)); + + $dummy = new ObjectOuter(); + $dummy->setInner(new ObjectInner()); + + $obj = $normalizer->denormalize(['inner_foo' => 'foo'], ObjectOuter::class, 'json', [ + 'object_to_populate' => $dummy, + 'groups' => 'read', + ]); + + $expectedInner = new ObjectInner(); + $expectedInner->foo = 'foo'; + $expected = new ObjectOuter(); + $expected->setInner($expectedInner); + + $this->assertEquals($expected, $obj); + } } class ProxyObjectDummy extends ObjectDummy From 40341a1f6dd2e0387b6f3da32b1492a339123525 Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Tue, 11 Jun 2024 17:49:33 +0200 Subject: [PATCH 28/79] [HttpKernel][Security] Fix accessing session for stateless request --- .../HttpKernel/DataCollector/RequestDataCollector.php | 2 +- .../Component/HttpKernel/EventListener/ProfilerListener.php | 2 +- .../HttpKernel/Tests/EventListener/ProfilerListenerTest.php | 4 ++-- .../Component/Security/Http/Firewall/ContextListener.php | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/DataCollector/RequestDataCollector.php b/src/Symfony/Component/HttpKernel/DataCollector/RequestDataCollector.php index 6931336f06d17..2a4392aa8c340 100644 --- a/src/Symfony/Component/HttpKernel/DataCollector/RequestDataCollector.php +++ b/src/Symfony/Component/HttpKernel/DataCollector/RequestDataCollector.php @@ -66,7 +66,7 @@ public function collect(Request $request, Response $response, ?\Throwable $excep $sessionMetadata = []; $sessionAttributes = []; $flashes = []; - if ($request->hasSession()) { + if (!$request->attributes->getBoolean('_stateless') && $request->hasSession()) { $session = $request->getSession(); if ($session->isStarted()) { $sessionMetadata['Created'] = date(\DATE_RFC822, $session->getMetadataBag()->getCreated()); diff --git a/src/Symfony/Component/HttpKernel/EventListener/ProfilerListener.php b/src/Symfony/Component/HttpKernel/EventListener/ProfilerListener.php index e4261871b0e72..c7950b8365b57 100644 --- a/src/Symfony/Component/HttpKernel/EventListener/ProfilerListener.php +++ b/src/Symfony/Component/HttpKernel/EventListener/ProfilerListener.php @@ -97,7 +97,7 @@ public function onKernelResponse(ResponseEvent $event) return; } - $session = $request->hasPreviousSession() && $request->hasSession() ? $request->getSession() : null; + $session = !$request->attributes->getBoolean('_stateless') && $request->hasPreviousSession() && $request->hasSession() ? $request->getSession() : null; if ($session instanceof Session) { $usageIndexValue = $usageIndexReference = &$session->getUsageIndex(); diff --git a/src/Symfony/Component/HttpKernel/Tests/EventListener/ProfilerListenerTest.php b/src/Symfony/Component/HttpKernel/Tests/EventListener/ProfilerListenerTest.php index 57f8f53b1e9f7..fdf550d0ecd41 100644 --- a/src/Symfony/Component/HttpKernel/Tests/EventListener/ProfilerListenerTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/EventListener/ProfilerListenerTest.php @@ -40,8 +40,8 @@ public function testKernelTerminate() ->willReturn($profile); $kernel = $this->createMock(HttpKernelInterface::class); - $mainRequest = $this->createMock(Request::class); - $subRequest = $this->createMock(Request::class); + $mainRequest = new Request(); + $subRequest = new Request(); $response = $this->createMock(Response::class); $requestStack = new RequestStack(); diff --git a/src/Symfony/Component/Security/Http/Firewall/ContextListener.php b/src/Symfony/Component/Security/Http/Firewall/ContextListener.php index 06f2c3907b2f6..a48ca7e38482e 100644 --- a/src/Symfony/Component/Security/Http/Firewall/ContextListener.php +++ b/src/Symfony/Component/Security/Http/Firewall/ContextListener.php @@ -95,7 +95,7 @@ public function authenticate(RequestEvent $event) } $request = $event->getRequest(); - $session = $request->hasPreviousSession() && $request->hasSession() ? $request->getSession() : null; + $session = !$request->attributes->getBoolean('_stateless') && $request->hasPreviousSession() && $request->hasSession() ? $request->getSession() : null; $request->attributes->set('_security_firewall_run', $this->sessionKey); From caee5b9d144816d1681d146cbac507b0868c08f7 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Mon, 17 Jun 2024 23:57:43 +0200 Subject: [PATCH 29/79] inject the missing logger service --- .../DependencyInjection/FrameworkExtension.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index 437d5402ae9a2..3c82b4b0c61b7 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -2855,6 +2855,11 @@ private function registerNotifierConfiguration(array $config, ContainerBuilder $ ->addArgument(new Reference('http_client', ContainerBuilder::NULL_ON_INVALID_REFERENCE)); } + if (ContainerBuilder::willBeAvailable('symfony/bluesky-notifier', NotifierBridge\Bluesky\BlueskyTransportFactory::class, ['symfony/framework-bundle', 'symfony/notifier'])) { + $container->getDefinition($classToServices[NotifierBridge\Bluesky\BlueskyTransportFactory::class]) + ->addArgument(new Reference('logger')); + } + if (isset($config['admin_recipients'])) { $notifier = $container->getDefinition('notifier'); foreach ($config['admin_recipients'] as $i => $recipient) { From e792b164202fd3167ffb98d74c583f9267dc28c2 Mon Sep 17 00:00:00 2001 From: Romain Jacquart Date: Tue, 18 Jun 2024 23:59:25 +0200 Subject: [PATCH 30/79] [Notifier] Fix thread key in GoogleChat bridge Google Chat API has deprecated the use of `threadKey` query parameter in favor of `thread.threadKey` in request's body. --- .../Bridge/GoogleChat/GoogleChatTransport.php | 12 +++++++++--- .../GoogleChat/Tests/GoogleChatTransportTest.php | 4 ++-- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/Symfony/Component/Notifier/Bridge/GoogleChat/GoogleChatTransport.php b/src/Symfony/Component/Notifier/Bridge/GoogleChat/GoogleChatTransport.php index 41666a7cf182e..735744e5e6da4 100644 --- a/src/Symfony/Component/Notifier/Bridge/GoogleChat/GoogleChatTransport.php +++ b/src/Symfony/Component/Notifier/Bridge/GoogleChat/GoogleChatTransport.php @@ -96,16 +96,22 @@ protected function doSend(MessageInterface $message): SentMessage $threadKey = $opts->getThreadKey() ?: $this->threadKey; - $options = $opts->toArray(); $url = sprintf('https://%s/v1/spaces/%s/messages?key=%s&token=%s%s', $this->getEndpoint(), $this->space, urlencode($this->accessKey), urlencode($this->accessToken), - $threadKey ? '&threadKey='.urlencode($threadKey) : '' + $threadKey ? '&messageReplyOption=REPLY_MESSAGE_FALLBACK_TO_NEW_THREAD' : '' ); + + $body = array_filter($opts->toArray()); + + if ($threadKey) { + $body['thread']['threadKey'] = $threadKey; + } + $response = $this->client->request('POST', $url, [ - 'json' => array_filter($options), + 'json' => $body, ]); try { diff --git a/src/Symfony/Component/Notifier/Bridge/GoogleChat/Tests/GoogleChatTransportTest.php b/src/Symfony/Component/Notifier/Bridge/GoogleChat/Tests/GoogleChatTransportTest.php index c8df301ca96c6..b6cbf8176d55c 100644 --- a/src/Symfony/Component/Notifier/Bridge/GoogleChat/Tests/GoogleChatTransportTest.php +++ b/src/Symfony/Component/Notifier/Bridge/GoogleChat/Tests/GoogleChatTransportTest.php @@ -116,11 +116,11 @@ public function testSendWithOptions() ->method('getContent') ->willReturn('{"name":"spaces/My-Space/messages/abcdefg.hijklmno"}'); - $expectedBody = json_encode(['text' => $message]); + $expectedBody = json_encode(['text' => $message, 'thread' => ['threadKey' => 'My-Thread']]); $client = new MockHttpClient(function (string $method, string $url, array $options = []) use ($response, $expectedBody): ResponseInterface { $this->assertSame('POST', $method); - $this->assertSame('https://chat.googleapis.com/v1/spaces/My-Space/messages?key=theAccessKey&token=theAccessToken%3D&threadKey=My-Thread', $url); + $this->assertSame('https://chat.googleapis.com/v1/spaces/My-Space/messages?key=theAccessKey&token=theAccessToken%3D&messageReplyOption=REPLY_MESSAGE_FALLBACK_TO_NEW_THREAD', $url); $this->assertSame($expectedBody, $options['body']); return $response; From 55d270365aa684d4284d188644748ae53058b9d1 Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Wed, 19 Jun 2024 11:45:47 +0200 Subject: [PATCH 31/79] [DependencyInjection] Add test coverage for `AutowireCallable::buildDefinition()` --- .../Tests/Attribute/AutowireCallableTest.php | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/Symfony/Component/DependencyInjection/Tests/Attribute/AutowireCallableTest.php b/src/Symfony/Component/DependencyInjection/Tests/Attribute/AutowireCallableTest.php index f5aeb35d44939..9e1a0d85429ff 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Attribute/AutowireCallableTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Attribute/AutowireCallableTest.php @@ -93,4 +93,35 @@ public function testArrayCallableWithServiceOnly() self::assertEquals([new Reference('my_service'), '__invoke'], $attribute->value); self::assertFalse($attribute->lazy); } + + public function testLazyAsArrayInDefinition() + { + $attribute = new AutowireCallable(callable: [Foo::class, 'myMethod'], lazy: 'my_lazy_class'); + + self::assertSame([Foo::class, 'myMethod'], $attribute->value); + + $definition = $attribute->buildDefinition('my_value', 'my_custom_type', new \ReflectionParameter([Foo::class, 'myMethod'], 'myParameter')); + + self::assertSame('my_lazy_class', $definition->getClass()); + self::assertTrue($definition->isLazy()); + } + + public function testLazyIsFalseInDefinition() + { + $attribute = new AutowireCallable(callable: [Foo::class, 'myMethod'], lazy: false); + + self::assertFalse($attribute->lazy); + + $definition = $attribute->buildDefinition('my_value', 'my_custom_type', new \ReflectionParameter([Foo::class, 'myMethod'], 'myParameter')); + + self::assertSame('my_custom_type', $definition->getClass()); + self::assertFalse($definition->isLazy()); + } +} + +class Foo +{ + public function myMethod(callable $myParameter) + { + } } From add5d45fb0f07563f12a0c0b542372021d1cec73 Mon Sep 17 00:00:00 2001 From: Thomas Calvet Date: Wed, 19 Jun 2024 15:12:14 +0200 Subject: [PATCH 32/79] [HttpClient] Fix parsing SSE --- .../HttpClient/EventSourceHttpClient.php | 37 +++++---- .../Tests/EventSourceHttpClientTest.php | 78 +++++++++---------- 2 files changed, 55 insertions(+), 60 deletions(-) diff --git a/src/Symfony/Component/HttpClient/EventSourceHttpClient.php b/src/Symfony/Component/HttpClient/EventSourceHttpClient.php index 89d12e87764fa..6626cbeba6ba5 100644 --- a/src/Symfony/Component/HttpClient/EventSourceHttpClient.php +++ b/src/Symfony/Component/HttpClient/EventSourceHttpClient.php @@ -11,6 +11,7 @@ namespace Symfony\Component\HttpClient; +use Symfony\Component\HttpClient\Chunk\DataChunk; use Symfony\Component\HttpClient\Chunk\ServerSentEvent; use Symfony\Component\HttpClient\Exception\EventSourceException; use Symfony\Component\HttpClient\Response\AsyncContext; @@ -121,17 +122,30 @@ public function request(string $method, string $url, array $options = []): Respo return; } - $rx = '/((?:\r\n){2,}|\r{2,}|\n{2,})/'; - $content = $state->buffer.$chunk->getContent(); - if ($chunk->isLast()) { - $rx = substr_replace($rx, '|$', -2, 0); + if ('' !== $content = $state->buffer) { + $state->buffer = ''; + yield new DataChunk(-1, $content); + } + + yield $chunk; + + return; } - $events = preg_split($rx, $content, -1, \PREG_SPLIT_DELIM_CAPTURE); + + $content = $state->buffer.$chunk->getContent(); + $events = preg_split('/((?:\r\n){2,}|\r{2,}|\n{2,})/', $content, -1, \PREG_SPLIT_DELIM_CAPTURE); $state->buffer = array_pop($events); for ($i = 0; isset($events[$i]); $i += 2) { - $event = new ServerSentEvent($events[$i].$events[1 + $i]); + $content = $events[$i].$events[1 + $i]; + if (!preg_match('/(?:^|\r\n|[\r\n])[^:\r\n]/', $content)) { + yield new DataChunk(-1, $content); + + continue; + } + + $event = new ServerSentEvent($content); if ('' !== $event->getId()) { $context->setInfo('last_event_id', $state->lastEventId = $event->getId()); @@ -143,17 +157,6 @@ public function request(string $method, string $url, array $options = []): Respo yield $event; } - - if (preg_match('/^(?::[^\r\n]*+(?:\r\n|[\r\n]))+$/m', $state->buffer)) { - $content = $state->buffer; - $state->buffer = ''; - - yield $context->createChunk($content); - } - - if ($chunk->isLast()) { - yield $chunk; - } }); } } diff --git a/src/Symfony/Component/HttpClient/Tests/EventSourceHttpClientTest.php b/src/Symfony/Component/HttpClient/Tests/EventSourceHttpClientTest.php index fff3a0e7c18b7..536979e864672 100644 --- a/src/Symfony/Component/HttpClient/Tests/EventSourceHttpClientTest.php +++ b/src/Symfony/Component/HttpClient/Tests/EventSourceHttpClientTest.php @@ -15,9 +15,11 @@ use Symfony\Component\HttpClient\Chunk\DataChunk; use Symfony\Component\HttpClient\Chunk\ErrorChunk; use Symfony\Component\HttpClient\Chunk\FirstChunk; +use Symfony\Component\HttpClient\Chunk\LastChunk; use Symfony\Component\HttpClient\Chunk\ServerSentEvent; use Symfony\Component\HttpClient\EventSourceHttpClient; use Symfony\Component\HttpClient\Exception\EventSourceException; +use Symfony\Component\HttpClient\MockHttpClient; use Symfony\Component\HttpClient\Response\MockResponse; use Symfony\Component\HttpClient\Response\ResponseStream; use Symfony\Contracts\HttpClient\HttpClientInterface; @@ -34,7 +36,11 @@ class EventSourceHttpClientTest extends TestCase */ public function testGetServerSentEvents(string $sep) { - $rawData = <<assertSame(['Accept: text/event-stream', 'Cache-Control: no-cache'], $options['headers']); + + return new MockResponse([ + str_replace("\n", $sep, << false, 'http_method' => 'GET', 'url' => 'http://localhost:8080/events', 'response_headers' => ['content-type: text/event-stream']]); - $responseStream = new ResponseStream((function () use ($response, $chunk) { - yield $response => new FirstChunk(); - yield $response => $chunk; - yield $response => new ErrorChunk(0, 'timeout'); - })()); - - $hasCorrectHeaders = function ($options) { - $this->assertSame(['Accept: text/event-stream', 'Cache-Control: no-cache'], $options['headers']); - - return true; - }; - - $httpClient = $this->createMock(HttpClientInterface::class); - $httpClient->method('request')->with('GET', 'http://localhost:8080/events', $this->callback($hasCorrectHeaders))->willReturn($response); - - $httpClient->method('stream')->willReturn($responseStream); - - $es = new EventSourceHttpClient($httpClient); +TXT + ), + ], [ + 'canceled' => false, + 'http_method' => 'GET', + 'url' => 'http://localhost:8080/events', + 'response_headers' => ['content-type: text/event-stream'], + ]); + })); $res = $es->connect('http://localhost:8080/events'); $expected = [ new FirstChunk(), new ServerSentEvent(str_replace("\n", $sep, "event: builderror\nid: 46\ndata: {\"foo\": \"bar\"}\n\n")), new ServerSentEvent(str_replace("\n", $sep, "event: reload\nid: 47\ndata: {}\n\n")), - new ServerSentEvent(str_replace("\n", $sep, "event: reload\nid: 48\ndata: {}\n\n")), + new DataChunk(-1, str_replace("\n", $sep, ": this is a oneline comment\n\n")), + new DataChunk(-1, str_replace("\n", $sep, ": this is a\n: multiline comment\n\n")), + new ServerSentEvent(str_replace("\n", $sep, ": comments are ignored\nevent: reload\n: anywhere\nid: 48\ndata: {}\n\n")), new ServerSentEvent(str_replace("\n", $sep, "data: test\ndata:test\nid: 49\nevent: testEvent\n\n\n")), new ServerSentEvent(str_replace("\n", $sep, "id: 50\ndata: \ndata\ndata: \ndata\ndata: \n\n")), + new DataChunk(-1, str_replace("\n", $sep, "id: 60\ndata")), + new LastChunk("\r\n" === $sep ? 355 : 322), ]; - $i = 0; - - $this->expectExceptionMessage('Response has been canceled'); - while ($res) { - if ($i > 0) { - $res->cancel(); - } - foreach ($es->stream($res) as $chunk) { - if ($chunk->isTimeout()) { - continue; - } - - if ($chunk->isLast()) { - continue; - } - - $this->assertEquals($expected[$i++], $chunk); - } + foreach ($es->stream($res) as $chunk) { + $this->assertEquals(array_shift($expected), $chunk); } + $this->assertSame([], $expected); } /** From 59f83312daa5bdf3cd934b4285eece0ebcdeabcd Mon Sep 17 00:00:00 2001 From: Andrey Lebedev Date: Tue, 18 Jun 2024 19:38:55 +0400 Subject: [PATCH 33/79] [Notifier] [Lox24] Fix request body format to JSON string --- .../Notifier/Bridge/Lox24/Lox24Transport.php | 2 +- .../Component/Notifier/Bridge/Lox24/README.md | 2 +- .../Bridge/Lox24/Tests/Lox24TransportTest.php | 100 +++++++++++------- 3 files changed, 63 insertions(+), 41 deletions(-) diff --git a/src/Symfony/Component/Notifier/Bridge/Lox24/Lox24Transport.php b/src/Symfony/Component/Notifier/Bridge/Lox24/Lox24Transport.php index 31c71c8d9c6a6..19ac34b5edaf4 100644 --- a/src/Symfony/Component/Notifier/Bridge/Lox24/Lox24Transport.php +++ b/src/Symfony/Component/Notifier/Bridge/Lox24/Lox24Transport.php @@ -101,7 +101,7 @@ protected function doSend(MessageInterface $message): SentMessage 'Content-Type' => 'application/json', 'User-Agent' => 'LOX24 Symfony Notifier', ], - 'body' => $body, + 'json' => $body, ]); try { diff --git a/src/Symfony/Component/Notifier/Bridge/Lox24/README.md b/src/Symfony/Component/Notifier/Bridge/Lox24/README.md index 8d84187fcf46a..5a02bc10a2aa3 100644 --- a/src/Symfony/Component/Notifier/Bridge/Lox24/README.md +++ b/src/Symfony/Component/Notifier/Bridge/Lox24/README.md @@ -8,7 +8,7 @@ DSN example ----------- ``` -LOX24_DSN=lox24://USER:TOKEN@default?from=FROM&type=SERVICE_CODE&voice_lang=VOICE_LANGUAGE&delete_text=DELETE_TEXT&callback_data=CALLBACK_DATA +LOX24_DSN=lox24://USER:TOKEN@default?from=FROM&type=TYPE&voice_lang=VOICE_LANGUAGE&delete_text=DELETE_TEXT&callback_data=CALLBACK_DATA ``` where: diff --git a/src/Symfony/Component/Notifier/Bridge/Lox24/Tests/Lox24TransportTest.php b/src/Symfony/Component/Notifier/Bridge/Lox24/Tests/Lox24TransportTest.php index 3db43acbcdcc3..f24fc9b31c7e4 100644 --- a/src/Symfony/Component/Notifier/Bridge/Lox24/Tests/Lox24TransportTest.php +++ b/src/Symfony/Component/Notifier/Bridge/Lox24/Tests/Lox24TransportTest.php @@ -9,8 +9,9 @@ * file that was distributed with this source code. */ -use PHPUnit\Framework\MockObject\MockObject; use Symfony\Component\HttpClient\MockHttpClient; +use Symfony\Component\HttpClient\Response\JsonMockResponse; +use Symfony\Component\HttpClient\Response\MockResponse; use Symfony\Component\Notifier\Bridge\Lox24\Lox24Options; use Symfony\Component\Notifier\Bridge\Lox24\Lox24Transport; use Symfony\Component\Notifier\Bridge\Lox24\Type; @@ -25,7 +26,6 @@ use Symfony\Component\Notifier\Message\SmsMessage; use Symfony\Component\Notifier\Test\TransportTestCase; use Symfony\Contracts\HttpClient\HttpClientInterface; -use Symfony\Contracts\HttpClient\ResponseInterface; /** * @author Andrei Lebedev @@ -48,13 +48,6 @@ class Lox24TransportTest extends TransportTestCase 'service_code' => 'direct', ]; - private MockObject|HttpClientInterface $client; - - protected function setUp(): void - { - $this->client = $this->createMock(HttpClientInterface::class); - } - public static function createTransport(?HttpClientInterface $client = null): Lox24Transport { return (new Lox24Transport('user', 'token', 'sender', ['type' => 'voice'], $client ?? new MockHttpClient()))->setHost('host.test'); @@ -102,7 +95,7 @@ public function testSendWithInvalidMessageType() public function testMessageFromNotEmpty() { - $this->assertRequestBody([ + $client = $this->mockHttpClient([ 'sender_id' => 'testFrom2', 'phone' => '+1411111111', 'text' => 'test text', @@ -110,14 +103,15 @@ public function testMessageFromNotEmpty() 'delivery_at' => 0, 'service_code' => 'direct', ], [], 201, ['uuid' => '123456']); - $transport = new Lox24Transport('user', 'token', 'testFrom', [], $this->client); + + $transport = new Lox24Transport('user', 'token', 'testFrom', [], $client); $message = new SmsMessage('+1411111111', 'test text', 'testFrom2'); $transport->send($message); } public function testMessageFromEmpty() { - $this->assertRequestBody([ + $client = $this->mockHttpClient([ 'sender_id' => 'testFrom', 'phone' => '+1411111111', 'text' => 'test text', @@ -125,7 +119,7 @@ public function testMessageFromEmpty() 'delivery_at' => 0, 'service_code' => 'direct', ], [], 201, ['uuid' => '123456']); - $transport = new Lox24Transport('user', 'token', 'testFrom', [], $this->client); + $transport = new Lox24Transport('user', 'token', 'testFrom', [], $client); $message = new SmsMessage('+1411111111', 'test text'); $transport->send($message); } @@ -143,7 +137,7 @@ public function testMessageFromInvalid() public function testOptionIsTextDeleted() { - $this->assertRequestBody([ + $client = $this->mockHttpClient([ 'sender_id' => 'testFrom', 'phone' => '+1411111111', 'text' => 'test text', @@ -151,7 +145,7 @@ public function testOptionIsTextDeleted() 'delivery_at' => 0, 'service_code' => 'direct', ], [], 201, ['uuid' => '123456']); - $transport = new Lox24Transport('user', 'token', 'testFrom', [], $this->client); + $transport = new Lox24Transport('user', 'token', 'testFrom', [], $client); $options = (new Lox24Options())->deleteTextAfterSending(true); $message = new SmsMessage('+1411111111', 'test text'); @@ -162,7 +156,7 @@ public function testOptionIsTextDeleted() public function testOptionDeliveryAtGreaterThanZero() { - $this->assertRequestBody([ + $client = $this->mockHttpClient([ 'sender_id' => 'testFrom', 'phone' => '+1411111111', 'text' => 'test text', @@ -170,7 +164,7 @@ public function testOptionDeliveryAtGreaterThanZero() 'delivery_at' => 1000000000, 'service_code' => 'direct', ], [], 201, ['uuid' => '123456']); - $transport = new Lox24Transport('user', 'token', 'testFrom', [], $this->client); + $transport = new Lox24Transport('user', 'token', 'testFrom', [], $client); $options = (new Lox24Options())->deliveryAt((new DateTimeImmutable())->setTimestamp(1000000000)); $message = new SmsMessage('+1411111111', 'test text'); @@ -181,7 +175,7 @@ public function testOptionDeliveryAtGreaterThanZero() public function testOptionVoiceLanguageSpanish() { - $this->assertRequestBody([ + $client = $this->mockHttpClient([ 'sender_id' => 'testFrom', 'phone' => '+1411111111', 'text' => 'test text', @@ -190,7 +184,7 @@ public function testOptionVoiceLanguageSpanish() 'service_code' => 'text2speech', 'voice_lang' => 'ES', ], [], 201, ['uuid' => '123456']); - $transport = new Lox24Transport('user', 'token', 'testFrom', [], $this->client); + $transport = new Lox24Transport('user', 'token', 'testFrom', [], $client); $options = (new Lox24Options()) ->voiceLanguage(VoiceLanguage::Spanish) @@ -203,7 +197,7 @@ public function testOptionVoiceLanguageSpanish() public function testOptionVoiceLanguageAuto() { - $this->assertRequestBody([ + $client = $this->mockHttpClient([ 'sender_id' => 'testFrom', 'phone' => '+1411111111', 'text' => 'test text', @@ -211,7 +205,7 @@ public function testOptionVoiceLanguageAuto() 'delivery_at' => 0, 'service_code' => 'text2speech', ], [], 201, ['uuid' => '123456']); - $transport = new Lox24Transport('user', 'token', 'testFrom', [], $this->client); + $transport = new Lox24Transport('user', 'token', 'testFrom', [], $client); $options = (new Lox24Options()) ->voiceLanguage(VoiceLanguage::Auto) @@ -224,7 +218,7 @@ public function testOptionVoiceLanguageAuto() public function testOptionType() { - $this->assertRequestBody([ + $client = $this->mockHttpClient([ 'sender_id' => 'testFrom', 'phone' => '+1411111111', 'text' => 'test text', @@ -233,7 +227,7 @@ public function testOptionType() 'service_code' => 'direct', ], [], 201, ['uuid' => '123456']); - $transport = new Lox24Transport('user', 'token', 'testFrom', ['type' => 'voice'], $this->client); + $transport = new Lox24Transport('user', 'token', 'testFrom', ['type' => 'voice'], $client); $options = (new Lox24Options())->type(Type::Sms); $message = new SmsMessage('+1411111111', 'test text'); @@ -244,7 +238,7 @@ public function testOptionType() public function testOptionCallbackData() { - $this->assertRequestBody([ + $client = $this->mockHttpClient([ 'sender_id' => 'testFrom', 'phone' => '+1411111111', 'text' => 'test text', @@ -254,7 +248,7 @@ public function testOptionCallbackData() 'callback_data' => 'callback_data', ], [], 201, ['uuid' => '123456']); - $transport = new Lox24Transport('user', 'token', 'testFrom', ['type' => 'voice'], $this->client); + $transport = new Lox24Transport('user', 'token', 'testFrom', ['type' => 'voice'], $client); $options = (new Lox24Options())->callbackData('callback_data'); $message = new SmsMessage('+1411111111', 'test text'); @@ -270,7 +264,7 @@ public function testResponseStatusCodeNotEqual201() 'Unable to send the SMS: "service_code: Service\'s code is invalid or unavailable.".' ); - $this->assertRequestBody([ + $client = $this->mockHttpClient([ 'sender_id' => 'testFrom', 'phone' => '+1411111111', 'text' => 'test text', @@ -294,28 +288,56 @@ public function testResponseStatusCodeNotEqual201() ], ); - $transport = new Lox24Transport('user', 'token', 'testFrom', [], $this->client); + $transport = new Lox24Transport('user', 'token', 'testFrom', [], $client); $message = new SmsMessage('+1411111111', 'test text'); $transport->send($message); } - private function assertRequestBody( + public function mockHttpClient( array $bodyOverride = [], array $headersOverride = [], int $responseStatus = 200, array $responseContent = [], - ): void { - $body = array_merge(self::REQUEST_BODY, $bodyOverride); + ): MockHttpClient { + $body = json_encode(array_merge(self::REQUEST_BODY, $bodyOverride)); $headers = array_merge(self::REQUEST_HEADERS, $headersOverride); - $response = $this->createMock(ResponseInterface::class); - $response->expects($this->once())->method('getStatusCode')->willReturn($responseStatus); - $response->expects($this->once())->method('toArray')->willReturn($responseContent); - $this->client->expects($this->once()) - ->method('request') - ->with('POST', 'https://api.lox24.eu/sms', [ - 'body' => $body, - 'headers' => $headers, - ])->willReturn($response); + + $factory = function ($method, $url, $options) use ( + $body, + $headers, + $responseStatus, + $responseContent + ): MockResponse { + $this->assertSame('POST', $method); + $this->assertSame('https://api.lox24.eu/sms', $url); + $this->assertHeaders($headers, $options['headers']); + $this->assertJsonStringEqualsJsonString($body, $options['body']); + + return new JsonMockResponse($responseContent, [ + 'http_code' => $responseStatus, + 'headers' => ['content-type' => 'application/json'], + ]); + }; + + return new MockHttpClient($factory); + } + + private function assertHeaders(array $expected, array $headers): void + { + foreach ($this->normalizeHeaders($expected) as $expectedHeader) { + $headerExists = in_array($expectedHeader, $headers, true); + $this->assertTrue($headerExists, "Header '$expectedHeader' not found in request's headers"); + } + } + + private function normalizeHeaders(array $headers): array + { + $normalized = []; + foreach ($headers as $key => $value) { + $normalized[] = $key.': '.$value; + } + + return $normalized; } } From 69e8b2f9f0cb6669d13bc4420abf5eadcfec4fa7 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Fri, 21 Jun 2024 11:18:42 +0200 Subject: [PATCH 34/79] Sync php-cs-fixer config file with 7.2 --- .php-cs-fixer.dist.php | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php index 853399385adc0..6d5d4c2dfa0a5 100644 --- a/.php-cs-fixer.dist.php +++ b/.php-cs-fixer.dist.php @@ -29,9 +29,11 @@ '@Symfony' => true, '@Symfony:risky' => true, 'protected_to_private' => false, - 'native_constant_invocation' => ['strict' => false], - 'nullable_type_declaration_for_default_null_value' => true, 'header_comment' => ['header' => $fileHeaderComment], + // TODO: Remove once the "compiler_optimized" set includes "sprintf" + 'native_function_invocation' => ['include' => ['@compiler_optimized', 'sprintf'], 'scope' => 'namespaced', 'strict' => true], + 'nullable_type_declaration' => true, + 'nullable_type_declaration_for_default_null_value' => true, ]) ->setRiskyAllowed(true) ->setFinder( @@ -40,29 +42,27 @@ ->append([__FILE__]) ->notPath('#/Fixtures/#') ->exclude([ - // directories containing files with content that is autogenerated by `var_export`, which breaks CS in output code - // fixture templates - 'Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/Resources/Custom', - // resource templates - 'Symfony/Bundle/FrameworkBundle/Resources/views/Form', // explicit trigger_error tests 'Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/', 'Symfony/Component/Intl/Resources/data/', ]) + // explicit tests for ommited @param type, against `no_superfluous_phpdoc_tags` + ->notPath('Symfony/Component/PropertyInfo/Tests/Extractor/PhpDocExtractorTest.php') + ->notPath('Symfony/Component/PropertyInfo/Tests/Extractor/PhpStanExtractorTest.php') // Support for older PHPunit version ->notPath('Symfony/Bridge/PhpUnit/SymfonyTestsListener.php') ->notPath('#Symfony/Bridge/PhpUnit/.*Mock\.php#') ->notPath('#Symfony/Bridge/PhpUnit/.*Legacy#') // file content autogenerated by `var_export` ->notPath('Symfony/Component/Translation/Tests/fixtures/resources.php') - // file content autogenerated by `VarExporter::export` - ->notPath('Symfony/Component/Serializer/Tests/Fixtures/serializer.class.metadata.php') - // test template - ->notPath('Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/Resources/Custom/_name_entry_label.html.php') // explicit trigger_error tests ->notPath('Symfony/Component/ErrorHandler/Tests/DebugClassLoaderTest.php') // stop removing spaces on the end of the line in strings ->notPath('Symfony/Component/Messenger/Tests/Command/FailedMessagesShowCommandTest.php') + // svg + ->notPath('Symfony/Component/ErrorHandler/Resources/assets/images/symfony-ghost.svg.php') + // HTML templates + ->notPath('#Symfony/.*\.html\.php#') ) ->setCacheFile('.php-cs-fixer.cache') ; From 79b03dbdc42892379bf3a1567b202e13219b3cab Mon Sep 17 00:00:00 2001 From: MatTheCat Date: Fri, 21 Jun 2024 15:04:29 +0200 Subject: [PATCH 35/79] [DoctrineBridge] Test reset with a true manager --- .../Doctrine/Tests/Fixtures/DummyManager.php | 69 +++++++++++++++++++ .../Doctrine/Tests/ManagerRegistryTest.php | 25 ++++--- 2 files changed, 85 insertions(+), 9 deletions(-) create mode 100644 src/Symfony/Bridge/Doctrine/Tests/Fixtures/DummyManager.php diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/DummyManager.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/DummyManager.php new file mode 100644 index 0000000000000..04e5a2acdd334 --- /dev/null +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/DummyManager.php @@ -0,0 +1,69 @@ +testDumpContainerWithProxyServiceWillShareProxies(); + $container = new ContainerBuilder(); + + $container->register('foo', DummyManager::class)->setPublic(true); + $container->getDefinition('foo')->setLazy(true); + $container->compile(); + + $dumper = new PhpDumper($container); + $dumper->setProxyDumper(new ProxyDumper()); + eval('?>'.$dumper->dump(['class' => 'LazyServiceDoctrineBridgeContainer'])); } public function testResetService() { - $container = new \LazyServiceProjectServiceContainer(); + $container = new \LazyServiceDoctrineBridgeContainer(); $registry = new TestManagerRegistry('name', [], ['defaultManager' => 'foo'], 'defaultConnection', 'defaultManager', 'proxyInterfaceName'); $registry->setTestContainer($container); @@ -44,8 +51,8 @@ public function testResetService() $registry->resetManager(); $this->assertSame($foo, $container->get('foo')); - $this->assertInstanceOf(\stdClass::class, $foo); - $this->assertFalse(property_exists($foo, 'bar')); + $this->assertInstanceOf(DummyManager::class, $foo); + $this->assertFalse(isset($foo->bar)); } /** @@ -77,7 +84,7 @@ public function testResetServiceWillNotNestFurtherLazyServicesWithinEachOther() $service = $container->get('foo'); - self::assertInstanceOf(\stdClass::class, $service); + self::assertInstanceOf(DummyManager::class, $service); self::assertInstanceOf(LazyLoadingInterface::class, $service); self::assertInstanceOf(ValueHolderInterface::class, $service); self::assertFalse($service->isProxyInitialized()); @@ -91,7 +98,7 @@ public function testResetServiceWillNotNestFurtherLazyServicesWithinEachOther() $service->initializeProxy(); $wrappedValue = $service->getWrappedValueHolderValue(); - self::assertInstanceOf(\stdClass::class, $wrappedValue); + self::assertInstanceOf(DummyManager::class, $wrappedValue); self::assertNotInstanceOf(LazyLoadingInterface::class, $wrappedValue); self::assertNotInstanceOf(ValueHolderInterface::class, $wrappedValue); } @@ -104,7 +111,7 @@ private function dumpLazyServiceProjectAsFilesServiceContainer() $container = new ContainerBuilder(); - $container->register('foo', \stdClass::class) + $container->register('foo', DummyManager::class) ->setPublic(true) ->setLazy(true); $container->compile(); From 5f16bbdce11dc2b6b28f6e41046cd2c1b85d1c90 Mon Sep 17 00:00:00 2001 From: MatTheCat Date: Thu, 20 Jun 2024 16:39:18 +0200 Subject: [PATCH 36/79] =?UTF-8?q?[SecurityBundle]=20Add=20`provider`=20XML?= =?UTF-8?q?=20attribute=20to=20the=20authenticators=20it=E2=80=99s=20missi?= =?UTF-8?q?ng=20from?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../SecurityBundle/Resources/config/schema/security-1.0.xsd | 1 + .../Tests/DependencyInjection/Fixtures/xml/container1.xml | 3 +-- .../DependencyInjection/Fixtures/xml/firewall_provider.xml | 2 +- .../Fixtures/xml/firewall_undefined_provider.xml | 2 +- .../DependencyInjection/Fixtures/xml/legacy_container1.xml | 3 +-- .../Tests/DependencyInjection/Fixtures/xml/legacy_encoders.xml | 3 +-- .../DependencyInjection/Fixtures/xml/listener_provider.xml | 2 +- .../Fixtures/xml/listener_undefined_provider.xml | 2 +- .../Fixtures/xml/no_custom_user_checker.xml | 1 - 9 files changed, 8 insertions(+), 11 deletions(-) diff --git a/src/Symfony/Bundle/SecurityBundle/Resources/config/schema/security-1.0.xsd b/src/Symfony/Bundle/SecurityBundle/Resources/config/schema/security-1.0.xsd index 70b682e4065ca..1a367b8397213 100644 --- a/src/Symfony/Bundle/SecurityBundle/Resources/config/schema/security-1.0.xsd +++ b/src/Symfony/Bundle/SecurityBundle/Resources/config/schema/security-1.0.xsd @@ -230,6 +230,7 @@ + diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/container1.xml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/container1.xml index c97dd5bf7ebf0..01ecdbaecc5c4 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/container1.xml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/container1.xml @@ -64,9 +64,8 @@ - + - app.user_checker ROLE_USER diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/firewall_provider.xml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/firewall_provider.xml index 6f74984045970..66da3c4a28307 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/firewall_provider.xml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/firewall_provider.xml @@ -15,7 +15,7 @@ - + diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/firewall_undefined_provider.xml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/firewall_undefined_provider.xml index a80f613e00331..a55ffdacc2fc3 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/firewall_undefined_provider.xml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/firewall_undefined_provider.xml @@ -15,7 +15,7 @@ - + diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/legacy_container1.xml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/legacy_container1.xml index ed7afe5e833ee..15f27b4ff1351 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/legacy_container1.xml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/legacy_container1.xml @@ -66,10 +66,9 @@ - + - app.user_checker ROLE_USER diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/legacy_encoders.xml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/legacy_encoders.xml index a362a59a15b80..cb5c04b7f82aa 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/legacy_encoders.xml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/legacy_encoders.xml @@ -66,10 +66,9 @@ - + - app.user_checker ROLE_USER diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/listener_provider.xml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/listener_provider.xml index b45f378a5ba68..d4a6a1d41aa47 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/listener_provider.xml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/listener_provider.xml @@ -15,7 +15,7 @@ - + diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/listener_undefined_provider.xml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/listener_undefined_provider.xml index bdf9d5ec837f0..312cb803960d2 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/listener_undefined_provider.xml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/listener_undefined_provider.xml @@ -15,7 +15,7 @@ - + diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/no_custom_user_checker.xml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/no_custom_user_checker.xml index c4dea529ba452..fe81171b56977 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/no_custom_user_checker.xml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/no_custom_user_checker.xml @@ -22,7 +22,6 @@ - From 61be333f68acc4b9b6b232888e4adb12675e5ead Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Fri, 21 Jun 2024 09:25:59 +0200 Subject: [PATCH 37/79] [Mailer] Document the usage of custom headers in Infobip bridge --- .../Component/Mailer/Bridge/Infobip/README.md | 24 +++++++++++++++---- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/src/Symfony/Component/Mailer/Bridge/Infobip/README.md b/src/Symfony/Component/Mailer/Bridge/Infobip/README.md index c86458c3991f9..b040f259e20b5 100644 --- a/src/Symfony/Component/Mailer/Bridge/Infobip/README.md +++ b/src/Symfony/Component/Mailer/Bridge/Infobip/README.md @@ -12,10 +12,24 @@ MAILER_DSN=infobip+api://KEY@BASE_URL MAILER_DSN=infobip+smtp://KEY@default ``` +Custom Headers +-------------- + +This transport supports the following custom headers: + +| Header | Type | Description | +| -------------------------------- | ------- | -------------------------------------------------------------------------------------------- | +| `X-Infobip-IntermediateReport` | boolean | The real-time Intermediate delivery report that will be sent on your callback server. | +| `X-Infobip-NotifyUrl` | string | The URL on your callback server on which the Delivery report will be sent. | +| `X-Infobip-NotifyContentType` | string | Preferred Delivery report content type. Can be application/json or application/xml. | +| `X-Infobip-MessageId` | string | The ID that uniquely identifies the message sent to a recipient. | +| `X-Infobip-Track` | boolean | Enable or disable open and click tracking. | + Resources --------- -* [Infobip Api Docs](https://www.infobip.com/docs/api#channels/email) -* [Contributing](https://symfony.com/doc/current/contributing/index.html) -* [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) + + * [Infobip Api Docs](https://www.infobip.com/docs/api#channels/email) + * [Contributing](https://symfony.com/doc/current/contributing/index.html) + * [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) From 0fc7293c30ff456e81f0067cafcc52ac8e070d2e Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Sat, 22 Jun 2024 07:12:06 +0200 Subject: [PATCH 38/79] change test to use a real ObjectManager --- .../Tests/LegacyManagerRegistryTest.php | 26 ++++++++++++------- .../Doctrine/Tests/ManagerRegistryTest.php | 4 --- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/src/Symfony/Bridge/Doctrine/Tests/LegacyManagerRegistryTest.php b/src/Symfony/Bridge/Doctrine/Tests/LegacyManagerRegistryTest.php index d34266515a4d7..87965db3c44d4 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/LegacyManagerRegistryTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/LegacyManagerRegistryTest.php @@ -11,11 +11,12 @@ namespace Symfony\Bridge\Doctrine\Tests; +use Doctrine\Persistence\ObjectManager; use PHPUnit\Framework\TestCase; use ProxyManager\Proxy\LazyLoadingInterface; use ProxyManager\Proxy\ValueHolderInterface; +use Symfony\Bridge\Doctrine\Tests\Fixtures\DummyManager; use Symfony\Bridge\ProxyManager\LazyProxy\PhpDumper\ProxyDumper; -use Symfony\Bridge\ProxyManager\Tests\LazyProxy\Dumper\PhpDumperTest; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\Dumper\PhpDumper; @@ -28,9 +29,16 @@ class LegacyManagerRegistryTest extends TestCase { public static function setUpBeforeClass(): void { - if (!class_exists(\LazyServiceProjectServiceContainer::class, false)) { - eval('?>'.PhpDumperTest::dumpLazyServiceProjectServiceContainer()); - } + $container = new ContainerBuilder(); + + $container->register('foo', DummyManager::class)->setPublic(true); + $container->getDefinition('foo')->setLazy(true)->addTag('proxy', ['interface' => ObjectManager::class]); + $container->compile(); + + $dumper = new PhpDumper($container); + $dumper->setProxyDumper(new ProxyDumper()); + + eval('?>'.$dumper->dump(['class' => 'LazyServiceProjectServiceContainer'])); } public function testResetService() @@ -47,8 +55,8 @@ public function testResetService() $registry->resetManager(); $this->assertSame($foo, $container->get('foo')); - $this->assertInstanceOf(\stdClass::class, $foo); - $this->assertFalse(property_exists($foo, 'bar')); + $this->assertInstanceOf(ObjectManager::class, $foo); + $this->assertFalse(isset($foo->bar)); } /** @@ -80,7 +88,7 @@ public function testResetServiceWillNotNestFurtherLazyServicesWithinEachOther() $service = $container->get('foo'); - self::assertInstanceOf(\stdClass::class, $service); + self::assertInstanceOf(DummyManager::class, $service); self::assertInstanceOf(LazyLoadingInterface::class, $service); self::assertInstanceOf(ValueHolderInterface::class, $service); self::assertFalse($service->isProxyInitialized()); @@ -94,7 +102,7 @@ public function testResetServiceWillNotNestFurtherLazyServicesWithinEachOther() $service->initializeProxy(); $wrappedValue = $service->getWrappedValueHolderValue(); - self::assertInstanceOf(\stdClass::class, $wrappedValue); + self::assertInstanceOf(DummyManager::class, $wrappedValue); self::assertNotInstanceOf(LazyLoadingInterface::class, $wrappedValue); self::assertNotInstanceOf(ValueHolderInterface::class, $wrappedValue); } @@ -107,7 +115,7 @@ private function dumpLazyServiceProjectAsFilesServiceContainer() $container = new ContainerBuilder(); - $container->register('foo', \stdClass::class) + $container->register('foo', DummyManager::class) ->setPublic(true) ->setLazy(true); $container->compile(); diff --git a/src/Symfony/Bridge/Doctrine/Tests/ManagerRegistryTest.php b/src/Symfony/Bridge/Doctrine/Tests/ManagerRegistryTest.php index a8d1f73893f72..fa44ba0a00bbb 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/ManagerRegistryTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/ManagerRegistryTest.php @@ -13,11 +13,7 @@ use Doctrine\Persistence\ObjectManager; use PHPUnit\Framework\TestCase; -use ProxyManager\Proxy\LazyLoadingInterface; -use ProxyManager\Proxy\ValueHolderInterface; -use Symfony\Bridge\Doctrine\ManagerRegistry; use Symfony\Bridge\Doctrine\Tests\Fixtures\DummyManager; -use Symfony\Bridge\ProxyManager\LazyProxy\PhpDumper\ProxyDumper; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\Dumper\PhpDumper; From f2883baf12bc598ed492461453946f98a3927ebb Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Sat, 22 Jun 2024 09:42:41 +0200 Subject: [PATCH 39/79] fix test --- .../Tests/Constraints/UniqueValidatorTest.php | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/Symfony/Component/Validator/Tests/Constraints/UniqueValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/UniqueValidatorTest.php index 8861d44bffe19..0d5ccdb8e56e2 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/UniqueValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/UniqueValidatorTest.php @@ -246,14 +246,14 @@ public static function getInvalidFieldNames(): array /** * @dataProvider getInvalidCollectionValues */ - public function testInvalidCollectionValues(array $value, array $fields) + public function testInvalidCollectionValues(array $value, array $fields, string $expectedMessageParam) { $this->validator->validate($value, new Unique([ 'message' => 'myMessage', ], fields: $fields)); $this->buildViolation('myMessage') - ->setParameter('{{ value }}', 'array') + ->setParameter('{{ value }}', $expectedMessageParam) ->setCode(Unique::IS_NOT_UNIQUE) ->assertRaised(); } @@ -264,23 +264,25 @@ public static function getInvalidCollectionValues(): array 'unique string' => [[ ['lang' => 'eng', 'translation' => 'hi'], ['lang' => 'eng', 'translation' => 'hello'], - ], ['lang']], + ], ['lang'], 'array'], 'unique floats' => [[ ['latitude' => 51.509865, 'longitude' => -0.118092, 'poi' => 'capital'], ['latitude' => 52.520008, 'longitude' => 13.404954], ['latitude' => 51.509865, 'longitude' => -0.118092], - ], ['latitude', 'longitude']], + ], ['latitude', 'longitude'], 'array'], 'unique int' => [[ ['id' => 1, 'email' => 'bar@email.com'], ['id' => 1, 'email' => 'foo@email.com'], - ], ['id']], + ], ['id'], 'array'], 'unique null' => [ [null, null], [], + 'null', ], 'unique field null' => [ [['nullField' => null], ['nullField' => null]], ['nullField'], + 'array', ], ]; } From 5b800258e1979452950aa6f793d390b8914ca29f Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Sat, 22 Jun 2024 09:48:46 +0200 Subject: [PATCH 40/79] fix merge --- .../Tests/Normalizer/AbstractObjectNormalizerTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php index 67f7e3d1d1d51..f5c0b07532f92 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php @@ -1522,7 +1522,7 @@ protected function extractAttributes(object $object, ?string $format = null, arr return []; } - protected function getAttributeValue(object $object, string $attribute, ?string $format = null, array $context = []) + protected function getAttributeValue(object $object, string $attribute, ?string $format = null, array $context = []): mixed { return null; } From 8594b2fd1a9d28f6b7b155fab0e321b6d629aaf7 Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Thu, 13 Jun 2024 13:09:00 +0200 Subject: [PATCH 41/79] [VarDumper] Fix FFI caster test --- .../Component/VarDumper/Caster/FFICaster.php | 14 ++++++++++++-- .../VarDumper/Tests/Caster/FFICasterTest.php | 18 ++---------------- 2 files changed, 14 insertions(+), 18 deletions(-) diff --git a/src/Symfony/Component/VarDumper/Caster/FFICaster.php b/src/Symfony/Component/VarDumper/Caster/FFICaster.php index f1984eef368ee..ffed9f315ba56 100644 --- a/src/Symfony/Component/VarDumper/Caster/FFICaster.php +++ b/src/Symfony/Component/VarDumper/Caster/FFICaster.php @@ -115,11 +115,21 @@ private static function castFFIPointer(Stub $stub, CType $type, ?CData $data = n private static function castFFIStringValue(CData $data): string|CutStub { $result = []; + $ffi = \FFI::cdef(<<zend_get_page_size(); + + // get cdata address + $start = $ffi->cast('uintptr_t', $ffi->cast('char*', $data))->cdata; + // accessing memory in the same page as $start is safe + $max = min(self::MAX_STRING_LENGTH, ($start | ($pageSize - 1)) - $start); + + for ($i = 0; $i < $max; ++$i) { $result[$i] = $data[$i]; - if ("\0" === $result[$i]) { + if ("\0" === $data[$i]) { return implode('', $result); } } diff --git a/src/Symfony/Component/VarDumper/Tests/Caster/FFICasterTest.php b/src/Symfony/Component/VarDumper/Tests/Caster/FFICasterTest.php index 5e7ec147bbbe6..d06b29963ce98 100644 --- a/src/Symfony/Component/VarDumper/Tests/Caster/FFICasterTest.php +++ b/src/Symfony/Component/VarDumper/Tests/Caster/FFICasterTest.php @@ -12,7 +12,6 @@ namespace Symfony\Component\VarDumper\Tests\Caster; use PHPUnit\Framework\TestCase; -use Symfony\Component\VarDumper\Caster\FFICaster; use Symfony\Component\VarDumper\Test\VarDumperTestTrait; /** @@ -191,34 +190,21 @@ public function testCastCuttedPointerToChar() PHP, $pointer); } - /** - * It is worth noting that such a test can cause SIGSEGV, as it breaks - * into "foreign" memory. However, this is only theoretical, since - * memory is allocated within the PHP process and almost always "garbage - * data" will be read from the PHP process itself. - * - * If this test fails for some reason, please report it: We may have to - * disable the dumping of strings ("char*") feature in VarDumper. - * - * @see FFICaster::castFFIStringValue() - */ public function testCastNonTrailingCharPointer() { $actualMessage = 'Hello World!'; $actualLength = \strlen($actualMessage); - $string = \FFI::cdef()->new('char['.$actualLength.']'); + $string = \FFI::cdef()->new('char['.($actualLength + 1).']'); $pointer = \FFI::addr($string[0]); - \FFI::memcpy($pointer, $actualMessage, $actualLength); - // Remove automatically addition of the trailing "\0" and remove trailing "\0" $pointer = \FFI::cdef()->cast('char*', \FFI::cdef()->cast('void*', $pointer)); $pointer[$actualLength] = "\x01"; $this->assertDumpMatchesFormat(<< size 8 align 8 { - cdata: "$actualMessage%s" + cdata: %A"$actualMessage%s" } PHP, $pointer); } From ce2b32e36f53c2bea6b3c0582074dba926db6ef8 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Sat, 22 Jun 2024 10:10:52 +0200 Subject: [PATCH 42/79] fix test --- src/Symfony/Bridge/Doctrine/Tests/LegacyManagerRegistryTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Bridge/Doctrine/Tests/LegacyManagerRegistryTest.php b/src/Symfony/Bridge/Doctrine/Tests/LegacyManagerRegistryTest.php index 87965db3c44d4..65944b406669c 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/LegacyManagerRegistryTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/LegacyManagerRegistryTest.php @@ -56,7 +56,7 @@ public function testResetService() $this->assertSame($foo, $container->get('foo')); $this->assertInstanceOf(ObjectManager::class, $foo); - $this->assertFalse(isset($foo->bar)); + $this->assertFalse(property_exists($foo, 'bar')); } /** From 836a93bb80c27eba397b998cf02f4344a3d9aae4 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Sat, 22 Jun 2024 10:46:19 +0200 Subject: [PATCH 43/79] fix merge --- .../Component/Serializer/Normalizer/ObjectNormalizer.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/Serializer/Normalizer/ObjectNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/ObjectNormalizer.php index 4f6d5f3b958bb..a663083dbac5a 100644 --- a/src/Symfony/Component/Serializer/Normalizer/ObjectNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/ObjectNormalizer.php @@ -192,7 +192,7 @@ protected function isAllowedAttribute($classOrObject, string $attribute, ?string if ($context['_read_attributes'] ?? true) { if (!isset(self::$isReadableCache[$class.$attribute])) { - self::$isReadableCache[$class.$attribute] = (\is_object($classOrObject) && $this->propertyAccessor->isReadable($classOrObject, $attribute)) || $this->hasAttributeAccessorMethod($class, $attribute); + self::$isReadableCache[$class.$attribute] = (\is_object($classOrObject) && $this->propertyAccessor->isReadable($classOrObject, $attribute)) || $this->propertyInfoExtractor->isReadable($class, $attribute) || $this->hasAttributeAccessorMethod($class, $attribute); } return self::$isReadableCache[$class.$attribute]; From 17c0709d5f395d35ec330fcba61e5caffc2a632c Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Sat, 22 Jun 2024 10:53:28 +0200 Subject: [PATCH 44/79] add missing method --- .../Tests/Normalizer/AbstractObjectNormalizerTest.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php index f5c0b07532f92..c8eab2a0820ff 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php @@ -1517,6 +1517,11 @@ public function __construct() parent::__construct(new ClassMetadataFactory(new AttributeLoader()), null, new PropertyInfoExtractor([], [new PhpDocExtractor(), new ReflectionExtractor()])); } + public function getSupportedTypes(?string $format): array + { + return ['*' => false]; + } + protected function extractAttributes(object $object, ?string $format = null, array $context = []): array { return []; From 2a46a7d515d278ffa80f48fcbefb64e32e4c7768 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Sat, 22 Jun 2024 13:31:15 +0200 Subject: [PATCH 45/79] fix tests --- .../FrameworkBundle/Tests/Functional/SerializerTest.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/SerializerTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/SerializerTest.php index 49019ab2a2b8c..9d75c5bf675a2 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/SerializerTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/SerializerTest.php @@ -44,6 +44,10 @@ public function testNormalizersAndEncodersUseDefaultContextConfigOption() $kernel = static::bootKernel(['test_case' => 'Serializer', 'root_config' => 'default_context.yaml']); foreach ($kernel->normalizersAndEncoders as $normalizerOrEncoderId) { + if (!static::getContainer()->has($normalizerOrEncoderId)) { + continue; + } + $normalizerOrEncoder = static::getContainer()->get($normalizerOrEncoderId); $reflectionObject = new \ReflectionObject($normalizerOrEncoder); @@ -68,7 +72,7 @@ class SerializerKernel extends AppKernel implements CompilerPassInterface 'serializer.normalizer.property.alias', // Special case as this normalizer isn't tagged ]; - public function process(ContainerBuilder $container) + public function process(ContainerBuilder $container): void { $services = array_merge( $container->findTaggedServiceIds('serializer.normalizer'), From 79265d64db3b7456f0fe40eb2aba361e288735af Mon Sep 17 00:00:00 2001 From: MrMicky Date: Sun, 23 Jun 2024 11:26:55 +0200 Subject: [PATCH 46/79] Add additional headers in Scaleway bridge --- .../Transport/ScalewayApiTransportTest.php | 3 +++ .../Transport/ScalewayApiTransport.php | 21 +++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/src/Symfony/Component/Mailer/Bridge/Scaleway/Tests/Transport/ScalewayApiTransportTest.php b/src/Symfony/Component/Mailer/Bridge/Scaleway/Tests/Transport/ScalewayApiTransportTest.php index 0d91d002cd21c..f31e041ea73d0 100644 --- a/src/Symfony/Component/Mailer/Bridge/Scaleway/Tests/Transport/ScalewayApiTransportTest.php +++ b/src/Symfony/Component/Mailer/Bridge/Scaleway/Tests/Transport/ScalewayApiTransportTest.php @@ -69,6 +69,8 @@ public function testSend() $this->assertSame('attachment.txt', $body['attachments'][0]['name']); $this->assertSame('text/plain', $body['attachments'][0]['type']); $this->assertSame(base64_encode('some attachment'), $body['attachments'][0]['content']); + $this->assertSame('Reply-To', $body['additional_headers'][0]['key']); + $this->assertStringContainsString('foo@bar.fr', $body['additional_headers'][0]['value']); return new JsonMockResponse(['emails' => [['message_id' => 'foobar']]], [ 'http_code' => 200, @@ -81,6 +83,7 @@ public function testSend() $mail->subject('Hello!') ->to(new Address('saif.gmati@symfony.com', 'Saif Eddin')) ->from(new Address('fabpot@symfony.com', 'Fabien')) + ->replyTo(new Address('foo@bar.fr', 'Foo')) ->text('Hello There!') ->addPart(new DataPart('some attachment', 'attachment.txt', 'text/plain')); diff --git a/src/Symfony/Component/Mailer/Bridge/Scaleway/Transport/ScalewayApiTransport.php b/src/Symfony/Component/Mailer/Bridge/Scaleway/Transport/ScalewayApiTransport.php index 60cb198d3b5ab..3c277abb6e719 100644 --- a/src/Symfony/Component/Mailer/Bridge/Scaleway/Transport/ScalewayApiTransport.php +++ b/src/Symfony/Component/Mailer/Bridge/Scaleway/Transport/ScalewayApiTransport.php @@ -101,6 +101,9 @@ private function getPayload(Email $email, Envelope $envelope): array if ($attachements = $this->prepareAttachments($email)) { $payload['attachments'] = $attachements; } + if ($headers = $this->getCustomHeaders($email)) { + $payload['additional_headers'] = $headers; + } return $payload; } @@ -122,6 +125,24 @@ private function prepareAttachments(Email $email): array return $attachments; } + private function getCustomHeaders(Email $email): array + { + $headers = []; + $headersToBypass = ['from', 'to', 'cc', 'bcc', 'subject', 'content-type', 'sender']; + foreach ($email->getHeaders()->all() as $name => $header) { + if (\in_array($name, $headersToBypass, true)) { + continue; + } + + $headers[] = [ + 'key' => $header->getName(), + 'value' => $header->getBodyAsString(), + ]; + } + + return $headers; + } + private function formatAddress(Address $address): array { $array = ['email' => $address->getAddress()]; From 765ecebd665744c766b810c007cc9034c673b1b6 Mon Sep 17 00:00:00 2001 From: Yanick Witschi Date: Mon, 24 Jun 2024 16:04:31 +0200 Subject: [PATCH 47/79] Double check if pcntl function exists --- .../Messenger/EventListener/DispatchPcntlSignalListener.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Symfony/Component/Messenger/EventListener/DispatchPcntlSignalListener.php b/src/Symfony/Component/Messenger/EventListener/DispatchPcntlSignalListener.php index 37b88ca1b852a..258ce64480344 100644 --- a/src/Symfony/Component/Messenger/EventListener/DispatchPcntlSignalListener.php +++ b/src/Symfony/Component/Messenger/EventListener/DispatchPcntlSignalListener.php @@ -21,6 +21,10 @@ class DispatchPcntlSignalListener implements EventSubscriberInterface { public function onWorkerRunning(): void { + if (!\function_exists('pcntl_signal_dispatch')) { + return; + } + pcntl_signal_dispatch(); } From 6630f5e06e5c80ae7576f1224c099e96607010f6 Mon Sep 17 00:00:00 2001 From: Nicolas PHILIPPE Date: Thu, 20 Jun 2024 09:38:29 +0200 Subject: [PATCH 48/79] [VarExporter] generate __doUnserialize() method in ProxyHelper::generateLazyProxy() --- .../Component/VarExporter/ProxyHelper.php | 27 ++++++++++- .../VarExporter/Tests/ProxyHelperTest.php | 45 +++++++++++++++++++ 2 files changed, 71 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/VarExporter/ProxyHelper.php b/src/Symfony/Component/VarExporter/ProxyHelper.php index 8d19d38373ce4..d5a8d7418b807 100644 --- a/src/Symfony/Component/VarExporter/ProxyHelper.php +++ b/src/Symfony/Component/VarExporter/ProxyHelper.php @@ -197,10 +197,35 @@ public static function generateLazyProxy(?\ReflectionClass $class, array $interf $body = $methods ? "\n".implode("\n\n", $methods)."\n" : ''; $propertyScopes = $class ? self::exportPropertyScopes($class->name) : '[]'; + if ( + $class?->hasMethod('__unserialize') + && !$class->getMethod('__unserialize')->getParameters()[0]->getType() + ) { + // fix contravariance type problem when $class declares a `__unserialize()` method without typehint. + $lazyProxyTraitStatement = <<__doUnserialize(\$data); + } + + EOPHP; + } else { + $lazyProxyTraitStatement = <<assertSame($expected, ProxyHelper::generateLazyProxy(null, [new \ReflectionClass(TestForProxyHelperInterface1::class), new \ReflectionClass(TestForProxyHelperInterface2::class)])); } + /** + * @dataProvider classWithUnserializeMagicMethodProvider + */ + public function testGenerateLazyProxyForClassWithUnserializeMagicMethod(object $obj, string $expected) + { + $this->assertStringContainsString($expected, ProxyHelper::generateLazyProxy(new \ReflectionClass($obj::class))); + } + + public static function classWithUnserializeMagicMethodProvider(): iterable + { + yield 'not type hinted __unserialize method' => [new class() { + public function __unserialize($array) + { + } + }, <<<'EOPHP' + implements \Symfony\Component\VarExporter\LazyObjectInterface + { + use \Symfony\Component\VarExporter\LazyProxyTrait { + __unserialize as private __doUnserialize; + } + + private const LAZY_OBJECT_PROPERTY_SCOPES = []; + + public function __unserialize($data): void + { + $this->__doUnserialize($data); + } + } + EOPHP]; + + yield 'type hinted __unserialize method' => [new class() { + public function __unserialize(array $array) + { + } + }, <<<'EOPHP' + implements \Symfony\Component\VarExporter\LazyObjectInterface + { + use \Symfony\Component\VarExporter\LazyProxyTrait; + + private const LAZY_OBJECT_PROPERTY_SCOPES = []; + } + EOPHP]; + } + public function testAttributes() { $expected = <<<'EOPHP' @@ -182,6 +226,7 @@ public function foo(#[\SensitiveParameter, AnotherAttribute] $a): int { } }); + $this->assertStringContainsString($expected, ProxyHelper::generateLazyProxy($class)); } From a94fe8208b835cae4c258ddae80a58372ee5946d Mon Sep 17 00:00:00 2001 From: eltharin Date: Fri, 21 Jun 2024 18:44:40 +0200 Subject: [PATCH 49/79] [Security] check token in payload instead just request --- .../IsCsrfTokenValidAttributeListener.php | 2 +- .../IsCsrfTokenValidAttributeListenerTest.php | 22 +++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/Security/Http/EventListener/IsCsrfTokenValidAttributeListener.php b/src/Symfony/Component/Security/Http/EventListener/IsCsrfTokenValidAttributeListener.php index 269c37709b547..3e05c71dbbcd5 100644 --- a/src/Symfony/Component/Security/Http/EventListener/IsCsrfTokenValidAttributeListener.php +++ b/src/Symfony/Component/Security/Http/EventListener/IsCsrfTokenValidAttributeListener.php @@ -46,7 +46,7 @@ public function onKernelControllerArguments(ControllerArgumentsEvent $event): vo foreach ($attributes as $attribute) { $id = $this->getTokenId($attribute->id, $request, $arguments); - if (!$this->csrfTokenManager->isTokenValid(new CsrfToken($id, $request->request->getString($attribute->tokenKey)))) { + if (!$this->csrfTokenManager->isTokenValid(new CsrfToken($id, $request->getPayload()->getString($attribute->tokenKey)))) { throw new InvalidCsrfTokenException('Invalid CSRF token.'); } } diff --git a/src/Symfony/Component/Security/Http/Tests/EventListener/IsCsrfTokenValidAttributeListenerTest.php b/src/Symfony/Component/Security/Http/Tests/EventListener/IsCsrfTokenValidAttributeListenerTest.php index cbbdc3b15fe62..00d464a6c69da 100644 --- a/src/Symfony/Component/Security/Http/Tests/EventListener/IsCsrfTokenValidAttributeListenerTest.php +++ b/src/Symfony/Component/Security/Http/Tests/EventListener/IsCsrfTokenValidAttributeListenerTest.php @@ -88,6 +88,28 @@ public function testIsCsrfTokenValidCalledCorrectly() $listener->onKernelControllerArguments($event); } + public function testIsCsrfTokenValidCalledCorrectlyInPayload() + { + $request = new Request(server: ['headers' => ['content-type' => 'application/json']], content: json_encode(['_token' => 'bar'])); + + $csrfTokenManager = $this->createMock(CsrfTokenManagerInterface::class); + $csrfTokenManager->expects($this->once()) + ->method('isTokenValid') + ->with(new CsrfToken('foo', 'bar')) + ->willReturn(true); + + $event = new ControllerArgumentsEvent( + $this->createMock(HttpKernelInterface::class), + [new IsCsrfTokenValidAttributeMethodsController(), 'withDefaultTokenKey'], + [], + $request, + null + ); + + $listener = new IsCsrfTokenValidAttributeListener($csrfTokenManager); + $listener->onKernelControllerArguments($event); + } + public function testIsCsrfTokenValidCalledCorrectlyWithCustomExpressionId() { $request = new Request(query: ['id' => '123'], request: ['_token' => 'bar']); From 656f4988478b8a7017459f93bd81c7c01bf50fd5 Mon Sep 17 00:00:00 2001 From: MatTheCat Date: Tue, 25 Jun 2024 13:50:27 +0200 Subject: [PATCH 50/79] =?UTF-8?q?[SecurityBundle]=20Remove=20unused=20memo?= =?UTF-8?q?ry=20users=E2=80=99=20`name`=20attribute=20from=20the=20XSD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../SecurityBundle/Resources/config/schema/security-1.0.xsd | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Symfony/Bundle/SecurityBundle/Resources/config/schema/security-1.0.xsd b/src/Symfony/Bundle/SecurityBundle/Resources/config/schema/security-1.0.xsd index d3d7752eeac0d..bf15a5db164ec 100644 --- a/src/Symfony/Bundle/SecurityBundle/Resources/config/schema/security-1.0.xsd +++ b/src/Symfony/Bundle/SecurityBundle/Resources/config/schema/security-1.0.xsd @@ -113,7 +113,6 @@ - From ebb27d2396f09c980e7e4b51cf6c59bdadcb39c9 Mon Sep 17 00:00:00 2001 From: Thomas Trautner Date: Sun, 2 Jun 2024 13:09:28 +0200 Subject: [PATCH 51/79] [DependencyInjection] Fix phpdoc for $calls --- .../Component/DependencyInjection/Attribute/Autoconfigure.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/DependencyInjection/Attribute/Autoconfigure.php b/src/Symfony/Component/DependencyInjection/Attribute/Autoconfigure.php index 637a24e3ba1b6..dc2c84ca29a5e 100644 --- a/src/Symfony/Component/DependencyInjection/Attribute/Autoconfigure.php +++ b/src/Symfony/Component/DependencyInjection/Attribute/Autoconfigure.php @@ -21,7 +21,7 @@ class Autoconfigure { /** * @param array>|string[]|null $tags The tags to add to the service - * @param array>|null $calls The calls to be made when instantiating the service + * @param array>|null $calls The calls to be made when instantiating the service * @param array|null $bind The bindings to declare for the service * @param bool|string|null $lazy Whether the service is lazy-loaded * @param bool|null $public Whether to declare the service as public From 924556176285ea2150df14e34925878718f145cb Mon Sep 17 00:00:00 2001 From: Mathias Arlaud Date: Wed, 26 Jun 2024 09:21:35 +0200 Subject: [PATCH 52/79] [PropertyInfo] Fix generics related test --- .../PropertyInfo/Tests/Extractor/PhpStanExtractorTest.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/PropertyInfo/Tests/Extractor/PhpStanExtractorTest.php b/src/Symfony/Component/PropertyInfo/Tests/Extractor/PhpStanExtractorTest.php index 9dd33ff9658f4..892a7e4d30acc 100644 --- a/src/Symfony/Component/PropertyInfo/Tests/Extractor/PhpStanExtractorTest.php +++ b/src/Symfony/Component/PropertyInfo/Tests/Extractor/PhpStanExtractorTest.php @@ -840,7 +840,10 @@ public static function unionTypesProvider(): iterable Type::generic( Type::object(Dummy::class), Type::array(Type::string(), Type::mixed()), - Type::union(Type::int(), Type::list(Type::generic(Type::string(), Type::object(DefaultValue::class)))), + Type::union( + Type::int(), + Type::list(Type::collection(Type::object(\Traversable::class), Type::object(DefaultValue::class))), + ), ), Type::object(ParentDummy::class), Type::null(), From 6543ec233f2aa8f6b66248a4c6413cc1c0911323 Mon Sep 17 00:00:00 2001 From: Mathias Arlaud Date: Wed, 26 Jun 2024 09:40:14 +0200 Subject: [PATCH 53/79] [Serializer] Fix access to wrong Type class --- .../Normalizer/AbstractObjectNormalizer.php | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php index 2d06713592ddf..16cf59e8bb2f1 100644 --- a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php @@ -571,16 +571,16 @@ private function validateAndDenormalizeLegacy(array $types, string $currentClass } switch ($builtinType) { - case Type::BUILTIN_TYPE_ARRAY: - case Type::BUILTIN_TYPE_BOOL: - case Type::BUILTIN_TYPE_CALLABLE: - case Type::BUILTIN_TYPE_FLOAT: - case Type::BUILTIN_TYPE_INT: - case Type::BUILTIN_TYPE_ITERABLE: - case Type::BUILTIN_TYPE_NULL: - case Type::BUILTIN_TYPE_OBJECT: - case Type::BUILTIN_TYPE_RESOURCE: - case Type::BUILTIN_TYPE_STRING: + case LegacyType::BUILTIN_TYPE_ARRAY: + case LegacyType::BUILTIN_TYPE_BOOL: + case LegacyType::BUILTIN_TYPE_CALLABLE: + case LegacyType::BUILTIN_TYPE_FLOAT: + case LegacyType::BUILTIN_TYPE_INT: + case LegacyType::BUILTIN_TYPE_ITERABLE: + case LegacyType::BUILTIN_TYPE_NULL: + case LegacyType::BUILTIN_TYPE_OBJECT: + case LegacyType::BUILTIN_TYPE_RESOURCE: + case LegacyType::BUILTIN_TYPE_STRING: if (('is_'.$builtinType)($data)) { return $data; } From df133a915f4f345caffe2f1c30c48290ab16991f Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 26 Jun 2024 10:32:27 +0200 Subject: [PATCH 54/79] [FrameworkBundle] Throw runtime exception when trying to use asset-mapper while http-client is disabled --- .../DependencyInjection/FrameworkExtension.php | 10 ++++++---- .../FrameworkBundle/Resources/config/asset_mapper.php | 8 +++++--- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index 6f7579f36c91e..84dbeabe98bdd 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -349,7 +349,7 @@ public function load(array $configs, ContainerBuilder $container) throw new LogicException('AssetMapper support cannot be enabled as the AssetMapper component is not installed. Try running "composer require symfony/asset-mapper".'); } - $this->registerAssetMapperConfiguration($config['asset_mapper'], $container, $loader, $this->readConfigEnabled('assets', $container, $config['assets'])); + $this->registerAssetMapperConfiguration($config['asset_mapper'], $container, $loader, $this->readConfigEnabled('assets', $container, $config['assets']), $this->readConfigEnabled('http_client', $container, $config['http_client'])); } else { $container->removeDefinition('cache.asset_mapper'); } @@ -1330,12 +1330,14 @@ private function registerAssetsConfiguration(array $config, ContainerBuilder $co } } - private function registerAssetMapperConfiguration(array $config, ContainerBuilder $container, PhpFileLoader $loader, bool $assetEnabled): void + private function registerAssetMapperConfiguration(array $config, ContainerBuilder $container, PhpFileLoader $loader, bool $assetEnabled, bool $httpClientEnabled): void { $loader->load('asset_mapper.php'); - if (!$assetEnabled) { - $container->removeDefinition('asset_mapper.asset_package'); + if (!$httpClientEnabled) { + $container->register('asset_mapper.http_client', HttpClientInterface::class) + ->addTag('container.error') + ->addError('You cannot use the AssetMapper integration since the HttpClient component is not enabled. Try enabling the "framework.http_client" config option.'); } $paths = $config['paths']; diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/asset_mapper.php b/src/Symfony/Bundle/FrameworkBundle/Resources/config/asset_mapper.php index b7ce65f030345..404e7af18d0a1 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/asset_mapper.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/asset_mapper.php @@ -54,6 +54,8 @@ ]) ->alias(AssetMapperInterface::class, 'asset_mapper') + ->alias('asset_mapper.http_client', 'http_client') + ->set('asset_mapper.mapped_asset_factory', MappedAssetFactory::class) ->args([ service('asset_mapper.public_assets_path_resolver'), @@ -197,7 +199,7 @@ ]) ->set('asset_mapper.importmap.resolver', JsDelivrEsmResolver::class) - ->args([service('http_client')]) + ->args([service('asset_mapper.http_client')]) ->set('asset_mapper.importmap.renderer', ImportMapRenderer::class) ->args([ @@ -212,12 +214,12 @@ ->set('asset_mapper.importmap.auditor', ImportMapAuditor::class) ->args([ service('asset_mapper.importmap.config_reader'), - service('http_client'), + service('asset_mapper.http_client'), ]) ->set('asset_mapper.importmap.update_checker', ImportMapUpdateChecker::class) ->args([ service('asset_mapper.importmap.config_reader'), - service('http_client'), + service('asset_mapper.http_client'), ]) ->set('asset_mapper.importmap.command.require', ImportMapRequireCommand::class) From 5468d3848ce90ea63a28a29771da6dd83f04638e Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Wed, 26 Jun 2024 13:42:50 +0200 Subject: [PATCH 55/79] [String] Add `alias` case to `EnglishInflector` --- src/Symfony/Component/String/Inflector/EnglishInflector.php | 3 +++ .../Component/String/Tests/Inflector/EnglishInflectorTest.php | 1 + 2 files changed, 4 insertions(+) diff --git a/src/Symfony/Component/String/Inflector/EnglishInflector.php b/src/Symfony/Component/String/Inflector/EnglishInflector.php index e068fcbcd6d98..77ebc134a436f 100644 --- a/src/Symfony/Component/String/Inflector/EnglishInflector.php +++ b/src/Symfony/Component/String/Inflector/EnglishInflector.php @@ -289,6 +289,9 @@ final class EnglishInflector implements InflectorInterface // atlases (atlas) ['salta', 5, true, true, 'atlases'], + // aliases (alias) + ['saila', 5, true, true, 'aliases'], + // irises (iris) ['siri', 4, true, true, 'irises'], diff --git a/src/Symfony/Component/String/Tests/Inflector/EnglishInflectorTest.php b/src/Symfony/Component/String/Tests/Inflector/EnglishInflectorTest.php index 6744814b66603..fb5d04300305a 100644 --- a/src/Symfony/Component/String/Tests/Inflector/EnglishInflectorTest.php +++ b/src/Symfony/Component/String/Tests/Inflector/EnglishInflectorTest.php @@ -317,6 +317,7 @@ public static function pluralizeProvider() ['hippocampus', 'hippocampi'], ['campus', 'campuses'], ['hardware', 'hardware'], + ['alias', 'aliases'], // test casing: if the first letter was uppercase, it should remain so ['Man', 'Men'], From f93113e80ef4799793db5cf4140ac4bdbf2a8c17 Mon Sep 17 00:00:00 2001 From: Boris Grishenko Date: Sat, 22 Jun 2024 21:53:47 +0200 Subject: [PATCH 56/79] [String] Fix *String::snake methods The ByteString::snake and AbstractUnitcodeString::snake methods had a bug that caused incorrect string conversion results for all uppercase words separated by space or "_" character. Ex. "GREAT SYMFONY" was converted to "greatsymfony" instead of "great_symfony" --- src/Symfony/Component/String/AbstractUnicodeString.php | 4 ++-- src/Symfony/Component/String/ByteString.php | 4 ++-- src/Symfony/Component/String/Tests/AbstractAsciiTestCase.php | 2 ++ 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Symfony/Component/String/AbstractUnicodeString.php b/src/Symfony/Component/String/AbstractUnicodeString.php index 239f234239fb8..0f840f19d717d 100644 --- a/src/Symfony/Component/String/AbstractUnicodeString.php +++ b/src/Symfony/Component/String/AbstractUnicodeString.php @@ -366,8 +366,8 @@ public function reverse(): parent public function snake(): parent { - $str = $this->camel(); - $str->string = mb_strtolower(preg_replace(['/(\p{Lu}+)(\p{Lu}\p{Ll})/u', '/([\p{Ll}0-9])(\p{Lu})/u'], '\1_\2', $str->string), 'UTF-8'); + $str = clone $this; + $str->string = str_replace(' ', '_', mb_strtolower(preg_replace(['/(\p{Lu}+)(\p{Lu}\p{Ll})/u', '/([\p{Ll}0-9])(\p{Lu})/u'], '\1 \2', $str->string), 'UTF-8')); return $str; } diff --git a/src/Symfony/Component/String/ByteString.php b/src/Symfony/Component/String/ByteString.php index 05170da801c0e..86887d7901a74 100644 --- a/src/Symfony/Component/String/ByteString.php +++ b/src/Symfony/Component/String/ByteString.php @@ -366,8 +366,8 @@ public function slice(int $start = 0, ?int $length = null): parent public function snake(): parent { - $str = $this->camel(); - $str->string = strtolower(preg_replace(['/([A-Z]+)([A-Z][a-z])/', '/([a-z\d])([A-Z])/'], '\1_\2', $str->string)); + $str = clone $this; + $str->string = str_replace(' ', '_', strtolower(preg_replace(['/([A-Z]+)([A-Z][a-z])/', '/([a-z\d])([A-Z])/'], '\1 \2', $str->string))); return $str; } diff --git a/src/Symfony/Component/String/Tests/AbstractAsciiTestCase.php b/src/Symfony/Component/String/Tests/AbstractAsciiTestCase.php index 8bde9bc8b3df3..132d558dbade4 100644 --- a/src/Symfony/Component/String/Tests/AbstractAsciiTestCase.php +++ b/src/Symfony/Component/String/Tests/AbstractAsciiTestCase.php @@ -1077,6 +1077,8 @@ public static function provideSnake() ['symfony_is_great', 'symfonyIsGREAT'], ['symfony_is_really_great', 'symfonyIsREALLYGreat'], ['symfony', 'SYMFONY'], + ['symfony_is_great', 'SYMFONY IS GREAT'], + ['symfony_is_great', 'SYMFONY_IS_GREAT'], ]; } From d7678f26dd3e1a9815e4ebfa82ec362b48cca28e Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Thu, 27 Jun 2024 14:56:10 +0200 Subject: [PATCH 57/79] [VarDumper] Fix `FFICaster` test to be platform-adaptable --- .../VarDumper/Tests/Caster/FFICasterTest.php | 25 ++++++++++++++----- 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/src/Symfony/Component/VarDumper/Tests/Caster/FFICasterTest.php b/src/Symfony/Component/VarDumper/Tests/Caster/FFICasterTest.php index d06b29963ce98..362e0a2c532c9 100644 --- a/src/Symfony/Component/VarDumper/Tests/Caster/FFICasterTest.php +++ b/src/Symfony/Component/VarDumper/Tests/Caster/FFICasterTest.php @@ -12,6 +12,7 @@ namespace Symfony\Component\VarDumper\Tests\Caster; use PHPUnit\Framework\TestCase; +use Symfony\Component\VarDumper\Caster\FFICaster; use Symfony\Component\VarDumper\Test\VarDumperTestTrait; /** @@ -23,6 +24,11 @@ class FFICasterTest extends TestCase { use VarDumperTestTrait; + /** + * @see FFICaster::MAX_STRING_LENGTH + */ + private const MAX_STRING_LENGTH = 255; + protected function setUp(): void { if (\in_array(\PHP_SAPI, ['cli', 'phpdbg'], true) && 'preload' === \ini_get('ffi.enable')) { @@ -172,17 +178,24 @@ public function testCastCuttedPointerToChar() { $actualMessage = str_repeat('Hello World!', 30)."\0"; $actualLength = \strlen($actualMessage); - - $expectedMessage = 'Hello World!Hello World!Hello World!Hello World!' - .'Hello World!Hello World!Hello World!Hello World!Hello World!Hel' - .'lo World!Hello World!Hello World!Hello World!Hello World!Hello ' - .'World!Hello World!Hello World!Hello World!Hello World!Hello Wor' - .'ld!Hello World!Hel'; + $expectedMessage = substr($actualMessage, 0, self::MAX_STRING_LENGTH); $string = \FFI::cdef()->new('char['.$actualLength.']'); $pointer = \FFI::addr($string[0]); \FFI::memcpy($pointer, $actualMessage, $actualLength); + // the max length is platform-dependent and can be less than 255, + // so we need to cut the expected message to the maximum length + // allowed by pages size of the current system + $ffi = \FFI::cdef(<<zend_get_page_size(); + $start = $ffi->cast('uintptr_t', $ffi->cast('char*', $pointer))->cdata; + $max = min(self::MAX_STRING_LENGTH, ($start | ($pageSize - 1)) - $start); + $expectedMessage = substr($expectedMessage, 0, $max); + $this->assertDumpEquals(<< size 8 align 8 { cdata: "$expectedMessage"… From cc918e9fd586cff6dcfacf51ca32e8a02e8c2532 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 27 Jun 2024 14:04:56 +0200 Subject: [PATCH 58/79] [FrameworkBundle] Fix warming up routes --- .../Bundle/FrameworkBundle/Routing/Router.php | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/Routing/Router.php b/src/Symfony/Bundle/FrameworkBundle/Routing/Router.php index 4dfb71e747487..69428a1b70928 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Routing/Router.php +++ b/src/Symfony/Bundle/FrameworkBundle/Routing/Router.php @@ -69,7 +69,7 @@ public function getRouteCollection(): RouteCollection $this->collection->addResource(new ContainerParametersResource($this->collectedParameters)); try { - $containerFile = ($this->paramFetcher)('kernel.cache_dir').'/'.($this->paramFetcher)('kernel.container_class').'.php'; + $containerFile = ($this->paramFetcher)('kernel.build_dir').'/'.($this->paramFetcher)('kernel.container_class').'.php'; if (file_exists($containerFile)) { $this->collection->addResource(new FileResource($containerFile)); } else { @@ -84,14 +84,12 @@ public function getRouteCollection(): RouteCollection public function warmUp(string $cacheDir, ?string $buildDir = null): array { - if (!$buildDir) { - return []; + if (null === $currentDir = $this->getOption('cache_dir')) { + return []; // skip warmUp when router doesn't use cache } - $currentDir = $this->getOption('cache_dir'); - - // force cache generation in build_dir - $this->setOption('cache_dir', $buildDir); + // force cache generation + $this->setOption('cache_dir', $buildDir ?? $cacheDir); $this->getMatcher(); $this->getGenerator(); From d23ea3335d7ed8bd2e384f2c00430f04c16580e0 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 27 Jun 2024 09:48:26 +0200 Subject: [PATCH 59/79] Ibexa is sponsoring Symfony 5.4, thanks to them! \o/ --- README.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 7c4f1a85899bb..8ef86422a84ae 100644 --- a/README.md +++ b/README.md @@ -17,13 +17,17 @@ Installation Sponsor ------- -Symfony 5.4 is [backed][27] by [Private Packagist][28]. +Symfony 5.4 is [backed][27] by [Private Packagist][28] and [Ibexa][29]. Private Packagist is a fast, reliable, and secure Composer repository for your private packages. It mirrors all your open-source dependencies for better availability and monitors them for security vulnerabilities. -Help Symfony by [sponsoring][29] its development! +Ibexa is the leading DXP for Symfony developers. Ibexa DXP is used across the +world by thousands of websites/shops/portals and supported by a fantastic, +passionate community of developers, agencies, and users. They love Symfony! + +Help Symfony by [sponsoring][30] its development! Documentation ------------- @@ -87,4 +91,5 @@ and supported by [Symfony contributors][19]. [26]: https://symfony.com/book [27]: https://symfony.com/backers [28]: https://packagist.com/ -[29]: https://symfony.com/sponsor +[29]: https://ibexa.co/ +[30]: https://symfony.com/sponsor From 56bc624f55bf54cb11109f91a8329ae245afd122 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Thu, 27 Jun 2024 17:26:34 +0200 Subject: [PATCH 60/79] take the new DOM HTMLElement class into account --- src/Symfony/Component/VarDumper/Tests/Caster/DOMCasterTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/VarDumper/Tests/Caster/DOMCasterTest.php b/src/Symfony/Component/VarDumper/Tests/Caster/DOMCasterTest.php index 817acd3e0ef10..1f952f6f9bb15 100644 --- a/src/Symfony/Component/VarDumper/Tests/Caster/DOMCasterTest.php +++ b/src/Symfony/Component/VarDumper/Tests/Caster/DOMCasterTest.php @@ -227,7 +227,7 @@ public function testCastModernElement() $attr = \Dom\HTMLDocument::createEmpty()->createElement('foo'); $this->assertDumpMatchesFormat(<<<'EODUMP' - Dom\Element {%A + Dom\HTMLElement {%A +tagName: ? string %A} EODUMP, From 2fc878977aac0a41e956721ea6e59006d134b636 Mon Sep 17 00:00:00 2001 From: HypeMC Date: Thu, 27 Jun 2024 18:09:31 +0200 Subject: [PATCH 61/79] [Serializer] Check if exception message in test is correct --- .../Serializer/Tests/Fixtures/NotNormalizableDummy.php | 2 +- .../Tests/Normalizer/AbstractObjectNormalizerTest.php | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/Serializer/Tests/Fixtures/NotNormalizableDummy.php b/src/Symfony/Component/Serializer/Tests/Fixtures/NotNormalizableDummy.php index 86ee6dbe5da91..d146c1a8b1aa8 100644 --- a/src/Symfony/Component/Serializer/Tests/Fixtures/NotNormalizableDummy.php +++ b/src/Symfony/Component/Serializer/Tests/Fixtures/NotNormalizableDummy.php @@ -26,6 +26,6 @@ public function __construct() public function denormalize(DenormalizerInterface $denormalizer, $data, ?string $format = null, array $context = []) { - throw new NotNormalizableValueException(); + throw new NotNormalizableValueException('Custom exception message'); } } diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php index afaf57ea06b52..b700f6ee713f6 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php @@ -514,6 +514,7 @@ public function testDenormalizeUntypedFormat() public function testDenormalizeUntypedFormatNotNormalizable() { $this->expectException(NotNormalizableValueException::class); + $this->expectExceptionMessage('Custom exception message'); $serializer = new Serializer([new CustomNormalizer(), new ObjectNormalizer(null, null, null, new PropertyInfoExtractor([], [new PhpDocExtractor(), new ReflectionExtractor()]))]); $serializer->denormalize(['value' => 'test'], DummyWithNotNormalizable::class, 'xml'); } From 71d67d49abcb0cd885886d09d5fa628ad80d0e2a Mon Sep 17 00:00:00 2001 From: HypeMC Date: Thu, 27 Jun 2024 18:09:31 +0200 Subject: [PATCH 62/79] [Serializer] Check if exception message in test is correct --- .../Serializer/Tests/Fixtures/NotNormalizableDummy.php | 2 +- .../Tests/Normalizer/AbstractObjectNormalizerTest.php | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/Serializer/Tests/Fixtures/NotNormalizableDummy.php b/src/Symfony/Component/Serializer/Tests/Fixtures/NotNormalizableDummy.php index 41da0eac8a999..ef7a8c906dc51 100644 --- a/src/Symfony/Component/Serializer/Tests/Fixtures/NotNormalizableDummy.php +++ b/src/Symfony/Component/Serializer/Tests/Fixtures/NotNormalizableDummy.php @@ -26,6 +26,6 @@ public function __construct() public function denormalize(DenormalizerInterface $denormalizer, $data, ?string $format = null, array $context = []): void { - throw new NotNormalizableValueException(); + throw new NotNormalizableValueException('Custom exception message'); } } diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php index c5c1f6f0ba016..6f6256506d47e 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php @@ -946,6 +946,7 @@ public function testDenormalizeUntypedFormat() public function testDenormalizeUntypedFormatNotNormalizable() { $this->expectException(NotNormalizableValueException::class); + $this->expectExceptionMessage('Custom exception message'); $serializer = new Serializer([new CustomNormalizer(), new ObjectNormalizer(null, null, null, new PropertyInfoExtractor([], [new PhpDocExtractor(), new ReflectionExtractor()]))]); $serializer->denormalize(['value' => 'test'], DummyWithNotNormalizable::class, 'xml'); } From 6a16b59516ca7701df733b9a9faa8237fb69e0d6 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Mon, 17 Jun 2024 23:31:30 +0200 Subject: [PATCH 63/79] forward exceptions caught in the AbstractObjectNormalizer --- .../Normalizer/AbstractObjectNormalizer.php | 30 ++++++++++++++----- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php index 16cf59e8bb2f1..e15c89e827a20 100644 --- a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php @@ -640,8 +640,11 @@ private function validateAndDenormalizeLegacy(array $types, string $currentClass private function validateAndDenormalize(Type $type, string $currentClass, string $attribute, mixed $data, ?string $format, array $context): mixed { $expectedTypes = []; + $isUnionType = $type->asNonNullable() instanceof UnionType; + $e = null; $extraAttributesException = null; $missingConstructorArgumentsException = null; + $isNullable = false; $types = match (true) { $type instanceof IntersectionType => throw new LogicException('Unable to handle intersection type.'), @@ -679,12 +682,19 @@ private function validateAndDenormalize(Type $type, string $currentClass, string // That's why we have to transform the values, if one of these non-string basic datatypes is expected. $typeIdentifier = $t->getTypeIdentifier(); if (\is_string($data) && (XmlEncoder::FORMAT === $format || CsvEncoder::FORMAT === $format)) { + if ('' === $data) { + if (TypeIdentifier::ARRAY === $typeIdentifier) { + return []; + } + + if (TypeIdentifier::STRING === $typeIdentifier) { + return ''; + } + + $isNullable = $isNullable ?: $type->isNullable(); + } + switch ($typeIdentifier) { - case TypeIdentifier::ARRAY: - if ('' === $data) { - return []; - } - break; case TypeIdentifier::BOOL: // according to https://www.w3.org/TR/xmlschema-2/#boolean, valid representations are "false", "true", "0" and "1" if ('false' === $data || '0' === $data) { @@ -808,17 +818,17 @@ private function validateAndDenormalize(Type $type, string $currentClass, string return $data; } } catch (NotNormalizableValueException|InvalidArgumentException $e) { - if (!$type instanceof UnionType) { + if (!$isUnionType && !$isNullable) { throw $e; } } catch (ExtraAttributesException $e) { - if (!$type instanceof UnionType) { + if (!$isUnionType && !$isNullable) { throw $e; } $extraAttributesException ??= $e; } catch (MissingConstructorArgumentsException $e) { - if (!$type instanceof UnionType) { + if (!$isUnionType && !$isNullable) { throw $e; } @@ -838,6 +848,10 @@ private function validateAndDenormalize(Type $type, string $currentClass, string throw $missingConstructorArgumentsException; } + if (!$isUnionType && $e) { + throw $e; + } + if ($context[self::DISABLE_TYPE_ENFORCEMENT] ?? $this->defaultContext[self::DISABLE_TYPE_ENFORCEMENT] ?? false) { return $data; } From 786abd125e13131ea5b982ae692373f0f8b677ba Mon Sep 17 00:00:00 2001 From: bocharsky-bw Date: Thu, 27 Jun 2024 22:38:52 +0200 Subject: [PATCH 64/79] Fix typo: synchronous -> synchronously --- src/Symfony/Component/Mailer/MailerInterface.php | 2 +- src/Symfony/Component/Notifier/ChatterInterface.php | 2 +- src/Symfony/Component/Notifier/TexterInterface.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Symfony/Component/Mailer/MailerInterface.php b/src/Symfony/Component/Mailer/MailerInterface.php index 8d9540a3e5e3f..ebac4b53efa4e 100644 --- a/src/Symfony/Component/Mailer/MailerInterface.php +++ b/src/Symfony/Component/Mailer/MailerInterface.php @@ -15,7 +15,7 @@ use Symfony\Component\Mime\RawMessage; /** - * Interface for mailers able to send emails synchronous and/or asynchronous. + * Interface for mailers able to send emails synchronously and/or asynchronously. * * Implementations must support synchronous and asynchronous sending. * diff --git a/src/Symfony/Component/Notifier/ChatterInterface.php b/src/Symfony/Component/Notifier/ChatterInterface.php index 915190e623aaa..6d89ca921e970 100644 --- a/src/Symfony/Component/Notifier/ChatterInterface.php +++ b/src/Symfony/Component/Notifier/ChatterInterface.php @@ -14,7 +14,7 @@ use Symfony\Component\Notifier\Transport\TransportInterface; /** - * Interface for classes able to send chat messages synchronous and/or asynchronous. + * Interface for classes able to send chat messages synchronously and/or asynchronously. * * @author Fabien Potencier */ diff --git a/src/Symfony/Component/Notifier/TexterInterface.php b/src/Symfony/Component/Notifier/TexterInterface.php index e65547755cd70..a044bb6d5d835 100644 --- a/src/Symfony/Component/Notifier/TexterInterface.php +++ b/src/Symfony/Component/Notifier/TexterInterface.php @@ -14,7 +14,7 @@ use Symfony\Component\Notifier\Transport\TransportInterface; /** - * Interface for classes able to send SMS messages synchronous and/or asynchronous. + * Interface for classes able to send SMS messages synchronously and/or asynchronously. * * @author Fabien Potencier */ From faf42b287f1af596a1d4f2ce8462ae5a93fbef9d Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Fri, 28 Jun 2024 00:22:45 +0200 Subject: [PATCH 65/79] [HttpClient] Fix initializing InformationalChunk --- src/Symfony/Component/HttpClient/Chunk/InformationalChunk.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Symfony/Component/HttpClient/Chunk/InformationalChunk.php b/src/Symfony/Component/HttpClient/Chunk/InformationalChunk.php index 31ed1aa248d18..ca9eb470913f7 100644 --- a/src/Symfony/Component/HttpClient/Chunk/InformationalChunk.php +++ b/src/Symfony/Component/HttpClient/Chunk/InformationalChunk.php @@ -23,6 +23,8 @@ class InformationalChunk extends DataChunk public function __construct(int $statusCode, array $headers) { $this->status = [$statusCode, $headers]; + + parent::__construct(); } public function getInformationalStatus(): ?array From 385d1e885f66f88bd1971cc476a1188cb1da0dff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20Andr=C3=A9?= Date: Fri, 28 Jun 2024 00:54:54 +0200 Subject: [PATCH 66/79] [AssetMapper] Upgrade importmap polyfill Not sure if we should backport this on 6.4 ? --- .../Component/AssetMapper/ImportMap/ImportMapRenderer.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/AssetMapper/ImportMap/ImportMapRenderer.php b/src/Symfony/Component/AssetMapper/ImportMap/ImportMapRenderer.php index e8ad69953cf1c..ebd2948c56790 100644 --- a/src/Symfony/Component/AssetMapper/ImportMap/ImportMapRenderer.php +++ b/src/Symfony/Component/AssetMapper/ImportMap/ImportMapRenderer.php @@ -27,7 +27,9 @@ */ class ImportMapRenderer { - private const DEFAULT_ES_MODULE_SHIMS_POLYFILL_URL = 'https://ga.jspm.io/npm:es-module-shims@1.8.0/dist/es-module-shims.js'; + // https://generator.jspm.io/#S2NnYGAIzSvJLMlJTWEAAMYOgCAOAA + private const DEFAULT_ES_MODULE_SHIMS_POLYFILL_URL = 'https://ga.jspm.io/npm:es-module-shims@1.10.0/dist/es-module-shims.js'; + private const DEFAULT_ES_MODULE_SHIMS_POLYFILL_INTEGRITY = 'sha384-ie1x72Xck445i0j4SlNJ5W5iGeL3Dpa0zD48MZopgWsjNB/lt60SuG1iduZGNnJn'; public function __construct( private readonly ImportMapGenerator $importMapGenerator, From de0cd23dc30cf0dfcf2ed930238409e660f099d0 Mon Sep 17 00:00:00 2001 From: Gerard Date: Fri, 28 Jun 2024 00:48:14 +0200 Subject: [PATCH 67/79] Reviewed Catalan missing translations --- .../Security/Core/Resources/translations/security.ca.xlf | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Symfony/Component/Security/Core/Resources/translations/security.ca.xlf b/src/Symfony/Component/Security/Core/Resources/translations/security.ca.xlf index 6d7dc7fc23e33..93ff24f330735 100644 --- a/src/Symfony/Component/Security/Core/Resources/translations/security.ca.xlf +++ b/src/Symfony/Component/Security/Core/Resources/translations/security.ca.xlf @@ -64,7 +64,7 @@ Too many failed login attempts, please try again later. - Massa intents d'inici de sessió fallits, torneu-ho a provar més tard. + Massa intents d'inici de sessió fallits, si us plau torneu-ho a provar més tard. Invalid or expired login link. @@ -72,11 +72,11 @@ Too many failed login attempts, please try again in %minutes% minute. - Massa intents d'inici de sessió fallits, torneu-ho a provar en %minutes% minut. + Massa intents d'inici de sessió fallits, si us plau torneu-ho a provar en %minutes% minut. Too many failed login attempts, please try again in %minutes% minutes. - Massa intents fallits d'inici de sessió, torneu-ho a provar d'aquí a %minutes% minuts. + Massa intents d'inici de sessió fallits, si us plau torneu-ho a provar en %minutes% minuts. From efc93cdfe6b3907220f59f435886338773a0b034 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 27 Jun 2024 17:05:20 +0200 Subject: [PATCH 68/79] [HttpClient][Mailer] Revert "Let curl handle transfer encoding", use HTTP/1.1 for Mailgun --- src/Symfony/Component/HttpClient/CurlHttpClient.php | 5 +++-- .../Mailer/Bridge/Mailgun/Transport/MailgunHttpTransport.php | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/HttpClient/CurlHttpClient.php b/src/Symfony/Component/HttpClient/CurlHttpClient.php index 4c5ced322d5de..3a2fba025aeff 100644 --- a/src/Symfony/Component/HttpClient/CurlHttpClient.php +++ b/src/Symfony/Component/HttpClient/CurlHttpClient.php @@ -246,8 +246,9 @@ public function request(string $method, string $url, array $options = []): Respo if (isset($options['normalized_headers']['content-length'][0])) { $curlopts[\CURLOPT_INFILESIZE] = (int) substr($options['normalized_headers']['content-length'][0], \strlen('Content-Length: ')); - } elseif (!isset($options['normalized_headers']['transfer-encoding'])) { - $curlopts[\CURLOPT_INFILESIZE] = -1; + } + if (!isset($options['normalized_headers']['transfer-encoding'])) { + $curlopts[\CURLOPT_HTTPHEADER][] = 'Transfer-Encoding:'.(isset($curlopts[\CURLOPT_INFILESIZE]) ? '' : ' chunked'); } if ('POST' !== $method) { diff --git a/src/Symfony/Component/Mailer/Bridge/Mailgun/Transport/MailgunHttpTransport.php b/src/Symfony/Component/Mailer/Bridge/Mailgun/Transport/MailgunHttpTransport.php index c621ae5b16a77..5fa28ef0e494b 100644 --- a/src/Symfony/Component/Mailer/Bridge/Mailgun/Transport/MailgunHttpTransport.php +++ b/src/Symfony/Component/Mailer/Bridge/Mailgun/Transport/MailgunHttpTransport.php @@ -63,6 +63,7 @@ protected function doSendHttp(SentMessage $message): ResponseInterface $endpoint = sprintf('%s/v3/%s/messages.mime', $this->getEndpoint(), urlencode($this->domain)); $response = $this->client->request('POST', 'https://'.$endpoint, [ + 'http_version' => '1.1', 'auth_basic' => 'api:'.$this->key, 'headers' => $headers, 'body' => $body->bodyToIterable(), From 6e657e8e916ff4e88aa6e61144761d306427be08 Mon Sep 17 00:00:00 2001 From: Maximilian Zumbansen Date: Wed, 26 Jun 2024 14:46:59 +0200 Subject: [PATCH 69/79] [Serializer] [ObjectNormalizer] Use bool filter when FILTER_BOOL is set --- .../Normalizer/AbstractObjectNormalizer.php | 8 +++++ .../AbstractObjectNormalizerTest.php | 34 +++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php index 2d06713592ddf..ab95684578018 100644 --- a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php @@ -566,6 +566,10 @@ private function validateAndDenormalizeLegacy(array $types, string $currentClass return (float) $data; } + if (LegacyType::BUILTIN_TYPE_BOOL === $builtinType && \is_string($data) && ($context[self::FILTER_BOOL] ?? false)) { + return filter_var($data, \FILTER_VALIDATE_BOOL, \FILTER_NULL_ON_FAILURE); + } + if ((LegacyType::BUILTIN_TYPE_FALSE === $builtinType && false === $data) || (LegacyType::BUILTIN_TYPE_TRUE === $builtinType && true === $data)) { return $data; } @@ -787,6 +791,10 @@ private function validateAndDenormalize(Type $type, string $currentClass, string return (float) $data; } + if (TypeIdentifier::BOOL === $typeIdentifier && \is_string($data) && ($context[self::FILTER_BOOL] ?? false)) { + return filter_var($data, \FILTER_VALIDATE_BOOL, \FILTER_NULL_ON_FAILURE); + } + $dataMatchesExpectedType = match ($typeIdentifier) { TypeIdentifier::ARRAY => \is_array($data), TypeIdentifier::BOOL => \is_bool($data), diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php index c5c1f6f0ba016..90e6be9523499 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php @@ -1195,6 +1195,34 @@ public function provideBooleanTypesData() [['foo' => false], TruePropertyDummy::class], ]; } + + /** + * @dataProvider provideDenormalizeWithFilterBoolData + */ + public function testDenormalizeBooleanTypeWithFilterBool(array $data, ?bool $expectedFoo) + { + $normalizer = new AbstractObjectNormalizerWithMetadataAndPropertyTypeExtractors(); + + $dummy = $normalizer->denormalize($data, BoolPropertyDummy::class, null, [AbstractNormalizer::FILTER_BOOL => true]); + + $this->assertSame($expectedFoo, $dummy->foo); + } + + public function provideDenormalizeWithFilterBoolData(): array + { + return [ + [['foo' => 'true'], true], + [['foo' => '1'], true], + [['foo' => 'yes'], true], + [['foo' => 'false'], false], + [['foo' => '0'], false], + [['foo' => 'no'], false], + [['foo' => ''], false], + [['foo' => null], null], + [['foo' => 'null'], null], + [['foo' => 'something'], null], + ]; + } } class AbstractObjectNormalizerDummy extends AbstractObjectNormalizer @@ -1480,6 +1508,12 @@ class TruePropertyDummy public $foo; } +class BoolPropertyDummy +{ + /** @var null|bool */ + public $foo; +} + class SerializerCollectionDummy implements SerializerInterface, DenormalizerInterface { private array $normalizers; From 0a7477d7540c7d1f954b36d65d8a9c85eee30df6 Mon Sep 17 00:00:00 2001 From: Quynh Nguyen Date: Fri, 28 Jun 2024 14:45:31 +0700 Subject: [PATCH 70/79] Test convert CompletionInput into string --- .../Console/Completion/CompletionInput.php | 2 +- .../Tests/Completion/CompletionInputTest.php | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/Console/Completion/CompletionInput.php b/src/Symfony/Component/Console/Completion/CompletionInput.php index 368b945079484..2f631bcd8484f 100644 --- a/src/Symfony/Component/Console/Completion/CompletionInput.php +++ b/src/Symfony/Component/Console/Completion/CompletionInput.php @@ -53,7 +53,7 @@ public static function fromString(string $inputStr, int $currentIndex): self * Create an input based on an COMP_WORDS token list. * * @param string[] $tokens the set of split tokens (e.g. COMP_WORDS or argv) - * @param $currentIndex the index of the cursor (e.g. COMP_CWORD) + * @param int $currentIndex the index of the cursor (e.g. COMP_CWORD) */ public static function fromTokens(array $tokens, int $currentIndex): self { diff --git a/src/Symfony/Component/Console/Tests/Completion/CompletionInputTest.php b/src/Symfony/Component/Console/Tests/Completion/CompletionInputTest.php index d98da682cd90d..65708d3ec8659 100644 --- a/src/Symfony/Component/Console/Tests/Completion/CompletionInputTest.php +++ b/src/Symfony/Component/Console/Tests/Completion/CompletionInputTest.php @@ -133,4 +133,19 @@ public static function provideFromStringData() yield ['bin/console cache:clear "multi word string"', ['bin/console', 'cache:clear', '"multi word string"']]; yield ['bin/console cache:clear \'multi word string\'', ['bin/console', 'cache:clear', '\'multi word string\'']]; } + + public function testToString() + { + $input = CompletionInput::fromTokens(['foo', 'bar', 'baz'], 0); + $this->assertSame('foo| bar baz', (string) $input); + + $input = CompletionInput::fromTokens(['foo', 'bar', 'baz'], 1); + $this->assertSame('foo bar| baz', (string) $input); + + $input = CompletionInput::fromTokens(['foo', 'bar', 'baz'], 2); + $this->assertSame('foo bar baz|', (string) $input); + + $input = CompletionInput::fromTokens(['foo', 'bar', 'baz'], 11); + $this->assertSame('foo bar baz |', (string) $input); + } } From a319e94d0b2ea350e40a112c3cb9ddb8631bdc1b Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Fri, 28 Jun 2024 10:53:38 +0200 Subject: [PATCH 71/79] [DoctrineBridge] Fix compat with DI >= 6.4 --- src/Symfony/Bridge/Doctrine/ManagerRegistry.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Symfony/Bridge/Doctrine/ManagerRegistry.php b/src/Symfony/Bridge/Doctrine/ManagerRegistry.php index c3d48fc558518..b290ae5d9b039 100644 --- a/src/Symfony/Bridge/Doctrine/ManagerRegistry.php +++ b/src/Symfony/Bridge/Doctrine/ManagerRegistry.php @@ -72,6 +72,8 @@ function (&$wrappedInstance, LazyLoadingInterface $manager) use ($name) { } if (isset($this->fileMap[$name])) { $wrappedInstance = $this->load($this->fileMap[$name], false); + } elseif ((new \ReflectionMethod($this, $this->methodMap[$name]))->isStatic()) { + $wrappedInstance = $this->{$this->methodMap[$name]}($this, false); } else { $wrappedInstance = $this->{$this->methodMap[$name]}(false); } From 2b50dea4f6164db0dd84bee3f8566a19c23a4f59 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Fri, 28 Jun 2024 11:10:31 +0200 Subject: [PATCH 72/79] [Filesystem] Fix Filesystem::remove() on Windows --- src/Symfony/Component/Filesystem/Filesystem.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/Filesystem/Filesystem.php b/src/Symfony/Component/Filesystem/Filesystem.php index 700311d5843fc..958ef178db2fb 100644 --- a/src/Symfony/Component/Filesystem/Filesystem.php +++ b/src/Symfony/Component/Filesystem/Filesystem.php @@ -173,7 +173,7 @@ private static function doRemove(array $files, bool $isRecursive): void } } elseif (is_dir($file)) { if (!$isRecursive) { - $tmpName = \dirname(realpath($file)).'/.'.strrev(strtr(base64_encode(random_bytes(2)), '/=', '-_')); + $tmpName = \dirname(realpath($file)).'/.!'.strrev(strtr(base64_encode(random_bytes(2)), '/=', '-!')); if (file_exists($tmpName)) { try { From 7aa625bd75db15bb7702ca7936719ac06780ab39 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 27 Jun 2024 14:04:56 +0200 Subject: [PATCH 73/79] [HttpKernel] Enable optional cache-warmers when cache-dir != build-dir --- src/Symfony/Component/HttpKernel/Kernel.php | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index 04485d450f0dc..344db521d9519 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -539,10 +539,17 @@ protected function initializeContainer() touch($oldContainerDir.'.legacy'); } - $preload = $this instanceof WarmableInterface ? (array) $this->warmUp($this->container->getParameter('kernel.cache_dir'), $buildDir) : []; + $cacheDir = $this->container->getParameter('kernel.cache_dir'); + $preload = $this instanceof WarmableInterface ? (array) $this->warmUp($cacheDir, $buildDir) : []; if ($this->container->has('cache_warmer')) { - $preload = array_merge($preload, (array) $this->container->get('cache_warmer')->warmUp($this->container->getParameter('kernel.cache_dir'), $buildDir)); + $cacheWarmer = $this->container->get('cache_warmer'); + + if ($cacheDir !== $buildDir) { + $cacheWarmer->enableOptionalWarmers(); + } + + $preload = array_merge($preload, (array) $cacheWarmer->warmUp($cacheDir, $buildDir)); } if ($preload && file_exists($preloadFile = $buildDir.'/'.$class.'.preload.php')) { From cdb8354a152b5c8299ab2bb66933922b644920a9 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Fri, 28 Jun 2024 11:26:01 +0200 Subject: [PATCH 74/79] =?UTF-8?q?Revert=20"bug=20#57520=20[SecurityBundle]?= =?UTF-8?q?=20Remove=20unused=20memory=20users=E2=80=99=20`name`=20attribu?= =?UTF-8?q?te=20from=20the=20XSD=20(MatTheCat)"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 28ad602851e591826dcd85120e62821330c4019d, reversing changes made to f8c5cf02a2813b130db3da839a78ae1f477997bf. --- .../SecurityBundle/Resources/config/schema/security-1.0.xsd | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Symfony/Bundle/SecurityBundle/Resources/config/schema/security-1.0.xsd b/src/Symfony/Bundle/SecurityBundle/Resources/config/schema/security-1.0.xsd index bf15a5db164ec..d3d7752eeac0d 100644 --- a/src/Symfony/Bundle/SecurityBundle/Resources/config/schema/security-1.0.xsd +++ b/src/Symfony/Bundle/SecurityBundle/Resources/config/schema/security-1.0.xsd @@ -113,6 +113,7 @@ + From c3bf9a60746cd04cf8f98f817e38d1617b02959e Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Fri, 28 Jun 2024 11:36:24 +0200 Subject: [PATCH 75/79] [Filesystem][Mime] Fix transient tests --- src/Symfony/Component/Filesystem/Tests/FilesystemTest.php | 6 +++--- src/Symfony/Component/Mime/Tests/Part/DataPartTest.php | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Symfony/Component/Filesystem/Tests/FilesystemTest.php b/src/Symfony/Component/Filesystem/Tests/FilesystemTest.php index 101f30f898714..eea5fe1a68952 100644 --- a/src/Symfony/Component/Filesystem/Tests/FilesystemTest.php +++ b/src/Symfony/Component/Filesystem/Tests/FilesystemTest.php @@ -171,17 +171,17 @@ public function testCopyForOriginUrlsAndExistingLocalFileDefaultsToCopy() } $finder = new PhpExecutableFinder(); - $process = new Process(array_merge([$finder->find(false)], $finder->findArguments(), ['-dopcache.enable=0', '-dvariables_order=EGPCS', '-S', 'localhost:8057'])); + $process = new Process(array_merge([$finder->find(false)], $finder->findArguments(), ['-dopcache.enable=0', '-dvariables_order=EGPCS', '-S', 'localhost:8857'])); $process->setWorkingDirectory(__DIR__.'/Fixtures/web'); $process->start(); do { usleep(50000); - } while (!@fopen('http://localhost:8057', 'r')); + } while (!@fopen('http://localhost:8857', 'r')); try { - $sourceFilePath = 'http://localhost:8057/logo_symfony_header.png'; + $sourceFilePath = 'http://localhost:8857/logo_symfony_header.png'; $targetFilePath = $this->workspace.\DIRECTORY_SEPARATOR.'copy_target_file'; file_put_contents($targetFilePath, 'TARGET FILE'); $this->filesystem->copy($sourceFilePath, $targetFilePath, false); diff --git a/src/Symfony/Component/Mime/Tests/Part/DataPartTest.php b/src/Symfony/Component/Mime/Tests/Part/DataPartTest.php index 3d306f2e8ff86..7a9913b969a64 100644 --- a/src/Symfony/Component/Mime/Tests/Part/DataPartTest.php +++ b/src/Symfony/Component/Mime/Tests/Part/DataPartTest.php @@ -143,15 +143,15 @@ public function testFromPathWithUrl() } $finder = new PhpExecutableFinder(); - $process = new Process(array_merge([$finder->find(false)], $finder->findArguments(), ['-dopcache.enable=0', '-dvariables_order=EGPCS', '-S', 'localhost:8057'])); + $process = new Process(array_merge([$finder->find(false)], $finder->findArguments(), ['-dopcache.enable=0', '-dvariables_order=EGPCS', '-S', 'localhost:8856'])); $process->setWorkingDirectory(__DIR__.'/../Fixtures/web'); $process->start(); try { do { usleep(50000); - } while (!@fopen('http://localhost:8057', 'r')); - $p = DataPart::fromPath($file = 'http://localhost:8057/logo_symfony_header.png'); + } while (!@fopen('http://localhost:8856', 'r')); + $p = DataPart::fromPath($file = 'http://localhost:8856/logo_symfony_header.png'); $content = file_get_contents($file); $this->assertEquals($content, $p->getBody()); $maxLineLength = 76; From d4b812cdc55bb9929ec942f558114baffa608560 Mon Sep 17 00:00:00 2001 From: Dave Long Date: Fri, 7 Jun 2024 10:09:46 +0100 Subject: [PATCH 76/79] Allow service locators to be ordered by priority. --- .../Compiler/ServiceLocatorTagPass.php | 1 - .../Tests/Compiler/IntegrationTest.php | 4 ++-- .../Compiler/RegisterServiceSubscribersPassTest.php | 2 +- .../Tests/Compiler/ServiceLocatorTagPassTest.php | 2 +- .../Tests/Fixtures/php/services_subscriber.php | 10 +++++----- 5 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/Symfony/Component/DependencyInjection/Compiler/ServiceLocatorTagPass.php b/src/Symfony/Component/DependencyInjection/Compiler/ServiceLocatorTagPass.php index 728feb0bd732f..032e905095c5d 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/ServiceLocatorTagPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/ServiceLocatorTagPass.php @@ -131,7 +131,6 @@ public static function map(array $services): array $services[$k] = new ServiceClosureArgument($v); } - ksort($services); return $services; } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/IntegrationTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/IntegrationTest.php index cd0ac69738674..0916e21314392 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/IntegrationTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/IntegrationTest.php @@ -623,7 +623,7 @@ public function testTaggedLocatorWithDefaultPriorityMethodConfiguredViaAttribute // We need to check priority of instances in the factories $factories = (new \ReflectionClass($locator))->getProperty('factories'); - self::assertSame([BarTagClass::class, FooTagClass::class], array_keys($factories->getValue($locator))); + self::assertSame([FooTagClass::class, BarTagClass::class], array_keys($factories->getValue($locator))); } public function testTaggedLocatorWithDefaultIndexMethodAndWithDefaultPriorityMethodConfiguredViaAttribute() @@ -652,7 +652,7 @@ public function testTaggedLocatorWithDefaultIndexMethodAndWithDefaultPriorityMet // We need to check priority of instances in the factories $factories = (new \ReflectionClass($locator))->getProperty('factories'); - self::assertSame(['bar_tag_class', 'foo_tag_class'], array_keys($factories->getValue($locator))); + self::assertSame(['foo_tag_class', 'bar_tag_class'], array_keys($factories->getValue($locator))); self::assertSame($container->get(BarTagClass::class), $locator->get('bar_tag_class')); self::assertSame($container->get(FooTagClass::class), $locator->get('foo_tag_class')); } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/RegisterServiceSubscribersPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/RegisterServiceSubscribersPassTest.php index b5e2458c337e3..d9e3e921eab5c 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/RegisterServiceSubscribersPassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/RegisterServiceSubscribersPassTest.php @@ -462,7 +462,7 @@ public static function getSubscribedServices(): array 'autowired' => new ServiceClosureArgument(new TypedReference('service.id', 'stdClass', ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, 'autowired', [new Autowire(service: 'service.id')])), 'autowired.nullable' => new ServiceClosureArgument(new TypedReference('service.id', 'stdClass', ContainerInterface::IGNORE_ON_INVALID_REFERENCE, 'autowired.nullable', [new Autowire(service: 'service.id')])), 'autowired.parameter' => new ServiceClosureArgument('foobar'), - 'autowire.decorated' => new ServiceClosureArgument(new Reference('.service_locator.oO4rxCy.inner', ContainerInterface::NULL_ON_INVALID_REFERENCE)), + 'autowire.decorated' => new ServiceClosureArgument(new Reference('.service_locator.0tSxobl.inner', ContainerInterface::NULL_ON_INVALID_REFERENCE)), 'target' => new ServiceClosureArgument(new TypedReference('stdClass', 'stdClass', ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, 'target', [new Target('someTarget')])), ]; $this->assertEquals($expected, $container->getDefinition((string) $locator->getFactory()[0])->getArgument(0)); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ServiceLocatorTagPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ServiceLocatorTagPassTest.php index faeb743162d11..812b47c7a6f1f 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ServiceLocatorTagPassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ServiceLocatorTagPassTest.php @@ -214,7 +214,7 @@ public function testDefinitionOrderIsTheSame() $locator = $container->getDefinition($locator); $factories = $locator->getArguments()[0]; - static::assertSame(['service-1', 'service-2'], array_keys($factories)); + static::assertSame(['service-2', 'service-1'], array_keys($factories)); } public function testBindingsAreProcessed() diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_subscriber.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_subscriber.php index 0565bd68ce279..b3368a0990037 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_subscriber.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_subscriber.php @@ -43,9 +43,9 @@ public function isCompiled(): bool public function getRemovedIds(): array { return [ - '.service_locator.2hyyc9y' => true, - '.service_locator.KGUGnmw' => true, - '.service_locator.KGUGnmw.foo_service' => true, + '.service_locator.0H1ht0q' => true, + '.service_locator.0H1ht0q.foo_service' => true, + '.service_locator.tfSHZa1' => true, 'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\CustomDefinition' => true, ]; } @@ -68,14 +68,14 @@ protected static function getTestServiceSubscriberService($container) protected static function getFooServiceService($container) { return $container->services['foo_service'] = new \Symfony\Component\DependencyInjection\Tests\Fixtures\TestServiceSubscriber((new \Symfony\Component\DependencyInjection\Argument\ServiceLocator($container->getService ??= $container->getService(...), [ - 'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\CustomDefinition' => ['privates', 'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\CustomDefinition', 'getCustomDefinitionService', false], 'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\TestServiceSubscriber' => ['services', 'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\TestServiceSubscriber', 'getTestServiceSubscriberService', false], + 'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\CustomDefinition' => ['privates', 'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\CustomDefinition', 'getCustomDefinitionService', false], 'bar' => ['services', 'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\TestServiceSubscriber', 'getTestServiceSubscriberService', false], 'baz' => ['privates', 'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\CustomDefinition', 'getCustomDefinitionService', false], 'late_alias' => ['services', 'late_alias', 'getLateAliasService', false], ], [ - 'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\CustomDefinition' => 'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\CustomDefinition', 'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\TestServiceSubscriber' => 'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\TestServiceSubscriber', + 'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\CustomDefinition' => 'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\CustomDefinition', 'bar' => 'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\CustomDefinition', 'baz' => 'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\CustomDefinition', 'late_alias' => 'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\TestDefinition1', From ed5c26c9dbeac02cb310fa4b78c2ff5be855ba38 Mon Sep 17 00:00:00 2001 From: MatTheCat Date: Fri, 28 Jun 2024 12:06:43 +0200 Subject: [PATCH 77/79] =?UTF-8?q?[SecurityBundle]=20Remove=20unused=20memo?= =?UTF-8?q?ry=20users=E2=80=99=20`name`=20attribute=20from=20the=20XSD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../SecurityBundle/Resources/config/schema/security-1.0.xsd | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Symfony/Bundle/SecurityBundle/Resources/config/schema/security-1.0.xsd b/src/Symfony/Bundle/SecurityBundle/Resources/config/schema/security-1.0.xsd index d3d7752eeac0d..bf15a5db164ec 100644 --- a/src/Symfony/Bundle/SecurityBundle/Resources/config/schema/security-1.0.xsd +++ b/src/Symfony/Bundle/SecurityBundle/Resources/config/schema/security-1.0.xsd @@ -113,7 +113,6 @@ - From bbd1dc05921bc16b3e973ffbe2e133331e169552 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Fri, 28 Jun 2024 15:13:26 +0200 Subject: [PATCH 78/79] Update CHANGELOG for 7.1.2 --- CHANGELOG-7.1.md | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/CHANGELOG-7.1.md b/CHANGELOG-7.1.md index 45a9e7fa2bbec..e78ae8f2b8b1c 100644 --- a/CHANGELOG-7.1.md +++ b/CHANGELOG-7.1.md @@ -7,6 +7,50 @@ in 7.1 minor versions. To get the diff for a specific change, go to https://github.com/symfony/symfony/commit/XXX where XXX is the change hash To get the diff between two versions, go to https://github.com/symfony/symfony/compare/v7.1.0...v7.1.1 +* 7.1.2 (2024-06-28) + + * bug #57345 [DependencyInjection] Fix regression in ordering service locators by priority (longwave) + * bug #57553 [HttpKernel] Enable optional cache-warmers when cache-dir != build-dir (nicolas-grekas) + * bug #57497 [String] Fixed u()->snake(), b()->snake() and s()->snake() methods (arczinosek) + * bug #57574 [Filesystem] Fix Filesystem::remove() on Windows (nicolas-grekas) + * bug #57572 [DoctrineBridge] Fix compat with DI >= 6.4 (nicolas-grekas) + * bug #57541 [Serializer] [ObjectNormalizer] Use bool filter when FILTER_BOOL is set (Maximilian Zumbansen) + * bug #57538 [String] Add `alias` case to `EnglishInflector` (alexandre-daubois) + * bug #57533 [FrameworkBundle] Throw runtime exception when trying to use asset-mapper while http-client is disabled (nicolas-grekas) + * bug #57520 [SecurityBundle] Remove unused memory users’ `name` attribute from the XSD (MatTheCat) + * bug #57554 [FrameworkBundle] Fix warming up routes (nicolas-grekas) + * feature #57557 Ibexa is sponsoring Symfony 5.4, thanks to them! \o/ (nicolas-grekas) + * bug #57433 [Serializer] forward exceptions caught in the `AbstractObjectNormalizer` (HypeMC, xabbuh) + * bug #57569 [HttpClient][Mailer] Revert "Let curl handle transfer encoding", use HTTP/1.1 for Mailgun (nicolas-grekas) + * bug #57564 [HttpClient] Fix initializing InformationalChunk (nicolas-grekas) + * bug #57289 [DependencyInjection] Fix phpdoc for $calls in class Autoconfigure (ThomasTr) + * bug #57499 [Mailer] Add additional headers in Scaleway bridge (MrMicky-FR) + * bug #57488 [Security] check token in payload instead just request (eltharin) + * bug #57460 [VarExporter] fix contravariance problem with __unserialize() in lazy proxy (nikophil) + * bug #57397 [VarDumper] Fix FFI caster test (alexandre-daubois) + * bug #57453 [HttpClient] Fix parsing SSE (fancyweb) + * bug #57467 [SecurityBundle] Add `provider` XML attribute to the authenticators it’s missing from (MatTheCat) + * bug #57447 [Notifier] [Lox24] Fix request body format to JSON string (alebedev80) + * bug #57434 [FrameworkBundle] inject the missing logger service (xabbuh) + * bug #57384 [Notifier] Fix thread key in GoogleChat bridge (romain-jacquart) + * bug #57372 [HttpKernel][Security] Fix accessing session for stateless request (VincentLanglet) + * bug #57112 [Messenger] Handle `AMQPConnectionException` when publishing a message (jwage) + * bug #57341 [Serializer] properly handle invalid data for false/true types (xabbuh) + * bug #57187 [Serializer] Fix `ObjectNormalizer` with property path (HypeMC) + * bug #57355 [ErrorHandler] Fix rendered exception code highlighting on PHP 8.3 (tscni) + * bug #57310 [DependencyInjection] Fix ternary in `AutowireCallable` attribute (alamirault) + * bug #57405 [DoctrineBridge] fix handling of special "value" constraint option (xabbuh) + * bug #57273 [FrameworkBundle] Fix setting default context for certain normalizers (HypeMC) + * bug #57395 [Notifier]  send the recipient phone number as an array (xabbuh) + * bug #57378 [Security] Change to `BadCredentialsException` when empty username / password (llupa) + * bug #52699 [Serializer] [PropertyAccessor] Ignore non-collection interface generics (mtarld) + * bug #54634 [String] Fix #54611 pluralization of -on ending words + singularization of -a ending foreign words (Geordie, DesLynx) + * bug #57213 [Validator] [UniqueValidator] Use correct variable as parameter in (custom) error message (seho-nl, Sebastien Hoek) + * bug #54920 [Messenger] Comply with Amazon SQS requirements for message body (VincentLanglet) + * bug #57348 [Notifier][Brevo] change type from chatter to texter (xabbuh) + * bug #57321 [AssetMapper] fix npm version constraint conversion (Jean-Beru) + * bug #57320 [Serializer] avoid calling undefined built-in is_*() functions (xabbuh) + * 7.1.1 (2024-06-04) * bug #57110 [PhpUnitBridge] Fix error handler triggered outside of tests (HypeMC) From 7d2c4318729bd3043c0b113d01f25c4c75731a40 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Fri, 28 Jun 2024 15:13:31 +0200 Subject: [PATCH 79/79] Update VERSION for 7.1.2 --- src/Symfony/Component/HttpKernel/Kernel.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index e3cec13085c3c..a7a0232ffa826 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -73,12 +73,12 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl */ private static array $freshCache = []; - public const VERSION = '7.1.2-DEV'; + public const VERSION = '7.1.2'; public const VERSION_ID = 70102; public const MAJOR_VERSION = 7; public const MINOR_VERSION = 1; public const RELEASE_VERSION = 2; - public const EXTRA_VERSION = 'DEV'; + public const EXTRA_VERSION = ''; public const END_OF_MAINTENANCE = '01/2025'; public const END_OF_LIFE = '01/2025'; pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy