From 6c2d27eb74d605a4e57ab5f056afcb46bf363a02 Mon Sep 17 00:00:00 2001 From: miloszowi Date: Sun, 13 Apr 2025 13:29:07 +0200 Subject: [PATCH 01/80] Allow to set block_id for SlackActionsBlock and set url to nullable, allow setting value in SlackButtonBlockElement --- .../Bridge/Slack/Block/SlackActionsBlock.php | 14 ++++++++++++-- .../Bridge/Slack/Block/SlackButtonBlockElement.php | 11 +++++++++-- .../Slack/Tests/Block/SlackActionsBlockTest.php | 12 +++++++++++- 3 files changed, 32 insertions(+), 5 deletions(-) diff --git a/src/Symfony/Component/Notifier/Bridge/Slack/Block/SlackActionsBlock.php b/src/Symfony/Component/Notifier/Bridge/Slack/Block/SlackActionsBlock.php index 25da853677415..848b9bfd98f83 100644 --- a/src/Symfony/Component/Notifier/Bridge/Slack/Block/SlackActionsBlock.php +++ b/src/Symfony/Component/Notifier/Bridge/Slack/Block/SlackActionsBlock.php @@ -24,16 +24,26 @@ public function __construct() /** * @return $this */ - public function button(string $text, string $url, ?string $style = null): static + public function button(string $text, ?string $url = null, ?string $style = null, ?string $value = null): static { if (25 === \count($this->options['elements'] ?? [])) { throw new \LogicException('Maximum number of buttons should not exceed 25.'); } - $element = new SlackButtonBlockElement($text, $url, $style); + $element = new SlackButtonBlockElement($text, $url, $style, $value); $this->options['elements'][] = $element->toArray(); return $this; } + + /** + * @return $this + */ + public function id(string $id): static + { + $this->options['block_id'] = $id; + + return $this; + } } diff --git a/src/Symfony/Component/Notifier/Bridge/Slack/Block/SlackButtonBlockElement.php b/src/Symfony/Component/Notifier/Bridge/Slack/Block/SlackButtonBlockElement.php index ff83bb9d870a5..8b1eed19472cb 100644 --- a/src/Symfony/Component/Notifier/Bridge/Slack/Block/SlackButtonBlockElement.php +++ b/src/Symfony/Component/Notifier/Bridge/Slack/Block/SlackButtonBlockElement.php @@ -16,7 +16,7 @@ */ final class SlackButtonBlockElement extends AbstractSlackBlockElement { - public function __construct(string $text, string $url, ?string $style = null) + public function __construct(string $text, ?string $url = null, ?string $style = null, ?string $value = null) { $this->options = [ 'type' => 'button', @@ -24,12 +24,19 @@ public function __construct(string $text, string $url, ?string $style = null) 'type' => 'plain_text', 'text' => $text, ], - 'url' => $url, ]; + if ($url) { + $this->options['url'] = $url; + } + if ($style) { // primary or danger $this->options['style'] = $style; } + + if ($value) { + $this->options['value'] = $value; + } } } diff --git a/src/Symfony/Component/Notifier/Bridge/Slack/Tests/Block/SlackActionsBlockTest.php b/src/Symfony/Component/Notifier/Bridge/Slack/Tests/Block/SlackActionsBlockTest.php index 2a21a39133c1f..682a895bdffc0 100644 --- a/src/Symfony/Component/Notifier/Bridge/Slack/Tests/Block/SlackActionsBlockTest.php +++ b/src/Symfony/Component/Notifier/Bridge/Slack/Tests/Block/SlackActionsBlockTest.php @@ -19,8 +19,9 @@ final class SlackActionsBlockTest extends TestCase public function testCanBeInstantiated() { $actions = new SlackActionsBlock(); - $actions->button('first button text', 'https://example.org') + $actions->button('first button text', 'https://example.org', null, 'test-value') ->button('second button text', 'https://example.org/slack', 'danger') + ->button('third button text', null, null, 'test-value-3') ; $this->assertSame([ @@ -33,6 +34,7 @@ public function testCanBeInstantiated() 'text' => 'first button text', ], 'url' => 'https://example.org', + 'value' => 'test-value' ], [ 'type' => 'button', @@ -43,6 +45,14 @@ public function testCanBeInstantiated() 'url' => 'https://example.org/slack', 'style' => 'danger', ], + [ + 'type' => 'button', + 'text' => [ + 'type' => 'plain_text', + 'text' => 'third button text', + ], + 'value' => 'test-value-3', + ] ], ], $actions->toArray()); } From 4bfb8da9f74bfc5be0789f52ae7ee5fb0ba17ae5 Mon Sep 17 00:00:00 2001 From: thecaliskan Date: Mon, 26 May 2025 11:39:29 +0300 Subject: [PATCH 02/80] fixed Via regex --- src/Symfony/Component/HttpFoundation/Request.php | 2 +- src/Symfony/Component/HttpFoundation/Tests/RequestTest.php | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/HttpFoundation/Request.php b/src/Symfony/Component/HttpFoundation/Request.php index 922014133293e..42a3a8a2c660c 100644 --- a/src/Symfony/Component/HttpFoundation/Request.php +++ b/src/Symfony/Component/HttpFoundation/Request.php @@ -1466,7 +1466,7 @@ public function isMethodCacheable(): bool public function getProtocolVersion(): ?string { if ($this->isFromTrustedProxy()) { - preg_match('~^(HTTP/)?([1-9]\.[0-9]) ~', $this->headers->get('Via') ?? '', $matches); + preg_match('~^(HTTP/)?([1-9]\.[0-9])\b~', $this->headers->get('Via') ?? '', $matches); if ($matches) { return 'HTTP/'.$matches[2]; diff --git a/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php b/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php index f1aa0ebeab928..a2eace70e6e80 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php @@ -2402,6 +2402,8 @@ public static function protocolVersionProvider() 'trusted with via and protocol name' => ['HTTP/2.0', true, 'HTTP/1.0 fred, HTTP/1.1 nowhere.com (Apache/1.1)', 'HTTP/1.0'], 'trusted with broken via' => ['HTTP/2.0', true, 'HTTP/1^0 foo', 'HTTP/2.0'], 'trusted with partially-broken via' => ['HTTP/2.0', true, '1.0 fred, foo', 'HTTP/1.0'], + 'trusted with simple via' => ['HTTP/2.0', true, 'HTTP/1.0', 'HTTP/1.0'], + 'trusted with only version via' => ['HTTP/2.0', true, '1.0', 'HTTP/1.0'], ]; } From e5930b3a897e919fe8379a5323033e8981c3db9a Mon Sep 17 00:00:00 2001 From: Mathias Arlaud Date: Mon, 26 May 2025 08:30:12 +0200 Subject: [PATCH 03/80] [JsonStreamer] Remove "nikic/php-parser" dependency --- .../Component/JsonStreamer/CHANGELOG.md | 5 + .../DataModel/DataAccessorInterface.php | 29 - .../DataModel/FunctionDataAccessor.php | 57 -- .../DataModel/PhpExprDataAccessor.php | 34 - .../DataModel/PropertyDataAccessor.php | 46 -- .../DataModel/Read/ObjectNode.php | 5 +- .../DataModel/ScalarDataAccessor.php | 35 -- .../DataModel/VariableDataAccessor.php | 35 -- .../DataModel/Write/BackedEnumNode.php | 7 +- .../DataModel/Write/CollectionNode.php | 7 +- .../DataModel/Write/CompositeNode.php | 7 +- .../Write/DataModelNodeInterface.php | 5 +- .../DataModel/Write/ObjectNode.php | 19 +- .../DataModel/Write/ScalarNode.php | 7 +- .../JsonStreamer/Read/PhpAstBuilder.php | 590 ------------------ .../JsonStreamer/Read/PhpGenerator.php | 337 ++++++++++ .../Read/StreamReaderGenerator.php | 29 +- .../DataModel/Write/CompositeNodeTest.php | 31 +- .../Tests/DataModel/Write/ObjectNodeTest.php | 25 +- .../Write/MergingStringVisitor.php | 60 -- .../JsonStreamer/Write/PhpAstBuilder.php | 436 ------------- .../JsonStreamer/Write/PhpGenerator.php | 388 ++++++++++++ .../JsonStreamer/Write/PhpOptimizer.php | 43 -- .../Write/StreamWriterGenerator.php | 40 +- .../Component/JsonStreamer/composer.json | 1 - 25 files changed, 798 insertions(+), 1480 deletions(-) delete mode 100644 src/Symfony/Component/JsonStreamer/DataModel/DataAccessorInterface.php delete mode 100644 src/Symfony/Component/JsonStreamer/DataModel/FunctionDataAccessor.php delete mode 100644 src/Symfony/Component/JsonStreamer/DataModel/PhpExprDataAccessor.php delete mode 100644 src/Symfony/Component/JsonStreamer/DataModel/PropertyDataAccessor.php delete mode 100644 src/Symfony/Component/JsonStreamer/DataModel/ScalarDataAccessor.php delete mode 100644 src/Symfony/Component/JsonStreamer/DataModel/VariableDataAccessor.php delete mode 100644 src/Symfony/Component/JsonStreamer/Read/PhpAstBuilder.php create mode 100644 src/Symfony/Component/JsonStreamer/Read/PhpGenerator.php delete mode 100644 src/Symfony/Component/JsonStreamer/Write/MergingStringVisitor.php delete mode 100644 src/Symfony/Component/JsonStreamer/Write/PhpAstBuilder.php create mode 100644 src/Symfony/Component/JsonStreamer/Write/PhpGenerator.php delete mode 100644 src/Symfony/Component/JsonStreamer/Write/PhpOptimizer.php diff --git a/src/Symfony/Component/JsonStreamer/CHANGELOG.md b/src/Symfony/Component/JsonStreamer/CHANGELOG.md index 5294c5b5f3637..87f1e74c951da 100644 --- a/src/Symfony/Component/JsonStreamer/CHANGELOG.md +++ b/src/Symfony/Component/JsonStreamer/CHANGELOG.md @@ -1,6 +1,11 @@ CHANGELOG ========= +7.4 +--- + + * Remove `nikic/php-parser` dependency + 7.3 --- diff --git a/src/Symfony/Component/JsonStreamer/DataModel/DataAccessorInterface.php b/src/Symfony/Component/JsonStreamer/DataModel/DataAccessorInterface.php deleted file mode 100644 index 99f3dbfd0e9b8..0000000000000 --- a/src/Symfony/Component/JsonStreamer/DataModel/DataAccessorInterface.php +++ /dev/null @@ -1,29 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\JsonStreamer\DataModel; - -use PhpParser\Node\Expr; - -/** - * Represents a way to access data on PHP. - * - * @author Mathias Arlaud - * - * @internal - */ -interface DataAccessorInterface -{ - /** - * Converts to "nikic/php-parser" PHP expression. - */ - public function toPhpExpr(): Expr; -} diff --git a/src/Symfony/Component/JsonStreamer/DataModel/FunctionDataAccessor.php b/src/Symfony/Component/JsonStreamer/DataModel/FunctionDataAccessor.php deleted file mode 100644 index 8ad8960674d57..0000000000000 --- a/src/Symfony/Component/JsonStreamer/DataModel/FunctionDataAccessor.php +++ /dev/null @@ -1,57 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\JsonStreamer\DataModel; - -use PhpParser\BuilderFactory; -use PhpParser\Node\Expr; - -/** - * Defines the way to access data using a function (or a method). - * - * @author Mathias Arlaud - * - * @internal - */ -final class FunctionDataAccessor implements DataAccessorInterface -{ - /** - * @param list $arguments - */ - public function __construct( - private string $functionName, - private array $arguments, - private ?DataAccessorInterface $objectAccessor = null, - ) { - } - - public function getObjectAccessor(): ?DataAccessorInterface - { - return $this->objectAccessor; - } - - public function withObjectAccessor(?DataAccessorInterface $accessor): self - { - return new self($this->functionName, $this->arguments, $accessor); - } - - public function toPhpExpr(): Expr - { - $builder = new BuilderFactory(); - $arguments = array_map(static fn (DataAccessorInterface $argument): Expr => $argument->toPhpExpr(), $this->arguments); - - if (null === $this->objectAccessor) { - return $builder->funcCall($this->functionName, $arguments); - } - - return $builder->methodCall($this->objectAccessor->toPhpExpr(), $this->functionName, $arguments); - } -} diff --git a/src/Symfony/Component/JsonStreamer/DataModel/PhpExprDataAccessor.php b/src/Symfony/Component/JsonStreamer/DataModel/PhpExprDataAccessor.php deleted file mode 100644 index 9806b94ed0a9f..0000000000000 --- a/src/Symfony/Component/JsonStreamer/DataModel/PhpExprDataAccessor.php +++ /dev/null @@ -1,34 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\JsonStreamer\DataModel; - -use PhpParser\Node\Expr; - -/** - * Defines the way to access data using PHP AST. - * - * @author Mathias Arlaud - * - * @internal - */ -final class PhpExprDataAccessor implements DataAccessorInterface -{ - public function __construct( - private Expr $php, - ) { - } - - public function toPhpExpr(): Expr - { - return $this->php; - } -} diff --git a/src/Symfony/Component/JsonStreamer/DataModel/PropertyDataAccessor.php b/src/Symfony/Component/JsonStreamer/DataModel/PropertyDataAccessor.php deleted file mode 100644 index f48c98064bb65..0000000000000 --- a/src/Symfony/Component/JsonStreamer/DataModel/PropertyDataAccessor.php +++ /dev/null @@ -1,46 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\JsonStreamer\DataModel; - -use PhpParser\BuilderFactory; -use PhpParser\Node\Expr; - -/** - * Defines the way to access data using an object property. - * - * @author Mathias Arlaud - * - * @internal - */ -final class PropertyDataAccessor implements DataAccessorInterface -{ - public function __construct( - private DataAccessorInterface $objectAccessor, - private string $propertyName, - ) { - } - - public function getObjectAccessor(): DataAccessorInterface - { - return $this->objectAccessor; - } - - public function withObjectAccessor(DataAccessorInterface $accessor): self - { - return new self($accessor, $this->propertyName); - } - - public function toPhpExpr(): Expr - { - return (new BuilderFactory())->propertyFetch($this->objectAccessor->toPhpExpr(), $this->propertyName); - } -} diff --git a/src/Symfony/Component/JsonStreamer/DataModel/Read/ObjectNode.php b/src/Symfony/Component/JsonStreamer/DataModel/Read/ObjectNode.php index 25d53c15fff60..e1a7e68927a6e 100644 --- a/src/Symfony/Component/JsonStreamer/DataModel/Read/ObjectNode.php +++ b/src/Symfony/Component/JsonStreamer/DataModel/Read/ObjectNode.php @@ -11,7 +11,6 @@ namespace Symfony\Component\JsonStreamer\DataModel\Read; -use Symfony\Component\JsonStreamer\DataModel\DataAccessorInterface; use Symfony\Component\TypeInfo\Type\ObjectType; use Symfony\Component\TypeInfo\Type\UnionType; @@ -25,7 +24,7 @@ final class ObjectNode implements DataModelNodeInterface { /** - * @param array $properties + * @param array $properties */ public function __construct( private ObjectType $type, @@ -50,7 +49,7 @@ public function getType(): ObjectType } /** - * @return array + * @return array */ public function getProperties(): array { diff --git a/src/Symfony/Component/JsonStreamer/DataModel/ScalarDataAccessor.php b/src/Symfony/Component/JsonStreamer/DataModel/ScalarDataAccessor.php deleted file mode 100644 index f60220dd82e7a..0000000000000 --- a/src/Symfony/Component/JsonStreamer/DataModel/ScalarDataAccessor.php +++ /dev/null @@ -1,35 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\JsonStreamer\DataModel; - -use PhpParser\BuilderFactory; -use PhpParser\Node\Expr; - -/** - * Defines the way to access a scalar value. - * - * @author Mathias Arlaud - * - * @internal - */ -final class ScalarDataAccessor implements DataAccessorInterface -{ - public function __construct( - private mixed $value, - ) { - } - - public function toPhpExpr(): Expr - { - return (new BuilderFactory())->val($this->value); - } -} diff --git a/src/Symfony/Component/JsonStreamer/DataModel/VariableDataAccessor.php b/src/Symfony/Component/JsonStreamer/DataModel/VariableDataAccessor.php deleted file mode 100644 index 0046f55b4e7e0..0000000000000 --- a/src/Symfony/Component/JsonStreamer/DataModel/VariableDataAccessor.php +++ /dev/null @@ -1,35 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\JsonStreamer\DataModel; - -use PhpParser\BuilderFactory; -use PhpParser\Node\Expr; - -/** - * Defines the way to access data using a variable. - * - * @author Mathias Arlaud - * - * @internal - */ -final class VariableDataAccessor implements DataAccessorInterface -{ - public function __construct( - private string $name, - ) { - } - - public function toPhpExpr(): Expr - { - return (new BuilderFactory())->var($this->name); - } -} diff --git a/src/Symfony/Component/JsonStreamer/DataModel/Write/BackedEnumNode.php b/src/Symfony/Component/JsonStreamer/DataModel/Write/BackedEnumNode.php index ba96b98319d1e..5a3b74861c3cd 100644 --- a/src/Symfony/Component/JsonStreamer/DataModel/Write/BackedEnumNode.php +++ b/src/Symfony/Component/JsonStreamer/DataModel/Write/BackedEnumNode.php @@ -11,7 +11,6 @@ namespace Symfony\Component\JsonStreamer\DataModel\Write; -use Symfony\Component\JsonStreamer\DataModel\DataAccessorInterface; use Symfony\Component\TypeInfo\Type\BackedEnumType; /** @@ -26,12 +25,12 @@ final class BackedEnumNode implements DataModelNodeInterface { public function __construct( - private DataAccessorInterface $accessor, + private string $accessor, private BackedEnumType $type, ) { } - public function withAccessor(DataAccessorInterface $accessor): self + public function withAccessor(string $accessor): self { return new self($accessor, $this->type); } @@ -41,7 +40,7 @@ public function getIdentifier(): string return (string) $this->getType(); } - public function getAccessor(): DataAccessorInterface + public function getAccessor(): string { return $this->accessor; } diff --git a/src/Symfony/Component/JsonStreamer/DataModel/Write/CollectionNode.php b/src/Symfony/Component/JsonStreamer/DataModel/Write/CollectionNode.php index 2f324fb404908..a334437c6891b 100644 --- a/src/Symfony/Component/JsonStreamer/DataModel/Write/CollectionNode.php +++ b/src/Symfony/Component/JsonStreamer/DataModel/Write/CollectionNode.php @@ -11,7 +11,6 @@ namespace Symfony\Component\JsonStreamer\DataModel\Write; -use Symfony\Component\JsonStreamer\DataModel\DataAccessorInterface; use Symfony\Component\TypeInfo\Type\CollectionType; /** @@ -24,13 +23,13 @@ final class CollectionNode implements DataModelNodeInterface { public function __construct( - private DataAccessorInterface $accessor, + private string $accessor, private CollectionType $type, private DataModelNodeInterface $item, ) { } - public function withAccessor(DataAccessorInterface $accessor): self + public function withAccessor(string $accessor): self { return new self($accessor, $this->type, $this->item); } @@ -40,7 +39,7 @@ public function getIdentifier(): string return (string) $this->getType(); } - public function getAccessor(): DataAccessorInterface + public function getAccessor(): string { return $this->accessor; } diff --git a/src/Symfony/Component/JsonStreamer/DataModel/Write/CompositeNode.php b/src/Symfony/Component/JsonStreamer/DataModel/Write/CompositeNode.php index 705d610fe7932..2469fbfb0e14c 100644 --- a/src/Symfony/Component/JsonStreamer/DataModel/Write/CompositeNode.php +++ b/src/Symfony/Component/JsonStreamer/DataModel/Write/CompositeNode.php @@ -11,7 +11,6 @@ namespace Symfony\Component\JsonStreamer\DataModel\Write; -use Symfony\Component\JsonStreamer\DataModel\DataAccessorInterface; use Symfony\Component\JsonStreamer\Exception\InvalidArgumentException; use Symfony\Component\TypeInfo\Type; use Symfony\Component\TypeInfo\Type\UnionType; @@ -43,7 +42,7 @@ final class CompositeNode implements DataModelNodeInterface * @param list $nodes */ public function __construct( - private DataAccessorInterface $accessor, + private string $accessor, array $nodes, ) { if (\count($nodes) < 2) { @@ -60,7 +59,7 @@ public function __construct( $this->nodes = $nodes; } - public function withAccessor(DataAccessorInterface $accessor): self + public function withAccessor(string $accessor): self { return new self($accessor, array_map(static fn (DataModelNodeInterface $n): DataModelNodeInterface => $n->withAccessor($accessor), $this->nodes)); } @@ -70,7 +69,7 @@ public function getIdentifier(): string return (string) $this->getType(); } - public function getAccessor(): DataAccessorInterface + public function getAccessor(): string { return $this->accessor; } diff --git a/src/Symfony/Component/JsonStreamer/DataModel/Write/DataModelNodeInterface.php b/src/Symfony/Component/JsonStreamer/DataModel/Write/DataModelNodeInterface.php index fa94649cda40a..7768cd4179a85 100644 --- a/src/Symfony/Component/JsonStreamer/DataModel/Write/DataModelNodeInterface.php +++ b/src/Symfony/Component/JsonStreamer/DataModel/Write/DataModelNodeInterface.php @@ -11,7 +11,6 @@ namespace Symfony\Component\JsonStreamer\DataModel\Write; -use Symfony\Component\JsonStreamer\DataModel\DataAccessorInterface; use Symfony\Component\TypeInfo\Type; /** @@ -27,7 +26,7 @@ public function getIdentifier(): string; public function getType(): Type; - public function getAccessor(): DataAccessorInterface; + public function getAccessor(): string; - public function withAccessor(DataAccessorInterface $accessor): self; + public function withAccessor(string $accessor): self; } diff --git a/src/Symfony/Component/JsonStreamer/DataModel/Write/ObjectNode.php b/src/Symfony/Component/JsonStreamer/DataModel/Write/ObjectNode.php index 56dfcad38c0fe..1f8f79a171067 100644 --- a/src/Symfony/Component/JsonStreamer/DataModel/Write/ObjectNode.php +++ b/src/Symfony/Component/JsonStreamer/DataModel/Write/ObjectNode.php @@ -11,9 +11,6 @@ namespace Symfony\Component\JsonStreamer\DataModel\Write; -use Symfony\Component\JsonStreamer\DataModel\DataAccessorInterface; -use Symfony\Component\JsonStreamer\DataModel\FunctionDataAccessor; -use Symfony\Component\JsonStreamer\DataModel\PropertyDataAccessor; use Symfony\Component\TypeInfo\Type\ObjectType; /** @@ -29,29 +26,23 @@ final class ObjectNode implements DataModelNodeInterface * @param array $properties */ public function __construct( - private DataAccessorInterface $accessor, + private string $accessor, private ObjectType $type, private array $properties, private bool $mock = false, ) { } - public static function createMock(DataAccessorInterface $accessor, ObjectType $type): self + public static function createMock(string $accessor, ObjectType $type): self { return new self($accessor, $type, [], true); } - public function withAccessor(DataAccessorInterface $accessor): self + public function withAccessor(string $accessor): self { $properties = []; foreach ($this->properties as $key => $property) { - $propertyAccessor = $property->getAccessor(); - - if ($propertyAccessor instanceof PropertyDataAccessor || $propertyAccessor instanceof FunctionDataAccessor && $propertyAccessor->getObjectAccessor()) { - $propertyAccessor = $propertyAccessor->withObjectAccessor($accessor); - } - - $properties[$key] = $property->withAccessor($propertyAccessor); + $properties[$key] = $property->withAccessor(str_replace($this->accessor, $accessor, $property->getAccessor())); } return new self($accessor, $this->type, $properties, $this->mock); @@ -62,7 +53,7 @@ public function getIdentifier(): string return (string) $this->getType(); } - public function getAccessor(): DataAccessorInterface + public function getAccessor(): string { return $this->accessor; } diff --git a/src/Symfony/Component/JsonStreamer/DataModel/Write/ScalarNode.php b/src/Symfony/Component/JsonStreamer/DataModel/Write/ScalarNode.php index 53dc88b321d3f..d40319e0e5013 100644 --- a/src/Symfony/Component/JsonStreamer/DataModel/Write/ScalarNode.php +++ b/src/Symfony/Component/JsonStreamer/DataModel/Write/ScalarNode.php @@ -11,7 +11,6 @@ namespace Symfony\Component\JsonStreamer\DataModel\Write; -use Symfony\Component\JsonStreamer\DataModel\DataAccessorInterface; use Symfony\Component\TypeInfo\Type\BuiltinType; /** @@ -26,12 +25,12 @@ final class ScalarNode implements DataModelNodeInterface { public function __construct( - private DataAccessorInterface $accessor, + private string $accessor, private BuiltinType $type, ) { } - public function withAccessor(DataAccessorInterface $accessor): self + public function withAccessor(string $accessor): self { return new self($accessor, $this->type); } @@ -41,7 +40,7 @@ public function getIdentifier(): string return (string) $this->getType(); } - public function getAccessor(): DataAccessorInterface + public function getAccessor(): string { return $this->accessor; } diff --git a/src/Symfony/Component/JsonStreamer/Read/PhpAstBuilder.php b/src/Symfony/Component/JsonStreamer/Read/PhpAstBuilder.php deleted file mode 100644 index 7a6e23762beca..0000000000000 --- a/src/Symfony/Component/JsonStreamer/Read/PhpAstBuilder.php +++ /dev/null @@ -1,590 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\JsonStreamer\Read; - -use PhpParser\BuilderFactory; -use PhpParser\Node; -use PhpParser\Node\Expr; -use PhpParser\Node\Expr\Array_; -use PhpParser\Node\Expr\ArrayDimFetch; -use PhpParser\Node\Expr\ArrayItem; -use PhpParser\Node\Expr\Assign; -use PhpParser\Node\Expr\BinaryOp\BooleanAnd; -use PhpParser\Node\Expr\BinaryOp\Coalesce; -use PhpParser\Node\Expr\BinaryOp\Identical; -use PhpParser\Node\Expr\BinaryOp\NotIdentical; -use PhpParser\Node\Expr\Cast\Object_ as ObjectCast; -use PhpParser\Node\Expr\Cast\String_ as StringCast; -use PhpParser\Node\Expr\ClassConstFetch; -use PhpParser\Node\Expr\Closure; -use PhpParser\Node\Expr\ClosureUse; -use PhpParser\Node\Expr\Match_; -use PhpParser\Node\Expr\Ternary; -use PhpParser\Node\Expr\Throw_; -use PhpParser\Node\Expr\Yield_; -use PhpParser\Node\Identifier; -use PhpParser\Node\MatchArm; -use PhpParser\Node\Name\FullyQualified; -use PhpParser\Node\Param; -use PhpParser\Node\Stmt; -use PhpParser\Node\Stmt\Expression; -use PhpParser\Node\Stmt\Foreach_; -use PhpParser\Node\Stmt\If_; -use PhpParser\Node\Stmt\Return_; -use Psr\Container\ContainerInterface; -use Symfony\Component\JsonStreamer\DataModel\PhpExprDataAccessor; -use Symfony\Component\JsonStreamer\DataModel\Read\BackedEnumNode; -use Symfony\Component\JsonStreamer\DataModel\Read\CollectionNode; -use Symfony\Component\JsonStreamer\DataModel\Read\CompositeNode; -use Symfony\Component\JsonStreamer\DataModel\Read\DataModelNodeInterface; -use Symfony\Component\JsonStreamer\DataModel\Read\ObjectNode; -use Symfony\Component\JsonStreamer\DataModel\Read\ScalarNode; -use Symfony\Component\JsonStreamer\Exception\LogicException; -use Symfony\Component\JsonStreamer\Exception\UnexpectedValueException; -use Symfony\Component\TypeInfo\Type\BackedEnumType; -use Symfony\Component\TypeInfo\Type\BuiltinType; -use Symfony\Component\TypeInfo\Type\CollectionType; -use Symfony\Component\TypeInfo\Type\ObjectType; -use Symfony\Component\TypeInfo\Type\WrappingTypeInterface; -use Symfony\Component\TypeInfo\TypeIdentifier; - -/** - * Builds a PHP syntax tree that reads JSON stream. - * - * @author Mathias Arlaud - * - * @internal - */ -final class PhpAstBuilder -{ - private BuilderFactory $builder; - - public function __construct() - { - $this->builder = new BuilderFactory(); - } - - /** - * @param array $options - * @param array $context - * - * @return list - */ - public function build(DataModelNodeInterface $dataModel, bool $decodeFromStream, array $options = [], array $context = []): array - { - if ($decodeFromStream) { - return [new Return_(new Closure([ - 'static' => true, - 'params' => [ - new Param($this->builder->var('stream'), type: new Identifier('mixed')), - new Param($this->builder->var('valueTransformers'), type: new FullyQualified(ContainerInterface::class)), - new Param($this->builder->var('instantiator'), type: new FullyQualified(LazyInstantiator::class)), - new Param($this->builder->var('options'), type: new Identifier('array')), - ], - 'returnType' => new Identifier('mixed'), - 'stmts' => [ - ...$this->buildProvidersStatements($dataModel, $decodeFromStream, $context), - new Return_( - $this->nodeOnlyNeedsDecode($dataModel, $decodeFromStream) - ? $this->builder->staticCall(new FullyQualified(Decoder::class), 'decodeStream', [ - $this->builder->var('stream'), - $this->builder->val(0), - $this->builder->val(null), - ]) - : $this->builder->funcCall(new ArrayDimFetch($this->builder->var('providers'), $this->builder->val($dataModel->getIdentifier())), [ - $this->builder->var('stream'), - $this->builder->val(0), - $this->builder->val(null), - ]), - ), - ], - ]))]; - } - - return [new Return_(new Closure([ - 'static' => true, - 'params' => [ - new Param($this->builder->var('string'), type: new Identifier('string|\\Stringable')), - new Param($this->builder->var('valueTransformers'), type: new FullyQualified(ContainerInterface::class)), - new Param($this->builder->var('instantiator'), type: new FullyQualified(Instantiator::class)), - new Param($this->builder->var('options'), type: new Identifier('array')), - ], - 'returnType' => new Identifier('mixed'), - 'stmts' => [ - ...$this->buildProvidersStatements($dataModel, $decodeFromStream, $context), - new Return_( - $this->nodeOnlyNeedsDecode($dataModel, $decodeFromStream) - ? $this->builder->staticCall(new FullyQualified(Decoder::class), 'decodeString', [new StringCast($this->builder->var('string'))]) - : $this->builder->funcCall(new ArrayDimFetch($this->builder->var('providers'), $this->builder->val($dataModel->getIdentifier())), [ - $this->builder->staticCall(new FullyQualified(Decoder::class), 'decodeString', [new StringCast($this->builder->var('string'))]), - ]), - ), - ], - ]))]; - } - - /** - * @param array $context - * - * @return list - */ - private function buildProvidersStatements(DataModelNodeInterface $node, bool $decodeFromStream, array &$context): array - { - if ($context['providers'][$node->getIdentifier()] ?? false) { - return []; - } - - $context['providers'][$node->getIdentifier()] = true; - - if ($this->nodeOnlyNeedsDecode($node, $decodeFromStream)) { - return []; - } - - return match (true) { - $node instanceof ScalarNode || $node instanceof BackedEnumNode => $this->buildLeafProviderStatements($node, $decodeFromStream), - $node instanceof CompositeNode => $this->buildCompositeNodeStatements($node, $decodeFromStream, $context), - $node instanceof CollectionNode => $this->buildCollectionNodeStatements($node, $decodeFromStream, $context), - $node instanceof ObjectNode => $this->buildObjectNodeStatements($node, $decodeFromStream, $context), - default => throw new LogicException(\sprintf('Unexpected "%s" data model node.', $node::class)), - }; - } - - /** - * @return list - */ - private function buildLeafProviderStatements(ScalarNode|BackedEnumNode $node, bool $decodeFromStream): array - { - $accessor = $decodeFromStream - ? $this->builder->staticCall(new FullyQualified(Decoder::class), 'decodeStream', [ - $this->builder->var('stream'), - $this->builder->var('offset'), - $this->builder->var('length'), - ]) - : $this->builder->var('data'); - - $params = $decodeFromStream - ? [new Param($this->builder->var('stream')), new Param($this->builder->var('offset')), new Param($this->builder->var('length'))] - : [new Param($this->builder->var('data'))]; - - return [ - new Expression(new Assign( - new ArrayDimFetch($this->builder->var('providers'), $this->builder->val($node->getIdentifier())), - new Closure([ - 'static' => true, - 'params' => $params, - 'stmts' => [new Return_($this->buildFormatValueStatement($node, $accessor))], - ]), - )), - ]; - } - - private function buildFormatValueStatement(DataModelNodeInterface $node, Expr $accessor): Node - { - if ($node instanceof BackedEnumNode) { - /** @var ObjectType $type */ - $type = $node->getType(); - - return $this->builder->staticCall(new FullyQualified($type->getClassName()), 'from', [$accessor]); - } - - if ($node instanceof ScalarNode) { - /** @var BuiltinType $type */ - $type = $node->getType(); - - return match (true) { - TypeIdentifier::NULL === $type->getTypeIdentifier() => $this->builder->val(null), - TypeIdentifier::OBJECT === $type->getTypeIdentifier() => new ObjectCast($accessor), - default => $accessor, - }; - } - - return $accessor; - } - - /** - * @param array $context - * - * @return list - */ - private function buildCompositeNodeStatements(CompositeNode $node, bool $decodeFromStream, array &$context): array - { - $prepareDataStmts = $decodeFromStream ? [ - new Expression(new Assign($this->builder->var('data'), $this->builder->staticCall(new FullyQualified(Decoder::class), 'decodeStream', [ - $this->builder->var('stream'), - $this->builder->var('offset'), - $this->builder->var('length'), - ]))), - ] : []; - - $providersStmts = []; - $nodesStmts = []; - - $nodeCondition = function (DataModelNodeInterface $node, Expr $accessor): Expr { - $type = $node->getType(); - - if ($type->isIdentifiedBy(TypeIdentifier::NULL)) { - return new Identical($this->builder->val(null), $this->builder->var('data')); - } - - if ($type->isIdentifiedBy(TypeIdentifier::TRUE)) { - return new Identical($this->builder->val(true), $this->builder->var('data')); - } - - if ($type->isIdentifiedBy(TypeIdentifier::FALSE)) { - return new Identical($this->builder->val(false), $this->builder->var('data')); - } - - if ($type->isIdentifiedBy(TypeIdentifier::MIXED)) { - return $this->builder->val(true); - } - - if ($type instanceof CollectionType) { - return $type->isList() - ? new BooleanAnd($this->builder->funcCall('\is_array', [$this->builder->var('data')]), $this->builder->funcCall('\array_is_list', [$this->builder->var('data')])) - : $this->builder->funcCall('\is_array', [$this->builder->var('data')]); - } - - while ($type instanceof WrappingTypeInterface) { - $type = $type->getWrappedType(); - } - - if ($type instanceof BackedEnumType) { - return $this->builder->funcCall('\is_'.$type->getBackingType()->getTypeIdentifier()->value, [$this->builder->var('data')]); - } - - if ($type instanceof ObjectType) { - return $this->builder->funcCall('\is_array', [$this->builder->var('data')]); - } - - if ($type instanceof BuiltinType) { - return $this->builder->funcCall('\is_'.$type->getTypeIdentifier()->value, [$this->builder->var('data')]); - } - - throw new LogicException(\sprintf('Unexpected "%s" type.', $type::class)); - }; - - foreach ($node->getNodes() as $n) { - if ($this->nodeOnlyNeedsDecode($n, $decodeFromStream)) { - $nodeValueStmt = $this->buildFormatValueStatement($n, $this->builder->var('data')); - } else { - $providersStmts = [...$providersStmts, ...$this->buildProvidersStatements($n, $decodeFromStream, $context)]; - $nodeValueStmt = $this->builder->funcCall( - new ArrayDimFetch($this->builder->var('providers'), $this->builder->val($n->getIdentifier())), - [$this->builder->var('data')], - ); - } - - $nodesStmts[] = new If_($nodeCondition($n, $this->builder->var('data')), ['stmts' => [new Return_($nodeValueStmt)]]); - } - - $params = $decodeFromStream - ? [new Param($this->builder->var('stream')), new Param($this->builder->var('offset')), new Param($this->builder->var('length'))] - : [new Param($this->builder->var('data'))]; - - return [ - ...$providersStmts, - new Expression(new Assign( - new ArrayDimFetch($this->builder->var('providers'), $this->builder->val($node->getIdentifier())), - new Closure([ - 'static' => true, - 'params' => $params, - 'uses' => [ - new ClosureUse($this->builder->var('options')), - new ClosureUse($this->builder->var('valueTransformers')), - new ClosureUse($this->builder->var('instantiator')), - new ClosureUse($this->builder->var('providers'), byRef: true), - ], - 'stmts' => [ - ...$prepareDataStmts, - ...$nodesStmts, - new Expression(new Throw_($this->builder->new(new FullyQualified(UnexpectedValueException::class), [$this->builder->funcCall('\sprintf', [ - $this->builder->val(\sprintf('Unexpected "%%s" value for "%s".', $node->getIdentifier())), - $this->builder->funcCall('\get_debug_type', [$this->builder->var('data')]), - ])]))), - ], - ]), - )), - ]; - } - - /** - * @param array $context - * - * @return list - */ - private function buildCollectionNodeStatements(CollectionNode $node, bool $decodeFromStream, array &$context): array - { - if ($decodeFromStream) { - $itemValueStmt = $this->nodeOnlyNeedsDecode($node->getItemNode(), $decodeFromStream) - ? $this->buildFormatValueStatement( - $node->getItemNode(), - $this->builder->staticCall(new FullyQualified(Decoder::class), 'decodeStream', [ - $this->builder->var('stream'), - new ArrayDimFetch($this->builder->var('v'), $this->builder->val(0)), - new ArrayDimFetch($this->builder->var('v'), $this->builder->val(1)), - ]), - ) - : $this->builder->funcCall( - new ArrayDimFetch($this->builder->var('providers'), $this->builder->val($node->getItemNode()->getIdentifier())), [ - $this->builder->var('stream'), - new ArrayDimFetch($this->builder->var('v'), $this->builder->val(0)), - new ArrayDimFetch($this->builder->var('v'), $this->builder->val(1)), - ], - ); - } else { - $itemValueStmt = $this->nodeOnlyNeedsDecode($node->getItemNode(), $decodeFromStream) - ? $this->builder->var('v') - : $this->builder->funcCall( - new ArrayDimFetch($this->builder->var('providers'), $this->builder->val($node->getItemNode()->getIdentifier())), - [$this->builder->var('v')], - ); - } - - $iterableClosureParams = $decodeFromStream - ? [new Param($this->builder->var('stream')), new Param($this->builder->var('data'))] - : [new Param($this->builder->var('data'))]; - - $iterableClosureStmts = [ - new Expression(new Assign( - $this->builder->var('iterable'), - new Closure([ - 'static' => true, - 'params' => $iterableClosureParams, - 'uses' => [ - new ClosureUse($this->builder->var('options')), - new ClosureUse($this->builder->var('valueTransformers')), - new ClosureUse($this->builder->var('instantiator')), - new ClosureUse($this->builder->var('providers'), byRef: true), - ], - 'stmts' => [ - new Foreach_($this->builder->var('data'), $this->builder->var('v'), [ - 'keyVar' => $this->builder->var('k'), - 'stmts' => [new Expression(new Yield_($itemValueStmt, $this->builder->var('k')))], - ]), - ], - ]), - )), - ]; - - $iterableValueStmt = $decodeFromStream - ? $this->builder->funcCall($this->builder->var('iterable'), [$this->builder->var('stream'), $this->builder->var('data')]) - : $this->builder->funcCall($this->builder->var('iterable'), [$this->builder->var('data')]); - - $prepareDataStmts = $decodeFromStream ? [ - new Expression(new Assign($this->builder->var('data'), $this->builder->staticCall( - new FullyQualified(Splitter::class), - $node->getType()->isList() ? 'splitList' : 'splitDict', - [$this->builder->var('stream'), $this->builder->var('offset'), $this->builder->var('length')], - ))), - ] : []; - - $params = $decodeFromStream - ? [new Param($this->builder->var('stream')), new Param($this->builder->var('offset')), new Param($this->builder->var('length'))] - : [new Param($this->builder->var('data'))]; - - return [ - new Expression(new Assign( - new ArrayDimFetch($this->builder->var('providers'), $this->builder->val($node->getIdentifier())), - new Closure([ - 'static' => true, - 'params' => $params, - 'uses' => [ - new ClosureUse($this->builder->var('options')), - new ClosureUse($this->builder->var('valueTransformers')), - new ClosureUse($this->builder->var('instantiator')), - new ClosureUse($this->builder->var('providers'), byRef: true), - ], - 'stmts' => [ - ...$prepareDataStmts, - ...$iterableClosureStmts, - new Return_($node->getType()->isIdentifiedBy(TypeIdentifier::ARRAY) ? $this->builder->funcCall('\iterator_to_array', [$iterableValueStmt]) : $iterableValueStmt), - ], - ]), - )), - ...($this->nodeOnlyNeedsDecode($node->getItemNode(), $decodeFromStream) ? [] : $this->buildProvidersStatements($node->getItemNode(), $decodeFromStream, $context)), - ]; - } - - /** - * @param array $context - * - * @return list - */ - private function buildObjectNodeStatements(ObjectNode $node, bool $decodeFromStream, array &$context): array - { - if ($node->isMock()) { - return []; - } - - $propertyValueProvidersStmts = []; - $stringPropertiesValuesStmts = []; - $streamPropertiesValuesStmts = []; - - foreach ($node->getProperties() as $streamedName => $property) { - $propertyValueProvidersStmts = [ - ...$propertyValueProvidersStmts, - ...($this->nodeOnlyNeedsDecode($property['value'], $decodeFromStream) ? [] : $this->buildProvidersStatements($property['value'], $decodeFromStream, $context)), - ]; - - if ($decodeFromStream) { - $propertyValueStmt = $this->nodeOnlyNeedsDecode($property['value'], $decodeFromStream) - ? $this->buildFormatValueStatement( - $property['value'], - $this->builder->staticCall(new FullyQualified(Decoder::class), 'decodeStream', [ - $this->builder->var('stream'), - new ArrayDimFetch($this->builder->var('v'), $this->builder->val(0)), - new ArrayDimFetch($this->builder->var('v'), $this->builder->val(1)), - ]), - ) - : $this->builder->funcCall( - new ArrayDimFetch($this->builder->var('providers'), $this->builder->val($property['value']->getIdentifier())), [ - $this->builder->var('stream'), - new ArrayDimFetch($this->builder->var('v'), $this->builder->val(0)), - new ArrayDimFetch($this->builder->var('v'), $this->builder->val(1)), - ], - ); - - $streamPropertiesValuesStmts[] = new MatchArm([$this->builder->val($streamedName)], new Assign( - $this->builder->propertyFetch($this->builder->var('object'), $property['name']), - $property['accessor'](new PhpExprDataAccessor($propertyValueStmt))->toPhpExpr(), - )); - } else { - $propertyValueStmt = $this->nodeOnlyNeedsDecode($property['value'], $decodeFromStream) - ? new Coalesce(new ArrayDimFetch($this->builder->var('data'), $this->builder->val($streamedName)), $this->builder->val('_symfony_missing_value')) - : new Ternary( - $this->builder->funcCall('\array_key_exists', [$this->builder->val($streamedName), $this->builder->var('data')]), - $this->builder->funcCall( - new ArrayDimFetch($this->builder->var('providers'), $this->builder->val($property['value']->getIdentifier())), - [new ArrayDimFetch($this->builder->var('data'), $this->builder->val($streamedName))], - ), - $this->builder->val('_symfony_missing_value'), - ); - - $stringPropertiesValuesStmts[] = new ArrayItem( - $property['accessor'](new PhpExprDataAccessor($propertyValueStmt))->toPhpExpr(), - $this->builder->val($property['name']), - ); - } - } - - $params = $decodeFromStream - ? [new Param($this->builder->var('stream')), new Param($this->builder->var('offset')), new Param($this->builder->var('length'))] - : [new Param($this->builder->var('data'))]; - - $prepareDataStmts = $decodeFromStream ? [ - new Expression(new Assign($this->builder->var('data'), $this->builder->staticCall( - new FullyQualified(Splitter::class), - 'splitDict', - [$this->builder->var('stream'), $this->builder->var('offset'), $this->builder->var('length')], - ))), - ] : []; - - if ($decodeFromStream) { - $instantiateStmts = [ - new Return_($this->builder->methodCall($this->builder->var('instantiator'), 'instantiate', [ - new ClassConstFetch(new FullyQualified($node->getType()->getClassName()), 'class'), - new Closure([ - 'static' => true, - 'params' => [new Param($this->builder->var('object'))], - 'uses' => [ - new ClosureUse($this->builder->var('stream')), - new ClosureUse($this->builder->var('data')), - new ClosureUse($this->builder->var('options')), - new ClosureUse($this->builder->var('valueTransformers')), - new ClosureUse($this->builder->var('instantiator')), - new ClosureUse($this->builder->var('providers'), byRef: true), - ], - 'stmts' => [ - new Foreach_($this->builder->var('data'), $this->builder->var('v'), [ - 'keyVar' => $this->builder->var('k'), - 'stmts' => [new Expression(new Match_( - $this->builder->var('k'), - [...$streamPropertiesValuesStmts, new MatchArm(null, $this->builder->val(null))], - ))], - ]), - ], - ]), - ])), - ]; - } else { - $instantiateStmts = [ - new Return_($this->builder->methodCall($this->builder->var('instantiator'), 'instantiate', [ - new ClassConstFetch(new FullyQualified($node->getType()->getClassName()), 'class'), - $this->builder->funcCall('\array_filter', [ - new Array_($stringPropertiesValuesStmts, ['kind' => Array_::KIND_SHORT]), - new Closure([ - 'static' => true, - 'params' => [new Param($this->builder->var('v'))], - 'stmts' => [new Return_(new NotIdentical($this->builder->val('_symfony_missing_value'), $this->builder->var('v')))], - ]), - ]), - ])), - ]; - } - - return [ - new Expression(new Assign( - new ArrayDimFetch($this->builder->var('providers'), $this->builder->val($node->getIdentifier())), - new Closure([ - 'static' => true, - 'params' => $params, - 'uses' => [ - new ClosureUse($this->builder->var('options')), - new ClosureUse($this->builder->var('valueTransformers')), - new ClosureUse($this->builder->var('instantiator')), - new ClosureUse($this->builder->var('providers'), byRef: true), - ], - 'stmts' => [ - ...$prepareDataStmts, - ...$instantiateStmts, - ], - ]), - )), - ...$propertyValueProvidersStmts, - ]; - } - - private function nodeOnlyNeedsDecode(DataModelNodeInterface $node, bool $decodeFromStream): bool - { - if ($node instanceof CompositeNode) { - foreach ($node->getNodes() as $n) { - if (!$this->nodeOnlyNeedsDecode($n, $decodeFromStream)) { - return false; - } - } - - return true; - } - - if ($node instanceof CollectionNode) { - if ($decodeFromStream) { - return false; - } - - return $this->nodeOnlyNeedsDecode($node->getItemNode(), $decodeFromStream); - } - - if ($node instanceof ObjectNode) { - return false; - } - - if ($node instanceof BackedEnumNode) { - return false; - } - - if ($node instanceof ScalarNode) { - return !$node->getType()->isIdentifiedBy(TypeIdentifier::OBJECT); - } - - return true; - } -} diff --git a/src/Symfony/Component/JsonStreamer/Read/PhpGenerator.php b/src/Symfony/Component/JsonStreamer/Read/PhpGenerator.php new file mode 100644 index 0000000000000..28a9cc9200121 --- /dev/null +++ b/src/Symfony/Component/JsonStreamer/Read/PhpGenerator.php @@ -0,0 +1,337 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\JsonStreamer\Read; + +use Psr\Container\ContainerInterface; +use Symfony\Component\JsonStreamer\DataModel\Read\BackedEnumNode; +use Symfony\Component\JsonStreamer\DataModel\Read\CollectionNode; +use Symfony\Component\JsonStreamer\DataModel\Read\CompositeNode; +use Symfony\Component\JsonStreamer\DataModel\Read\DataModelNodeInterface; +use Symfony\Component\JsonStreamer\DataModel\Read\ObjectNode; +use Symfony\Component\JsonStreamer\DataModel\Read\ScalarNode; +use Symfony\Component\JsonStreamer\Exception\LogicException; +use Symfony\Component\JsonStreamer\Exception\UnexpectedValueException; +use Symfony\Component\TypeInfo\Type\BackedEnumType; +use Symfony\Component\TypeInfo\Type\BuiltinType; +use Symfony\Component\TypeInfo\Type\CollectionType; +use Symfony\Component\TypeInfo\Type\ObjectType; +use Symfony\Component\TypeInfo\Type\WrappingTypeInterface; +use Symfony\Component\TypeInfo\TypeIdentifier; + +/** + * Generates PHP code that reads JSON stream. + * + * @author Mathias Arlaud + * + * @internal + */ +final class PhpGenerator +{ + /** + * @param array $options + * @param array $context + */ + public function generate(DataModelNodeInterface $dataModel, bool $decodeFromStream, array $options = [], array $context = []): string + { + $context['indentation_level'] = 1; + + $providers = $this->generateProviders($dataModel, $decodeFromStream, $context); + + $context['indentation_level'] = 0; + + if ($decodeFromStream) { + return $this->line('line('', $context) + .$this->line('return static function (mixed $stream, \\'.ContainerInterface::class.' $valueTransformers, \\'.LazyInstantiator::class.' $instantiator, array $options): mixed {', $context) + .$providers + .($this->canBeDecodedWithJsonDecode($dataModel, $decodeFromStream) + ? $this->line(' return \\'.Decoder::class.'::decodeStream($stream, 0, null);', $context) + : $this->line(' return $providers[\''.$dataModel->getIdentifier().'\']($stream, 0, null);', $context)) + .$this->line('};', $context); + } + + return $this->line('line('', $context) + .$this->line('return static function (string|\\Stringable $string, \\'.ContainerInterface::class.' $valueTransformers, \\'.Instantiator::class.' $instantiator, array $options): mixed {', $context) + .$providers + .($this->canBeDecodedWithJsonDecode($dataModel, $decodeFromStream) + ? $this->line(' return \\'.Decoder::class.'::decodeString((string) $string);', $context) + : $this->line(' return $providers[\''.$dataModel->getIdentifier().'\'](\\'.Decoder::class.'::decodeString((string) $string));', $context)) + .$this->line('};', $context); + } + + /** + * @param array $context + */ + private function generateProviders(DataModelNodeInterface $node, bool $decodeFromStream, array $context): string + { + if ($context['providers'][$node->getIdentifier()] ?? false) { + return ''; + } + + $context['providers'][$node->getIdentifier()] = true; + + if ($this->canBeDecodedWithJsonDecode($node, $decodeFromStream)) { + return ''; + } + + if ($node instanceof ScalarNode || $node instanceof BackedEnumNode) { + $accessor = $decodeFromStream ? '\\'.Decoder::class.'::decodeStream($stream, $offset, $length)' : '$data'; + $arguments = $decodeFromStream ? '$stream, $offset, $length' : '$data'; + + return $this->line("\$providers['".$node->getIdentifier()."'] = static function ($arguments) {", $context) + .$this->line(' return '.$this->generateValueFormat($node, $accessor).';', $context) + .$this->line('};', $context); + } + + if ($node instanceof CompositeNode) { + $php = ''; + foreach ($node->getNodes() as $n) { + if (!$this->canBeDecodedWithJsonDecode($n, $decodeFromStream)) { + $php .= $this->generateProviders($n, $decodeFromStream, $context); + } + } + + $arguments = $decodeFromStream ? '$stream, $offset, $length' : '$data'; + + $php .= $this->line("\$providers['".$node->getIdentifier()."'] = static function ($arguments) use (\$options, \$valueTransformers, \$instantiator, &\$providers) {", $context); + + ++$context['indentation_level']; + + $php .= $decodeFromStream ? $this->line('$data = \\'.Decoder::class.'::decodeStream($stream, $offset, $length);', $context) : ''; + + foreach ($node->getNodes() as $n) { + $value = $this->canBeDecodedWithJsonDecode($n, $decodeFromStream) ? $this->generateValueFormat($n, '$data') : '$providers[\''.$n->getIdentifier().'\']($data)'; + $php .= $this->line('if ('.$this->generateCompositeNodeItemCondition($n, '$data').') {', $context) + .$this->line(" return $value;", $context) + .$this->line('}', $context); + } + + $php .= $this->line('throw new \\'.UnexpectedValueException::class.'(\\sprintf(\'Unexpected "%s" value for "'.$node->getIdentifier().'".\', \\get_debug_type($data)));', $context); + + --$context['indentation_level']; + + return $php.$this->line('};', $context); + } + + if ($node instanceof CollectionNode) { + $arguments = $decodeFromStream ? '$stream, $offset, $length' : '$data'; + + $php = $this->line("\$providers['".$node->getIdentifier()."'] = static function ($arguments) use (\$options, \$valueTransformers, \$instantiator, &\$providers) {", $context); + + ++$context['indentation_level']; + + $arguments = $decodeFromStream ? '$stream, $data' : '$data'; + $php .= ($decodeFromStream ? $this->line('$data = \\'.Splitter::class.'::'.($node->getType()->isList() ? 'splitList' : 'splitDict').'($stream, $offset, $length);', $context) : '') + .$this->line("\$iterable = static function ($arguments) use (\$options, \$valueTransformers, \$instantiator, &\$providers) {", $context) + .$this->line(' foreach ($data as $k => $v) {', $context); + + if ($decodeFromStream) { + $php .= $this->canBeDecodedWithJsonDecode($node->getItemNode(), $decodeFromStream) + ? $this->line(' yield $k => '.$this->generateValueFormat($node->getItemNode(), '\\'.Decoder::class.'::decodeStream($stream, $v[0], $v[1]);'), $context) + : $this->line(' yield $k => $providers[\''.$node->getItemNode()->getIdentifier().'\']($stream, $v[0], $v[1]);', $context); + } else { + $php .= $this->canBeDecodedWithJsonDecode($node->getItemNode(), $decodeFromStream) + ? $this->line(' yield $k => $v;', $context) + : $this->line(' yield $k => $providers[\''.$node->getItemNode()->getIdentifier().'\']($v);', $context); + } + + $php .= $this->line(' }', $context) + .$this->line('};', $context) + .$this->line('return '.($node->getType()->isIdentifiedBy(TypeIdentifier::ARRAY) ? "\\iterator_to_array(\$iterable($arguments))" : "\$iterable($arguments)").';', $context); + + --$context['indentation_level']; + + $php .= $this->line('};', $context); + + if (!$this->canBeDecodedWithJsonDecode($node->getItemNode(), $decodeFromStream)) { + $php .= $this->generateProviders($node->getItemNode(), $decodeFromStream, $context); + } + + return $php; + } + + if ($node instanceof ObjectNode) { + if ($node->isMock()) { + return ''; + } + + $arguments = $decodeFromStream ? '$stream, $offset, $length' : '$data'; + + $php = $this->line("\$providers['".$node->getIdentifier()."'] = static function ($arguments) use (\$options, \$valueTransformers, \$instantiator, &\$providers) {", $context); + + ++$context['indentation_level']; + + $php .= $decodeFromStream ? $this->line('$data = \\'.Splitter::class.'::splitDict($stream, $offset, $length);', $context) : ''; + + if ($decodeFromStream) { + $php .= $this->line('return $instantiator->instantiate(\\'.$node->getType()->getClassName().'::class, static function ($object) use ($stream, $data, $options, $valueTransformers, $instantiator, &$providers) {', $context) + .$this->line(' foreach ($data as $k => $v) {', $context) + .$this->line(' match ($k) {', $context); + + foreach ($node->getProperties() as $streamedName => $property) { + $propertyValuePhp = $this->canBeDecodedWithJsonDecode($property['value'], $decodeFromStream) + ? $this->generateValueFormat($property['value'], '\\'.Decoder::class.'::decodeStream($stream, $v[0], $v[1])') + : '$providers[\''.$property['value']->getIdentifier().'\']($stream, $v[0], $v[1])'; + + $php .= $this->line(" '$streamedName' => \$object->".$property['name'].' = '.$property['accessor']($propertyValuePhp).',', $context); + } + + $php .= $this->line(' default => null,', $context) + .$this->line(' };', $context) + .$this->line(' }', $context) + .$this->line('});', $context); + } else { + $propertiesValuePhp = '['; + $separator = ''; + foreach ($node->getProperties() as $streamedName => $property) { + $propertyValuePhp = $this->canBeDecodedWithJsonDecode($property['value'], $decodeFromStream) + ? "\$data['$streamedName'] ?? '_symfony_missing_value'" + : "\\array_key_exists('$streamedName', \$data) ? \$providers['".$property['value']->getIdentifier()."'](\$data['$streamedName']) : '_symfony_missing_value'"; + $propertiesValuePhp .= "$separator'".$property['name']."' => ".$property['accessor']($propertyValuePhp); + $separator = ', '; + } + $propertiesValuePhp .= ']'; + + $php .= $this->line('return $instantiator->instantiate(\\'.$node->getType()->getClassName()."::class, \\array_filter($propertiesValuePhp, static function (\$v) {", $context) + .$this->line(' return \'_symfony_missing_value\' !== $v;', $context) + .$this->line('}));', $context); + } + + --$context['indentation_level']; + + $php .= $this->line('};', $context); + + foreach ($node->getProperties() as $streamedName => $property) { + if (!$this->canBeDecodedWithJsonDecode($property['value'], $decodeFromStream)) { + $php .= $this->generateProviders($property['value'], $decodeFromStream, $context); + } + } + + return $php; + } + + throw new LogicException(\sprintf('Unexpected "%s" data model node.', $node::class)); + } + + private function generateValueFormat(DataModelNodeInterface $node, string $accessor): string + { + if ($node instanceof BackedEnumNode) { + /** @var ObjectType $type */ + $type = $node->getType(); + + return '\\'.$type->getClassName()."::from($accessor)"; + } + + if ($node instanceof ScalarNode) { + /** @var BuiltinType $type */ + $type = $node->getType(); + + return match (true) { + TypeIdentifier::NULL === $type->getTypeIdentifier() => 'null', + TypeIdentifier::OBJECT === $type->getTypeIdentifier() => "(object) $accessor", + default => $accessor, + }; + } + + return $accessor; + } + + private function generateCompositeNodeItemCondition(DataModelNodeInterface $node, string $accessor): string + { + $type = $node->getType(); + + if ($type->isIdentifiedBy(TypeIdentifier::NULL)) { + return "null === $accessor"; + } + + if ($type->isIdentifiedBy(TypeIdentifier::TRUE)) { + return "true === $accessor"; + } + + if ($type->isIdentifiedBy(TypeIdentifier::FALSE)) { + return "false === $accessor"; + } + + if ($type->isIdentifiedBy(TypeIdentifier::MIXED)) { + return 'true'; + } + + if ($type instanceof CollectionType) { + return $type->isList() ? "\\is_array($accessor) && \\array_is_list($accessor)" : "\\is_array($accessor)"; + } + + while ($type instanceof WrappingTypeInterface) { + $type = $type->getWrappedType(); + } + + if ($type instanceof BackedEnumType) { + return '\\is_'.$type->getBackingType()->getTypeIdentifier()->value."($accessor)"; + } + + if ($type instanceof ObjectType) { + return "\\is_array($accessor)"; + } + + if ($type instanceof BuiltinType) { + return '\\is_'.$type->getTypeIdentifier()->value."($accessor)"; + } + + throw new LogicException(\sprintf('Unexpected "%s" type.', $type::class)); + } + + /** + * @param array $context + */ + private function line(string $line, array $context): string + { + return str_repeat(' ', $context['indentation_level']).$line."\n"; + } + + /** + * Determines if the $node can be decoded using a simple "json_decode". + */ + private function canBeDecodedWithJsonDecode(DataModelNodeInterface $node, bool $decodeFromStream): bool + { + if ($node instanceof CompositeNode) { + foreach ($node->getNodes() as $n) { + if (!$this->canBeDecodedWithJsonDecode($n, $decodeFromStream)) { + return false; + } + } + + return true; + } + + if ($node instanceof CollectionNode) { + if ($decodeFromStream) { + return false; + } + + return $this->canBeDecodedWithJsonDecode($node->getItemNode(), $decodeFromStream); + } + + if ($node instanceof ObjectNode) { + return false; + } + + if ($node instanceof BackedEnumNode) { + return false; + } + + if ($node instanceof ScalarNode) { + return !$node->getType()->isIdentifiedBy(TypeIdentifier::OBJECT); + } + + return true; + } +} diff --git a/src/Symfony/Component/JsonStreamer/Read/StreamReaderGenerator.php b/src/Symfony/Component/JsonStreamer/Read/StreamReaderGenerator.php index 18720297b16c6..8f4dc27685351 100644 --- a/src/Symfony/Component/JsonStreamer/Read/StreamReaderGenerator.php +++ b/src/Symfony/Component/JsonStreamer/Read/StreamReaderGenerator.php @@ -11,21 +11,14 @@ namespace Symfony\Component\JsonStreamer\Read; -use PhpParser\PhpVersion; -use PhpParser\PrettyPrinter; -use PhpParser\PrettyPrinter\Standard; use Symfony\Component\Filesystem\Exception\IOException; use Symfony\Component\Filesystem\Filesystem; -use Symfony\Component\JsonStreamer\DataModel\DataAccessorInterface; -use Symfony\Component\JsonStreamer\DataModel\FunctionDataAccessor; use Symfony\Component\JsonStreamer\DataModel\Read\BackedEnumNode; use Symfony\Component\JsonStreamer\DataModel\Read\CollectionNode; use Symfony\Component\JsonStreamer\DataModel\Read\CompositeNode; use Symfony\Component\JsonStreamer\DataModel\Read\DataModelNodeInterface; use Symfony\Component\JsonStreamer\DataModel\Read\ObjectNode; use Symfony\Component\JsonStreamer\DataModel\Read\ScalarNode; -use Symfony\Component\JsonStreamer\DataModel\ScalarDataAccessor; -use Symfony\Component\JsonStreamer\DataModel\VariableDataAccessor; use Symfony\Component\JsonStreamer\Exception\RuntimeException; use Symfony\Component\JsonStreamer\Exception\UnsupportedException; use Symfony\Component\JsonStreamer\Mapping\PropertyMetadataLoaderInterface; @@ -47,8 +40,7 @@ */ final class StreamReaderGenerator { - private ?PhpAstBuilder $phpAstBuilder = null; - private ?PrettyPrinter $phpPrinter = null; + private ?PhpGenerator $phpGenerator = null; private ?Filesystem $fs = null; public function __construct( @@ -69,13 +61,11 @@ public function generate(Type $type, bool $decodeFromStream, array $options = [] return $path; } - $this->phpAstBuilder ??= new PhpAstBuilder(); - $this->phpPrinter ??= new Standard(['phpVersion' => PhpVersion::fromComponents(8, 2)]); + $this->phpGenerator ??= new PhpGenerator(); $this->fs ??= new Filesystem(); $dataModel = $this->createDataModel($type, $options); - $nodes = $this->phpAstBuilder->build($dataModel, $decodeFromStream, $options); - $content = $this->phpPrinter->prettyPrintFile($nodes)."\n"; + $php = $this->phpGenerator->generate($dataModel, $decodeFromStream, $options); if (!$this->fs->exists($this->streamReadersDir)) { $this->fs->mkdir($this->streamReadersDir); @@ -84,7 +74,7 @@ public function generate(Type $type, bool $decodeFromStream, array $options = [] $tmpFile = $this->fs->tempnam(\dirname($path), basename($path)); try { - $this->fs->dumpFile($tmpFile, $content); + $this->fs->dumpFile($tmpFile, $php); $this->fs->rename($tmpFile, $path); $this->fs->chmod($path, 0666 & ~umask()); } catch (IOException $e) { @@ -103,7 +93,7 @@ private function getPath(Type $type, bool $decodeFromStream): string * @param array $options * @param array $context */ - public function createDataModel(Type $type, array $options = [], array $context = []): DataModelNodeInterface + private function createDataModel(Type $type, array $options = [], array $context = []): DataModelNodeInterface { $context['original_type'] ??= $type; @@ -140,11 +130,10 @@ public function createDataModel(Type $type, array $options = [], array $context $propertiesNodes[$streamedName] = [ 'name' => $propertyMetadata->getName(), 'value' => $this->createDataModel($propertyMetadata->getType(), $options, $context), - 'accessor' => function (DataAccessorInterface $accessor) use ($propertyMetadata): DataAccessorInterface { + 'accessor' => function (string $accessor) use ($propertyMetadata): string { foreach ($propertyMetadata->getStreamToNativeValueTransformers() as $valueTransformer) { if (\is_string($valueTransformer)) { - $valueTransformerServiceAccessor = new FunctionDataAccessor('get', [new ScalarDataAccessor($valueTransformer)], new VariableDataAccessor('valueTransformers')); - $accessor = new FunctionDataAccessor('transform', [$accessor, new VariableDataAccessor('options')], $valueTransformerServiceAccessor); + $accessor = "\$valueTransformers->get('$valueTransformer')->transform($accessor, \$options)"; continue; } @@ -158,9 +147,9 @@ public function createDataModel(Type $type, array $options = [], array $context $functionName = !$functionReflection->getClosureCalledClass() ? $functionReflection->getName() : \sprintf('%s::%s', $functionReflection->getClosureCalledClass()->getName(), $functionReflection->getName()); - $arguments = $functionReflection->isUserDefined() ? [$accessor, new VariableDataAccessor('options')] : [$accessor]; + $arguments = $functionReflection->isUserDefined() ? "$accessor, \$options" : $accessor; - $accessor = new FunctionDataAccessor($functionName, $arguments); + $accessor = "$functionName($arguments)"; } return $accessor; diff --git a/src/Symfony/Component/JsonStreamer/Tests/DataModel/Write/CompositeNodeTest.php b/src/Symfony/Component/JsonStreamer/Tests/DataModel/Write/CompositeNodeTest.php index a7ef7df343d6f..fb57df19ff044 100644 --- a/src/Symfony/Component/JsonStreamer/Tests/DataModel/Write/CompositeNodeTest.php +++ b/src/Symfony/Component/JsonStreamer/Tests/DataModel/Write/CompositeNodeTest.php @@ -12,7 +12,6 @@ namespace Symfony\Component\JsonStreamer\Tests\DataModel\Write; use PHPUnit\Framework\TestCase; -use Symfony\Component\JsonStreamer\DataModel\VariableDataAccessor; use Symfony\Component\JsonStreamer\DataModel\Write\CollectionNode; use Symfony\Component\JsonStreamer\DataModel\Write\CompositeNode; use Symfony\Component\JsonStreamer\DataModel\Write\ObjectNode; @@ -27,7 +26,7 @@ public function testCannotCreateWithOnlyOneType() $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage(\sprintf('"%s" expects at least 2 nodes.', CompositeNode::class)); - new CompositeNode(new VariableDataAccessor('data'), [new ScalarNode(new VariableDataAccessor('data'), Type::int())]); + new CompositeNode('$data', [new ScalarNode('$data', Type::int())]); } public function testCannotCreateWithCompositeNodeParts() @@ -35,21 +34,21 @@ public function testCannotCreateWithCompositeNodeParts() $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage(\sprintf('Cannot set "%s" as a "%s" node.', CompositeNode::class, CompositeNode::class)); - new CompositeNode(new VariableDataAccessor('data'), [ - new CompositeNode(new VariableDataAccessor('data'), [ - new ScalarNode(new VariableDataAccessor('data'), Type::int()), - new ScalarNode(new VariableDataAccessor('data'), Type::int()), + new CompositeNode('$data', [ + new CompositeNode('$data', [ + new ScalarNode('$data', Type::int()), + new ScalarNode('$data', Type::int()), ]), - new ScalarNode(new VariableDataAccessor('data'), Type::int()), + new ScalarNode('$data', Type::int()), ]); } public function testSortNodesOnCreation() { - $composite = new CompositeNode(new VariableDataAccessor('data'), [ - $scalar = new ScalarNode(new VariableDataAccessor('data'), Type::int()), - $object = new ObjectNode(new VariableDataAccessor('data'), Type::object(self::class), []), - $collection = new CollectionNode(new VariableDataAccessor('data'), Type::list(), new ScalarNode(new VariableDataAccessor('data'), Type::int())), + $composite = new CompositeNode('$data', [ + $scalar = new ScalarNode('$data', Type::int()), + $object = new ObjectNode('$data', Type::object(self::class), []), + $collection = new CollectionNode('$data', Type::list(), new ScalarNode('$data', Type::int())), ]); $this->assertSame([$collection, $object, $scalar], $composite->getNodes()); @@ -57,14 +56,14 @@ public function testSortNodesOnCreation() public function testWithAccessor() { - $composite = new CompositeNode(new VariableDataAccessor('data'), [ - new ScalarNode(new VariableDataAccessor('foo'), Type::int()), - new ScalarNode(new VariableDataAccessor('bar'), Type::int()), + $composite = new CompositeNode('$data', [ + new ScalarNode('$foo', Type::int()), + new ScalarNode('$bar', Type::int()), ]); - $composite = $composite->withAccessor($newAccessor = new VariableDataAccessor('baz')); + $composite = $composite->withAccessor('$baz'); foreach ($composite->getNodes() as $node) { - $this->assertSame($newAccessor, $node->getAccessor()); + $this->assertSame('$baz', $node->getAccessor()); } } } diff --git a/src/Symfony/Component/JsonStreamer/Tests/DataModel/Write/ObjectNodeTest.php b/src/Symfony/Component/JsonStreamer/Tests/DataModel/Write/ObjectNodeTest.php index 0667f731e3d9f..cdc6bf71f4a15 100644 --- a/src/Symfony/Component/JsonStreamer/Tests/DataModel/Write/ObjectNodeTest.php +++ b/src/Symfony/Component/JsonStreamer/Tests/DataModel/Write/ObjectNodeTest.php @@ -12,9 +12,6 @@ namespace Symfony\Component\JsonStreamer\Tests\DataModel\Write; use PHPUnit\Framework\TestCase; -use Symfony\Component\JsonStreamer\DataModel\FunctionDataAccessor; -use Symfony\Component\JsonStreamer\DataModel\PropertyDataAccessor; -use Symfony\Component\JsonStreamer\DataModel\VariableDataAccessor; use Symfony\Component\JsonStreamer\DataModel\Write\ObjectNode; use Symfony\Component\JsonStreamer\DataModel\Write\ScalarNode; use Symfony\Component\TypeInfo\Type; @@ -23,18 +20,18 @@ class ObjectNodeTest extends TestCase { public function testWithAccessor() { - $object = new ObjectNode(new VariableDataAccessor('foo'), Type::object(self::class), [ - new ScalarNode(new PropertyDataAccessor(new VariableDataAccessor('foo'), 'property'), Type::int()), - new ScalarNode(new FunctionDataAccessor('function', [], new VariableDataAccessor('foo')), Type::int()), - new ScalarNode(new FunctionDataAccessor('function', []), Type::int()), - new ScalarNode(new VariableDataAccessor('bar'), Type::int()), + $object = new ObjectNode('$foo', Type::object(self::class), [ + new ScalarNode('$foo->property', Type::int()), + new ScalarNode('$foo->method()', Type::int()), + new ScalarNode('function()', Type::int()), + new ScalarNode('$bar', Type::int()), ]); - $object = $object->withAccessor($newAccessor = new VariableDataAccessor('baz')); + $object = $object->withAccessor('$baz'); - $this->assertSame($newAccessor, $object->getAccessor()); - $this->assertSame($newAccessor, $object->getProperties()[0]->getAccessor()->getObjectAccessor()); - $this->assertSame($newAccessor, $object->getProperties()[1]->getAccessor()->getObjectAccessor()); - $this->assertNull($object->getProperties()[2]->getAccessor()->getObjectAccessor()); - $this->assertNotSame($newAccessor, $object->getProperties()[3]->getAccessor()); + $this->assertSame('$baz', $object->getAccessor()); + $this->assertSame('$baz->property', $object->getProperties()[0]->getAccessor()); + $this->assertSame('$baz->method()', $object->getProperties()[1]->getAccessor()); + $this->assertSame('function()', $object->getProperties()[2]->getAccessor()); + $this->assertSame('$bar', $object->getProperties()[3]->getAccessor()); } } diff --git a/src/Symfony/Component/JsonStreamer/Write/MergingStringVisitor.php b/src/Symfony/Component/JsonStreamer/Write/MergingStringVisitor.php deleted file mode 100644 index 289448ba465e8..0000000000000 --- a/src/Symfony/Component/JsonStreamer/Write/MergingStringVisitor.php +++ /dev/null @@ -1,60 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\JsonStreamer\Write; - -use PhpParser\Node; -use PhpParser\Node\Expr\Yield_; -use PhpParser\Node\Scalar\String_; -use PhpParser\Node\Stmt\Expression; -use PhpParser\NodeVisitor; -use PhpParser\NodeVisitorAbstract; - -/** - * Merges strings that are yielded consequently - * to reduce the call instructions amount. - * - * @author Mathias Arlaud - * - * @internal - */ -final class MergingStringVisitor extends NodeVisitorAbstract -{ - private string $buffer = ''; - - public function leaveNode(Node $node): int|Node|array|null - { - if (!$this->isMergeableNode($node)) { - return null; - } - - /** @var Node|null $next */ - $next = $node->getAttribute('next'); - - if ($next && $this->isMergeableNode($next)) { - $this->buffer .= $node->expr->value->value; - - return NodeVisitor::REMOVE_NODE; - } - - $string = $this->buffer.$node->expr->value->value; - $this->buffer = ''; - - return new Expression(new Yield_(new String_($string))); - } - - private function isMergeableNode(Node $node): bool - { - return $node instanceof Expression - && $node->expr instanceof Yield_ - && $node->expr->value instanceof String_; - } -} diff --git a/src/Symfony/Component/JsonStreamer/Write/PhpAstBuilder.php b/src/Symfony/Component/JsonStreamer/Write/PhpAstBuilder.php deleted file mode 100644 index f0b429b42c8f3..0000000000000 --- a/src/Symfony/Component/JsonStreamer/Write/PhpAstBuilder.php +++ /dev/null @@ -1,436 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\JsonStreamer\Write; - -use PhpParser\BuilderFactory; -use PhpParser\Node\ClosureUse; -use PhpParser\Node\Expr; -use PhpParser\Node\Expr\ArrayDimFetch; -use PhpParser\Node\Expr\Assign; -use PhpParser\Node\Expr\BinaryOp\GreaterOrEqual; -use PhpParser\Node\Expr\BinaryOp\Identical; -use PhpParser\Node\Expr\BinaryOp\Plus; -use PhpParser\Node\Expr\Closure; -use PhpParser\Node\Expr\Instanceof_; -use PhpParser\Node\Expr\PropertyFetch; -use PhpParser\Node\Expr\Ternary; -use PhpParser\Node\Expr\Throw_; -use PhpParser\Node\Expr\Yield_; -use PhpParser\Node\Expr\YieldFrom; -use PhpParser\Node\Identifier; -use PhpParser\Node\Name\FullyQualified; -use PhpParser\Node\Param; -use PhpParser\Node\Scalar\Encapsed; -use PhpParser\Node\Scalar\EncapsedStringPart; -use PhpParser\Node\Stmt; -use PhpParser\Node\Stmt\Catch_; -use PhpParser\Node\Stmt\Else_; -use PhpParser\Node\Stmt\ElseIf_; -use PhpParser\Node\Stmt\Expression; -use PhpParser\Node\Stmt\Foreach_; -use PhpParser\Node\Stmt\If_; -use PhpParser\Node\Stmt\Return_; -use PhpParser\Node\Stmt\TryCatch; -use Psr\Container\ContainerInterface; -use Symfony\Component\JsonStreamer\DataModel\VariableDataAccessor; -use Symfony\Component\JsonStreamer\DataModel\Write\BackedEnumNode; -use Symfony\Component\JsonStreamer\DataModel\Write\CollectionNode; -use Symfony\Component\JsonStreamer\DataModel\Write\CompositeNode; -use Symfony\Component\JsonStreamer\DataModel\Write\DataModelNodeInterface; -use Symfony\Component\JsonStreamer\DataModel\Write\ObjectNode; -use Symfony\Component\JsonStreamer\DataModel\Write\ScalarNode; -use Symfony\Component\JsonStreamer\Exception\LogicException; -use Symfony\Component\JsonStreamer\Exception\NotEncodableValueException; -use Symfony\Component\JsonStreamer\Exception\RuntimeException; -use Symfony\Component\JsonStreamer\Exception\UnexpectedValueException; -use Symfony\Component\TypeInfo\Type\BuiltinType; -use Symfony\Component\TypeInfo\Type\ObjectType; -use Symfony\Component\TypeInfo\Type\WrappingTypeInterface; -use Symfony\Component\TypeInfo\TypeIdentifier; - -/** - * Builds a PHP syntax tree that writes data to JSON stream. - * - * @author Mathias Arlaud - * - * @internal - */ -final class PhpAstBuilder -{ - private BuilderFactory $builder; - - public function __construct() - { - $this->builder = new BuilderFactory(); - } - - /** - * @param array $options - * @param array $context - * - * @return list - */ - public function build(DataModelNodeInterface $dataModel, array $options = [], array $context = []): array - { - $context['depth'] = 0; - - $generatorStmts = $this->buildGeneratorStatementsByIdentifiers($dataModel, $options, $context); - - // filter generators to mock only - $generatorStmts = array_merge(...array_values(array_intersect_key($generatorStmts, $context['mocks'] ?? []))); - $context['generators'] = array_intersect_key($context['generators'] ?? [], $context['mocks'] ?? []); - - return [new Return_(new Closure([ - 'static' => true, - 'params' => [ - new Param($this->builder->var('data'), type: new Identifier('mixed')), - new Param($this->builder->var('valueTransformers'), type: new FullyQualified(ContainerInterface::class)), - new Param($this->builder->var('options'), type: new Identifier('array')), - ], - 'returnType' => new FullyQualified(\Traversable::class), - 'stmts' => [ - ...$generatorStmts, - new TryCatch( - $this->buildYieldStatements($dataModel, $options, $context), - [new Catch_([new FullyQualified(\JsonException::class)], $this->builder->var('e'), [ - new Expression(new Throw_($this->builder->new(new FullyQualified(NotEncodableValueException::class), [ - $this->builder->methodCall($this->builder->var('e'), 'getMessage'), - $this->builder->val(0), - $this->builder->var('e'), - ]))), - ])] - ), - ], - ]))]; - } - - /** - * @param array $options - * @param array $context - * - * @return array> - */ - private function buildGeneratorStatementsByIdentifiers(DataModelNodeInterface $node, array $options, array &$context): array - { - if ($context['generators'][$node->getIdentifier()] ?? false) { - return []; - } - - if ($node instanceof CollectionNode) { - return $this->buildGeneratorStatementsByIdentifiers($node->getItemNode(), $options, $context); - } - - if ($node instanceof CompositeNode) { - $stmts = []; - - foreach ($node->getNodes() as $n) { - $stmts = [ - ...$stmts, - ...$this->buildGeneratorStatementsByIdentifiers($n, $options, $context), - ]; - } - - return $stmts; - } - - if (!$node instanceof ObjectNode) { - return []; - } - - if ($node->isMock()) { - $context['mocks'][$node->getIdentifier()] = true; - - return []; - } - - $context['building_generator'] = true; - - $stmts = [ - $node->getIdentifier() => [ - new Expression(new Assign( - new ArrayDimFetch($this->builder->var('generators'), $this->builder->val($node->getIdentifier())), - new Closure([ - 'static' => true, - 'params' => [ - new Param($this->builder->var('data')), - new Param($this->builder->var('depth')), - ], - 'uses' => [ - new ClosureUse($this->builder->var('valueTransformers')), - new ClosureUse($this->builder->var('options')), - new ClosureUse($this->builder->var('generators'), byRef: true), - ], - 'stmts' => [ - new If_(new GreaterOrEqual($this->builder->var('depth'), $this->builder->val(512)), [ - 'stmts' => [new Expression(new Throw_($this->builder->new(new FullyQualified(NotEncodableValueException::class), [$this->builder->val('Maximum stack depth exceeded')])))], - ]), - ...$this->buildYieldStatements($node->withAccessor(new VariableDataAccessor('data')), $options, $context), - ], - ]), - )), - ], - ]; - - foreach ($node->getProperties() as $n) { - $stmts = [ - ...$stmts, - ...$this->buildGeneratorStatementsByIdentifiers($n, $options, $context), - ]; - } - - unset($context['building_generator']); - $context['generators'][$node->getIdentifier()] = true; - - return $stmts; - } - - /** - * @param array $options - * @param array $context - * - * @return list - */ - private function buildYieldStatements(DataModelNodeInterface $dataModelNode, array $options, array $context): array - { - $accessor = $dataModelNode->getAccessor()->toPhpExpr(); - - if ($this->dataModelOnlyNeedsEncode($dataModelNode)) { - return [ - new Expression(new Yield_($this->encodeValue($accessor, $context))), - ]; - } - - if ($context['depth'] >= 512) { - return [ - new Expression(new Throw_($this->builder->new(new FullyQualified(NotEncodableValueException::class), [$this->builder->val('Maximum stack depth exceeded')]))), - ]; - } - - if ($dataModelNode instanceof ScalarNode) { - $scalarAccessor = match (true) { - TypeIdentifier::NULL === $dataModelNode->getType()->getTypeIdentifier() => $this->builder->val('null'), - TypeIdentifier::BOOL === $dataModelNode->getType()->getTypeIdentifier() => new Ternary($accessor, $this->builder->val('true'), $this->builder->val('false')), - default => $this->encodeValue($accessor, $context), - }; - - return [ - new Expression(new Yield_($scalarAccessor)), - ]; - } - - if ($dataModelNode instanceof BackedEnumNode) { - return [ - new Expression(new Yield_($this->encodeValue(new PropertyFetch($accessor, 'value'), $context))), - ]; - } - - if ($dataModelNode instanceof CompositeNode) { - $nodeCondition = function (DataModelNodeInterface $node): Expr { - $accessor = $node->getAccessor()->toPhpExpr(); - $type = $node->getType(); - - if ($type->isIdentifiedBy(TypeIdentifier::NULL, TypeIdentifier::NEVER, TypeIdentifier::VOID)) { - return new Identical($this->builder->val(null), $accessor); - } - - if ($type->isIdentifiedBy(TypeIdentifier::TRUE)) { - return new Identical($this->builder->val(true), $accessor); - } - - if ($type->isIdentifiedBy(TypeIdentifier::FALSE)) { - return new Identical($this->builder->val(false), $accessor); - } - - if ($type->isIdentifiedBy(TypeIdentifier::MIXED)) { - return $this->builder->val(true); - } - - while ($type instanceof WrappingTypeInterface) { - $type = $type->getWrappedType(); - } - - if ($type instanceof ObjectType) { - return new Instanceof_($accessor, new FullyQualified($type->getClassName())); - } - - if ($type instanceof BuiltinType) { - return $this->builder->funcCall('\is_'.$type->getTypeIdentifier()->value, [$accessor]); - } - - throw new LogicException(\sprintf('Unexpected "%s" type.', $type::class)); - }; - - $stmtsAndConditions = array_map(fn (DataModelNodeInterface $n): array => [ - 'condition' => $nodeCondition($n), - 'stmts' => $this->buildYieldStatements($n, $options, $context), - ], $dataModelNode->getNodes()); - - $if = $stmtsAndConditions[0]; - unset($stmtsAndConditions[0]); - - return [ - new If_($if['condition'], [ - 'stmts' => $if['stmts'], - 'elseifs' => array_map(fn (array $s): ElseIf_ => new ElseIf_($s['condition'], $s['stmts']), $stmtsAndConditions), - 'else' => new Else_([ - new Expression(new Throw_($this->builder->new(new FullyQualified(UnexpectedValueException::class), [$this->builder->funcCall('\sprintf', [ - $this->builder->val('Unexpected "%s" value.'), - $this->builder->funcCall('\get_debug_type', [$accessor]), - ])]))), - ]), - ]), - ]; - } - - if ($dataModelNode instanceof CollectionNode) { - ++$context['depth']; - - if ($dataModelNode->getType()->isList()) { - return [ - new Expression(new Yield_($this->builder->val('['))), - new Expression(new Assign($this->builder->var('prefix'), $this->builder->val(''))), - new Foreach_($accessor, $dataModelNode->getItemNode()->getAccessor()->toPhpExpr(), [ - 'stmts' => [ - new Expression(new Yield_($this->builder->var('prefix'))), - ...$this->buildYieldStatements($dataModelNode->getItemNode(), $options, $context), - new Expression(new Assign($this->builder->var('prefix'), $this->builder->val(','))), - ], - ]), - new Expression(new Yield_($this->builder->val(']'))), - ]; - } - - $escapedKey = $dataModelNode->getType()->getCollectionKeyType()->isIdentifiedBy(TypeIdentifier::INT) - ? new Ternary($this->builder->funcCall('is_int', [$this->builder->var('key')]), $this->builder->var('key'), $this->escapeString($this->builder->var('key'))) - : $this->escapeString($this->builder->var('key')); - - return [ - new Expression(new Yield_($this->builder->val('{'))), - new Expression(new Assign($this->builder->var('prefix'), $this->builder->val(''))), - new Foreach_($accessor, $dataModelNode->getItemNode()->getAccessor()->toPhpExpr(), [ - 'keyVar' => $this->builder->var('key'), - 'stmts' => [ - new Expression(new Assign($this->builder->var('key'), $escapedKey)), - new Expression(new Yield_(new Encapsed([ - $this->builder->var('prefix'), - new EncapsedStringPart('"'), - $this->builder->var('key'), - new EncapsedStringPart('":'), - ]))), - ...$this->buildYieldStatements($dataModelNode->getItemNode(), $options, $context), - new Expression(new Assign($this->builder->var('prefix'), $this->builder->val(','))), - ], - ]), - new Expression(new Yield_($this->builder->val('}'))), - ]; - } - - if ($dataModelNode instanceof ObjectNode) { - if (isset($context['generators'][$dataModelNode->getIdentifier()]) || $dataModelNode->isMock()) { - $depthArgument = ($context['building_generator'] ?? false) - ? new Plus($this->builder->var('depth'), $this->builder->val(1)) - : $this->builder->val($context['depth']); - - return [ - new Expression(new YieldFrom($this->builder->funcCall( - new ArrayDimFetch($this->builder->var('generators'), $this->builder->val($dataModelNode->getIdentifier())), - [$accessor, $depthArgument], - ))), - ]; - } - - $objectStmts = [new Expression(new Yield_($this->builder->val('{')))]; - $separator = ''; - - ++$context['depth']; - - foreach ($dataModelNode->getProperties() as $name => $propertyNode) { - $encodedName = json_encode($name); - if (false === $encodedName) { - throw new RuntimeException(\sprintf('Cannot encode "%s"', $name)); - } - - $encodedName = substr($encodedName, 1, -1); - - $objectStmts = [ - ...$objectStmts, - new Expression(new Yield_($this->builder->val($separator))), - new Expression(new Yield_($this->builder->val('"'))), - new Expression(new Yield_($this->builder->val($encodedName))), - new Expression(new Yield_($this->builder->val('":'))), - ...$this->buildYieldStatements($propertyNode, $options, $context), - ]; - - $separator = ','; - } - - $objectStmts[] = new Expression(new Yield_($this->builder->val('}'))); - - return $objectStmts; - } - - throw new LogicException(\sprintf('Unexpected "%s" node', $dataModelNode::class)); - } - - /** - * @param array $context - */ - private function encodeValue(Expr $value, array $context): Expr - { - return $this->builder->funcCall('\json_encode', [ - $value, - $this->builder->constFetch('\\JSON_THROW_ON_ERROR'), - $this->builder->val(512 - $context['depth']), - ]); - } - - private function escapeString(Expr $string): Expr - { - return $this->builder->funcCall('\substr', [ - $this->builder->funcCall('\json_encode', [$string]), - $this->builder->val(1), - $this->builder->val(-1), - ]); - } - - private function dataModelOnlyNeedsEncode(DataModelNodeInterface $dataModel, int $depth = 0): bool - { - if ($dataModel instanceof CompositeNode) { - foreach ($dataModel->getNodes() as $node) { - if (!$this->dataModelOnlyNeedsEncode($node, $depth)) { - return false; - } - } - - return true; - } - - if ($dataModel instanceof CollectionNode) { - return $this->dataModelOnlyNeedsEncode($dataModel->getItemNode(), $depth + 1); - } - - if (!$dataModel instanceof ScalarNode) { - return false; - } - - $type = $dataModel->getType(); - - // "null" will be written directly using the "null" string - // "bool" will be written directly using the "true" or "false" string - // but it must not prevent any json_encode if nested - if ($type->isIdentifiedBy(TypeIdentifier::NULL) || $type->isIdentifiedBy(TypeIdentifier::BOOL)) { - return $depth > 0; - } - - return true; - } -} diff --git a/src/Symfony/Component/JsonStreamer/Write/PhpGenerator.php b/src/Symfony/Component/JsonStreamer/Write/PhpGenerator.php new file mode 100644 index 0000000000000..0e79481007a65 --- /dev/null +++ b/src/Symfony/Component/JsonStreamer/Write/PhpGenerator.php @@ -0,0 +1,388 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\JsonStreamer\Write; + +use Psr\Container\ContainerInterface; +use Symfony\Component\JsonStreamer\DataModel\Write\BackedEnumNode; +use Symfony\Component\JsonStreamer\DataModel\Write\CollectionNode; +use Symfony\Component\JsonStreamer\DataModel\Write\CompositeNode; +use Symfony\Component\JsonStreamer\DataModel\Write\DataModelNodeInterface; +use Symfony\Component\JsonStreamer\DataModel\Write\ObjectNode; +use Symfony\Component\JsonStreamer\DataModel\Write\ScalarNode; +use Symfony\Component\JsonStreamer\Exception\LogicException; +use Symfony\Component\JsonStreamer\Exception\NotEncodableValueException; +use Symfony\Component\JsonStreamer\Exception\RuntimeException; +use Symfony\Component\JsonStreamer\Exception\UnexpectedValueException; +use Symfony\Component\TypeInfo\Type\BuiltinType; +use Symfony\Component\TypeInfo\Type\ObjectType; +use Symfony\Component\TypeInfo\Type\WrappingTypeInterface; +use Symfony\Component\TypeInfo\TypeIdentifier; + +/** + * Generates PHP code that writes data to JSON stream. + * + * @author Mathias Arlaud + * + * @internal + */ +final class PhpGenerator +{ + private string $yieldBuffer = ''; + + /** + * @param array $options + * @param array $context + */ + public function generate(DataModelNodeInterface $dataModel, array $options = [], array $context = []): string + { + $context['depth'] = 0; + $context['indentation_level'] = 1; + + $generators = $this->generateObjectGenerators($dataModel, $options, $context); + + // filter generators to mock only + $generators = array_intersect_key($generators, $context['mocks'] ?? []); + $context['generated_generators'] = array_intersect_key($context['generated_generators'] ?? [], $context['mocks'] ?? []); + + $context['indentation_level'] = 2; + $yields = $this->generateYields($dataModel, $options, $context) + .$this->flushYieldBuffer($context); + + $context['indentation_level'] = 0; + + return $this->line('line('', $context) + .$this->line('return static function (mixed $data, \\'.ContainerInterface::class.' $valueTransformers, array $options): \\Traversable {', $context) + .implode('', $generators) + .$this->line(' try {', $context) + .$yields + .$this->line(' } catch (\\JsonException $e) {', $context) + .$this->line(' throw new \\'.NotEncodableValueException::class.'($e->getMessage(), 0, $e);', $context) + .$this->line(' }', $context) + .$this->line('};', $context); + } + + /** + * @param array $options + * @param array $context + * + * @return array + */ + private function generateObjectGenerators(DataModelNodeInterface $node, array $options, array &$context): array + { + if ($context['generated_generators'][$node->getIdentifier()] ?? false) { + return []; + } + + if ($node instanceof CollectionNode) { + return $this->generateObjectGenerators($node->getItemNode(), $options, $context); + } + + if ($node instanceof CompositeNode) { + $generators = []; + foreach ($node->getNodes() as $n) { + $generators = [ + ...$generators, + ...$this->generateObjectGenerators($n, $options, $context), + ]; + } + + return $generators; + } + + if ($node instanceof ObjectNode) { + if ($node->isMock()) { + $context['mocks'][$node->getIdentifier()] = true; + + return []; + } + + $context['generating_generator'] = true; + + ++$context['indentation_level']; + $yields = $this->generateYields($node->withAccessor('$data'), $options, $context) + .$this->flushYieldBuffer($context); + --$context['indentation_level']; + + $generators = [ + $node->getIdentifier() => $this->line('$generators[\''.$node->getIdentifier().'\'] = static function ($data, $depth) use ($valueTransformers, $options, &$generators) {', $context) + .$this->line(' if ($depth >= 512) {', $context) + .$this->line(' throw new \\'.NotEncodableValueException::class.'(\'Maximum stack depth exceeded\');', $context) + .$this->line(' }', $context) + .$yields + .$this->line('};', $context), + ]; + + foreach ($node->getProperties() as $n) { + $generators = [ + ...$generators, + ...$this->generateObjectGenerators($n, $options, $context), + ]; + } + + unset($context['generating_generator']); + $context['generated_generators'][$node->getIdentifier()] = true; + + return $generators; + } + + return []; + } + + /** + * @param array $options + * @param array $context + */ + private function generateYields(DataModelNodeInterface $dataModelNode, array $options, array $context): string + { + $accessor = $dataModelNode->getAccessor(); + + if ($this->canBeEncodedWithJsonEncode($dataModelNode)) { + return $this->yield($this->encode($accessor, $context), $context); + } + + if ($context['depth'] >= 512) { + return $this->line('throw new '.NotEncodableValueException::class.'(\'Maximum stack depth exceeded\');', $context); + } + + if ($dataModelNode instanceof ScalarNode) { + return match (true) { + TypeIdentifier::NULL === $dataModelNode->getType()->getTypeIdentifier() => $this->yieldString('null', $context), + TypeIdentifier::BOOL === $dataModelNode->getType()->getTypeIdentifier() => $this->yield("$accessor ? 'true' : 'false'", $context), + default => $this->yield($this->encode($accessor, $context), $context), + }; + } + + if ($dataModelNode instanceof BackedEnumNode) { + return $this->yield($this->encode("{$accessor}->value", $context), $context); + } + + if ($dataModelNode instanceof CompositeNode) { + $php = $this->flushYieldBuffer($context); + foreach ($dataModelNode->getNodes() as $i => $node) { + $php .= $this->line((0 === $i ? 'if' : '} elseif').' ('.$this->generateCompositeNodeItemCondition($node).') {', $context); + + ++$context['indentation_level']; + $php .= $this->generateYields($node, $options, $context) + .$this->flushYieldBuffer($context); + --$context['indentation_level']; + } + + return $php + .$this->flushYieldBuffer($context) + .$this->line('} else {', $context) + .$this->line(' throw new \\'.UnexpectedValueException::class."(\\sprintf('Unexpected \"%s\" value.', \get_debug_type($accessor)));", $context) + .$this->line('}', $context); + } + + if ($dataModelNode instanceof CollectionNode) { + ++$context['depth']; + + if ($dataModelNode->getType()->isList()) { + $php = $this->yieldString('[', $context) + .$this->flushYieldBuffer($context) + .$this->line('$prefix = \'\';', $context) + .$this->line("foreach ($accessor as ".$dataModelNode->getItemNode()->getAccessor().') {', $context); + + ++$context['indentation_level']; + $php .= $this->yield('$prefix', $context) + .$this->generateYields($dataModelNode->getItemNode(), $options, $context) + .$this->flushYieldBuffer($context) + .$this->line('$prefix = \',\';', $context); + + --$context['indentation_level']; + + return $php + .$this->line('}', $context) + .$this->yieldString(']', $context); + } + + $escapedKey = $dataModelNode->getType()->getCollectionKeyType()->isIdentifiedBy(TypeIdentifier::INT) + ? '$key = is_int($key) ? $key : \substr(\json_encode($key), 1, -1);' + : '$key = \substr(\json_encode($key), 1, -1);'; + + $php = $this->yieldString('{', $context) + .$this->flushYieldBuffer($context) + .$this->line('$prefix = \'\';', $context) + .$this->line("foreach ($accessor as \$key => ".$dataModelNode->getItemNode()->getAccessor().') {', $context); + + ++$context['indentation_level']; + $php .= $this->line($escapedKey, $context) + .$this->yield('"{$prefix}\"{$key}\":"', $context) + .$this->generateYields($dataModelNode->getItemNode(), $options, $context) + .$this->flushYieldBuffer($context) + .$this->line('$prefix = \',\';', $context); + + --$context['indentation_level']; + + return $php + .$this->line('}', $context) + .$this->yieldString('}', $context); + } + + if ($dataModelNode instanceof ObjectNode) { + if (isset($context['generated_generators'][$dataModelNode->getIdentifier()]) || $dataModelNode->isMock()) { + $depthArgument = ($context['generating_generator'] ?? false) ? '$depth + 1' : (string) $context['depth']; + + return $this->line('yield from $generators[\''.$dataModelNode->getIdentifier().'\']('.$accessor.', '.$depthArgument.');', $context); + } + + $php = $this->yieldString('{', $context); + $separator = ''; + + ++$context['depth']; + + foreach ($dataModelNode->getProperties() as $name => $propertyNode) { + $encodedName = json_encode($name); + if (false === $encodedName) { + throw new RuntimeException(\sprintf('Cannot encode "%s"', $name)); + } + + $encodedName = substr($encodedName, 1, -1); + + $php .= $this->yieldString($separator, $context) + .$this->yieldString('"', $context) + .$this->yieldString($encodedName, $context) + .$this->yieldString('":', $context) + .$this->generateYields($propertyNode, $options, $context); + + $separator = ','; + } + + return $php + .$this->yieldString('}', $context); + } + + throw new LogicException(\sprintf('Unexpected "%s" node', $dataModelNode::class)); + } + + /** + * @param array $context + */ + private function encode(string $value, array $context): string + { + return "\json_encode($value, \\JSON_THROW_ON_ERROR, ". 512 - $context['depth'].')'; + } + + /** + * @param array $context + */ + private function yield(string $value, array $context): string + { + return $this->flushYieldBuffer($context) + .$this->line("yield $value;", $context); + } + + /** + * @param array $context + */ + private function yieldString(string $string, array $context): string + { + $this->yieldBuffer .= $string; + + return ''; + } + + /** + * @param array $context + */ + private function flushYieldBuffer(array $context): string + { + if ('' === $this->yieldBuffer) { + return ''; + } + + $yieldBuffer = $this->yieldBuffer; + $this->yieldBuffer = ''; + + return $this->yield("'$yieldBuffer'", $context); + } + + private function generateCompositeNodeItemCondition(DataModelNodeInterface $node): string + { + $accessor = $node->getAccessor(); + $type = $node->getType(); + + if ($type->isIdentifiedBy(TypeIdentifier::NULL, TypeIdentifier::NEVER, TypeIdentifier::VOID)) { + return "null === $accessor"; + } + + if ($type->isIdentifiedBy(TypeIdentifier::TRUE)) { + return "true === $accessor"; + } + + if ($type->isIdentifiedBy(TypeIdentifier::FALSE)) { + return "false === $accessor"; + } + + if ($type->isIdentifiedBy(TypeIdentifier::MIXED)) { + return 'true'; + } + + while ($type instanceof WrappingTypeInterface) { + $type = $type->getWrappedType(); + } + + if ($type instanceof ObjectType) { + return "$accessor instanceof \\".$type->getClassName(); + } + + if ($type instanceof BuiltinType) { + return '\\is_'.$type->getTypeIdentifier()->value."($accessor)"; + } + + throw new LogicException(\sprintf('Unexpected "%s" type.', $type::class)); + } + + /** + * @param array $context + */ + private function line(string $line, array $context): string + { + return str_repeat(' ', $context['indentation_level']).$line."\n"; + } + + /** + * Determines if the $node can be encoded using a simple "json_encode". + */ + private function canBeEncodedWithJsonEncode(DataModelNodeInterface $node, int $depth = 0): bool + { + if ($node instanceof CompositeNode) { + foreach ($node->getNodes() as $n) { + if (!$this->canBeEncodedWithJsonEncode($n, $depth)) { + return false; + } + } + + return true; + } + + if ($node instanceof CollectionNode) { + return $this->canBeEncodedWithJsonEncode($node->getItemNode(), $depth + 1); + } + + if (!$node instanceof ScalarNode) { + return false; + } + + $type = $node->getType(); + + // "null" will be written directly using the "null" string + // "bool" will be written directly using the "true" or "false" string + // but it must not prevent any json_encode if nested + if ($type->isIdentifiedBy(TypeIdentifier::NULL) || $type->isIdentifiedBy(TypeIdentifier::BOOL)) { + return $depth > 0; + } + + return true; + } +} diff --git a/src/Symfony/Component/JsonStreamer/Write/PhpOptimizer.php b/src/Symfony/Component/JsonStreamer/Write/PhpOptimizer.php deleted file mode 100644 index 4dddaf47aac70..0000000000000 --- a/src/Symfony/Component/JsonStreamer/Write/PhpOptimizer.php +++ /dev/null @@ -1,43 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\JsonStreamer\Write; - -use PhpParser\Node; -use PhpParser\NodeTraverser; -use PhpParser\NodeVisitor\NodeConnectingVisitor; - -/** - * Optimizes a PHP syntax tree. - * - * @author Mathias Arlaud - * - * @internal - */ -final class PhpOptimizer -{ - /** - * @param list $nodes - * - * @return list - */ - public function optimize(array $nodes): array - { - $traverser = new NodeTraverser(); - $traverser->addVisitor(new NodeConnectingVisitor()); - $nodes = $traverser->traverse($nodes); - - $traverser = new NodeTraverser(); - $traverser->addVisitor(new MergingStringVisitor()); - - return $traverser->traverse($nodes); - } -} diff --git a/src/Symfony/Component/JsonStreamer/Write/StreamWriterGenerator.php b/src/Symfony/Component/JsonStreamer/Write/StreamWriterGenerator.php index c437ca0d179f5..4035d84770fbf 100644 --- a/src/Symfony/Component/JsonStreamer/Write/StreamWriterGenerator.php +++ b/src/Symfony/Component/JsonStreamer/Write/StreamWriterGenerator.php @@ -11,16 +11,8 @@ namespace Symfony\Component\JsonStreamer\Write; -use PhpParser\PhpVersion; -use PhpParser\PrettyPrinter; -use PhpParser\PrettyPrinter\Standard; use Symfony\Component\Filesystem\Exception\IOException; use Symfony\Component\Filesystem\Filesystem; -use Symfony\Component\JsonStreamer\DataModel\DataAccessorInterface; -use Symfony\Component\JsonStreamer\DataModel\FunctionDataAccessor; -use Symfony\Component\JsonStreamer\DataModel\PropertyDataAccessor; -use Symfony\Component\JsonStreamer\DataModel\ScalarDataAccessor; -use Symfony\Component\JsonStreamer\DataModel\VariableDataAccessor; use Symfony\Component\JsonStreamer\DataModel\Write\BackedEnumNode; use Symfony\Component\JsonStreamer\DataModel\Write\CollectionNode; use Symfony\Component\JsonStreamer\DataModel\Write\CompositeNode; @@ -48,9 +40,7 @@ */ final class StreamWriterGenerator { - private ?PhpAstBuilder $phpAstBuilder = null; - private ?PhpOptimizer $phpOptimizer = null; - private ?PrettyPrinter $phpPrinter = null; + private ?PhpGenerator $phpGenerator = null; private ?Filesystem $fs = null; public function __construct( @@ -71,17 +61,11 @@ public function generate(Type $type, array $options = []): string return $path; } - $this->phpAstBuilder ??= new PhpAstBuilder(); - $this->phpOptimizer ??= new PhpOptimizer(); - $this->phpPrinter ??= new Standard(['phpVersion' => PhpVersion::fromComponents(8, 2)]); + $this->phpGenerator ??= new PhpGenerator(); $this->fs ??= new Filesystem(); - $dataModel = $this->createDataModel($type, new VariableDataAccessor('data'), $options); - - $nodes = $this->phpAstBuilder->build($dataModel, $options); - $nodes = $this->phpOptimizer->optimize($nodes); - - $content = $this->phpPrinter->prettyPrintFile($nodes)."\n"; + $dataModel = $this->createDataModel($type, '$data', $options); + $php = $this->phpGenerator->generate($dataModel, $options); if (!$this->fs->exists($this->streamWritersDir)) { $this->fs->mkdir($this->streamWritersDir); @@ -90,7 +74,7 @@ public function generate(Type $type, array $options = []): string $tmpFile = $this->fs->tempnam(\dirname($path), basename($path)); try { - $this->fs->dumpFile($tmpFile, $content); + $this->fs->dumpFile($tmpFile, $php); $this->fs->rename($tmpFile, $path); $this->fs->chmod($path, 0666 & ~umask()); } catch (IOException $e) { @@ -109,7 +93,7 @@ private function getPath(Type $type): string * @param array $options * @param array $context */ - private function createDataModel(Type $type, DataAccessorInterface $accessor, array $options = [], array $context = []): DataModelNodeInterface + private function createDataModel(Type $type, string $accessor, array $options = [], array $context = []): DataModelNodeInterface { $context['original_type'] ??= $type; @@ -149,12 +133,12 @@ private function createDataModel(Type $type, DataAccessorInterface $accessor, ar $propertiesNodes = []; foreach ($propertiesMetadata as $streamedName => $propertyMetadata) { - $propertyAccessor = new PropertyDataAccessor($accessor, $propertyMetadata->getName()); + $propertyAccessor = $accessor.'->'.$propertyMetadata->getName(); foreach ($propertyMetadata->getNativeToStreamValueTransformer() as $valueTransformer) { if (\is_string($valueTransformer)) { - $valueTransformerServiceAccessor = new FunctionDataAccessor('get', [new ScalarDataAccessor($valueTransformer)], new VariableDataAccessor('valueTransformers')); - $propertyAccessor = new FunctionDataAccessor('transform', [$propertyAccessor, new VariableDataAccessor('options')], $valueTransformerServiceAccessor); + $valueTransformerServiceAccessor = "\$valueTransformers->get('$valueTransformer')"; + $propertyAccessor = "{$valueTransformerServiceAccessor}->transform($propertyAccessor, \$options)"; continue; } @@ -168,9 +152,9 @@ private function createDataModel(Type $type, DataAccessorInterface $accessor, ar $functionName = !$functionReflection->getClosureCalledClass() ? $functionReflection->getName() : \sprintf('%s::%s', $functionReflection->getClosureCalledClass()->getName(), $functionReflection->getName()); - $arguments = $functionReflection->isUserDefined() ? [$propertyAccessor, new VariableDataAccessor('options')] : [$propertyAccessor]; + $arguments = $functionReflection->isUserDefined() ? "$propertyAccessor, \$options" : $propertyAccessor; - $propertyAccessor = new FunctionDataAccessor($functionName, $arguments); + $propertyAccessor = "$functionName($arguments)"; } $propertiesNodes[$streamedName] = $this->createDataModel($propertyMetadata->getType(), $propertyAccessor, $options, $context); @@ -183,7 +167,7 @@ private function createDataModel(Type $type, DataAccessorInterface $accessor, ar return new CollectionNode( $accessor, $type, - $this->createDataModel($type->getCollectionValueType(), new VariableDataAccessor('value'), $options, $context), + $this->createDataModel($type->getCollectionValueType(), '$value', $options, $context), ); } diff --git a/src/Symfony/Component/JsonStreamer/composer.json b/src/Symfony/Component/JsonStreamer/composer.json index ba02d9fbc9172..a635710bbe5f1 100644 --- a/src/Symfony/Component/JsonStreamer/composer.json +++ b/src/Symfony/Component/JsonStreamer/composer.json @@ -16,7 +16,6 @@ } ], "require": { - "nikic/php-parser": "^5.3", "php": ">=8.2", "psr/container": "^1.1|^2.0", "psr/log": "^1|^2|^3", From 5add177fd59074401f7a1d8d65d807779ce84b86 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Mon, 26 May 2025 18:55:03 +0200 Subject: [PATCH 04/80] Bump version to 7.4 --- src/Symfony/Component/HttpKernel/Kernel.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index b5a41236d1899..49c6ecbac1cb1 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -73,15 +73,15 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl */ private static array $freshCache = []; - public const VERSION = '7.3.0-DEV'; - public const VERSION_ID = 70300; + public const VERSION = '7.4.0-DEV'; + public const VERSION_ID = 70400; public const MAJOR_VERSION = 7; - public const MINOR_VERSION = 3; + public const MINOR_VERSION = 4; public const RELEASE_VERSION = 0; public const EXTRA_VERSION = 'DEV'; - public const END_OF_MAINTENANCE = '05/2025'; - public const END_OF_LIFE = '01/2026'; + public const END_OF_MAINTENANCE = '11/2028'; + public const END_OF_LIFE = '11/2029'; public function __construct( protected string $environment, From 461f7930053f4a09ea3c52cf7cbf1f23d729dfa6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Tamarelle?= Date: Wed, 14 May 2025 11:53:12 +0200 Subject: [PATCH 05/80] [WebLink] Add class to parse Link headers from HTTP responses --- .../FrameworkExtension.php | 6 + .../Resources/config/web_link.php | 4 + src/Symfony/Component/WebLink/CHANGELOG.md | 6 + .../Component/WebLink/HttpHeaderParser.php | 87 ++++++++++++++ .../WebLink/HttpHeaderSerializer.php | 2 +- src/Symfony/Component/WebLink/Link.php | 15 ++- .../WebLink/Tests/HttpHeaderParserTest.php | 112 ++++++++++++++++++ .../Component/WebLink/Tests/LinkTest.php | 8 +- 8 files changed, 234 insertions(+), 6 deletions(-) create mode 100644 src/Symfony/Component/WebLink/HttpHeaderParser.php create mode 100644 src/Symfony/Component/WebLink/Tests/HttpHeaderParserTest.php diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index 912282f495dac..38cae0ae793f2 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -216,6 +216,7 @@ use Symfony\Component\Validator\ObjectInitializerInterface; use Symfony\Component\Validator\Validation; use Symfony\Component\Webhook\Controller\WebhookController; +use Symfony\Component\WebLink\HttpHeaderParser; use Symfony\Component\WebLink\HttpHeaderSerializer; use Symfony\Component\Workflow; use Symfony\Component\Workflow\WorkflowInterface; @@ -497,6 +498,11 @@ public function load(array $configs, ContainerBuilder $container): void } $loader->load('web_link.php'); + + // Require symfony/web-link 7.4 + if (!class_exists(HttpHeaderParser::class)) { + $container->removeDefinition('web_link.http_header_parser'); + } } if ($this->readConfigEnabled('uid', $container, $config['uid'])) { diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/web_link.php b/src/Symfony/Bundle/FrameworkBundle/Resources/config/web_link.php index 64345cc997717..df55d194734d5 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/web_link.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/web_link.php @@ -12,6 +12,7 @@ namespace Symfony\Component\DependencyInjection\Loader\Configurator; use Symfony\Component\WebLink\EventListener\AddLinkHeaderListener; +use Symfony\Component\WebLink\HttpHeaderParser; use Symfony\Component\WebLink\HttpHeaderSerializer; return static function (ContainerConfigurator $container) { @@ -20,6 +21,9 @@ ->set('web_link.http_header_serializer', HttpHeaderSerializer::class) ->alias(HttpHeaderSerializer::class, 'web_link.http_header_serializer') + ->set('web_link.http_header_parser', HttpHeaderParser::class) + ->alias(HttpHeaderParser::class, 'web_link.http_header_parser') + ->set('web_link.add_link_header_listener', AddLinkHeaderListener::class) ->args([ service('web_link.http_header_serializer'), diff --git a/src/Symfony/Component/WebLink/CHANGELOG.md b/src/Symfony/Component/WebLink/CHANGELOG.md index 28dad5abdd749..6da8115f91fcc 100644 --- a/src/Symfony/Component/WebLink/CHANGELOG.md +++ b/src/Symfony/Component/WebLink/CHANGELOG.md @@ -1,6 +1,12 @@ CHANGELOG ========= +7.4 +--- + + * Add `HttpHeaderParser` to read `Link` headers from HTTP responses + * Make `HttpHeaderSerializer` non-final + 4.4.0 ----- diff --git a/src/Symfony/Component/WebLink/HttpHeaderParser.php b/src/Symfony/Component/WebLink/HttpHeaderParser.php new file mode 100644 index 0000000000000..15fc91cde2522 --- /dev/null +++ b/src/Symfony/Component/WebLink/HttpHeaderParser.php @@ -0,0 +1,87 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\WebLink; + +use Psr\Link\EvolvableLinkProviderInterface; + +/** + * Parse a list of HTTP Link headers into a list of Link instances. + * + * @see https://tools.ietf.org/html/rfc5988 + * + * @author Jérôme Tamarelle + */ +class HttpHeaderParser +{ + // Regex to match each link entry: <...>; param1=...; param2=... + private const LINK_PATTERN = '/<([^>]*)>\s*((?:\s*;\s*[a-zA-Z0-9\-_]+(?:\s*=\s*(?:"(?:[^"\\\\]|\\\\.)*"|[^";,\s]+))?)*)/'; + + // Regex to match parameters: ; key[=value] + private const PARAM_PATTERN = '/;\s*([a-zA-Z0-9\-_]+)(?:\s*=\s*(?:"((?:[^"\\\\]|\\\\.)*)"|([^";,\s]+)))?/'; + + /** + * @param string|string[] $headers Value of the "Link" HTTP header + */ + public function parse(string|array $headers): EvolvableLinkProviderInterface + { + if (is_array($headers)) { + $headers = implode(', ', $headers); + } + $links = new GenericLinkProvider(); + + if (!preg_match_all(self::LINK_PATTERN, $headers, $matches, \PREG_SET_ORDER)) { + return $links; + } + + foreach ($matches as $match) { + $href = $match[1]; + $attributesString = $match[2]; + + $attributes = []; + if (preg_match_all(self::PARAM_PATTERN, $attributesString, $attributeMatches, \PREG_SET_ORDER)) { + $rels = null; + foreach ($attributeMatches as $pm) { + $key = $pm[1]; + $value = match (true) { + // Quoted value, unescape quotes + ($pm[2] ?? '') !== '' => stripcslashes($pm[2]), + ($pm[3] ?? '') !== '' => $pm[3], + // No value + default => true, + }; + + if ($key === 'rel') { + // Only the first occurrence of the "rel" attribute is read + $rels ??= $value === true ? [] : preg_split('/\s+/', $value, 0, \PREG_SPLIT_NO_EMPTY); + } elseif (is_array($attributes[$key] ?? null)) { + $attributes[$key][] = $value; + } elseif (isset($attributes[$key])) { + $attributes[$key] = [$attributes[$key], $value]; + } else { + $attributes[$key] = $value; + } + } + } + + $link = new Link(null, $href); + foreach ($rels ?? [] as $rel) { + $link = $link->withRel($rel); + } + foreach ($attributes as $k => $v) { + $link = $link->withAttribute($k, $v); + } + $links = $links->withLink($link); + } + + return $links; + } +} diff --git a/src/Symfony/Component/WebLink/HttpHeaderSerializer.php b/src/Symfony/Component/WebLink/HttpHeaderSerializer.php index 4d537c96f9cb8..d3b686add0baa 100644 --- a/src/Symfony/Component/WebLink/HttpHeaderSerializer.php +++ b/src/Symfony/Component/WebLink/HttpHeaderSerializer.php @@ -20,7 +20,7 @@ * * @author Kévin Dunglas */ -final class HttpHeaderSerializer +class HttpHeaderSerializer { /** * Builds the value of the "Link" HTTP header. diff --git a/src/Symfony/Component/WebLink/Link.php b/src/Symfony/Component/WebLink/Link.php index 1f5fbbdf9c6b5..519194c675206 100644 --- a/src/Symfony/Component/WebLink/Link.php +++ b/src/Symfony/Component/WebLink/Link.php @@ -153,7 +153,7 @@ class Link implements EvolvableLinkInterface private array $rel = []; /** - * @var array + * @var array> */ private array $attributes = []; @@ -181,6 +181,11 @@ public function getRels(): array return array_values($this->rel); } + /** + * Returns a list of attributes that describe the target URI. + * + * @return array> + */ public function getAttributes(): array { return $this->attributes; @@ -210,6 +215,14 @@ public function withoutRel(string $rel): static return $that; } + /** + * Returns an instance with the specified attribute added. + * + * If the specified attribute is already present, it will be overwritten + * with the new value. + * + * @param scalar|\Stringable|list $value + */ public function withAttribute(string $attribute, string|\Stringable|int|float|bool|array $value): static { $that = clone $this; diff --git a/src/Symfony/Component/WebLink/Tests/HttpHeaderParserTest.php b/src/Symfony/Component/WebLink/Tests/HttpHeaderParserTest.php new file mode 100644 index 0000000000000..b2ccc3e89163a --- /dev/null +++ b/src/Symfony/Component/WebLink/Tests/HttpHeaderParserTest.php @@ -0,0 +1,112 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\WebLink\Tests; + +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\TestCase; +use Symfony\Component\WebLink\HttpHeaderParser; + +class HttpHeaderParserTest extends TestCase +{ + public function testParse() + { + $parser = new HttpHeaderParser(); + + $header = [ + '; rel="prerender",; rel="dns-prefetch"; pr="0.7",; rel="preload"; as="script"', + '; rel="preload"; as="image"; nopush,; rel="alternate next"; hreflang="fr"; hreflang="de"; title="Hello"' + ]; + $provider = $parser->parse($header); + $links = $provider->getLinks(); + + self::assertCount(5, $links); + + self::assertSame(['prerender'], $links[0]->getRels()); + self::assertSame('/1', $links[0]->getHref()); + self::assertSame([], $links[0]->getAttributes()); + + self::assertSame(['dns-prefetch'], $links[1]->getRels()); + self::assertSame('/2', $links[1]->getHref()); + self::assertSame(['pr' => '0.7'], $links[1]->getAttributes()); + + self::assertSame(['preload'], $links[2]->getRels()); + self::assertSame('/3', $links[2]->getHref()); + self::assertSame(['as' => 'script'], $links[2]->getAttributes()); + + self::assertSame(['preload'], $links[3]->getRels()); + self::assertSame('/4', $links[3]->getHref()); + self::assertSame(['as' => 'image', 'nopush' => true], $links[3]->getAttributes()); + + self::assertSame(['alternate', 'next'], $links[4]->getRels()); + self::assertSame('/5', $links[4]->getHref()); + self::assertSame(['hreflang' => ['fr', 'de'], 'title' => 'Hello'], $links[4]->getAttributes()); + } + + public function testParseEmpty() + { + $parser = new HttpHeaderParser(); + $provider = $parser->parse(''); + self::assertCount(0, $provider->getLinks()); + } + + /** @dataProvider provideHeaderParsingCases */ + #[DataProvider('provideHeaderParsingCases')] + public function testParseVariousAttributes(string $header, array $expectedRels, array $expectedAttributes) + { + $parser = new HttpHeaderParser(); + $links = $parser->parse($header)->getLinks(); + + self::assertCount(1, $links); + self::assertSame('/foo', $links[0]->getHref()); + self::assertSame($expectedRels, $links[0]->getRels()); + self::assertSame($expectedAttributes, $links[0]->getAttributes()); + } + + public static function provideHeaderParsingCases() + { + yield 'double_quotes_in_attribute_value' => [ + '; rel="alternate"; title="\"escape me\" \"already escaped\" \"\"\""', + ['alternate'], + ['title' => '"escape me" "already escaped" """'], + ]; + + yield 'unquoted_attribute_value' => [ + '; rel=alternate; type=text/html', + ['alternate'], + ['type' => 'text/html'], + ]; + + yield 'attribute_with_punctuation' => [ + '; rel="alternate"; title=">; hello, world; test:case"', + ['alternate'], + ['title' => '>; hello, world; test:case'], + ]; + + yield 'no_rel' => [ + '; type=text/html', + [], + ['type' => 'text/html'], + ]; + + yield 'empty_rel' => [ + '; rel', + [], + [], + ]; + + yield 'multiple_rel_attributes_get_first' => [ + '; rel="alternate" rel="next"', + ['alternate'], + [], + ]; + } +} diff --git a/src/Symfony/Component/WebLink/Tests/LinkTest.php b/src/Symfony/Component/WebLink/Tests/LinkTest.php index 226bc3af11620..07946af9b0d01 100644 --- a/src/Symfony/Component/WebLink/Tests/LinkTest.php +++ b/src/Symfony/Component/WebLink/Tests/LinkTest.php @@ -27,10 +27,10 @@ public function testCanSetAndRetrieveValues() ->withAttribute('me', 'you') ; - $this->assertEquals('http://www.google.com', $link->getHref()); + $this->assertSame('http://www.google.com', $link->getHref()); $this->assertContains('next', $link->getRels()); $this->assertArrayHasKey('me', $link->getAttributes()); - $this->assertEquals('you', $link->getAttributes()['me']); + $this->assertSame('you', $link->getAttributes()['me']); } public function testCanRemoveValues() @@ -44,7 +44,7 @@ public function testCanRemoveValues() $link = $link->withoutAttribute('me') ->withoutRel('next'); - $this->assertEquals('http://www.google.com', $link->getHref()); + $this->assertSame('http://www.google.com', $link->getHref()); $this->assertFalse(\in_array('next', $link->getRels(), true)); $this->assertArrayNotHasKey('me', $link->getAttributes()); } @@ -65,7 +65,7 @@ public function testConstructor() { $link = new Link('next', 'http://www.google.com'); - $this->assertEquals('http://www.google.com', $link->getHref()); + $this->assertSame('http://www.google.com', $link->getHref()); $this->assertContains('next', $link->getRels()); } From 3bd14818649084f201c9f0887ced7537eb7635ef Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Thu, 29 May 2025 09:23:36 +0200 Subject: [PATCH 06/80] Update CHANGELOG for 6.4.22 --- CHANGELOG-6.4.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/CHANGELOG-6.4.md b/CHANGELOG-6.4.md index 7eb354e2603a5..78e2a5e01dec1 100644 --- a/CHANGELOG-6.4.md +++ b/CHANGELOG-6.4.md @@ -7,6 +7,25 @@ in 6.4 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/v6.4.0...v6.4.1 +* 6.4.22 (2025-05-29) + + * bug #60549 [Translation] Add intl-icu fallback for MessageCatalogue metadata (pontus-mp) + * bug #60571 [ErrorHandler] Do not transform file to link if it does not exist (lyrixx) + * bug #60494 [Messenger] fix: Add argument as integer (overexpOG) + * bug #60524 [Notifier] Fix Clicksend transport (BafS) + * bug #60478 [Validator] add missing `$extensions` and `$extensionsMessage` to the `Image` constraint (xabbuh) + * bug #60423 [DependencyInjection] Make `DefinitionErrorExceptionPass` consider `IGNORE_ON_UNINITIALIZED_REFERENCE` and `RUNTIME_EXCEPTION_ON_INVALID_REFERENCE` the same (MatTheCat) + * bug #60421 [VarExporter] Fixed lazy-loading ghost objects generation with property hooks (cheack) + * bug #60266 [Security] Exclude remember_me from default login authenticators (santysisi) + * bug #60400 [Config] Fix generated comment for multiline "info" (GromNaN) + * bug #60260 [Serializer] Prevent `Cannot traverse an already closed generator` error by materializing Traversable input (santysisi) + * bug #60292 [HttpFoundation] Encode path in `X-Accel-Redirect` header (Athorcis) + * bug #60379 [Security] Avoid failing when PersistentRememberMeHandler handles a malformed cookie (Seldaek) + * bug #60373 [FrameworkBundle] Ensure `Email` class exists before using it (Kocal) + * bug #60365 [FrameworkBundle] ensure that all supported e-mail validation modes can be configured (xabbuh) + * bug #60350 [Security][LoginLink] Throw `InvalidLoginLinkException` on invalid parameters (davidszkiba) + * bug #60340 [String] fix EmojiTransliterator return type compatibility with PHP 8.5 (xabbuh) + * 6.4.21 (2025-05-02) * bug #60288 [VarExporter] dump default value for property hooks if present (xabbuh) From d6fc1b5d472f6ae89db86b81da36e1030a68322f Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Thu, 29 May 2025 09:23:39 +0200 Subject: [PATCH 07/80] Update CONTRIBUTORS for 6.4.22 --- CONTRIBUTORS.md | 45 ++++++++++++++++++++++++++++++--------------- 1 file changed, 30 insertions(+), 15 deletions(-) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index ee2cb2a40889b..3e7f5ec2b6e78 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -30,9 +30,9 @@ The Symfony Connect username in parenthesis allows to get more information - Kris Wallsmith (kriswallsmith) - Jakub Zalas (jakubzalas) - Yonel Ceruto (yonelceruto) + - HypeMC (hypemc) - Hugo Hamon (hhamon) - Tobias Nyholm (tobias) - - HypeMC (hypemc) - Jérôme Tamarelle (gromnan) - Antoine Lamirault (alamirault) - Samuel ROZE (sroze) @@ -96,8 +96,8 @@ The Symfony Connect username in parenthesis allows to get more information - Henrik Bjørnskov (henrikbjorn) - Ruud Kamphuis (ruudk) - David Buchmann (dbu) - - Andrej Hudec (pulzarraider) - Tomas Norkūnas (norkunas) + - Andrej Hudec (pulzarraider) - Jáchym Toušek (enumag) - Hubert Lenoir (hubert_lenoir) - Christian Raue @@ -160,12 +160,13 @@ The Symfony Connect username in parenthesis allows to get more information - Włodzimierz Gajda (gajdaw) - Javier Spagnoletti (phansys) - Adrien Brault (adrienbrault) + - Florent Morselli (spomky_) + - soyuka - Florian Voutzinos (florianv) - Teoh Han Hui (teohhanhui) - Przemysław Bogusz (przemyslaw-bogusz) - Colin Frei - excelwebzone - - Florent Morselli (spomky_) - Paráda József (paradajozsef) - Maximilian Beckers (maxbeckers) - Baptiste Clavié (talus) @@ -175,17 +176,16 @@ The Symfony Connect username in parenthesis allows to get more information - Dāvis Zālītis (k0d3r1s) - Gordon Franke (gimler) - Malte Schlüter (maltemaltesich) - - soyuka - jeremyFreeAgent (jeremyfreeagent) - Michael Babker (mbabker) - Alexis Lefebvre + - Hugo Alliaume (kocal) - Christopher Hertel (chertel) - Joshua Thijssen - Vasilij Dusko - Daniel Wehner (dawehner) - Robert Schönthal (digitalkaoz) - Smaine Milianni (ismail1432) - - Hugo Alliaume (kocal) - François-Xavier de Guillebon (de-gui_f) - Andreas Schempp (aschempp) - noniagriconomie @@ -255,6 +255,7 @@ The Symfony Connect username in parenthesis allows to get more information - Alessandro Lai (jean85) - 77web - Gocha Ossinkine (ossinkine) + - matlec - Jesse Rushlow (geeshoe) - Matthieu Ouellette-Vachon (maoueh) - Michał Pipa (michal.pipa) @@ -286,7 +287,6 @@ The Symfony Connect username in parenthesis allows to get more information - Clément JOBEILI (dator) - Andreas Möller (localheinz) - Marek Štípek (maryo) - - matlec - Daniel Espendiller - Arnaud PETITPAS (apetitpa) - Michael Käfer (michael_kaefer) @@ -310,6 +310,7 @@ The Symfony Connect username in parenthesis allows to get more information - Patrick Landolt (scube) - Karoly Gossler (connorhu) - Timo Bakx (timobakx) + - Quentin Devos - Giorgio Premi - Alan Poulain (alanpoulain) - Ruben Gonzalez (rubenrua) @@ -337,6 +338,7 @@ The Symfony Connect username in parenthesis allows to get more information - Nikolay Labinskiy (e-moe) - Martin Schuhfuß (usefulthink) - apetitpa + - wkania - Guilliam Xavier - Pierre Minnieur (pminnieur) - Dominique Bongiraud @@ -377,6 +379,7 @@ The Symfony Connect username in parenthesis allows to get more information - Pascal Montoya - Julien Brochet - François Pluchino (francoispluchino) + - W0rma - Tristan Darricau (tristandsensio) - Jan Sorgalla (jsor) - henrikbjorn @@ -401,7 +404,6 @@ The Symfony Connect username in parenthesis allows to get more information - Zan Baldwin (zanbaldwin) - Tim Goudriaan (codedmonkey) - BoShurik - - Quentin Devos - Adam Prager (padam87) - Benoît Burnichon (bburnichon) - maxime.steinhausser @@ -428,7 +430,6 @@ The Symfony Connect username in parenthesis allows to get more information - Uwe Jäger (uwej711) - javaDeveloperKid - Chris Smith (cs278) - - W0rma - Lynn van der Berg (kjarli) - Michaël Perrin (michael.perrin) - Eugene Leonovich (rybakit) @@ -438,6 +439,7 @@ The Symfony Connect username in parenthesis allows to get more information - GordonsLondon - Ray - Philipp Cordes (corphi) + - Fabien S (bafs) - Chekote - Thomas Adam - Anderson Müller @@ -471,6 +473,7 @@ The Symfony Connect username in parenthesis allows to get more information - Marcos Sánchez - Emanuele Panzeri (thepanz) - Zmey + - Santiago San Martin (santysisi) - Kim Hemsø Rasmussen (kimhemsoe) - Maximilian Reichel (phramz) - Samaël Villette (samadu61) @@ -498,6 +501,7 @@ The Symfony Connect username in parenthesis allows to get more information - Manuel Kießling (manuelkiessling) - Alexey Kopytko (sanmai) - Warxcell (warxcell) + - SiD (plbsid) - Atsuhiro KUBO (iteman) - rudy onfroy (ronfroy) - Serkan Yildiz (srknyldz) @@ -507,7 +511,6 @@ The Symfony Connect username in parenthesis allows to get more information - Gabor Toth (tgabi333) - realmfoo - Joppe De Cuyper (joppedc) - - Fabien S (bafs) - Simon Podlipsky (simpod) - Thomas Tourlourat (armetiz) - Andrey Esaulov (andremaha) @@ -612,7 +615,6 @@ The Symfony Connect username in parenthesis allows to get more information - Alex (aik099) - Kieran Brahney - Fabien Villepinte - - SiD (plbsid) - Greg Thornton (xdissent) - Alex Bowers - Kev @@ -638,6 +640,7 @@ The Symfony Connect username in parenthesis allows to get more information - Ivan Sarastov (isarastov) - flack (flack) - Shein Alexey + - Link1515 - Joe Lencioni - Daniel Tschinder - Diego Agulló (aeoris) @@ -758,6 +761,7 @@ The Symfony Connect username in parenthesis allows to get more information - Jérémy REYNAUD (babeuloula) - Faizan Akram Dar (faizanakram) - Arkadius Stefanski (arkadius) + - Andy Palmer (andyexeter) - Jonas Flodén (flojon) - AnneKir - Tobias Weichart @@ -781,6 +785,7 @@ The Symfony Connect username in parenthesis allows to get more information - Giso Stallenberg (gisostallenberg) - Rob Bast - Roberto Espinoza (respinoza) + - Steven RENAUX (steven_renaux) - Marvin Feldmann (breyndotechse) - Soufian EZ ZANTAR (soezz) - Marek Zajac @@ -867,7 +872,6 @@ The Symfony Connect username in parenthesis allows to get more information - Dariusz Ruminski - Bahman Mehrdad (bahman) - Romain Gautier (mykiwi) - - Link1515 - Matthieu Bontemps - Erik Trapman - De Cock Xavier (xdecock) @@ -1010,7 +1014,6 @@ The Symfony Connect username in parenthesis allows to get more information - Jonas Elfering - Mihai Stancu - Nahuel Cuesta (ncuesta) - - Santiago San Martin - Chris Boden (cboden) - EStyles (insidestyles) - Christophe Villeger (seragan) @@ -1065,7 +1068,6 @@ The Symfony Connect username in parenthesis allows to get more information - Pierrick VIGNAND (pierrick) - Alex Bogomazov (alebo) - aaa2000 (aaa2000) - - Andy Palmer (andyexeter) - Andrew Neil Forster (krciga22) - Stefan Warman (warmans) - Tristan Maindron (tmaindron) @@ -1865,6 +1867,7 @@ The Symfony Connect username in parenthesis allows to get more information - Philipp Fritsche - Léon Gersen - tarlepp + - Giuseppe Arcuti - Dustin Wilson - Benjamin Paap (benjaminpaap) - Claus Due (namelesscoder) @@ -1958,7 +1961,6 @@ The Symfony Connect username in parenthesis allows to get more information - Bruno MATEU - Jeremy Bush - Lucas Bäuerle - - Steven RENAUX (steven_renaux) - Laurens Laman - Thomason, James - Dario Savella @@ -2195,6 +2197,7 @@ The Symfony Connect username in parenthesis allows to get more information - Tim Ward - Adiel Cristo (arcristo) - Christian Flach (cmfcmf) + - Dennis Jaschinski (d.jaschinski) - Fabian Kropfhamer (fabiank) - Jeffrey Cafferata (jcidnl) - Junaid Farooq (junaidfarooq) @@ -2264,6 +2267,7 @@ The Symfony Connect username in parenthesis allows to get more information - wivaku - Markus Reinhold - Jingyu Wang + - es - steveYeah - Asrorbek (asrorbek) - Samy D (dinduks) @@ -2278,6 +2282,7 @@ The Symfony Connect username in parenthesis allows to get more information - Alan Scott - Juanmi Rodriguez Cerón - twifty + - David Szkiba - Andy Raines - François Poguet - Anthony Ferrara @@ -2296,6 +2301,7 @@ The Symfony Connect username in parenthesis allows to get more information - xdavidwu - Benjamin RICHARD - Raphaël Droz + - Vladimir Pakhomchik - pdommelen - Eric Stern - ShiraNai7 @@ -2710,6 +2716,7 @@ The Symfony Connect username in parenthesis allows to get more information - Marcel Siegert - ryunosuke - Bruno BOUTAREL + - Athorcis - John Stevenson - everyx - Richard Heine @@ -2767,6 +2774,7 @@ The Symfony Connect username in parenthesis allows to get more information - Abdouarrahmane FOUAD (fabdouarrahmane) - Jakub Janata (janatjak) - Jm Aribau (jmaribau) + - Maciej Paprocki (maciekpaprocki) - Matthew Foster (mfoster) - Paul Seiffert (seiffert) - Vasily Khayrulin (sirian) @@ -3114,6 +3122,7 @@ The Symfony Connect username in parenthesis allows to get more information - Darryl Hein (xmmedia) - Vladimir Sadicov (xtech) - Marcel Berteler + - Ruud Seberechts - sdkawata - Frederik Schmitt - Peter van Dommelen @@ -3151,6 +3160,7 @@ The Symfony Connect username in parenthesis allows to get more information - Pierre Rineau - Florian Morello - Maxim Lovchikov + - ivelin vasilev - adenkejawen - Florent SEVESTRE (aniki-taicho) - Ari Pringle (apringle) @@ -3327,6 +3337,7 @@ The Symfony Connect username in parenthesis allows to get more information - Kevin Verschaeve (keversc) - Kevin Herrera (kherge) - Kubicki Kamil (kubik) + - Lauris Binde (laurisb) - Luis Ramón López López (lrlopez) - Vladislav Nikolayev (luxemate) - Martin Mandl (m2mtech) @@ -3372,7 +3383,6 @@ The Symfony Connect username in parenthesis allows to get more information - Youpie - Jason Stephens - Korvin Szanto - - wkania - srsbiz - Taylan Kasap - Michael Orlitzky @@ -3585,6 +3595,7 @@ The Symfony Connect username in parenthesis allows to get more information - mieszko4 - Steve Preston - ibasaw + - koyolgecen - Wojciech Skorodecki - Kevin Frantz - Neophy7e @@ -3614,6 +3625,7 @@ The Symfony Connect username in parenthesis allows to get more information - satalaondrej - Matthias Dötsch - jonmldr + - Nowfel2501 - Yevgen Kovalienia - Lebnik - Shude @@ -3635,6 +3647,7 @@ The Symfony Connect username in parenthesis allows to get more information - Egor Gorbachev - Julian Krzefski - Derek Stephen McLean + - PatrickRedStar - Norman Soetbeer - zorn - Yuriy Potemkin @@ -3744,6 +3757,7 @@ The Symfony Connect username in parenthesis allows to get more information - Brandon Kelly (brandonkelly) - Choong Wei Tjeng (choonge) - Bermon Clément (chou666) + - Chris Shennan (chrisshennan) - Citia (citia) - Kousuke Ebihara (co3k) - Loïc Vernet (coil) @@ -3906,6 +3920,7 @@ The Symfony Connect username in parenthesis allows to get more information - Romain - Xavier REN - Kevin Meijer + - Ignacio Alveal - max - Alexander Bauer (abauer) - Ahmad Mayahi (ahmadmayahi) From 6e3255b492dce243fd44bf553e80fdd28dc5fb3d Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Thu, 29 May 2025 09:23:40 +0200 Subject: [PATCH 08/80] Update VERSION for 6.4.22 --- 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 c30785f1ba758..bd7589173013e 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -76,12 +76,12 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl */ private static array $freshCache = []; - public const VERSION = '6.4.22-DEV'; + public const VERSION = '6.4.22'; public const VERSION_ID = 60422; public const MAJOR_VERSION = 6; public const MINOR_VERSION = 4; public const RELEASE_VERSION = 22; - public const EXTRA_VERSION = 'DEV'; + public const EXTRA_VERSION = ''; public const END_OF_MAINTENANCE = '11/2026'; public const END_OF_LIFE = '11/2027'; From 82a1563b6cdd26d6c6827d019be131d62e9adf3e Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Thu, 29 May 2025 09:34:10 +0200 Subject: [PATCH 09/80] Bump Symfony version to 6.4.23 --- 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 bd7589173013e..91c143f34298c 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -76,12 +76,12 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl */ private static array $freshCache = []; - public const VERSION = '6.4.22'; - public const VERSION_ID = 60422; + public const VERSION = '6.4.23-DEV'; + public const VERSION_ID = 60423; public const MAJOR_VERSION = 6; public const MINOR_VERSION = 4; - public const RELEASE_VERSION = 22; - public const EXTRA_VERSION = ''; + public const RELEASE_VERSION = 23; + public const EXTRA_VERSION = 'DEV'; public const END_OF_MAINTENANCE = '11/2026'; public const END_OF_LIFE = '11/2027'; From 5bb502a1085ef51c71837a5dc782a73795b313b1 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Thu, 29 May 2025 09:35:15 +0200 Subject: [PATCH 10/80] Update CHANGELOG for 7.2.7 --- CHANGELOG-7.2.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/CHANGELOG-7.2.md b/CHANGELOG-7.2.md index 93c489ae487bd..d6d188669de42 100644 --- a/CHANGELOG-7.2.md +++ b/CHANGELOG-7.2.md @@ -7,6 +7,32 @@ in 7.2 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.2.0...v7.2.1 +* 7.2.7 (2025-05-29) + + * bug #60549 [Translation] Add intl-icu fallback for MessageCatalogue metadata (pontus-mp) + * bug #60571 [ErrorHandler] Do not transform file to link if it does not exist (lyrixx) + * bug #60494 [Messenger] fix: Add argument as integer (overexpOG) + * bug #60524 [Notifier] Fix Clicksend transport (BafS) + * bug #60478 [Validator] add missing `$extensions` and `$extensionsMessage` to the `Image` constraint (xabbuh) + * bug #60484 [PhpUnitBridge] Clean up mocked features only when ``@group`` is present (HypeMC) + * bug #60490 [PhpUnitBridge] set path to the PHPUnit autoload file (xabbuh) + * bug #60423 [DependencyInjection] Make `DefinitionErrorExceptionPass` consider `IGNORE_ON_UNINITIALIZED_REFERENCE` and `RUNTIME_EXCEPTION_ON_INVALID_REFERENCE` the same (MatTheCat) + * bug #60439 [FrameworkBundle] Fix declaring field-attr tags in xml config files (nicolas-grekas) + * bug #60428 [DependencyInjection] Fix missing binding for ServiceCollectionInterface when declaring a service subscriber (nicolas-grekas) + * bug #60421 [VarExporter] Fixed lazy-loading ghost objects generation with property hooks (cheack) + * bug #60266 [Security] Exclude remember_me from default login authenticators (santysisi) + * bug #60400 [Config] Fix generated comment for multiline "info" (GromNaN) + * bug #60260 [Serializer] Prevent `Cannot traverse an already closed generator` error by materializing Traversable input (santysisi) + * bug #60292 [HttpFoundation] Encode path in `X-Accel-Redirect` header (Athorcis) + * bug #58643 [SecurityBundle] Use Composer `InstalledVersions` to check if flex is installed (andyexeter) + * bug #60275 [DoctrineBridge] Fix UniqueEntityValidator Stringable identifiers (GiuseppeArcuti, wkania) + * bug #60293 [Messenger] fix asking users to select an option if `--force` option is used in `messenger:failed:retry` command (W0rma) + * bug #60379 [Security] Avoid failing when PersistentRememberMeHandler handles a malformed cookie (Seldaek) + * bug #60373 [FrameworkBundle] Ensure `Email` class exists before using it (Kocal) + * bug #60365 [FrameworkBundle] ensure that all supported e-mail validation modes can be configured (xabbuh) + * bug #60350 [Security][LoginLink] Throw `InvalidLoginLinkException` on invalid parameters (davidszkiba) + * bug #60340 [String] fix EmojiTransliterator return type compatibility with PHP 8.5 (xabbuh) + * 7.2.6 (2025-05-02) * bug #60288 [VarExporter] dump default value for property hooks if present (xabbuh) From 330ffb50a155ea446b40841c375e5e7aab440b60 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Thu, 29 May 2025 09:35:19 +0200 Subject: [PATCH 11/80] Update VERSION for 7.2.7 --- 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 39964de47497f..09b362c1ff72c 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.2.7-DEV'; + public const VERSION = '7.2.7'; public const VERSION_ID = 70207; public const MAJOR_VERSION = 7; public const MINOR_VERSION = 2; public const RELEASE_VERSION = 7; - public const EXTRA_VERSION = 'DEV'; + public const EXTRA_VERSION = ''; public const END_OF_MAINTENANCE = '07/2025'; public const END_OF_LIFE = '07/2025'; From ed7b88c455a1a7029b6768c798413901cdf7d401 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Thu, 29 May 2025 09:38:07 +0200 Subject: [PATCH 12/80] Bump Symfony version to 7.2.8 --- 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 09b362c1ff72c..8948fc7533e96 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.2.7'; - public const VERSION_ID = 70207; + public const VERSION = '7.2.8-DEV'; + public const VERSION_ID = 70208; public const MAJOR_VERSION = 7; public const MINOR_VERSION = 2; - public const RELEASE_VERSION = 7; - public const EXTRA_VERSION = ''; + public const RELEASE_VERSION = 8; + public const EXTRA_VERSION = 'DEV'; public const END_OF_MAINTENANCE = '07/2025'; public const END_OF_LIFE = '07/2025'; From a68eac4fae60b8023c8ebdc970d2a3f8b2eb76de Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Thu, 29 May 2025 09:51:04 +0200 Subject: [PATCH 13/80] Bump Symfony version to 7.3.1 --- 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 bfef40fac58ad..dfee565d068cb 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.3.0'; - public const VERSION_ID = 70300; + public const VERSION = '7.3.1-DEV'; + public const VERSION_ID = 70301; public const MAJOR_VERSION = 7; public const MINOR_VERSION = 3; - public const RELEASE_VERSION = 0; - public const EXTRA_VERSION = ''; + public const RELEASE_VERSION = 1; + public const EXTRA_VERSION = 'DEV'; public const END_OF_MAINTENANCE = '05/2025'; public const END_OF_LIFE = '01/2026'; From e0a602bfc5753e1c7ad47ddd445f4849946b386f Mon Sep 17 00:00:00 2001 From: Antoine Lamirault Date: Thu, 29 May 2025 14:00:57 +0200 Subject: [PATCH 14/80] Replace get_class() calls by ::class --- src/Symfony/Component/Console/Debug/CliRequest.php | 2 +- .../Tests/Compiler/ServiceLocatorTagPassTest.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/Console/Debug/CliRequest.php b/src/Symfony/Component/Console/Debug/CliRequest.php index b023db07af95e..6e2c1012b16ef 100644 --- a/src/Symfony/Component/Console/Debug/CliRequest.php +++ b/src/Symfony/Component/Console/Debug/CliRequest.php @@ -24,7 +24,7 @@ public function __construct( public readonly TraceableCommand $command, ) { parent::__construct( - attributes: ['_controller' => \get_class($command->command), '_virtual_type' => 'command'], + attributes: ['_controller' => $command->command::class, '_virtual_type' => 'command'], server: $_SERVER, ); } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ServiceLocatorTagPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ServiceLocatorTagPassTest.php index 812b47c7a6f1f..7218db6deddc0 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ServiceLocatorTagPassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ServiceLocatorTagPassTest.php @@ -83,7 +83,7 @@ public function testProcessValue() $this->assertSame(CustomDefinition::class, $locator('bar')::class); $this->assertSame(CustomDefinition::class, $locator('baz')::class); $this->assertSame(CustomDefinition::class, $locator('some.service')::class); - $this->assertSame(CustomDefinition::class, \get_class($locator('inlines.service'))); + $this->assertSame(CustomDefinition::class, $locator('inlines.service')::class); } public function testServiceWithKeyOverwritesPreviousInheritedKey() From 558202c609802d899adce24d0747a02edff5bfa0 Mon Sep 17 00:00:00 2001 From: matlec Date: Thu, 29 May 2025 10:10:20 +0200 Subject: [PATCH 15/80] [DependencyInjection] Make `YamlDumper` quote resolved env vars if necessary --- .../DependencyInjection/Dumper/YamlDumper.php | 16 +++++++-------- .../Tests/Dumper/YamlDumperTest.php | 20 +++++++++++++++++++ .../yaml/container_with_env_placeholders.yml | 19 ++++++++++++++++++ 3 files changed, 47 insertions(+), 8 deletions(-) create mode 100644 src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/container_with_env_placeholders.yml diff --git a/src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php index 6b72aff14c2a7..299558039a632 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php @@ -50,18 +50,18 @@ public function dump(array $options = []): string $this->dumper ??= new YmlDumper(); - return $this->container->resolveEnvPlaceholders($this->addParameters()."\n".$this->addServices()); + return $this->addParameters()."\n".$this->addServices(); } private function addService(string $id, Definition $definition): string { - $code = " $id:\n"; + $code = " {$this->dumper->dump($id)}:\n"; if ($class = $definition->getClass()) { if (str_starts_with($class, '\\')) { $class = substr($class, 1); } - $code .= sprintf(" class: %s\n", $this->dumper->dump($class)); + $code .= sprintf(" class: %s\n", $this->dumper->dump($this->container->resolveEnvPlaceholders($class))); } if (!$definition->isPrivate()) { @@ -87,7 +87,7 @@ private function addService(string $id, Definition $definition): string } if ($definition->getFile()) { - $code .= sprintf(" file: %s\n", $this->dumper->dump($definition->getFile())); + $code .= sprintf(" file: %s\n", $this->dumper->dump($this->container->resolveEnvPlaceholders($definition->getFile()))); } if ($definition->isSynthetic()) { @@ -238,7 +238,7 @@ private function dumpCallable(mixed $callable): mixed } } - return $callable; + return $this->container->resolveEnvPlaceholders($callable); } /** @@ -299,7 +299,7 @@ private function dumpValue(mixed $value): mixed if (\is_array($value)) { $code = []; foreach ($value as $k => $v) { - $code[$k] = $this->dumpValue($v); + $code[$this->container->resolveEnvPlaceholders($k)] = $this->dumpValue($v); } return $code; @@ -319,7 +319,7 @@ private function dumpValue(mixed $value): mixed throw new RuntimeException(sprintf('Unable to dump a service container if a parameter is an object or a resource, got "%s".', get_debug_type($value))); } - return $value; + return $this->container->resolveEnvPlaceholders($value); } private function getServiceCall(string $id, ?Reference $reference = null): string @@ -359,7 +359,7 @@ private function prepareParameters(array $parameters, bool $escape = true): arra $filtered[$key] = $value; } - return $escape ? $this->escape($filtered) : $filtered; + return $escape ? $this->container->resolveEnvPlaceholders($this->escape($filtered)) : $filtered; } private function escape(array $arguments): array diff --git a/src/Symfony/Component/DependencyInjection/Tests/Dumper/YamlDumperTest.php b/src/Symfony/Component/DependencyInjection/Tests/Dumper/YamlDumperTest.php index f9ff3fff786a3..3a21d7aa9a9c5 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Dumper/YamlDumperTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Dumper/YamlDumperTest.php @@ -215,6 +215,26 @@ public function testDumpNonScalarTags() $this->assertEquals(file_get_contents(self::$fixturesPath.'/yaml/services_with_array_tags.yml'), $dumper->dump()); } + public function testDumpResolvedEnvPlaceholders() + { + $container = new ContainerBuilder(); + $container->setParameter('%env(PARAMETER_NAME)%', '%env(PARAMETER_VALUE)%'); + $container + ->register('service', '%env(SERVICE_CLASS)%') + ->setFile('%env(SERVICE_FILE)%') + ->addArgument('%env(SERVICE_ARGUMENT)%') + ->setProperty('%env(SERVICE_PROPERTY_NAME)%', '%env(SERVICE_PROPERTY_VALUE)%') + ->addMethodCall('%env(SERVICE_METHOD_NAME)%', ['%env(SERVICE_METHOD_ARGUMENT)%']) + ->setFactory('%env(SERVICE_FACTORY)%') + ->setConfigurator('%env(SERVICE_CONFIGURATOR)%') + ->setPublic(true) + ; + $container->compile(); + $dumper = new YamlDumper($container); + + $this->assertEquals(file_get_contents(self::$fixturesPath.'/yaml/container_with_env_placeholders.yml'), $dumper->dump()); + } + private function assertEqualYamlStructure(string $expected, string $yaml, string $message = '') { $parser = new Parser(); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/container_with_env_placeholders.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/container_with_env_placeholders.yml new file mode 100644 index 0000000000000..46c91130faecd --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/container_with_env_placeholders.yml @@ -0,0 +1,19 @@ +parameters: + '%env(PARAMETER_NAME)%': '%env(PARAMETER_VALUE)%' + +services: + service_container: + class: Symfony\Component\DependencyInjection\ContainerInterface + public: true + synthetic: true + service: + class: '%env(SERVICE_CLASS)%' + public: true + file: '%env(SERVICE_FILE)%' + arguments: ['%env(SERVICE_ARGUMENT)%'] + properties: { '%env(SERVICE_PROPERTY_NAME)%': '%env(SERVICE_PROPERTY_VALUE)%' } + calls: + - ['%env(SERVICE_METHOD_NAME)%', ['%env(SERVICE_METHOD_ARGUMENT)%']] + + factory: '%env(SERVICE_FACTORY)%' + configurator: '%env(SERVICE_CONFIGURATOR)%' From 5aebeb3805e00166c4e033ed995f357a02314f34 Mon Sep 17 00:00:00 2001 From: HypeMC Date: Thu, 29 May 2025 23:38:42 +0200 Subject: [PATCH 16/80] [PhpUnitBridge] Mark as dev package --- src/Symfony/Bridge/PhpUnit/composer.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Bridge/PhpUnit/composer.json b/src/Symfony/Bridge/PhpUnit/composer.json index 9febfdb8ee63e..a42c737f70b78 100644 --- a/src/Symfony/Bridge/PhpUnit/composer.json +++ b/src/Symfony/Bridge/PhpUnit/composer.json @@ -2,7 +2,9 @@ "name": "symfony/phpunit-bridge", "type": "symfony-bridge", "description": "Provides utilities for PHPUnit, especially user deprecation notices management", - "keywords": [], + "keywords": [ + "testing" + ], "homepage": "https://symfony.com", "license": "MIT", "authors": [ From 7e6e33eed689bbc5ca5c3fdff7aca1dbc5114bfb Mon Sep 17 00:00:00 2001 From: Alexander Schranz Date: Wed, 28 May 2025 09:33:08 +0200 Subject: [PATCH 17/80] [HttpKernel] Do not superseed private cache-control when no-store is set --- .../EventListener/CacheAttributeListener.php | 1 - .../EventListener/CacheAttributeListenerTest.php | 14 +++++++------- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/EventListener/CacheAttributeListener.php b/src/Symfony/Component/HttpKernel/EventListener/CacheAttributeListener.php index e913edf9e538a..436e031bbbcac 100644 --- a/src/Symfony/Component/HttpKernel/EventListener/CacheAttributeListener.php +++ b/src/Symfony/Component/HttpKernel/EventListener/CacheAttributeListener.php @@ -165,7 +165,6 @@ public function onKernelResponse(ResponseEvent $event): void } if (true === $cache->noStore) { - $response->setPrivate(); $response->headers->addCacheControlDirective('no-store'); } diff --git a/src/Symfony/Component/HttpKernel/Tests/EventListener/CacheAttributeListenerTest.php b/src/Symfony/Component/HttpKernel/Tests/EventListener/CacheAttributeListenerTest.php index b185ea8994b1f..d2c8ed0db63d5 100644 --- a/src/Symfony/Component/HttpKernel/Tests/EventListener/CacheAttributeListenerTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/EventListener/CacheAttributeListenerTest.php @@ -102,18 +102,18 @@ public function testResponseIsPublicIfConfigurationIsPublicTrueNoStoreFalse() $this->assertFalse($this->response->headers->hasCacheControlDirective('no-store')); } - public function testResponseIsPrivateIfConfigurationIsPublicTrueNoStoreTrue() + public function testResponseKeepPublicIfConfigurationIsPublicTrueNoStoreTrue() { $request = $this->createRequest(new Cache(public: true, noStore: true)); $this->listener->onKernelResponse($this->createEventMock($request, $this->response)); - $this->assertFalse($this->response->headers->hasCacheControlDirective('public')); - $this->assertTrue($this->response->headers->hasCacheControlDirective('private')); + $this->assertTrue($this->response->headers->hasCacheControlDirective('public')); + $this->assertFalse($this->response->headers->hasCacheControlDirective('private')); $this->assertTrue($this->response->headers->hasCacheControlDirective('no-store')); } - public function testResponseIsPrivateNoStoreIfConfigurationIsNoStoreTrue() + public function testResponseKeepPrivateNoStoreIfConfigurationIsNoStoreTrue() { $request = $this->createRequest(new Cache(noStore: true)); @@ -124,14 +124,14 @@ public function testResponseIsPrivateNoStoreIfConfigurationIsNoStoreTrue() $this->assertTrue($this->response->headers->hasCacheControlDirective('no-store')); } - public function testResponseIsPrivateIfSharedMaxAgeSetAndNoStoreIsTrue() + public function testResponseIsPublicIfSharedMaxAgeSetAndNoStoreIsTrue() { $request = $this->createRequest(new Cache(smaxage: 1, noStore: true)); $this->listener->onKernelResponse($this->createEventMock($request, $this->response)); - $this->assertFalse($this->response->headers->hasCacheControlDirective('public')); - $this->assertTrue($this->response->headers->hasCacheControlDirective('private')); + $this->assertTrue($this->response->headers->hasCacheControlDirective('public')); + $this->assertFalse($this->response->headers->hasCacheControlDirective('private')); $this->assertTrue($this->response->headers->hasCacheControlDirective('no-store')); } From 79d8097f932661c11c9f8124df3c2b1ad98b275a Mon Sep 17 00:00:00 2001 From: Matthias Pigulla Date: Wed, 21 May 2025 13:01:46 +0200 Subject: [PATCH 18/80] [HttpCache] Add a `waiting` trace when finding the cache locked --- src/Symfony/Component/HttpKernel/CHANGELOG.md | 3 +- .../HttpKernel/HttpCache/HttpCache.php | 2 ++ .../Tests/HttpCache/HttpCacheTest.php | 28 +++++++++++++++++++ .../Tests/HttpCache/HttpCacheTestCase.php | 11 ++++++-- 4 files changed, 41 insertions(+), 3 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/CHANGELOG.md b/src/Symfony/Component/HttpKernel/CHANGELOG.md index 6bf1a60ebc6e2..5df71549449f3 100644 --- a/src/Symfony/Component/HttpKernel/CHANGELOG.md +++ b/src/Symfony/Component/HttpKernel/CHANGELOG.md @@ -4,11 +4,12 @@ CHANGELOG 7.3 --- + * Record a `waiting` trace in the `HttpCache` when the cache had to wait for another request to finish * Add `$key` argument to `#[MapQueryString]` that allows using a specific key for argument resolving * Support `Uid` in `#[MapQueryParameter]` * Add `ServicesResetterInterface`, implemented by `ServicesResetter` * Allow configuring the logging channel per type of exceptions in ErrorListener - + 7.2 --- diff --git a/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php b/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php index 3ef1b8dcb821f..d5ed6d65597ac 100644 --- a/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php +++ b/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php @@ -558,6 +558,8 @@ protected function lock(Request $request, Response $entry): bool return true; } + $this->record($request, 'waiting'); + // wait for the lock to be released if ($this->waitForLock($request)) { // replace the current entry with the fresh one diff --git a/src/Symfony/Component/HttpKernel/Tests/HttpCache/HttpCacheTest.php b/src/Symfony/Component/HttpKernel/Tests/HttpCache/HttpCacheTest.php index e82e8fd81b481..2b553dc875d39 100644 --- a/src/Symfony/Component/HttpKernel/Tests/HttpCache/HttpCacheTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/HttpCache/HttpCacheTest.php @@ -17,6 +17,7 @@ use Symfony\Component\HttpKernel\Event\TerminateEvent; use Symfony\Component\HttpKernel\HttpCache\Esi; use Symfony\Component\HttpKernel\HttpCache\HttpCache; +use Symfony\Component\HttpKernel\HttpCache\Store; use Symfony\Component\HttpKernel\HttpCache\StoreInterface; use Symfony\Component\HttpKernel\HttpKernelInterface; use Symfony\Component\HttpKernel\Kernel; @@ -662,6 +663,7 @@ public function testDegradationWhenCacheLocked() */ sleep(10); + $this->store = $this->createStore(); // create another store instance that does not hold the current lock $this->request('GET', '/'); $this->assertHttpKernelIsNotCalled(); $this->assertEquals(200, $this->response->getStatusCode()); @@ -680,6 +682,32 @@ public function testDegradationWhenCacheLocked() $this->assertEquals('Old response', $this->response->getContent()); } + public function testTraceAddedWhenCacheLocked() + { + if ('\\' === \DIRECTORY_SEPARATOR) { + $this->markTestSkipped('Skips on windows to avoid permissions issues.'); + } + + // Disable stale-while-revalidate, which circumvents blocking access + $this->cacheConfig['stale_while_revalidate'] = 0; + + // The presence of Last-Modified makes this cacheable + $this->setNextResponse(200, ['Cache-Control' => 'public, no-cache', 'Last-Modified' => 'some while ago'], 'Old response'); + $this->request('GET', '/'); // warm the cache + + $primedStore = $this->store; + + $this->store = $this->createMock(Store::class); + $this->store->method('lookup')->willReturnCallback(fn (Request $request) => $primedStore->lookup($request)); + // Assume the cache is locked at the first attempt, but immediately treat the lock as released afterwards + $this->store->method('lock')->willReturnOnConsecutiveCalls(false, true); + $this->store->method('isLocked')->willReturn(false); + + $this->request('GET', '/'); + + $this->assertTraceContains('waiting'); + } + public function testHitsCachedResponseWithSMaxAgeDirective() { $time = \DateTimeImmutable::createFromFormat('U', time() - 5); diff --git a/src/Symfony/Component/HttpKernel/Tests/HttpCache/HttpCacheTestCase.php b/src/Symfony/Component/HttpKernel/Tests/HttpCache/HttpCacheTestCase.php index 26a29f16b2b75..b05d9136661c3 100644 --- a/src/Symfony/Component/HttpKernel/Tests/HttpCache/HttpCacheTestCase.php +++ b/src/Symfony/Component/HttpKernel/Tests/HttpCache/HttpCacheTestCase.php @@ -30,7 +30,7 @@ abstract class HttpCacheTestCase extends TestCase protected $responses; protected $catch; protected $esi; - protected Store $store; + protected ?Store $store = null; protected function setUp(): void { @@ -115,7 +115,9 @@ public function request($method, $uri = '/', $server = [], $cookies = [], $esi = $this->kernel->reset(); - $this->store = new Store(sys_get_temp_dir().'/http_cache'); + if (!$this->store) { + $this->store = $this->createStore(); + } if (!isset($this->cacheConfig['debug'])) { $this->cacheConfig['debug'] = true; @@ -183,4 +185,9 @@ public static function clearDirectory($directory) closedir($fp); } + + protected function createStore(): Store + { + return new Store(sys_get_temp_dir().'/http_cache'); + } } From 207e2f9a5eaae24c520a5013e1892659d4841a12 Mon Sep 17 00:00:00 2001 From: alifanau Date: Fri, 30 May 2025 03:16:57 +0300 Subject: [PATCH 19/80] Fix: Lack of recipient in case DSN does not have optional LIST_ID parameter. According to https://developers.clicksend.com/docs/messaging/sms/other/send-sms#other/send-sms/t=request&path=messages/list_id we need to provide the "to" parameter or the "list_id" parameter. Also fixed forwarding FROM_EMAIL parameter to request. --- .../Bridge/ClickSend/ClickSendTransport.php | 4 +-- .../Tests/ClickSendTransportTest.php | 29 ++++++++++++++++++- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/src/Symfony/Component/Notifier/Bridge/ClickSend/ClickSendTransport.php b/src/Symfony/Component/Notifier/Bridge/ClickSend/ClickSendTransport.php index f60e4e23ee3f9..13f839ba4946c 100644 --- a/src/Symfony/Component/Notifier/Bridge/ClickSend/ClickSendTransport.php +++ b/src/Symfony/Component/Notifier/Bridge/ClickSend/ClickSendTransport.php @@ -75,13 +75,13 @@ protected function doSend(MessageInterface $message): SentMessage $options['from'] = $message->getFrom() ?: $this->from; $options['source'] ??= $this->source; $options['list_id'] ??= $this->listId; - $options['from_email'] ?? $this->fromEmail; + $options['from_email'] ??= $this->fromEmail; if (isset($options['from']) && !preg_match('/^[a-zA-Z0-9\s]{3,11}$/', $options['from']) && !preg_match('/^\+[1-9]\d{1,14}$/', $options['from'])) { throw new InvalidArgumentException(sprintf('The "From" number "%s" is not a valid phone number, shortcode, or alphanumeric sender ID.', $options['from'])); } - if ($options['list_id'] ?? false) { + if (!$options['list_id']) { $options['to'] = $message->getPhone(); } diff --git a/src/Symfony/Component/Notifier/Bridge/ClickSend/Tests/ClickSendTransportTest.php b/src/Symfony/Component/Notifier/Bridge/ClickSend/Tests/ClickSendTransportTest.php index ba7923a7fa231..c7d1fa876b82f 100644 --- a/src/Symfony/Component/Notifier/Bridge/ClickSend/Tests/ClickSendTransportTest.php +++ b/src/Symfony/Component/Notifier/Bridge/ClickSend/Tests/ClickSendTransportTest.php @@ -24,7 +24,7 @@ final class ClickSendTransportTest extends TransportTestCase { - public static function createTransport(?HttpClientInterface $client = null, string $from = 'test_from', string $source = 'test_source', int $listId = 99, string $fromEmail = 'foo@bar.com'): ClickSendTransport + public static function createTransport(?HttpClientInterface $client = null, ?string $from = 'test_from', ?string $source = 'test_source', ?int $listId = 99, ?string $fromEmail = 'foo@bar.com'): ClickSendTransport { return new ClickSendTransport('test_username', 'test_key', $from, $source, $listId, $fromEmail, $client ?? new MockHttpClient()); } @@ -70,6 +70,10 @@ public function testNoInvalidArgumentExceptionIsThrownIfFromIsValid(string $from $body = json_decode($options['body'], true); self::assertIsArray($body); self::assertArrayHasKey('messages', $body); + $message = reset($body['messages']); + self::assertArrayHasKey('from_email', $message); + self::assertArrayHasKey('list_id', $message); + self::assertArrayNotHasKey('to', $message); return $response; }); @@ -77,6 +81,29 @@ public function testNoInvalidArgumentExceptionIsThrownIfFromIsValid(string $from $transport->send($message); } + public function testNoInvalidArgumentExceptionIsThrownIfFromIsValidWithoutOptionalParameters() + { + $message = new SmsMessage('+33612345678', 'Hello!'); + $response = $this->createMock(ResponseInterface::class); + $response->expects(self::exactly(2))->method('getStatusCode')->willReturn(200); + $response->expects(self::once())->method('getContent')->willReturn(''); + $client = new MockHttpClient(function (string $method, string $url, array $options) use ($response): ResponseInterface { + self::assertSame('POST', $method); + self::assertSame('https://rest.clicksend.com/v3/sms/send', $url); + + $body = json_decode($options['body'], true); + self::assertIsArray($body); + self::assertArrayHasKey('messages', $body); + $message = reset($body['messages']); + self::assertArrayNotHasKey('list_id', $message); + self::assertArrayHasKey('to', $message); + + return $response; + }); + $transport = $this->createTransport($client, null, null, null, null); + $transport->send($message); + } + public static function toStringProvider(): iterable { yield ['clicksend://rest.clicksend.com?from=test_from&source=test_source&list_id=99&from_email=foo%40bar.com', self::createTransport()]; From e51a81a9d93d6184452ee849969edc165ccb140d Mon Sep 17 00:00:00 2001 From: Abdelhakim ABOULHAJ Date: Wed, 28 May 2025 15:48:55 +0100 Subject: [PATCH 20/80] doc: update UserInterface header comments --- src/Symfony/Component/Security/Core/User/UserInterface.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Symfony/Component/Security/Core/User/UserInterface.php b/src/Symfony/Component/Security/Core/User/UserInterface.php index ef22340a6313a..a543c35caee98 100644 --- a/src/Symfony/Component/Security/Core/User/UserInterface.php +++ b/src/Symfony/Component/Security/Core/User/UserInterface.php @@ -15,9 +15,7 @@ * Represents the interface that all user classes must implement. * * This interface is useful because the authentication layer can deal with - * the object through its lifecycle, using the object to get the hashed - * password (for checking against a submitted password), assigning roles - * and so on. + * the object through its lifecycle, assigning roles and so on. * * Regardless of how your users are loaded or where they come from (a database, * configuration, web service, etc.), you will have a class that implements From 53e8d13112a086a94b83962f051f844915ebe84f Mon Sep 17 00:00:00 2001 From: Alexander Schranz Date: Wed, 28 May 2025 09:33:08 +0200 Subject: [PATCH 21/80] [HttpKernel] Do not superseed private cache-control when no-store is set --- .../EventListener/CacheAttributeListener.php | 1 - .../EventListener/CacheAttributeListenerTest.php | 14 +++++++------- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/EventListener/CacheAttributeListener.php b/src/Symfony/Component/HttpKernel/EventListener/CacheAttributeListener.php index e913edf9e538a..436e031bbbcac 100644 --- a/src/Symfony/Component/HttpKernel/EventListener/CacheAttributeListener.php +++ b/src/Symfony/Component/HttpKernel/EventListener/CacheAttributeListener.php @@ -165,7 +165,6 @@ public function onKernelResponse(ResponseEvent $event): void } if (true === $cache->noStore) { - $response->setPrivate(); $response->headers->addCacheControlDirective('no-store'); } diff --git a/src/Symfony/Component/HttpKernel/Tests/EventListener/CacheAttributeListenerTest.php b/src/Symfony/Component/HttpKernel/Tests/EventListener/CacheAttributeListenerTest.php index b185ea8994b1f..d2c8ed0db63d5 100644 --- a/src/Symfony/Component/HttpKernel/Tests/EventListener/CacheAttributeListenerTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/EventListener/CacheAttributeListenerTest.php @@ -102,18 +102,18 @@ public function testResponseIsPublicIfConfigurationIsPublicTrueNoStoreFalse() $this->assertFalse($this->response->headers->hasCacheControlDirective('no-store')); } - public function testResponseIsPrivateIfConfigurationIsPublicTrueNoStoreTrue() + public function testResponseKeepPublicIfConfigurationIsPublicTrueNoStoreTrue() { $request = $this->createRequest(new Cache(public: true, noStore: true)); $this->listener->onKernelResponse($this->createEventMock($request, $this->response)); - $this->assertFalse($this->response->headers->hasCacheControlDirective('public')); - $this->assertTrue($this->response->headers->hasCacheControlDirective('private')); + $this->assertTrue($this->response->headers->hasCacheControlDirective('public')); + $this->assertFalse($this->response->headers->hasCacheControlDirective('private')); $this->assertTrue($this->response->headers->hasCacheControlDirective('no-store')); } - public function testResponseIsPrivateNoStoreIfConfigurationIsNoStoreTrue() + public function testResponseKeepPrivateNoStoreIfConfigurationIsNoStoreTrue() { $request = $this->createRequest(new Cache(noStore: true)); @@ -124,14 +124,14 @@ public function testResponseIsPrivateNoStoreIfConfigurationIsNoStoreTrue() $this->assertTrue($this->response->headers->hasCacheControlDirective('no-store')); } - public function testResponseIsPrivateIfSharedMaxAgeSetAndNoStoreIsTrue() + public function testResponseIsPublicIfSharedMaxAgeSetAndNoStoreIsTrue() { $request = $this->createRequest(new Cache(smaxage: 1, noStore: true)); $this->listener->onKernelResponse($this->createEventMock($request, $this->response)); - $this->assertFalse($this->response->headers->hasCacheControlDirective('public')); - $this->assertTrue($this->response->headers->hasCacheControlDirective('private')); + $this->assertTrue($this->response->headers->hasCacheControlDirective('public')); + $this->assertFalse($this->response->headers->hasCacheControlDirective('private')); $this->assertTrue($this->response->headers->hasCacheControlDirective('no-store')); } From c24f895ef6b101b313a99e2ff832d8bb486c41f2 Mon Sep 17 00:00:00 2001 From: matlec Date: Fri, 30 May 2025 13:06:20 +0200 Subject: [PATCH 22/80] Fix `ContainerDebugCommandTest::testNoDumpedXML` --- .../Tests/Functional/ContainerDebugCommandTest.php | 2 +- .../Tests/Functional/app/ContainerDebug/no_dump.yml | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/ContainerDebug/no_dump.yml diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/ContainerDebugCommandTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/ContainerDebugCommandTest.php index 24c6faf332525..291a67cb83b4c 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/ContainerDebugCommandTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/ContainerDebugCommandTest.php @@ -53,7 +53,7 @@ public function testNoDebug() public function testNoDumpedXML() { - static::bootKernel(['test_case' => 'ContainerDebug', 'root_config' => 'config.yml', 'debug' => true, 'debug.container.dump' => false]); + static::bootKernel(['test_case' => 'ContainerDebug', 'root_config' => 'no_dump.yml', 'debug' => true]); $application = new Application(static::$kernel); $application->setAutoExit(false); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/ContainerDebug/no_dump.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/ContainerDebug/no_dump.yml new file mode 100644 index 0000000000000..a9c709e9a6425 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/ContainerDebug/no_dump.yml @@ -0,0 +1,5 @@ +imports: + - { resource: config.yml } + +parameters: + debug.container.dump: false From adfc7e97bcc8ee9f63de095aabb1af2e896ca16f Mon Sep 17 00:00:00 2001 From: Simon / Yami Date: Wed, 28 May 2025 11:01:39 +0200 Subject: [PATCH 23/80] [Dotenv] improve documentation for dotenv component --- src/Symfony/Component/Dotenv/README.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/Symfony/Component/Dotenv/README.md b/src/Symfony/Component/Dotenv/README.md index 2a1cc02ccfcb8..67ff66a07a802 100644 --- a/src/Symfony/Component/Dotenv/README.md +++ b/src/Symfony/Component/Dotenv/README.md @@ -11,6 +11,15 @@ Getting Started composer require symfony/dotenv ``` +Usage +----- + +> For an .env file with this format: + +```env +YOUR_VARIABLE_NAME=my-string +``` + ```php use Symfony\Component\Dotenv\Dotenv; @@ -25,6 +34,12 @@ $dotenv->overload(__DIR__.'/.env'); // loads .env, .env.local, and .env.$APP_ENV.local or .env.$APP_ENV $dotenv->loadEnv(__DIR__.'/.env'); + +// Usage with $_ENV +$envVariable = $_ENV['YOUR_VARIABLE_NAME']; + +// Usage with $_SERVER +$envVariable = $_SERVER['YOUR_VARIABLE_NAME']; ``` Resources From 7ce3bb5e7d7f02105d908c228ea94dc142cb5f56 Mon Sep 17 00:00:00 2001 From: tcoch Date: Wed, 14 May 2025 16:12:06 +0200 Subject: [PATCH 24/80] [Validator] Add tests for `MacAddress` --- .../Constraints/MacAddressValidatorTest.php | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/Symfony/Component/Validator/Tests/Constraints/MacAddressValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/MacAddressValidatorTest.php index d755df486e140..5abb7487ba328 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/MacAddressValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/MacAddressValidatorTest.php @@ -63,6 +63,19 @@ public function testValidMac($mac) $this->assertNoViolation(); } + /** + * @dataProvider getNotValidMacs + */ + public function testNotValidMac($mac) + { + $this->validator->validate($mac, new MacAddress()); + + $this->buildViolation('This value is not a valid MAC address.') + ->setParameter('{{ value }}', '"'.$mac.'"') + ->setCode(MacAddress::INVALID_MAC_ERROR) + ->assertRaised(); + } + public static function getValidMacs(): array { return [ @@ -76,6 +89,17 @@ public static function getValidMacs(): array ]; } + public static function getNotValidMacs(): array + { + return [ + ['00:00:00:00:00'], + ['00:00:00:00:00:0G'], + ['GG:GG:GG:GG:GG:GG'], + ['GG-GG-GG-GG-GG-GG'], + ['GGGG.GGGG.GGGG'], + ]; + } + public static function getValidLocalUnicastMacs(): array { return [ From f1160d6617f7eeaaf490eee47e0a737b8d5fc321 Mon Sep 17 00:00:00 2001 From: wkania Date: Thu, 1 May 2025 20:52:43 +0200 Subject: [PATCH 25/80] [Form] Add input=date_point to DateTimeType, DateType and TimeType --- src/Symfony/Component/Form/CHANGELOG.md | 5 ++ .../DatePointToDateTimeTransformer.php | 64 +++++++++++++++++++ .../Form/Extension/Core/Type/DateTimeType.php | 12 +++- .../Form/Extension/Core/Type/DateType.php | 12 +++- .../Form/Extension/Core/Type/TimeType.php | 12 +++- .../Extension/Core/Type/DateTimeTypeTest.php | 32 ++++++++++ .../Extension/Core/Type/DateTypeTest.php | 22 +++++++ .../Extension/Core/Type/TimeTypeTest.php | 27 ++++++++ src/Symfony/Component/Form/composer.json | 1 + 9 files changed, 181 insertions(+), 6 deletions(-) create mode 100644 src/Symfony/Component/Form/Extension/Core/DataTransformer/DatePointToDateTimeTransformer.php diff --git a/src/Symfony/Component/Form/CHANGELOG.md b/src/Symfony/Component/Form/CHANGELOG.md index 00d3b2fc4027b..b74d43e79d23f 100644 --- a/src/Symfony/Component/Form/CHANGELOG.md +++ b/src/Symfony/Component/Form/CHANGELOG.md @@ -1,6 +1,11 @@ CHANGELOG ========= +7.4 +--- + + * Add `input=date_point` to `DateTimeType`, `DateType` and `TimeType` + 7.3 --- diff --git a/src/Symfony/Component/Form/Extension/Core/DataTransformer/DatePointToDateTimeTransformer.php b/src/Symfony/Component/Form/Extension/Core/DataTransformer/DatePointToDateTimeTransformer.php new file mode 100644 index 0000000000000..dc1f7506822f9 --- /dev/null +++ b/src/Symfony/Component/Form/Extension/Core/DataTransformer/DatePointToDateTimeTransformer.php @@ -0,0 +1,64 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Core\DataTransformer; + +use Symfony\Component\Clock\DatePoint; +use Symfony\Component\Form\DataTransformerInterface; +use Symfony\Component\Form\Exception\TransformationFailedException; + +/** + * Transforms between a DatePoint object and a DateTime object. + * + * @implements DataTransformerInterface + */ +final class DatePointToDateTimeTransformer implements DataTransformerInterface +{ + /** + * Transforms a DatePoint into a DateTime object. + * + * @param DatePoint|null $value A DatePoint object + * + * @throws TransformationFailedException If the given value is not a DatePoint + */ + public function transform(mixed $value): ?\DateTime + { + if (null === $value) { + return null; + } + + if (!$value instanceof DatePoint) { + throw new TransformationFailedException(\sprintf('Expected a "%s".', DatePoint::class)); + } + + return \DateTime::createFromImmutable($value); + } + + /** + * Transforms a DateTime object into a DatePoint object. + * + * @param \DateTime|null $value A DateTime object + * + * @throws TransformationFailedException If the given value is not a \DateTime + */ + public function reverseTransform(mixed $value): ?DatePoint + { + if (null === $value) { + return null; + } + + if (!$value instanceof \DateTime) { + throw new TransformationFailedException('Expected a \DateTime.'); + } + + return DatePoint::createFromMutable($value); + } +} diff --git a/src/Symfony/Component/Form/Extension/Core/Type/DateTimeType.php b/src/Symfony/Component/Form/Extension/Core/Type/DateTimeType.php index cf4c2b7416be9..8ecaa63c078b8 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/DateTimeType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/DateTimeType.php @@ -11,10 +11,12 @@ namespace Symfony\Component\Form\Extension\Core\Type; +use Symfony\Component\Clock\DatePoint; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\Exception\LogicException; use Symfony\Component\Form\Extension\Core\DataTransformer\ArrayToPartsTransformer; use Symfony\Component\Form\Extension\Core\DataTransformer\DataTransformerChain; +use Symfony\Component\Form\Extension\Core\DataTransformer\DatePointToDateTimeTransformer; use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeImmutableToDateTimeTransformer; use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToArrayTransformer; use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToHtml5LocalDateTimeTransformer; @@ -178,7 +180,12 @@ public function buildForm(FormBuilderInterface $builder, array $options): void ; } - if ('datetime_immutable' === $options['input']) { + if ('date_point' === $options['input']) { + if (!class_exists(DatePoint::class)) { + throw new LogicException(\sprintf('The "symfony/clock" component is required to use "%s" with option "input=date_point". Try running "composer require symfony/clock".', self::class)); + } + $builder->addModelTransformer(new DatePointToDateTimeTransformer()); + } elseif ('datetime_immutable' === $options['input']) { $builder->addModelTransformer(new DateTimeImmutableToDateTimeTransformer()); } elseif ('string' === $options['input']) { $builder->addModelTransformer(new ReversedTransformer( @@ -194,7 +201,7 @@ public function buildForm(FormBuilderInterface $builder, array $options): void )); } - if (\in_array($options['input'], ['datetime', 'datetime_immutable'], true) && null !== $options['model_timezone']) { + if (\in_array($options['input'], ['datetime', 'datetime_immutable', 'date_point'], true) && null !== $options['model_timezone']) { $builder->addEventListener(FormEvents::POST_SET_DATA, static function (FormEvent $event) use ($options): void { $date = $event->getData(); @@ -283,6 +290,7 @@ public function configureOptions(OptionsResolver $resolver): void $resolver->setAllowedValues('input', [ 'datetime', 'datetime_immutable', + 'date_point', 'string', 'timestamp', 'array', diff --git a/src/Symfony/Component/Form/Extension/Core/Type/DateType.php b/src/Symfony/Component/Form/Extension/Core/Type/DateType.php index 36b430e144b58..5c8dfaa3c2b10 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/DateType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/DateType.php @@ -11,8 +11,10 @@ namespace Symfony\Component\Form\Extension\Core\Type; +use Symfony\Component\Clock\DatePoint; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\Exception\LogicException; +use Symfony\Component\Form\Extension\Core\DataTransformer\DatePointToDateTimeTransformer; use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeImmutableToDateTimeTransformer; use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToArrayTransformer; use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToLocalizedStringTransformer; @@ -156,7 +158,12 @@ public function buildForm(FormBuilderInterface $builder, array $options): void ; } - if ('datetime_immutable' === $options['input']) { + if ('date_point' === $options['input']) { + if (!class_exists(DatePoint::class)) { + throw new LogicException(\sprintf('The "symfony/clock" component is required to use "%s" with option "input=date_point". Try running "composer require symfony/clock".', self::class)); + } + $builder->addModelTransformer(new DatePointToDateTimeTransformer()); + } elseif ('datetime_immutable' === $options['input']) { $builder->addModelTransformer(new DateTimeImmutableToDateTimeTransformer()); } elseif ('string' === $options['input']) { $builder->addModelTransformer(new ReversedTransformer( @@ -172,7 +179,7 @@ public function buildForm(FormBuilderInterface $builder, array $options): void )); } - if (\in_array($options['input'], ['datetime', 'datetime_immutable'], true) && null !== $options['model_timezone']) { + if (\in_array($options['input'], ['datetime', 'datetime_immutable', 'date_point'], true) && null !== $options['model_timezone']) { $builder->addEventListener(FormEvents::POST_SET_DATA, static function (FormEvent $event) use ($options): void { $date = $event->getData(); @@ -298,6 +305,7 @@ public function configureOptions(OptionsResolver $resolver): void $resolver->setAllowedValues('input', [ 'datetime', 'datetime_immutable', + 'date_point', 'string', 'timestamp', 'array', diff --git a/src/Symfony/Component/Form/Extension/Core/Type/TimeType.php b/src/Symfony/Component/Form/Extension/Core/Type/TimeType.php index 92cf42d963e74..1622301aed631 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/TimeType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/TimeType.php @@ -11,9 +11,11 @@ namespace Symfony\Component\Form\Extension\Core\Type; +use Symfony\Component\Clock\DatePoint; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\Exception\InvalidConfigurationException; use Symfony\Component\Form\Exception\LogicException; +use Symfony\Component\Form\Extension\Core\DataTransformer\DatePointToDateTimeTransformer; use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeImmutableToDateTimeTransformer; use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToArrayTransformer; use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToStringTransformer; @@ -190,7 +192,12 @@ public function buildForm(FormBuilderInterface $builder, array $options): void $builder->addViewTransformer(new DateTimeToArrayTransformer($options['model_timezone'], $options['view_timezone'], $parts, 'text' === $options['widget'], $options['reference_date'])); } - if ('datetime_immutable' === $options['input']) { + if ('date_point' === $options['input']) { + if (!class_exists(DatePoint::class)) { + throw new LogicException(\sprintf('The "symfony/clock" component is required to use "%s" with option "input=date_point". Try running "composer require symfony/clock".', self::class)); + } + $builder->addModelTransformer(new DatePointToDateTimeTransformer()); + } elseif ('datetime_immutable' === $options['input']) { $builder->addModelTransformer(new DateTimeImmutableToDateTimeTransformer()); } elseif ('string' === $options['input']) { $builder->addModelTransformer(new ReversedTransformer( @@ -206,7 +213,7 @@ public function buildForm(FormBuilderInterface $builder, array $options): void )); } - if (\in_array($options['input'], ['datetime', 'datetime_immutable'], true) && null !== $options['model_timezone']) { + if (\in_array($options['input'], ['datetime', 'datetime_immutable', 'date_point'], true) && null !== $options['model_timezone']) { $builder->addEventListener(FormEvents::POST_SET_DATA, static function (FormEvent $event) use ($options): void { $date = $event->getData(); @@ -354,6 +361,7 @@ public function configureOptions(OptionsResolver $resolver): void $resolver->setAllowedValues('input', [ 'datetime', 'datetime_immutable', + 'date_point', 'string', 'timestamp', 'array', diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/DateTimeTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/DateTimeTypeTest.php index 5067bb05e7258..e655af51f7cef 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/DateTimeTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/DateTimeTypeTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Form\Tests\Extension\Core\Type; +use Symfony\Component\Clock\DatePoint; use Symfony\Component\Form\Exception\LogicException; use Symfony\Component\Form\Extension\Core\Type\DateTimeType; use Symfony\Component\Form\FormError; @@ -62,6 +63,37 @@ public function testSubmitDateTime() $this->assertEquals($dateTime, $form->getData()); } + public function testSubmitDatePoint() + { + $form = $this->factory->create(static::TESTED_TYPE, null, [ + 'model_timezone' => 'UTC', + 'view_timezone' => 'UTC', + 'date_widget' => 'choice', + 'years' => [2010], + 'time_widget' => 'choice', + 'input' => 'date_point', + ]); + + $input = [ + 'date' => [ + 'day' => '2', + 'month' => '6', + 'year' => '2010', + ], + 'time' => [ + 'hour' => '3', + 'minute' => '4', + ], + ]; + + $form->submit($input); + + $this->assertInstanceOf(DatePoint::class, $form->getData()); + $datePoint = DatePoint::createFromMutable(new \DateTime('2010-06-02 03:04:00 UTC')); + $this->assertEquals($datePoint, $form->getData()); + $this->assertEquals($input, $form->getViewData()); + } + public function testSubmitDateTimeImmutable() { $form = $this->factory->create(static::TESTED_TYPE, null, [ diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/DateTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/DateTypeTest.php index 5f4f896b5daed..b2af6f4bf8b13 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/DateTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/DateTypeTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Form\Tests\Extension\Core\Type; +use Symfony\Component\Clock\DatePoint; use Symfony\Component\Form\ChoiceList\View\ChoiceView; use Symfony\Component\Form\Exception\LogicException; use Symfony\Component\Form\Extension\Core\Type\DateType; @@ -115,6 +116,27 @@ public function testSubmitFromSingleTextDateTime() $this->assertEquals('02.06.2010', $form->getViewData()); } + public function testSubmitFromSingleTextDatePoint() + { + if (!class_exists(DatePoint::class)) { + self::markTestSkipped('The DatePoint class is not available.'); + } + + $form = $this->factory->create(static::TESTED_TYPE, null, [ + 'html5' => false, + 'model_timezone' => 'UTC', + 'view_timezone' => 'UTC', + 'widget' => 'single_text', + 'input' => 'date_point', + ]); + + $form->submit('2010-06-02'); + + $this->assertInstanceOf(DatePoint::class, $form->getData()); + $this->assertEquals(DatePoint::createFromMutable(new \DateTime('2010-06-02 UTC')), $form->getData()); + $this->assertEquals('2010-06-02', $form->getViewData()); + } + public function testSubmitFromSingleTextDateTimeImmutable() { // we test against "de_DE", so we need the full implementation diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/TimeTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/TimeTypeTest.php index 8a2baf1b4c708..6711fa55b6322 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/TimeTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/TimeTypeTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Form\Tests\Extension\Core\Type; +use Symfony\Component\Clock\DatePoint; use Symfony\Component\Form\ChoiceList\View\ChoiceView; use Symfony\Component\Form\Exception\InvalidConfigurationException; use Symfony\Component\Form\Exception\LogicException; @@ -45,6 +46,32 @@ public function testSubmitDateTime() $this->assertEquals($input, $form->getViewData()); } + public function testSubmitDatePoint() + { + if (!class_exists(DatePoint::class)) { + self::markTestSkipped('The DatePoint class is not available.'); + } + + $form = $this->factory->create(static::TESTED_TYPE, null, [ + 'model_timezone' => 'UTC', + 'view_timezone' => 'UTC', + 'widget' => 'choice', + 'input' => 'date_point', + ]); + + $input = [ + 'hour' => '3', + 'minute' => '4', + ]; + + $form->submit($input); + + $this->assertInstanceOf(DatePoint::class, $form->getData()); + $datePoint = DatePoint::createFromMutable(new \DateTime('1970-01-01 03:04:00 UTC')); + $this->assertEquals($datePoint, $form->getData()); + $this->assertEquals($input, $form->getViewData()); + } + public function testSubmitDateTimeImmutable() { $form = $this->factory->create(static::TESTED_TYPE, null, [ diff --git a/src/Symfony/Component/Form/composer.json b/src/Symfony/Component/Form/composer.json index f4403ba74d878..d9539c79fd103 100644 --- a/src/Symfony/Component/Form/composer.json +++ b/src/Symfony/Component/Form/composer.json @@ -31,6 +31,7 @@ "symfony/validator": "^6.4|^7.0", "symfony/dependency-injection": "^6.4|^7.0", "symfony/expression-language": "^6.4|^7.0", + "symfony/clock": "^6.4|^7.0", "symfony/config": "^6.4|^7.0", "symfony/console": "^6.4|^7.0", "symfony/html-sanitizer": "^6.4|^7.0", From 8548aac4af8ac3f539568d3f3928e0d611a3e186 Mon Sep 17 00:00:00 2001 From: Adrian Rudnik Date: Tue, 22 Apr 2025 19:45:21 +0200 Subject: [PATCH 26/80] [Scheduler] Throw error on duplicate schedule provider service registration on the schedule name --- src/Symfony/Component/Scheduler/CHANGELOG.md | 1 + .../AddScheduleMessengerPass.php | 5 +++ .../RegisterProviderTest.php | 34 +++++++++++++++++++ 3 files changed, 40 insertions(+) create mode 100644 src/Symfony/Component/Scheduler/Tests/DependencyInjection/RegisterProviderTest.php diff --git a/src/Symfony/Component/Scheduler/CHANGELOG.md b/src/Symfony/Component/Scheduler/CHANGELOG.md index 67512476a7a8e..26067e3589104 100644 --- a/src/Symfony/Component/Scheduler/CHANGELOG.md +++ b/src/Symfony/Component/Scheduler/CHANGELOG.md @@ -5,6 +5,7 @@ CHANGELOG --- * Add `TriggerNormalizer` + * Throw exception when multiple schedule provider services are registered under the same scheduler name 7.2 --- diff --git a/src/Symfony/Component/Scheduler/DependencyInjection/AddScheduleMessengerPass.php b/src/Symfony/Component/Scheduler/DependencyInjection/AddScheduleMessengerPass.php index 696422e0d28da..64880149244e1 100644 --- a/src/Symfony/Component/Scheduler/DependencyInjection/AddScheduleMessengerPass.php +++ b/src/Symfony/Component/Scheduler/DependencyInjection/AddScheduleMessengerPass.php @@ -46,6 +46,11 @@ public function process(ContainerBuilder $container): void $scheduleProviderIds = []; foreach ($container->findTaggedServiceIds('scheduler.schedule_provider') as $serviceId => $tags) { $name = $tags[0]['name']; + + if (isset($scheduleProviderIds[$name])) { + throw new InvalidArgumentException(\sprintf('Schedule provider service "%s" can not replace already registered service "%s" for schedule "%s". Make sure to register only one provider per schedule name.', $serviceId, $scheduleProviderIds[$name], $name), 1); + } + $scheduleProviderIds[$name] = $serviceId; } diff --git a/src/Symfony/Component/Scheduler/Tests/DependencyInjection/RegisterProviderTest.php b/src/Symfony/Component/Scheduler/Tests/DependencyInjection/RegisterProviderTest.php new file mode 100644 index 0000000000000..8bbfe41f71be4 --- /dev/null +++ b/src/Symfony/Component/Scheduler/Tests/DependencyInjection/RegisterProviderTest.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Scheduler\Tests\DependencyInjection; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; +use Symfony\Component\Scheduler\DependencyInjection\AddScheduleMessengerPass; +use Symfony\Component\Scheduler\Tests\Fixtures\SomeScheduleProvider; + +class RegisterProviderTest extends TestCase +{ + public function testErrorOnMultipleProvidersForTheSameSchedule() + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionCode(1); + + $container = new ContainerBuilder(); + + $container->register('provider_a', SomeScheduleProvider::class)->addTag('scheduler.schedule_provider', ['name' => 'default']); + $container->register('provider_b', SomeScheduleProvider::class)->addTag('scheduler.schedule_provider', ['name' => 'default']); + + (new AddScheduleMessengerPass())->process($container); + } +} From a2e3d7c76f8072eff4a86e237949c25b9c16a588 Mon Sep 17 00:00:00 2001 From: rewrit3 Date: Fri, 30 May 2025 15:08:24 -0400 Subject: [PATCH 27/80] [Translation] Validate of pt_BR translation by a native speaker --- .../Validator/Resources/translations/validators.pt_BR.xlf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.pt_BR.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.pt_BR.xlf index a6be16580c6bd..0acf6dbf23a6c 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.pt_BR.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.pt_BR.xlf @@ -468,7 +468,7 @@ This value is not a valid Twig template. - Este valor não é um modelo Twig válido. + Este valor não é um modelo Twig válido. From 43a549b35c290085438a68b04a725cbc5cd20883 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Sat, 31 May 2025 00:01:35 +0200 Subject: [PATCH 28/80] switch to Composer 2 metadata The Composer 1 metadata are no longer up-to-date and the legacy API will be turned off in August anyway. --- .github/build-packages.php | 24 ++++++++++++++++++++---- .github/composer.json | 5 +++++ .github/workflows/unit-tests.yml | 10 ++++++++-- 3 files changed, 33 insertions(+), 6 deletions(-) create mode 100644 .github/composer.json diff --git a/.github/build-packages.php b/.github/build-packages.php index d69a3c8198ec0..dda58049ab842 100644 --- a/.github/build-packages.php +++ b/.github/build-packages.php @@ -1,5 +1,9 @@ $_SERVER['argc']) { echo "Usage: branch version dir1 dir2 ... dirN\n"; exit(1); @@ -52,11 +56,23 @@ $packages[$package->name][$package->version] = $package; - $versions = @file_get_contents('https://repo.packagist.org/p/'.$package->name.'.json') ?: sprintf('{"packages":{"%s":{"%s":%s}}}', $package->name, $package->version, file_get_contents($dir.'/composer.json')); - $versions = json_decode($versions)->packages->{$package->name}; + if (false !== $taggedReleases = @file_get_contents('https://repo.packagist.org/p2/'.$package->name.'.json')) { + $versions = MetadataMinifier::expand(json_decode($taggedReleases, true)['packages'][$package->name]); + + foreach ($versions as $v => $p) { + $packages[$package->name] += [$v => $p]; + } + } + + if (false !== $devReleases = @file_get_contents('https://repo.packagist.org/p2/'.$package->name.'~dev.json')) { + $versions = MetadataMinifier::expand(json_decode($taggedReleases, true)['packages'][$package->name]); + } else { + $versions = sprintf('{"packages":{"%s":{"%s":%s}}}', $package->name, $package->version, file_get_contents($dir.'/composer.json')); + $versions = json_decode($versions, true)['packages'][$package->name]; + } - foreach ($versions as $v => $package) { - $packages[$package->name] += [$v => $package]; + foreach ($versions as $v => $p) { + $packages[$package->name] += [$v => $p]; } } diff --git a/.github/composer.json b/.github/composer.json new file mode 100644 index 0000000000000..5bd3935482174 --- /dev/null +++ b/.github/composer.json @@ -0,0 +1,5 @@ +{ + "require": { + "composer/metadata-minifier": "^1.0" + } +} diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index 8e4c8516dad81..a8b46ce823cc0 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -92,12 +92,18 @@ jobs: # Create local composer packages for each patched components and reference them in composer.json files when cross-testing components if [[ ! "${{ matrix.mode }}" = *-deps ]]; then - php .github/build-packages.php HEAD^ $SYMFONY_VERSION src/Symfony/Bridge/PhpUnit + cd .github + composer install + php ./build-packages.php HEAD^ $SYMFONY_VERSION src/Symfony/Bridge/PhpUnit + cd .. else echo SYMFONY_DEPRECATIONS_HELPER=weak >> $GITHUB_ENV cp composer.json composer.json.orig echo -e '{\n"require":{'"$(grep phpunit-bridge composer.json)"'"php":"*"},"minimum-stability":"dev"}' > composer.json - php .github/build-packages.php HEAD^ $SYMFONY_VERSION $(find src/Symfony -mindepth 2 -type f -name composer.json -printf '%h\n' | grep -v src/Symfony/Component/Intl/Resources/emoji) + cd .github + composer install + php ./build-packages.php HEAD^ $SYMFONY_VERSION $(find src/Symfony -mindepth 2 -type f -name composer.json -printf '%h\n' | grep -v src/Symfony/Component/Intl/Resources/emoji) + cd .. mv composer.json composer.json.phpunit mv composer.json.orig composer.json fi From 5440dad7702eb081e6f76a1d83526a2cf00a7619 Mon Sep 17 00:00:00 2001 From: Adam Date: Sat, 31 May 2025 13:46:05 +0200 Subject: [PATCH 29/80] [HttpKernel] Fix Symfony 7.3 end of maintenance date --- src/Symfony/Component/HttpKernel/Kernel.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index dfee565d068cb..10e2512cc0629 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -80,7 +80,7 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl public const RELEASE_VERSION = 1; public const EXTRA_VERSION = 'DEV'; - public const END_OF_MAINTENANCE = '05/2025'; + public const END_OF_MAINTENANCE = '01/2026'; public const END_OF_LIFE = '01/2026'; public function __construct( From ae19577ad326e39962e9acdc45822f9ae1e35809 Mon Sep 17 00:00:00 2001 From: HypeMC Date: Sat, 31 May 2025 17:20:52 +0200 Subject: [PATCH 30/80] [WebProfilerBundle] Fix toolbar with ajax requests not closing --- .../Resources/views/Profiler/toolbar_js.html.twig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar_js.html.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar_js.html.twig index 91e6dc05e658c..5adfd27796acf 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar_js.html.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar_js.html.twig @@ -144,7 +144,7 @@ var ajaxToolbarPanel = document.querySelector('.sf-toolbar-block-ajax'); if (requestStack.length) { - ajaxToolbarPanel.style.display = 'block'; + ajaxToolbarPanel.style.display = ''; } else { ajaxToolbarPanel.style.display = 'none'; } From 0bd8eb210350538bb4e74d5aa7bc1912c83f4c37 Mon Sep 17 00:00:00 2001 From: matlec Date: Sun, 1 Jun 2025 15:12:01 +0200 Subject: [PATCH 31/80] [SecurityBundle] Remove `AnonymousFactory` leftovers --- .../SecurityBundle/DependencyInjection/SecurityExtension.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php index 383f68d203aca..d75a1d8fe63e1 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php @@ -334,7 +334,7 @@ private function createFirewalls(array $config, ContainerBuilder $container): vo $authenticators[$name] = ServiceLocatorTagPass::register($container, $firewallAuthenticatorRefs); } $contextId = 'security.firewall.map.context.'.$name; - $isLazy = !$firewall['stateless'] && (!empty($firewall['anonymous']['lazy']) || $firewall['lazy']); + $isLazy = !$firewall['stateless'] && $firewall['lazy']; $context = new ChildDefinition($isLazy ? 'security.firewall.lazy_context' : 'security.firewall.context'); $context = $container->setDefinition($contextId, $context); $context @@ -685,7 +685,7 @@ private function getUserProvider(ContainerBuilder $container, string $id, array return $this->createMissingUserProvider($container, $id, $factoryKey); } - if ('remember_me' === $factoryKey || 'anonymous' === $factoryKey || 'custom_authenticators' === $factoryKey) { + if ('remember_me' === $factoryKey || 'custom_authenticators' === $factoryKey) { if ('custom_authenticators' === $factoryKey) { trigger_deprecation('symfony/security-bundle', '5.4', 'Not configuring explicitly the provider for the "%s" firewall is deprecated because it\'s ambiguous as there is more than one registered provider. Set the "provider" key to one of the configured providers, even if your custom authenticators don\'t use it.', $id); } From c8082a94d7e7a35311ff6f6cc98d8dffc2290298 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Sun, 1 Jun 2025 21:36:09 +0200 Subject: [PATCH 32/80] skip interactive questions asked by Composer following #60587 so that the installation script is not blocked by Composer asking to install the bridge as a dev dependency: ``` The package you required is recommended to be placed in require-dev (because it is tagged as "testing") but you did not use --dev. Do you want to re-run the command with --dev? [yes]? ``` --- src/Symfony/Bridge/PhpUnit/bin/simple-phpunit.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit.php b/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit.php index 45ad2b636e878..ad6da8a2e9237 100644 --- a/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit.php +++ b/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit.php @@ -237,7 +237,7 @@ $passthruOrFail("$COMPOSER require --no-update --no-interaction ".$SYMFONY_PHPUNIT_REQUIRE); } if (5.1 <= $PHPUNIT_VERSION && $PHPUNIT_VERSION < 5.4) { - $passthruOrFail("$COMPOSER require --no-update phpunit/phpunit-mock-objects \"~3.1.0\""); + $passthruOrFail("$COMPOSER require --no-update --no-interaction phpunit/phpunit-mock-objects \"~3.1.0\""); } if (preg_match('{\^((\d++\.)\d++)[\d\.]*$}', $info['requires']['php'], $phpVersion) && version_compare($phpVersion[2].'99', \PHP_VERSION, '<')) { @@ -253,13 +253,13 @@ if (realpath($p) === realpath($path)) { $path = $p; } - $passthruOrFail("$COMPOSER require --no-update symfony/phpunit-bridge \"*@dev\""); + $passthruOrFail("$COMPOSER require --no-update --no-interaction symfony/phpunit-bridge \"*@dev\""); $passthruOrFail("$COMPOSER config repositories.phpunit-bridge path ".escapeshellarg(str_replace('/', \DIRECTORY_SEPARATOR, $path))); if ('\\' === \DIRECTORY_SEPARATOR) { file_put_contents('composer.json', preg_replace('/^( {8})"phpunit-bridge": \{$/m', "$0\n$1 ".'"options": {"symlink": false},', file_get_contents('composer.json'))); } } else { - $passthruOrFail("$COMPOSER require --no-update symfony/phpunit-bridge \"*\""); + $passthruOrFail("$COMPOSER require --no-update --no-interaction symfony/phpunit-bridge \"*\""); } $prevRoot = getenv('COMPOSER_ROOT_VERSION'); putenv("COMPOSER_ROOT_VERSION=$PHPUNIT_VERSION.99"); From 65a8d6132ae9bea0dcceac9b736b8d3be77510fe Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Sun, 1 Jun 2025 22:50:36 +0200 Subject: [PATCH 33/80] pass log level instead of exception to resolve the logger --- .../Component/HttpKernel/EventListener/ErrorListener.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/EventListener/ErrorListener.php b/src/Symfony/Component/HttpKernel/EventListener/ErrorListener.php index 18e8bff4413d8..2599b27de0c97 100644 --- a/src/Symfony/Component/HttpKernel/EventListener/ErrorListener.php +++ b/src/Symfony/Component/HttpKernel/EventListener/ErrorListener.php @@ -161,15 +161,15 @@ public static function getSubscribedEvents(): array /** * Logs an exception. - * + * * @param ?string $logChannel */ protected function logException(\Throwable $exception, string $message, ?string $logLevel = null, /* ?string $logChannel = null */): void { $logChannel = (3 < \func_num_args() ? \func_get_arg(3) : null) ?? $this->resolveLogChannel($exception); - + $logLevel ??= $this->resolveLogLevel($exception); - + if(!$logger = $this->getLogger($logChannel)) { return; } @@ -218,7 +218,7 @@ protected function duplicateRequest(\Throwable $exception, Request $request): Re $attributes = [ '_controller' => $this->controller, 'exception' => $exception, - 'logger' => DebugLoggerConfigurator::getDebugLogger($this->getLogger($exception)), + 'logger' => DebugLoggerConfigurator::getDebugLogger($this->getLogger($this->resolveLogChannel($exception))), ]; $request = $request->duplicate(null, null, $attributes); $request->setMethod('GET'); From d17ac3d50b2504e942ae53de36676b0cd5be1655 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Sun, 1 Jun 2025 23:04:34 +0200 Subject: [PATCH 34/80] do not restrict experimental components to a single minor version --- src/Symfony/Bundle/FrameworkBundle/composer.json | 6 ++---- src/Symfony/Component/JsonPath/composer.json | 5 +---- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/composer.json b/src/Symfony/Bundle/FrameworkBundle/composer.json index 15a9496d11067..fa4ec0074a8ef 100644 --- a/src/Symfony/Bundle/FrameworkBundle/composer.json +++ b/src/Symfony/Bundle/FrameworkBundle/composer.json @@ -54,7 +54,7 @@ "symfony/messenger": "^6.4|^7.0", "symfony/mime": "^6.4|^7.0", "symfony/notifier": "^6.4|^7.0", - "symfony/object-mapper": "^v7.3.0-beta2", + "symfony/object-mapper": "^7.3", "symfony/process": "^6.4|^7.0", "symfony/rate-limiter": "^6.4|^7.0", "symfony/scheduler": "^6.4.4|^7.0.4", @@ -70,7 +70,7 @@ "symfony/workflow": "^7.3", "symfony/yaml": "^6.4|^7.0", "symfony/property-info": "^6.4|^7.0", - "symfony/json-streamer": "7.3.*", + "symfony/json-streamer": "^7.3", "symfony/uid": "^6.4|^7.0", "symfony/web-link": "^6.4|^7.0", "symfony/webhook": "^7.2", @@ -89,12 +89,10 @@ "symfony/dom-crawler": "<6.4", "symfony/http-client": "<6.4", "symfony/form": "<6.4", - "symfony/json-streamer": ">=7.4", "symfony/lock": "<6.4", "symfony/mailer": "<6.4", "symfony/messenger": "<6.4", "symfony/mime": "<6.4", - "symfony/object-mapper": ">=7.4", "symfony/property-info": "<6.4", "symfony/property-access": "<6.4", "symfony/runtime": "<6.4.13|>=7.0,<7.1.6", diff --git a/src/Symfony/Component/JsonPath/composer.json b/src/Symfony/Component/JsonPath/composer.json index fe8ddf84dd82d..95b02675e7459 100644 --- a/src/Symfony/Component/JsonPath/composer.json +++ b/src/Symfony/Component/JsonPath/composer.json @@ -20,10 +20,7 @@ "symfony/polyfill-mbstring": "~1.0" }, "require-dev": { - "symfony/json-streamer": "7.3.*" - }, - "conflict": { - "symfony/json-streamer": ">=7.4" + "symfony/json-streamer": "^7.3" }, "autoload": { "psr-4": { "Symfony\\Component\\JsonPath\\": "" }, From d9254c8652b3d2812191e2e2735bfdd8a29084a4 Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Mon, 2 Jun 2025 09:19:08 +0200 Subject: [PATCH 35/80] Add table of contents in `UPGRADE-7.3.md` --- UPGRADE-7.3.md | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/UPGRADE-7.3.md b/UPGRADE-7.3.md index 5c279372b7626..5fa4d18677279 100644 --- a/UPGRADE-7.3.md +++ b/UPGRADE-7.3.md @@ -8,6 +8,37 @@ Read more about this in the [Symfony documentation](https://symfony.com/doc/7.3/ If you're upgrading from a version below 7.2, follow the [7.2 upgrade guide](UPGRADE-7.2.md) first. +Table of Contents +----------------- + +Bundles + + * [FrameworkBundle](#FrameworkBundle) + * [SecurityBundle](#SecurityBundle) + * [WebProfilerBundle](#WebProfilerBundle) + +Bridges + + * [DoctrineBridge](#DoctrineBridge) + +Components + + * [AssetMapper](#AssetMapper) + * [Console](#Console) + * [DependencyInjection](#DependencyInjection) + * [HttpFoundation](#HttpFoundation) + * [Ldap](#Ldap) + * [OptionsResolver](#OptionsResolver) + * [PropertyInfo](#PropertyInfo) + * [Security](#Security) + * [Notifier](#Notifier) + * [Serializer](#Serializer) + * [TypeInfo](#TypeInfo) + * [Validator](#Validator) + * [VarDumper](#VarDumper) + * [VarExporter](#VarExporter) + * [Workflow](#Workflow) + AssetMapper ----------- @@ -193,8 +224,8 @@ SecurityBundle * Deprecate the `security.hide_user_not_found` config option in favor of `security.expose_security_errors` - Notifier - -------- +Notifier +-------- * Deprecate the `Sms77` transport, use `SevenIo` instead From ad74742d6ad5e602b5b46a66ab247e0a912521e1 Mon Sep 17 00:00:00 2001 From: matlec Date: Mon, 2 Jun 2025 10:17:40 +0200 Subject: [PATCH 36/80] [Ldap] Fix `LdapUser::isEqualTo` --- .../Component/Ldap/Security/LdapUser.php | 4 +-- .../Ldap/Tests/Security/LdapUserTest.php | 27 +++++++++++++++++++ 2 files changed, 29 insertions(+), 2 deletions(-) create mode 100644 src/Symfony/Component/Ldap/Tests/Security/LdapUserTest.php diff --git a/src/Symfony/Component/Ldap/Security/LdapUser.php b/src/Symfony/Component/Ldap/Security/LdapUser.php index ef73b82422d0b..020fcb5441596 100644 --- a/src/Symfony/Component/Ldap/Security/LdapUser.php +++ b/src/Symfony/Component/Ldap/Security/LdapUser.php @@ -47,7 +47,7 @@ public function getRoles(): array public function getPassword(): ?string { - return $this->password; + return $this->password ?? null; } public function getSalt(): ?string @@ -89,7 +89,7 @@ public function isEqualTo(UserInterface $user): bool return false; } - if ($this->getPassword() !== $user->getPassword()) { + if (($this->getPassword() ?? $user->getPassword()) !== $user->getPassword()) { return false; } diff --git a/src/Symfony/Component/Ldap/Tests/Security/LdapUserTest.php b/src/Symfony/Component/Ldap/Tests/Security/LdapUserTest.php new file mode 100644 index 0000000000000..0a696bcd0c29d --- /dev/null +++ b/src/Symfony/Component/Ldap/Tests/Security/LdapUserTest.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Ldap\Tests\Security; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Ldap\Entry; +use Symfony\Component\Ldap\Security\LdapUser; + +class LdapUserTest extends TestCase +{ + public function testIsEqualToWorksOnUnserializedUser() + { + $user = new LdapUser(new Entry('uid=jonhdoe,ou=MyBusiness,dc=symfony,dc=com', []), 'jonhdoe', 'p455w0rd'); + $unserializedUser = unserialize(serialize($user)); + + $this->assertTrue($unserializedUser->isEqualTo($user)); + } +} From 81854a5f59633c9efe1ced347bac85f17bdfe32f Mon Sep 17 00:00:00 2001 From: Indra Gunawan Date: Mon, 2 Jun 2025 16:16:20 +0800 Subject: [PATCH 37/80] [FrameworkBundle] set NamespacedPoolInterface alias to cache.app --- .../DependencyInjection/FrameworkExtension.php | 4 ++++ src/Symfony/Bundle/FrameworkBundle/Resources/config/cache.php | 3 +++ 2 files changed, 7 insertions(+) diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index 347f3ed653c87..64d27bca9d63b 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -302,6 +302,10 @@ public function load(array $configs, ContainerBuilder $container): void // Load Cache configuration first as it is used by other components $loader->load('cache.php'); + if (!interface_exists(NamespacedPoolInterface::class)) { + $container->removeAlias(NamespacedPoolInterface::class); + } + $configuration = $this->getConfiguration($configs, $container); $config = $this->processConfiguration($configuration, $configs); diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/cache.php b/src/Symfony/Bundle/FrameworkBundle/Resources/config/cache.php index 3d96ba05994ca..ae9d426a498c6 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/cache.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/cache.php @@ -28,6 +28,7 @@ use Symfony\Component\Cache\Messenger\EarlyExpirationHandler; use Symfony\Component\HttpKernel\CacheClearer\Psr6CacheClearer; use Symfony\Contracts\Cache\CacheInterface; +use Symfony\Contracts\Cache\NamespacedPoolInterface; use Symfony\Contracts\Cache\TagAwareCacheInterface; return static function (ContainerConfigurator $container) { @@ -250,6 +251,8 @@ ->alias(CacheInterface::class, 'cache.app') + ->alias(NamespacedPoolInterface::class, 'cache.app') + ->alias(TagAwareCacheInterface::class, 'cache.app.taggable') ; }; From e3513bdf48ca74d89b000b42d0037ca9bb116e2c Mon Sep 17 00:00:00 2001 From: Benjamin Morel Date: Thu, 22 May 2025 09:35:49 +0200 Subject: [PATCH 38/80] Allow query-specific parameters in URL generator using `_query` --- src/Symfony/Component/Routing/CHANGELOG.md | 5 ++ .../Routing/Generator/UrlGenerator.php | 13 ++++ .../Tests/Generator/UrlGeneratorTest.php | 74 +++++++++++++++++++ 3 files changed, 92 insertions(+) diff --git a/src/Symfony/Component/Routing/CHANGELOG.md b/src/Symfony/Component/Routing/CHANGELOG.md index d21e550f9b57f..4ef96d53232fe 100644 --- a/src/Symfony/Component/Routing/CHANGELOG.md +++ b/src/Symfony/Component/Routing/CHANGELOG.md @@ -1,6 +1,11 @@ CHANGELOG ========= +7.4 +--- + + * Allow query-specific parameters in `UrlGenerator` using `_query` + 7.3 --- diff --git a/src/Symfony/Component/Routing/Generator/UrlGenerator.php b/src/Symfony/Component/Routing/Generator/UrlGenerator.php index 216b0d5479ac4..d82b91898194a 100644 --- a/src/Symfony/Component/Routing/Generator/UrlGenerator.php +++ b/src/Symfony/Component/Routing/Generator/UrlGenerator.php @@ -142,6 +142,18 @@ public function generate(string $name, array $parameters = [], int $referenceTyp */ protected function doGenerate(array $variables, array $defaults, array $requirements, array $tokens, array $parameters, string $name, int $referenceType, array $hostTokens, array $requiredSchemes = []): string { + $queryParameters = []; + + if (isset($parameters['_query'])) { + if (\is_array($parameters['_query'])) { + $queryParameters = $parameters['_query']; + unset($parameters['_query']); + } else { + trigger_deprecation('symfony/routing', '7.4', 'Parameter "_query" is reserved for passing an array of query parameters. Passing a scalar value is deprecated and will throw an exception in Symfony 8.0.'); + // throw new InvalidParameterException('Parameter "_query" must be an array of query parameters.'); + } + } + $variables = array_flip($variables); $mergedParams = array_replace($defaults, $this->context->getParameters(), $parameters); @@ -260,6 +272,7 @@ protected function doGenerate(array $variables, array $defaults, array $requirem // add a query string if needed $extra = array_udiff_assoc(array_diff_key($parameters, $variables), $defaults, fn ($a, $b) => $a == $b ? 0 : 1); + $extra = array_merge($extra, $queryParameters); array_walk_recursive($extra, $caster = static function (&$v) use (&$caster) { if (\is_object($v)) { diff --git a/src/Symfony/Component/Routing/Tests/Generator/UrlGeneratorTest.php b/src/Symfony/Component/Routing/Tests/Generator/UrlGeneratorTest.php index 25a4c67460c82..27af767947e16 100644 --- a/src/Symfony/Component/Routing/Tests/Generator/UrlGeneratorTest.php +++ b/src/Symfony/Component/Routing/Tests/Generator/UrlGeneratorTest.php @@ -1054,6 +1054,80 @@ public function testUtf8VarName() $this->assertSame('/app.php/foo/baz', $this->getGenerator($routes)->generate('test', ['bär' => 'baz'])); } + public function testQueryParameters() + { + $routes = $this->getRoutes('user', new Route('/user/{username}')); + $url = $this->getGenerator($routes)->generate('user', [ + 'username' => 'john', + 'a' => 'foo', + 'b' => 'bar', + 'c' => 'baz', + '_query' => [ + 'a' => '123', + 'd' => '789', + ], + ]); + $this->assertSame('/app.php/user/john?a=123&b=bar&c=baz&d=789', $url); + } + + public function testRouteHostParameterAndQueryParameterWithSameName() + { + $routes = $this->getRoutes('admin_stats', new Route('/admin/stats', requirements: ['domain' => '.+'], host: '{siteCode}.{domain}')); + $url = $this->getGenerator($routes)->generate('admin_stats', [ + 'siteCode' => 'fr', + 'domain' => 'example.com', + '_query' => [ + 'siteCode' => 'us', + ], + ], UrlGeneratorInterface::NETWORK_PATH); + $this->assertSame('//fr.example.com/app.php/admin/stats?siteCode=us', $url); + } + + public function testRoutePathParameterAndQueryParameterWithSameName() + { + $routes = $this->getRoutes('user', new Route('/user/{id}')); + $url = $this->getGenerator($routes)->generate('user', [ + 'id' => '123', + '_query' => [ + 'id' => '456', + ], + ]); + $this->assertSame('/app.php/user/123?id=456', $url); + } + + public function testQueryParameterCannotSubstituteRouteParameter() + { + $routes = $this->getRoutes('user', new Route('/user/{id}')); + + $this->expectException(MissingMandatoryParametersException::class); + $this->expectExceptionMessage('Some mandatory parameters are missing ("id") to generate a URL for route "user".'); + + $this->getGenerator($routes)->generate('user', [ + '_query' => [ + 'id' => '456', + ], + ]); + } + + /** + * @group legacy + */ + public function testQueryParametersWithScalarValue() + { + $routes = $this->getRoutes('user', new Route('/user/{id}')); + + $this->expectDeprecation( + 'Since symfony/routing 7.4: Parameter "_query" is reserved for passing an array of query parameters. ' . + 'Passing a scalar value is deprecated and will throw an exception in Symfony 8.0.', + ); + + $url = $this->getGenerator($routes)->generate('user', [ + 'id' => '123', + '_query' => 'foo', + ]); + $this->assertSame('/app.php/user/123?_query=foo', $url); + } + protected function getGenerator(RouteCollection $routes, array $parameters = [], $logger = null, ?string $defaultLocale = null) { $context = new RequestContext('/app.php'); From c0783c3c0e3c44426d256258e23fea0a8070eb96 Mon Sep 17 00:00:00 2001 From: Mathias Arlaud Date: Mon, 2 Jun 2025 14:14:48 +0200 Subject: [PATCH 39/80] [TypeInfo] Fix merging collection value types with union types --- .../TypeResolver/StringTypeResolverTest.php | 1 + .../TypeInfo/Type/CollectionType.php | 30 ++++++++++--------- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/src/Symfony/Component/TypeInfo/Tests/TypeResolver/StringTypeResolverTest.php b/src/Symfony/Component/TypeInfo/Tests/TypeResolver/StringTypeResolverTest.php index fcfe909cecf6e..a87c7b37f3f8e 100644 --- a/src/Symfony/Component/TypeInfo/Tests/TypeResolver/StringTypeResolverTest.php +++ b/src/Symfony/Component/TypeInfo/Tests/TypeResolver/StringTypeResolverTest.php @@ -79,6 +79,7 @@ public static function resolveDataProvider(): iterable yield [Type::arrayShape(['foo' => Type::bool()], sealed: false), 'array{foo: bool, ...}']; yield [Type::arrayShape(['foo' => Type::bool()], extraKeyType: Type::int(), extraValueType: Type::string()), 'array{foo: bool, ...}']; yield [Type::arrayShape(['foo' => Type::bool()], extraValueType: Type::int()), 'array{foo: bool, ...}']; + yield [Type::arrayShape(['foo' => Type::union(Type::bool(), Type::float(), Type::int(), Type::null(), Type::string()), 'bar' => Type::string()]), 'array{foo: scalar|null, bar: string}']; // object shape yield [Type::object(), 'object{foo: true, bar: false}']; diff --git a/src/Symfony/Component/TypeInfo/Type/CollectionType.php b/src/Symfony/Component/TypeInfo/Type/CollectionType.php index 80fbbdba6c3fa..a801f2b51f8d0 100644 --- a/src/Symfony/Component/TypeInfo/Type/CollectionType.php +++ b/src/Symfony/Component/TypeInfo/Type/CollectionType.php @@ -65,25 +65,27 @@ public static function mergeCollectionValueTypes(array $types): Type $boolTypes = []; $objectTypes = []; - foreach ($types as $t) { - // cannot create an union with a standalone type - if ($t->isIdentifiedBy(TypeIdentifier::MIXED)) { - return Type::mixed(); - } + foreach ($types as $type) { + foreach (($type instanceof UnionType ? $type->getTypes() : [$type]) as $t) { + // cannot create an union with a standalone type + if ($t->isIdentifiedBy(TypeIdentifier::MIXED)) { + return Type::mixed(); + } - if ($t->isIdentifiedBy(TypeIdentifier::TRUE, TypeIdentifier::FALSE, TypeIdentifier::BOOL)) { - $boolTypes[] = $t; + if ($t->isIdentifiedBy(TypeIdentifier::TRUE, TypeIdentifier::FALSE, TypeIdentifier::BOOL)) { + $boolTypes[] = $t; - continue; - } + continue; + } - if ($t->isIdentifiedBy(TypeIdentifier::OBJECT)) { - $objectTypes[] = $t; + if ($t->isIdentifiedBy(TypeIdentifier::OBJECT)) { + $objectTypes[] = $t; - continue; - } + continue; + } - $normalizedTypes[] = $t; + $normalizedTypes[] = $t; + } } $boolTypes = array_unique($boolTypes); From a2eeadca8b5f7bba98adf89db86e74cccdec2ce8 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Mon, 2 Jun 2025 16:08:14 +0200 Subject: [PATCH 40/80] Allow Symfony ^8.0 --- composer.json | 2 +- src/Symfony/Bridge/Doctrine/composer.json | 36 +++---- src/Symfony/Bridge/Monolog/composer.json | 16 ++-- src/Symfony/Bridge/PhpUnit/composer.json | 2 +- .../Bridge/PsrHttpMessage/composer.json | 12 +-- src/Symfony/Bridge/Twig/composer.json | 50 +++++----- src/Symfony/Bundle/DebugBundle/composer.json | 12 +-- .../Bundle/FrameworkBundle/composer.json | 94 +++++++++---------- .../Bundle/SecurityBundle/composer.json | 56 +++++------ src/Symfony/Bundle/TwigBundle/composer.json | 30 +++--- .../Bundle/WebProfilerBundle/composer.json | 18 ++-- src/Symfony/Component/Asset/composer.json | 6 +- .../Component/AssetMapper/composer.json | 22 ++--- .../Component/BrowserKit/composer.json | 10 +- src/Symfony/Component/Cache/composer.json | 16 ++-- src/Symfony/Component/Config/composer.json | 10 +- src/Symfony/Component/Console/composer.json | 22 ++--- .../DependencyInjection/composer.json | 8 +- .../Component/DomCrawler/composer.json | 2 +- src/Symfony/Component/Dotenv/composer.json | 4 +- .../Emoji/Resources/bin/composer.json | 6 +- src/Symfony/Component/Emoji/composer.json | 6 +- .../Component/ErrorHandler/composer.json | 8 +- .../Component/EventDispatcher/composer.json | 12 +-- .../ExpressionLanguage/composer.json | 2 +- .../Component/Filesystem/composer.json | 2 +- src/Symfony/Component/Finder/composer.json | 2 +- src/Symfony/Component/Form/composer.json | 36 +++---- .../Component/HttpClient/composer.json | 12 +-- .../Component/HttpFoundation/composer.json | 14 +-- .../Component/HttpKernel/composer.json | 44 ++++----- src/Symfony/Component/Intl/composer.json | 4 +- src/Symfony/Component/JsonPath/composer.json | 2 +- .../Component/JsonStreamer/composer.json | 10 +- src/Symfony/Component/Ldap/composer.json | 6 +- .../Mailer/Bridge/AhaSend/composer.json | 6 +- .../Mailer/Bridge/Amazon/composer.json | 4 +- .../Mailer/Bridge/Azure/composer.json | 4 +- .../Mailer/Bridge/Brevo/composer.json | 8 +- .../Mailer/Bridge/Google/composer.json | 4 +- .../Mailer/Bridge/Infobip/composer.json | 6 +- .../Mailer/Bridge/MailPace/composer.json | 4 +- .../Mailer/Bridge/Mailchimp/composer.json | 6 +- .../Mailer/Bridge/MailerSend/composer.json | 6 +- .../Mailer/Bridge/Mailgun/composer.json | 6 +- .../Mailer/Bridge/Mailjet/composer.json | 6 +- .../Mailer/Bridge/Mailomat/composer.json | 8 +- .../Mailer/Bridge/Mailtrap/composer.json | 6 +- .../Mailer/Bridge/Postal/composer.json | 4 +- .../Mailer/Bridge/Postmark/composer.json | 6 +- .../Mailer/Bridge/Resend/composer.json | 10 +- .../Mailer/Bridge/Scaleway/composer.json | 6 +- .../Mailer/Bridge/Sendgrid/composer.json | 6 +- .../Mailer/Bridge/Sweego/composer.json | 10 +- src/Symfony/Component/Mailer/composer.json | 12 +-- .../Messenger/Bridge/AmazonSqs/composer.json | 6 +- .../Messenger/Bridge/Amqp/composer.json | 10 +- .../Messenger/Bridge/Beanstalkd/composer.json | 6 +- .../Messenger/Bridge/Doctrine/composer.json | 6 +- .../Messenger/Bridge/Redis/composer.json | 6 +- src/Symfony/Component/Messenger/composer.json | 26 ++--- src/Symfony/Component/Mime/composer.json | 10 +- .../Notifier/Bridge/AllMySms/composer.json | 4 +- .../Notifier/Bridge/AmazonSns/composer.json | 4 +- .../Notifier/Bridge/Bandwidth/composer.json | 6 +- .../Notifier/Bridge/Bluesky/composer.json | 10 +- .../Notifier/Bridge/Brevo/composer.json | 6 +- .../Notifier/Bridge/Chatwork/composer.json | 4 +- .../Notifier/Bridge/ClickSend/composer.json | 6 +- .../Notifier/Bridge/Clickatell/composer.json | 4 +- .../Bridge/ContactEveryone/composer.json | 4 +- .../Notifier/Bridge/Discord/composer.json | 4 +- .../Notifier/Bridge/Engagespot/composer.json | 4 +- .../Notifier/Bridge/Esendex/composer.json | 4 +- .../Notifier/Bridge/Expo/composer.json | 4 +- .../Notifier/Bridge/FakeChat/composer.json | 6 +- .../Notifier/Bridge/FakeSms/composer.json | 6 +- .../Notifier/Bridge/Firebase/composer.json | 4 +- .../Bridge/FortySixElks/composer.json | 4 +- .../Notifier/Bridge/FreeMobile/composer.json | 4 +- .../Notifier/Bridge/GatewayApi/composer.json | 4 +- .../Notifier/Bridge/GoIp/composer.json | 4 +- .../Notifier/Bridge/GoogleChat/composer.json | 4 +- .../Notifier/Bridge/Infobip/composer.json | 4 +- .../Notifier/Bridge/Iqsms/composer.json | 4 +- .../Notifier/Bridge/Isendpro/composer.json | 6 +- .../Notifier/Bridge/JoliNotif/composer.json | 4 +- .../Notifier/Bridge/KazInfoTeh/composer.json | 4 +- .../Notifier/Bridge/LightSms/composer.json | 4 +- .../Notifier/Bridge/LineBot/composer.json | 6 +- .../Notifier/Bridge/LineNotify/composer.json | 6 +- .../Notifier/Bridge/LinkedIn/composer.json | 4 +- .../Notifier/Bridge/Lox24/composer.json | 8 +- .../Notifier/Bridge/Mailjet/composer.json | 4 +- .../Notifier/Bridge/Mastodon/composer.json | 6 +- .../Notifier/Bridge/Matrix/composer.json | 6 +- .../Notifier/Bridge/Mattermost/composer.json | 4 +- .../Notifier/Bridge/Mercure/composer.json | 2 +- .../Notifier/Bridge/MessageBird/composer.json | 4 +- .../Bridge/MessageMedia/composer.json | 4 +- .../Bridge/MicrosoftTeams/composer.json | 4 +- .../Notifier/Bridge/Mobyt/composer.json | 4 +- .../Notifier/Bridge/Novu/composer.json | 6 +- .../Notifier/Bridge/Ntfy/composer.json | 6 +- .../Notifier/Bridge/Octopush/composer.json | 4 +- .../Notifier/Bridge/OneSignal/composer.json | 4 +- .../Notifier/Bridge/OrangeSms/composer.json | 4 +- .../Notifier/Bridge/OvhCloud/composer.json | 4 +- .../Notifier/Bridge/PagerDuty/composer.json | 4 +- .../Notifier/Bridge/Plivo/composer.json | 6 +- .../Notifier/Bridge/Primotexto/composer.json | 4 +- .../Notifier/Bridge/Pushover/composer.json | 6 +- .../Notifier/Bridge/Pushy/composer.json | 6 +- .../Notifier/Bridge/Redlink/composer.json | 4 +- .../Notifier/Bridge/RingCentral/composer.json | 6 +- .../Notifier/Bridge/RocketChat/composer.json | 4 +- .../Notifier/Bridge/Sendberry/composer.json | 4 +- .../Notifier/Bridge/Sevenio/composer.json | 4 +- .../Bridge/SimpleTextin/composer.json | 6 +- .../Notifier/Bridge/Sinch/composer.json | 4 +- .../Notifier/Bridge/Sipgate/composer.json | 4 +- .../Notifier/Bridge/Slack/composer.json | 4 +- .../Notifier/Bridge/Sms77/composer.json | 4 +- .../Notifier/Bridge/SmsBiuras/composer.json | 4 +- .../Notifier/Bridge/SmsFactor/composer.json | 4 +- .../Notifier/Bridge/SmsSluzba/composer.json | 8 +- .../Notifier/Bridge/Smsapi/composer.json | 4 +- .../Notifier/Bridge/Smsbox/composer.json | 8 +- .../Notifier/Bridge/Smsc/composer.json | 4 +- .../Notifier/Bridge/Smsense/composer.json | 4 +- .../Notifier/Bridge/Smsmode/composer.json | 6 +- .../Notifier/Bridge/SpotHit/composer.json | 4 +- .../Notifier/Bridge/Sweego/composer.json | 6 +- .../Notifier/Bridge/Telegram/composer.json | 6 +- .../Notifier/Bridge/Telnyx/composer.json | 4 +- .../Notifier/Bridge/Termii/composer.json | 6 +- .../Notifier/Bridge/TurboSms/composer.json | 4 +- .../Notifier/Bridge/Twilio/composer.json | 6 +- .../Notifier/Bridge/Twitter/composer.json | 6 +- .../Notifier/Bridge/Unifonic/composer.json | 4 +- .../Notifier/Bridge/Vonage/composer.json | 6 +- .../Notifier/Bridge/Yunpian/composer.json | 4 +- .../Notifier/Bridge/Zendesk/composer.json | 4 +- .../Notifier/Bridge/Zulip/composer.json | 4 +- src/Symfony/Component/Notifier/composer.json | 4 +- .../Component/ObjectMapper/composer.json | 2 +- .../Component/PasswordHasher/composer.json | 4 +- .../Component/PropertyAccess/composer.json | 4 +- .../Component/PropertyInfo/composer.json | 10 +- .../Component/RateLimiter/composer.json | 4 +- .../Component/RemoteEvent/composer.json | 2 +- src/Symfony/Component/Routing/composer.json | 10 +- src/Symfony/Component/Runtime/composer.json | 8 +- src/Symfony/Component/Scheduler/composer.json | 16 ++-- .../Component/Security/Core/composer.json | 20 ++-- .../Component/Security/Csrf/composer.json | 6 +- .../Component/Security/Http/composer.json | 24 ++--- .../Component/Serializer/composer.json | 38 ++++---- src/Symfony/Component/String/composer.json | 10 +- .../Translation/Bridge/Crowdin/composer.json | 6 +- .../Translation/Bridge/Loco/composer.json | 6 +- .../Translation/Bridge/Lokalise/composer.json | 6 +- .../Translation/Bridge/Phrase/composer.json | 6 +- .../Component/Translation/composer.json | 16 ++-- src/Symfony/Component/Uid/composer.json | 2 +- src/Symfony/Component/Validator/composer.json | 34 +++---- src/Symfony/Component/VarDumper/composer.json | 8 +- .../Component/VarExporter/composer.json | 6 +- src/Symfony/Component/WebLink/composer.json | 2 +- src/Symfony/Component/Webhook/composer.json | 12 +-- src/Symfony/Component/Workflow/composer.json | 18 ++-- src/Symfony/Component/Yaml/composer.json | 2 +- 172 files changed, 735 insertions(+), 735 deletions(-) diff --git a/composer.json b/composer.json index 20bcb49c4b782..c21dfbfbd45c2 100644 --- a/composer.json +++ b/composer.json @@ -156,7 +156,7 @@ "seld/jsonlint": "^1.10", "symfony/amphp-http-client-meta": "^1.0|^2.0", "symfony/mercure-bundle": "^0.3", - "symfony/phpunit-bridge": "^6.4|^7.0", + "symfony/phpunit-bridge": "^6.4|^7.0|^8.0", "symfony/runtime": "self.version", "symfony/security-acl": "~2.8|~3.0", "symfony/webpack-encore-bundle": "^1.0|^2.0", diff --git a/src/Symfony/Bridge/Doctrine/composer.json b/src/Symfony/Bridge/Doctrine/composer.json index 9d95a8af14ca7..b2267ac5f69c3 100644 --- a/src/Symfony/Bridge/Doctrine/composer.json +++ b/src/Symfony/Bridge/Doctrine/composer.json @@ -25,24 +25,24 @@ "symfony/service-contracts": "^2.5|^3" }, "require-dev": { - "symfony/cache": "^6.4|^7.0", - "symfony/config": "^6.4|^7.0", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/doctrine-messenger": "^6.4|^7.0", - "symfony/expression-language": "^6.4|^7.0", - "symfony/form": "^6.4.6|^7.0.6", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/lock": "^6.4|^7.0", - "symfony/messenger": "^6.4|^7.0", - "symfony/property-access": "^6.4|^7.0", - "symfony/property-info": "^6.4|^7.0", - "symfony/security-core": "^6.4|^7.0", - "symfony/stopwatch": "^6.4|^7.0", - "symfony/translation": "^6.4|^7.0", - "symfony/type-info": "^7.1", - "symfony/uid": "^6.4|^7.0", - "symfony/validator": "^6.4|^7.0", - "symfony/var-dumper": "^6.4|^7.0", + "symfony/cache": "^6.4|^7.0|^8.0", + "symfony/config": "^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/doctrine-messenger": "^6.4|^7.0|^8.0", + "symfony/expression-language": "^6.4|^7.0|^8.0", + "symfony/form": "^6.4.6|^7.0.6|^8.0", + "symfony/http-kernel": "^6.4|^7.0|^8.0", + "symfony/lock": "^6.4|^7.0|^8.0", + "symfony/messenger": "^6.4|^7.0|^8.0", + "symfony/property-access": "^6.4|^7.0|^8.0", + "symfony/property-info": "^6.4|^7.0|^8.0", + "symfony/security-core": "^6.4|^7.0|^8.0", + "symfony/stopwatch": "^6.4|^7.0|^8.0", + "symfony/translation": "^6.4|^7.0|^8.0", + "symfony/type-info": "^7.1|^8.0", + "symfony/uid": "^6.4|^7.0|^8.0", + "symfony/validator": "^6.4|^7.0|^8.0", + "symfony/var-dumper": "^6.4|^7.0|^8.0", "doctrine/collections": "^1.8|^2.0", "doctrine/data-fixtures": "^1.1|^2", "doctrine/dbal": "^3.6|^4", diff --git a/src/Symfony/Bridge/Monolog/composer.json b/src/Symfony/Bridge/Monolog/composer.json index 50a23a5876931..745686777d1ce 100644 --- a/src/Symfony/Bridge/Monolog/composer.json +++ b/src/Symfony/Bridge/Monolog/composer.json @@ -19,16 +19,16 @@ "php": ">=8.2", "monolog/monolog": "^3", "symfony/service-contracts": "^2.5|^3", - "symfony/http-kernel": "^6.4|^7.0" + "symfony/http-kernel": "^6.4|^7.0|^8.0" }, "require-dev": { - "symfony/console": "^6.4|^7.0", - "symfony/http-client": "^6.4|^7.0", - "symfony/security-core": "^6.4|^7.0", - "symfony/var-dumper": "^6.4|^7.0", - "symfony/mailer": "^6.4|^7.0", - "symfony/mime": "^6.4|^7.0", - "symfony/messenger": "^6.4|^7.0" + "symfony/console": "^6.4|^7.0|^8.0", + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/security-core": "^6.4|^7.0|^8.0", + "symfony/var-dumper": "^6.4|^7.0|^8.0", + "symfony/mailer": "^6.4|^7.0|^8.0", + "symfony/mime": "^6.4|^7.0|^8.0", + "symfony/messenger": "^6.4|^7.0|^8.0" }, "conflict": { "symfony/console": "<6.4", diff --git a/src/Symfony/Bridge/PhpUnit/composer.json b/src/Symfony/Bridge/PhpUnit/composer.json index de9101f796d73..169f0e63a387b 100644 --- a/src/Symfony/Bridge/PhpUnit/composer.json +++ b/src/Symfony/Bridge/PhpUnit/composer.json @@ -24,7 +24,7 @@ }, "require-dev": { "symfony/deprecation-contracts": "^2.5|^3.0", - "symfony/error-handler": "^5.4|^6.4|^7.0", + "symfony/error-handler": "^6.4|^7.0|^8.0", "symfony/polyfill-php81": "^1.27" }, "conflict": { diff --git a/src/Symfony/Bridge/PsrHttpMessage/composer.json b/src/Symfony/Bridge/PsrHttpMessage/composer.json index a34dfb1008e5e..1bc5fa40fe34c 100644 --- a/src/Symfony/Bridge/PsrHttpMessage/composer.json +++ b/src/Symfony/Bridge/PsrHttpMessage/composer.json @@ -18,14 +18,14 @@ "require": { "php": ">=8.2", "psr/http-message": "^1.0|^2.0", - "symfony/http-foundation": "^6.4|^7.0" + "symfony/http-foundation": "^6.4|^7.0|^8.0" }, "require-dev": { - "symfony/browser-kit": "^6.4|^7.0", - "symfony/config": "^6.4|^7.0", - "symfony/event-dispatcher": "^6.4|^7.0", - "symfony/framework-bundle": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0", + "symfony/browser-kit": "^6.4|^7.0|^8.0", + "symfony/config": "^6.4|^7.0|^8.0", + "symfony/event-dispatcher": "^6.4|^7.0|^8.0", + "symfony/framework-bundle": "^6.4|^7.0|^8.0", + "symfony/http-kernel": "^6.4|^7.0|^8.0", "nyholm/psr7": "^1.1", "php-http/discovery": "^1.15", "psr/log": "^1.1.4|^2|^3" diff --git a/src/Symfony/Bridge/Twig/composer.json b/src/Symfony/Bridge/Twig/composer.json index dd2e55d752dc1..9fafcd55a0984 100644 --- a/src/Symfony/Bridge/Twig/composer.json +++ b/src/Symfony/Bridge/Twig/composer.json @@ -25,33 +25,33 @@ "egulias/email-validator": "^2.1.10|^3|^4", "league/html-to-markdown": "^5.0", "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0", - "symfony/asset": "^6.4|^7.0", - "symfony/asset-mapper": "^6.4|^7.0", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/emoji": "^7.1", - "symfony/finder": "^6.4|^7.0", - "symfony/form": "^6.4.20|^7.2.5", - "symfony/html-sanitizer": "^6.4|^7.0", - "symfony/http-foundation": "^7.3", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/intl": "^6.4|^7.0", - "symfony/mime": "^6.4|^7.0", + "symfony/asset": "^6.4|^7.0|^8.0", + "symfony/asset-mapper": "^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/emoji": "^7.1|^8.0", + "symfony/finder": "^6.4|^7.0|^8.0", + "symfony/form": "^6.4.20|^7.2.5|^8.0", + "symfony/html-sanitizer": "^6.4|^7.0|^8.0", + "symfony/http-foundation": "^7.3|^8.0", + "symfony/http-kernel": "^6.4|^7.0|^8.0", + "symfony/intl": "^6.4|^7.0|^8.0", + "symfony/mime": "^6.4|^7.0|^8.0", "symfony/polyfill-intl-icu": "~1.0", - "symfony/property-info": "^6.4|^7.0", - "symfony/routing": "^6.4|^7.0", - "symfony/translation": "^6.4|^7.0", - "symfony/validator": "^6.4|^7.0", - "symfony/yaml": "^6.4|^7.0", + "symfony/property-info": "^6.4|^7.0|^8.0", + "symfony/routing": "^6.4|^7.0|^8.0", + "symfony/translation": "^6.4|^7.0|^8.0", + "symfony/validator": "^6.4|^7.0|^8.0", + "symfony/yaml": "^6.4|^7.0|^8.0", "symfony/security-acl": "^2.8|^3.0", - "symfony/security-core": "^6.4|^7.0", - "symfony/security-csrf": "^6.4|^7.0", - "symfony/security-http": "^6.4|^7.0", - "symfony/serializer": "^6.4.3|^7.0.3", - "symfony/stopwatch": "^6.4|^7.0", - "symfony/console": "^6.4|^7.0", - "symfony/expression-language": "^6.4|^7.0", - "symfony/web-link": "^6.4|^7.0", - "symfony/workflow": "^6.4|^7.0", + "symfony/security-core": "^6.4|^7.0|^8.0", + "symfony/security-csrf": "^6.4|^7.0|^8.0", + "symfony/security-http": "^6.4|^7.0|^8.0", + "symfony/serializer": "^6.4.3|^7.0.3|^8.0", + "symfony/stopwatch": "^6.4|^7.0|^8.0", + "symfony/console": "^6.4|^7.0|^8.0", + "symfony/expression-language": "^6.4|^7.0|^8.0", + "symfony/web-link": "^6.4|^7.0|^8.0", + "symfony/workflow": "^6.4|^7.0|^8.0", "twig/cssinliner-extra": "^3", "twig/inky-extra": "^3", "twig/markdown-extra": "^3" diff --git a/src/Symfony/Bundle/DebugBundle/composer.json b/src/Symfony/Bundle/DebugBundle/composer.json index 31b480091abdc..07d7604aa9d7b 100644 --- a/src/Symfony/Bundle/DebugBundle/composer.json +++ b/src/Symfony/Bundle/DebugBundle/composer.json @@ -19,14 +19,14 @@ "php": ">=8.2", "ext-xml": "*", "composer-runtime-api": ">=2.1", - "symfony/config": "^7.3", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/twig-bridge": "^6.4|^7.0", - "symfony/var-dumper": "^6.4|^7.0" + "symfony/config": "^7.3|^8.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/http-kernel": "^6.4|^7.0|^8.0", + "symfony/twig-bridge": "^6.4|^7.0|^8.0", + "symfony/var-dumper": "^6.4|^7.0|^8.0" }, "require-dev": { - "symfony/web-profiler-bundle": "^6.4|^7.0" + "symfony/web-profiler-bundle": "^6.4|^7.0|^8.0" }, "autoload": { "psr-4": { "Symfony\\Bundle\\DebugBundle\\": "" }, diff --git a/src/Symfony/Bundle/FrameworkBundle/composer.json b/src/Symfony/Bundle/FrameworkBundle/composer.json index fa4ec0074a8ef..4814cc601c84b 100644 --- a/src/Symfony/Bundle/FrameworkBundle/composer.json +++ b/src/Symfony/Bundle/FrameworkBundle/composer.json @@ -19,61 +19,61 @@ "php": ">=8.2", "composer-runtime-api": ">=2.1", "ext-xml": "*", - "symfony/cache": "^6.4|^7.0", - "symfony/config": "^7.3", - "symfony/dependency-injection": "^7.2", + "symfony/cache": "^6.4|^7.0|^8.0", + "symfony/config": "^7.3|^8.0", + "symfony/dependency-injection": "^7.2|^8.0", "symfony/deprecation-contracts": "^2.5|^3", - "symfony/error-handler": "^7.3", - "symfony/event-dispatcher": "^6.4|^7.0", - "symfony/http-foundation": "^7.3", - "symfony/http-kernel": "^7.2", + "symfony/error-handler": "^7.3|^8.0", + "symfony/event-dispatcher": "^6.4|^7.0|^8.0", + "symfony/http-foundation": "^7.3|^8.0", + "symfony/http-kernel": "^7.2|^8.0", "symfony/polyfill-mbstring": "~1.0", - "symfony/filesystem": "^7.1", - "symfony/finder": "^6.4|^7.0", - "symfony/routing": "^6.4|^7.0" + "symfony/filesystem": "^7.1|^8.0", + "symfony/finder": "^6.4|^7.0|^8.0", + "symfony/routing": "^6.4|^7.0|^8.0" }, "require-dev": { "doctrine/persistence": "^1.3|^2|^3", "dragonmantank/cron-expression": "^3.1", "seld/jsonlint": "^1.10", - "symfony/asset": "^6.4|^7.0", - "symfony/asset-mapper": "^6.4|^7.0", - "symfony/browser-kit": "^6.4|^7.0", - "symfony/console": "^6.4|^7.0", - "symfony/clock": "^6.4|^7.0", - "symfony/css-selector": "^6.4|^7.0", - "symfony/dom-crawler": "^6.4|^7.0", - "symfony/dotenv": "^6.4|^7.0", + "symfony/asset": "^6.4|^7.0|^8.0", + "symfony/asset-mapper": "^6.4|^7.0|^8.0", + "symfony/browser-kit": "^6.4|^7.0|^8.0", + "symfony/console": "^6.4|^7.0|^8.0", + "symfony/clock": "^6.4|^7.0|^8.0", + "symfony/css-selector": "^6.4|^7.0|^8.0", + "symfony/dom-crawler": "^6.4|^7.0|^8.0", + "symfony/dotenv": "^6.4|^7.0|^8.0", "symfony/polyfill-intl-icu": "~1.0", - "symfony/form": "^6.4|^7.0", - "symfony/expression-language": "^6.4|^7.0", - "symfony/html-sanitizer": "^6.4|^7.0", - "symfony/http-client": "^6.4|^7.0", - "symfony/lock": "^6.4|^7.0", - "symfony/mailer": "^6.4|^7.0", - "symfony/messenger": "^6.4|^7.0", - "symfony/mime": "^6.4|^7.0", - "symfony/notifier": "^6.4|^7.0", - "symfony/object-mapper": "^7.3", - "symfony/process": "^6.4|^7.0", - "symfony/rate-limiter": "^6.4|^7.0", - "symfony/scheduler": "^6.4.4|^7.0.4", - "symfony/security-bundle": "^6.4|^7.0", - "symfony/semaphore": "^6.4|^7.0", - "symfony/serializer": "^7.2.5", - "symfony/stopwatch": "^6.4|^7.0", - "symfony/string": "^6.4|^7.0", - "symfony/translation": "^7.3", - "symfony/twig-bundle": "^6.4|^7.0", - "symfony/type-info": "^7.1", - "symfony/validator": "^6.4|^7.0", - "symfony/workflow": "^7.3", - "symfony/yaml": "^6.4|^7.0", - "symfony/property-info": "^6.4|^7.0", - "symfony/json-streamer": "^7.3", - "symfony/uid": "^6.4|^7.0", - "symfony/web-link": "^6.4|^7.0", - "symfony/webhook": "^7.2", + "symfony/form": "^6.4|^7.0|^8.0", + "symfony/expression-language": "^6.4|^7.0|^8.0", + "symfony/html-sanitizer": "^6.4|^7.0|^8.0", + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/lock": "^6.4|^7.0|^8.0", + "symfony/mailer": "^6.4|^7.0|^8.0", + "symfony/messenger": "^6.4|^7.0|^8.0", + "symfony/mime": "^6.4|^7.0|^8.0", + "symfony/notifier": "^6.4|^7.0|^8.0", + "symfony/object-mapper": "^7.3|^8.0", + "symfony/process": "^6.4|^7.0|^8.0", + "symfony/rate-limiter": "^6.4|^7.0|^8.0", + "symfony/scheduler": "^6.4.4|^7.0.4|^8.0", + "symfony/security-bundle": "^6.4|^7.0|^8.0", + "symfony/semaphore": "^6.4|^7.0|^8.0", + "symfony/serializer": "^7.2.5|^8.0", + "symfony/stopwatch": "^6.4|^7.0|^8.0", + "symfony/string": "^6.4|^7.0|^8.0", + "symfony/translation": "^7.3|^8.0", + "symfony/twig-bundle": "^6.4|^7.0|^8.0", + "symfony/type-info": "^7.1|^8.0", + "symfony/validator": "^6.4|^7.0|^8.0", + "symfony/workflow": "^7.3|^8.0", + "symfony/yaml": "^6.4|^7.0|^8.0", + "symfony/property-info": "^6.4|^7.0|^8.0", + "symfony/json-streamer": "^7.3|^8.0", + "symfony/uid": "^6.4|^7.0|^8.0", + "symfony/web-link": "^6.4|^7.0|^8.0", + "symfony/webhook": "^7.2|^8.0", "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0", "twig/twig": "^3.12" }, diff --git a/src/Symfony/Bundle/SecurityBundle/composer.json b/src/Symfony/Bundle/SecurityBundle/composer.json index 7459b0175b95f..66bc512f1d1ff 100644 --- a/src/Symfony/Bundle/SecurityBundle/composer.json +++ b/src/Symfony/Bundle/SecurityBundle/composer.json @@ -19,37 +19,37 @@ "php": ">=8.2", "composer-runtime-api": ">=2.1", "ext-xml": "*", - "symfony/clock": "^6.4|^7.0", - "symfony/config": "^7.3", - "symfony/dependency-injection": "^6.4.11|^7.1.4", - "symfony/event-dispatcher": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/http-foundation": "^6.4|^7.0", - "symfony/password-hasher": "^6.4|^7.0", - "symfony/security-core": "^7.3", - "symfony/security-csrf": "^6.4|^7.0", - "symfony/security-http": "^7.3", + "symfony/clock": "^6.4|^7.0|^8.0", + "symfony/config": "^7.3|^8.0", + "symfony/dependency-injection": "^6.4.11|^7.1.4|^8.0", + "symfony/event-dispatcher": "^6.4|^7.0|^8.0", + "symfony/http-kernel": "^6.4|^7.0|^8.0", + "symfony/http-foundation": "^6.4|^7.0|^8.0", + "symfony/password-hasher": "^6.4|^7.0|^8.0", + "symfony/security-core": "^7.3|^8.0", + "symfony/security-csrf": "^6.4|^7.0|^8.0", + "symfony/security-http": "^7.3|^8.0", "symfony/service-contracts": "^2.5|^3" }, "require-dev": { - "symfony/asset": "^6.4|^7.0", - "symfony/browser-kit": "^6.4|^7.0", - "symfony/console": "^6.4|^7.0", - "symfony/css-selector": "^6.4|^7.0", - "symfony/dom-crawler": "^6.4|^7.0", - "symfony/expression-language": "^6.4|^7.0", - "symfony/form": "^6.4|^7.0", - "symfony/framework-bundle": "^6.4|^7.0", - "symfony/http-client": "^6.4|^7.0", - "symfony/ldap": "^6.4|^7.0", - "symfony/process": "^6.4|^7.0", - "symfony/rate-limiter": "^6.4|^7.0", - "symfony/serializer": "^6.4|^7.0", - "symfony/translation": "^6.4|^7.0", - "symfony/twig-bundle": "^6.4|^7.0", - "symfony/twig-bridge": "^6.4|^7.0", - "symfony/validator": "^6.4|^7.0", - "symfony/yaml": "^6.4|^7.0", + "symfony/asset": "^6.4|^7.0|^8.0", + "symfony/browser-kit": "^6.4|^7.0|^8.0", + "symfony/console": "^6.4|^7.0|^8.0", + "symfony/css-selector": "^6.4|^7.0|^8.0", + "symfony/dom-crawler": "^6.4|^7.0|^8.0", + "symfony/expression-language": "^6.4|^7.0|^8.0", + "symfony/form": "^6.4|^7.0|^8.0", + "symfony/framework-bundle": "^6.4|^7.0|^8.0", + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/ldap": "^6.4|^7.0|^8.0", + "symfony/process": "^6.4|^7.0|^8.0", + "symfony/rate-limiter": "^6.4|^7.0|^8.0", + "symfony/serializer": "^6.4|^7.0|^8.0", + "symfony/translation": "^6.4|^7.0|^8.0", + "symfony/twig-bundle": "^6.4|^7.0|^8.0", + "symfony/twig-bridge": "^6.4|^7.0|^8.0", + "symfony/validator": "^6.4|^7.0|^8.0", + "symfony/yaml": "^6.4|^7.0|^8.0", "twig/twig": "^3.12", "web-token/jwt-library": "^3.3.2|^4.0" }, diff --git a/src/Symfony/Bundle/TwigBundle/composer.json b/src/Symfony/Bundle/TwigBundle/composer.json index 221a7f471290e..6fc30ca79a8cd 100644 --- a/src/Symfony/Bundle/TwigBundle/composer.json +++ b/src/Symfony/Bundle/TwigBundle/composer.json @@ -18,24 +18,24 @@ "require": { "php": ">=8.2", "composer-runtime-api": ">=2.1", - "symfony/config": "^7.3", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/twig-bridge": "^7.3", - "symfony/http-foundation": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0", + "symfony/config": "^7.3|^8.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/twig-bridge": "^7.3|^8.0", + "symfony/http-foundation": "^6.4|^7.0|^8.0", + "symfony/http-kernel": "^6.4|^7.0|^8.0", "twig/twig": "^3.12" }, "require-dev": { - "symfony/asset": "^6.4|^7.0", - "symfony/stopwatch": "^6.4|^7.0", - "symfony/expression-language": "^6.4|^7.0", - "symfony/finder": "^6.4|^7.0", - "symfony/form": "^6.4|^7.0", - "symfony/routing": "^6.4|^7.0", - "symfony/translation": "^6.4|^7.0", - "symfony/yaml": "^6.4|^7.0", - "symfony/framework-bundle": "^6.4|^7.0", - "symfony/web-link": "^6.4|^7.0" + "symfony/asset": "^6.4|^7.0|^8.0", + "symfony/stopwatch": "^6.4|^7.0|^8.0", + "symfony/expression-language": "^6.4|^7.0|^8.0", + "symfony/finder": "^6.4|^7.0|^8.0", + "symfony/form": "^6.4|^7.0|^8.0", + "symfony/routing": "^6.4|^7.0|^8.0", + "symfony/translation": "^6.4|^7.0|^8.0", + "symfony/yaml": "^6.4|^7.0|^8.0", + "symfony/framework-bundle": "^6.4|^7.0|^8.0", + "symfony/web-link": "^6.4|^7.0|^8.0" }, "conflict": { "symfony/framework-bundle": "<6.4", diff --git a/src/Symfony/Bundle/WebProfilerBundle/composer.json b/src/Symfony/Bundle/WebProfilerBundle/composer.json index 00269dd279d45..4bcc0e01c4fc0 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/composer.json +++ b/src/Symfony/Bundle/WebProfilerBundle/composer.json @@ -18,19 +18,19 @@ "require": { "php": ">=8.2", "composer-runtime-api": ">=2.1", - "symfony/config": "^7.3", + "symfony/config": "^7.3|^8.0", "symfony/deprecation-contracts": "^2.5|^3", - "symfony/framework-bundle": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/routing": "^6.4|^7.0", - "symfony/twig-bundle": "^6.4|^7.0", + "symfony/framework-bundle": "^6.4|^7.0|^8.0", + "symfony/http-kernel": "^6.4|^7.0|^8.0", + "symfony/routing": "^6.4|^7.0|^8.0", + "symfony/twig-bundle": "^6.4|^7.0|^8.0", "twig/twig": "^3.12" }, "require-dev": { - "symfony/browser-kit": "^6.4|^7.0", - "symfony/console": "^6.4|^7.0", - "symfony/css-selector": "^6.4|^7.0", - "symfony/stopwatch": "^6.4|^7.0" + "symfony/browser-kit": "^6.4|^7.0|^8.0", + "symfony/console": "^6.4|^7.0|^8.0", + "symfony/css-selector": "^6.4|^7.0|^8.0", + "symfony/stopwatch": "^6.4|^7.0|^8.0" }, "conflict": { "symfony/form": "<6.4", diff --git a/src/Symfony/Component/Asset/composer.json b/src/Symfony/Component/Asset/composer.json index e8e1368f0e01c..d0107ed898d70 100644 --- a/src/Symfony/Component/Asset/composer.json +++ b/src/Symfony/Component/Asset/composer.json @@ -19,9 +19,9 @@ "php": ">=8.2" }, "require-dev": { - "symfony/http-client": "^6.4|^7.0", - "symfony/http-foundation": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0" + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/http-foundation": "^6.4|^7.0|^8.0", + "symfony/http-kernel": "^6.4|^7.0|^8.0" }, "conflict": { "symfony/http-foundation": "<6.4" diff --git a/src/Symfony/Component/AssetMapper/composer.json b/src/Symfony/Component/AssetMapper/composer.json index 1286eefc09081..076f3bb9769d2 100644 --- a/src/Symfony/Component/AssetMapper/composer.json +++ b/src/Symfony/Component/AssetMapper/composer.json @@ -19,20 +19,20 @@ "php": ">=8.2", "composer/semver": "^3.0", "symfony/deprecation-contracts": "^2.1|^3", - "symfony/filesystem": "^7.1", - "symfony/http-client": "^6.4|^7.0" + "symfony/filesystem": "^7.1|^8.0", + "symfony/http-client": "^6.4|^7.0|^8.0" }, "require-dev": { - "symfony/asset": "^6.4|^7.0", - "symfony/browser-kit": "^6.4|^7.0", - "symfony/console": "^6.4|^7.0", + "symfony/asset": "^6.4|^7.0|^8.0", + "symfony/browser-kit": "^6.4|^7.0|^8.0", + "symfony/console": "^6.4|^7.0|^8.0", "symfony/event-dispatcher-contracts": "^3.0", - "symfony/finder": "^6.4|^7.0", - "symfony/framework-bundle": "^6.4|^7.0", - "symfony/http-foundation": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/process": "^6.4|^7.0", - "symfony/web-link": "^6.4|^7.0" + "symfony/finder": "^6.4|^7.0|^8.0", + "symfony/framework-bundle": "^6.4|^7.0|^8.0", + "symfony/http-foundation": "^6.4|^7.0|^8.0", + "symfony/http-kernel": "^6.4|^7.0|^8.0", + "symfony/process": "^6.4|^7.0|^8.0", + "symfony/web-link": "^6.4|^7.0|^8.0" }, "conflict": { "symfony/framework-bundle": "<6.4" diff --git a/src/Symfony/Component/BrowserKit/composer.json b/src/Symfony/Component/BrowserKit/composer.json index e145984e64eab..b2e6761dab249 100644 --- a/src/Symfony/Component/BrowserKit/composer.json +++ b/src/Symfony/Component/BrowserKit/composer.json @@ -17,13 +17,13 @@ ], "require": { "php": ">=8.2", - "symfony/dom-crawler": "^6.4|^7.0" + "symfony/dom-crawler": "^6.4|^7.0|^8.0" }, "require-dev": { - "symfony/css-selector": "^6.4|^7.0", - "symfony/http-client": "^6.4|^7.0", - "symfony/mime": "^6.4|^7.0", - "symfony/process": "^6.4|^7.0" + "symfony/css-selector": "^6.4|^7.0|^8.0", + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/mime": "^6.4|^7.0|^8.0", + "symfony/process": "^6.4|^7.0|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\BrowserKit\\": "" }, diff --git a/src/Symfony/Component/Cache/composer.json b/src/Symfony/Component/Cache/composer.json index c89d667288286..d56cec522a60c 100644 --- a/src/Symfony/Component/Cache/composer.json +++ b/src/Symfony/Component/Cache/composer.json @@ -27,20 +27,20 @@ "symfony/cache-contracts": "^3.6", "symfony/deprecation-contracts": "^2.5|^3.0", "symfony/service-contracts": "^2.5|^3", - "symfony/var-exporter": "^6.4|^7.0" + "symfony/var-exporter": "^6.4|^7.0|^8.0" }, "require-dev": { "cache/integration-tests": "dev-master", "doctrine/dbal": "^3.6|^4", "predis/predis": "^1.1|^2.0", "psr/simple-cache": "^1.0|^2.0|^3.0", - "symfony/clock": "^6.4|^7.0", - "symfony/config": "^6.4|^7.0", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/filesystem": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/messenger": "^6.4|^7.0", - "symfony/var-dumper": "^6.4|^7.0" + "symfony/clock": "^6.4|^7.0|^8.0", + "symfony/config": "^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/filesystem": "^6.4|^7.0|^8.0", + "symfony/http-kernel": "^6.4|^7.0|^8.0", + "symfony/messenger": "^6.4|^7.0|^8.0", + "symfony/var-dumper": "^6.4|^7.0|^8.0" }, "conflict": { "doctrine/dbal": "<3.6", diff --git a/src/Symfony/Component/Config/composer.json b/src/Symfony/Component/Config/composer.json index 37206042aa8b0..af999bafa38ff 100644 --- a/src/Symfony/Component/Config/composer.json +++ b/src/Symfony/Component/Config/composer.json @@ -18,15 +18,15 @@ "require": { "php": ">=8.2", "symfony/deprecation-contracts": "^2.5|^3", - "symfony/filesystem": "^7.1", + "symfony/filesystem": "^7.1|^8.0", "symfony/polyfill-ctype": "~1.8" }, "require-dev": { - "symfony/event-dispatcher": "^6.4|^7.0", - "symfony/finder": "^6.4|^7.0", - "symfony/messenger": "^6.4|^7.0", + "symfony/event-dispatcher": "^6.4|^7.0|^8.0", + "symfony/finder": "^6.4|^7.0|^8.0", + "symfony/messenger": "^6.4|^7.0|^8.0", "symfony/service-contracts": "^2.5|^3", - "symfony/yaml": "^6.4|^7.0" + "symfony/yaml": "^6.4|^7.0|^8.0" }, "conflict": { "symfony/finder": "<6.4", diff --git a/src/Symfony/Component/Console/composer.json b/src/Symfony/Component/Console/composer.json index 65d69913aa218..109cdd762f625 100644 --- a/src/Symfony/Component/Console/composer.json +++ b/src/Symfony/Component/Console/composer.json @@ -20,19 +20,19 @@ "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-mbstring": "~1.0", "symfony/service-contracts": "^2.5|^3", - "symfony/string": "^7.2" + "symfony/string": "^7.2|^8.0" }, "require-dev": { - "symfony/config": "^6.4|^7.0", - "symfony/event-dispatcher": "^6.4|^7.0", - "symfony/http-foundation": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/lock": "^6.4|^7.0", - "symfony/messenger": "^6.4|^7.0", - "symfony/process": "^6.4|^7.0", - "symfony/stopwatch": "^6.4|^7.0", - "symfony/var-dumper": "^6.4|^7.0", + "symfony/config": "^6.4|^7.0|^8.0", + "symfony/event-dispatcher": "^6.4|^7.0|^8.0", + "symfony/http-foundation": "^6.4|^7.0|^8.0", + "symfony/http-kernel": "^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/lock": "^6.4|^7.0|^8.0", + "symfony/messenger": "^6.4|^7.0|^8.0", + "symfony/process": "^6.4|^7.0|^8.0", + "symfony/stopwatch": "^6.4|^7.0|^8.0", + "symfony/var-dumper": "^6.4|^7.0|^8.0", "psr/log": "^1|^2|^3" }, "provide": { diff --git a/src/Symfony/Component/DependencyInjection/composer.json b/src/Symfony/Component/DependencyInjection/composer.json index 460751088f451..7b1e731b7d2eb 100644 --- a/src/Symfony/Component/DependencyInjection/composer.json +++ b/src/Symfony/Component/DependencyInjection/composer.json @@ -20,12 +20,12 @@ "psr/container": "^1.1|^2.0", "symfony/deprecation-contracts": "^2.5|^3", "symfony/service-contracts": "^3.5", - "symfony/var-exporter": "^6.4.20|^7.2.5" + "symfony/var-exporter": "^6.4.20|^7.2.5|^8.0" }, "require-dev": { - "symfony/yaml": "^6.4|^7.0", - "symfony/config": "^6.4|^7.0", - "symfony/expression-language": "^6.4|^7.0" + "symfony/yaml": "^6.4|^7.0|^8.0", + "symfony/config": "^6.4|^7.0|^8.0", + "symfony/expression-language": "^6.4|^7.0|^8.0" }, "conflict": { "ext-psr": "<1.1|>=2", diff --git a/src/Symfony/Component/DomCrawler/composer.json b/src/Symfony/Component/DomCrawler/composer.json index c47482794d0a0..0e5c984d09be2 100644 --- a/src/Symfony/Component/DomCrawler/composer.json +++ b/src/Symfony/Component/DomCrawler/composer.json @@ -22,7 +22,7 @@ "masterminds/html5": "^2.6" }, "require-dev": { - "symfony/css-selector": "^6.4|^7.0" + "symfony/css-selector": "^6.4|^7.0|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\DomCrawler\\": "" }, diff --git a/src/Symfony/Component/Dotenv/composer.json b/src/Symfony/Component/Dotenv/composer.json index 34c4718a76aeb..fe887ff0a31c5 100644 --- a/src/Symfony/Component/Dotenv/composer.json +++ b/src/Symfony/Component/Dotenv/composer.json @@ -19,8 +19,8 @@ "php": ">=8.2" }, "require-dev": { - "symfony/console": "^6.4|^7.0", - "symfony/process": "^6.4|^7.0" + "symfony/console": "^6.4|^7.0|^8.0", + "symfony/process": "^6.4|^7.0|^8.0" }, "conflict": { "symfony/console": "<6.4", diff --git a/src/Symfony/Component/Emoji/Resources/bin/composer.json b/src/Symfony/Component/Emoji/Resources/bin/composer.json index 29bf4d6466941..a120970a9bfb9 100644 --- a/src/Symfony/Component/Emoji/Resources/bin/composer.json +++ b/src/Symfony/Component/Emoji/Resources/bin/composer.json @@ -15,9 +15,9 @@ ], "minimum-stability": "dev", "require": { - "symfony/filesystem": "^6.4|^7.0", - "symfony/finder": "^6.4|^7.0", - "symfony/var-exporter": "^6.4|^7.0", + "symfony/filesystem": "^6.4|^7.0|^8.0", + "symfony/finder": "^6.4|^7.0|^8.0", + "symfony/var-exporter": "^6.4|^7.0|^8.0", "unicode-org/cldr": "*" } } diff --git a/src/Symfony/Component/Emoji/composer.json b/src/Symfony/Component/Emoji/composer.json index 9d9414c224aac..d4a6a083a108b 100644 --- a/src/Symfony/Component/Emoji/composer.json +++ b/src/Symfony/Component/Emoji/composer.json @@ -20,9 +20,9 @@ "ext-intl": "*" }, "require-dev": { - "symfony/filesystem": "^7.1", - "symfony/finder": "^6.4|^7.0", - "symfony/var-exporter": "^6.4|^7.0" + "symfony/filesystem": "^7.1|^8.0", + "symfony/finder": "^6.4|^7.0|^8.0", + "symfony/var-exporter": "^6.4|^7.0|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Emoji\\": "" }, diff --git a/src/Symfony/Component/ErrorHandler/composer.json b/src/Symfony/Component/ErrorHandler/composer.json index 98b94328364e8..dc56a36e414d0 100644 --- a/src/Symfony/Component/ErrorHandler/composer.json +++ b/src/Symfony/Component/ErrorHandler/composer.json @@ -18,12 +18,12 @@ "require": { "php": ">=8.2", "psr/log": "^1|^2|^3", - "symfony/var-dumper": "^6.4|^7.0" + "symfony/var-dumper": "^6.4|^7.0|^8.0" }, "require-dev": { - "symfony/console": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/serializer": "^6.4|^7.0", + "symfony/console": "^6.4|^7.0|^8.0", + "symfony/http-kernel": "^6.4|^7.0|^8.0", + "symfony/serializer": "^6.4|^7.0|^8.0", "symfony/deprecation-contracts": "^2.5|^3", "symfony/webpack-encore-bundle": "^1.0|^2.0" }, diff --git a/src/Symfony/Component/EventDispatcher/composer.json b/src/Symfony/Component/EventDispatcher/composer.json index 598bbdc5489a4..d125e7608f25f 100644 --- a/src/Symfony/Component/EventDispatcher/composer.json +++ b/src/Symfony/Component/EventDispatcher/composer.json @@ -20,13 +20,13 @@ "symfony/event-dispatcher-contracts": "^2.5|^3" }, "require-dev": { - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/expression-language": "^6.4|^7.0", - "symfony/config": "^6.4|^7.0", - "symfony/error-handler": "^6.4|^7.0", - "symfony/http-foundation": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/expression-language": "^6.4|^7.0|^8.0", + "symfony/config": "^6.4|^7.0|^8.0", + "symfony/error-handler": "^6.4|^7.0|^8.0", + "symfony/http-foundation": "^6.4|^7.0|^8.0", "symfony/service-contracts": "^2.5|^3", - "symfony/stopwatch": "^6.4|^7.0", + "symfony/stopwatch": "^6.4|^7.0|^8.0", "psr/log": "^1|^2|^3" }, "conflict": { diff --git a/src/Symfony/Component/ExpressionLanguage/composer.json b/src/Symfony/Component/ExpressionLanguage/composer.json index e24a315dae873..e3989cdefbf08 100644 --- a/src/Symfony/Component/ExpressionLanguage/composer.json +++ b/src/Symfony/Component/ExpressionLanguage/composer.json @@ -17,7 +17,7 @@ ], "require": { "php": ">=8.2", - "symfony/cache": "^6.4|^7.0", + "symfony/cache": "^6.4|^7.0|^8.0", "symfony/deprecation-contracts": "^2.5|^3", "symfony/service-contracts": "^2.5|^3" }, diff --git a/src/Symfony/Component/Filesystem/composer.json b/src/Symfony/Component/Filesystem/composer.json index c781e55b18438..42bbfa08a7a00 100644 --- a/src/Symfony/Component/Filesystem/composer.json +++ b/src/Symfony/Component/Filesystem/composer.json @@ -21,7 +21,7 @@ "symfony/polyfill-mbstring": "~1.8" }, "require-dev": { - "symfony/process": "^6.4|^7.0" + "symfony/process": "^6.4|^7.0|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Filesystem\\": "" }, diff --git a/src/Symfony/Component/Finder/composer.json b/src/Symfony/Component/Finder/composer.json index 2b70600d097cd..52bdb48162417 100644 --- a/src/Symfony/Component/Finder/composer.json +++ b/src/Symfony/Component/Finder/composer.json @@ -19,7 +19,7 @@ "php": ">=8.2" }, "require-dev": { - "symfony/filesystem": "^6.4|^7.0" + "symfony/filesystem": "^6.4|^7.0|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Finder\\": "" }, diff --git a/src/Symfony/Component/Form/composer.json b/src/Symfony/Component/Form/composer.json index d9539c79fd103..8ed9a50124d6f 100644 --- a/src/Symfony/Component/Form/composer.json +++ b/src/Symfony/Component/Form/composer.json @@ -18,31 +18,31 @@ "require": { "php": ">=8.2", "symfony/deprecation-contracts": "^2.5|^3", - "symfony/event-dispatcher": "^6.4|^7.0", - "symfony/options-resolver": "^7.3", + "symfony/event-dispatcher": "^6.4|^7.0|^8.0", + "symfony/options-resolver": "^7.3|^8.0", "symfony/polyfill-ctype": "~1.8", "symfony/polyfill-intl-icu": "^1.21", "symfony/polyfill-mbstring": "~1.0", - "symfony/property-access": "^6.4|^7.0", + "symfony/property-access": "^6.4|^7.0|^8.0", "symfony/service-contracts": "^2.5|^3" }, "require-dev": { "doctrine/collections": "^1.0|^2.0", - "symfony/validator": "^6.4|^7.0", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/expression-language": "^6.4|^7.0", - "symfony/clock": "^6.4|^7.0", - "symfony/config": "^6.4|^7.0", - "symfony/console": "^6.4|^7.0", - "symfony/html-sanitizer": "^6.4|^7.0", - "symfony/http-foundation": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/intl": "^6.4|^7.0", - "symfony/security-core": "^6.4|^7.0", - "symfony/security-csrf": "^6.4|^7.0", - "symfony/translation": "^6.4.3|^7.0.3", - "symfony/var-dumper": "^6.4|^7.0", - "symfony/uid": "^6.4|^7.0" + "symfony/validator": "^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/expression-language": "^6.4|^7.0|^8.0", + "symfony/clock": "^6.4|^7.0|^8.0", + "symfony/config": "^6.4|^7.0|^8.0", + "symfony/console": "^6.4|^7.0|^8.0", + "symfony/html-sanitizer": "^6.4|^7.0|^8.0", + "symfony/http-foundation": "^6.4|^7.0|^8.0", + "symfony/http-kernel": "^6.4|^7.0|^8.0", + "symfony/intl": "^6.4|^7.0|^8.0", + "symfony/security-core": "^6.4|^7.0|^8.0", + "symfony/security-csrf": "^6.4|^7.0|^8.0", + "symfony/translation": "^6.4.3|^7.0.3|^8.0", + "symfony/var-dumper": "^6.4|^7.0|^8.0", + "symfony/uid": "^6.4|^7.0|^8.0" }, "conflict": { "symfony/console": "<6.4", diff --git a/src/Symfony/Component/HttpClient/composer.json b/src/Symfony/Component/HttpClient/composer.json index 7ca008fd01f13..3fffa2e1e5158 100644 --- a/src/Symfony/Component/HttpClient/composer.json +++ b/src/Symfony/Component/HttpClient/composer.json @@ -37,12 +37,12 @@ "php-http/httplug": "^1.0|^2.0", "psr/http-client": "^1.0", "symfony/amphp-http-client-meta": "^1.0|^2.0", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/messenger": "^6.4|^7.0", - "symfony/process": "^6.4|^7.0", - "symfony/rate-limiter": "^6.4|^7.0", - "symfony/stopwatch": "^6.4|^7.0" + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/http-kernel": "^6.4|^7.0|^8.0", + "symfony/messenger": "^6.4|^7.0|^8.0", + "symfony/process": "^6.4|^7.0|^8.0", + "symfony/rate-limiter": "^6.4|^7.0|^8.0", + "symfony/stopwatch": "^6.4|^7.0|^8.0" }, "conflict": { "amphp/amp": "<2.5", diff --git a/src/Symfony/Component/HttpFoundation/composer.json b/src/Symfony/Component/HttpFoundation/composer.json index a86b21b7c728a..9fe4c1a4ba44a 100644 --- a/src/Symfony/Component/HttpFoundation/composer.json +++ b/src/Symfony/Component/HttpFoundation/composer.json @@ -24,13 +24,13 @@ "require-dev": { "doctrine/dbal": "^3.6|^4", "predis/predis": "^1.1|^2.0", - "symfony/cache": "^6.4.12|^7.1.5", - "symfony/clock": "^6.4|^7.0", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/mime": "^6.4|^7.0", - "symfony/expression-language": "^6.4|^7.0", - "symfony/rate-limiter": "^6.4|^7.0" + "symfony/cache": "^6.4.12|^7.1.5|^8.0", + "symfony/clock": "^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/http-kernel": "^6.4|^7.0|^8.0", + "symfony/mime": "^6.4|^7.0|^8.0", + "symfony/expression-language": "^6.4|^7.0|^8.0", + "symfony/rate-limiter": "^6.4|^7.0|^8.0" }, "conflict": { "doctrine/dbal": "<3.6", diff --git a/src/Symfony/Component/HttpKernel/composer.json b/src/Symfony/Component/HttpKernel/composer.json index bb9f4ba6175de..e3a8b9657d9e7 100644 --- a/src/Symfony/Component/HttpKernel/composer.json +++ b/src/Symfony/Component/HttpKernel/composer.json @@ -18,34 +18,34 @@ "require": { "php": ">=8.2", "symfony/deprecation-contracts": "^2.5|^3", - "symfony/error-handler": "^6.4|^7.0", - "symfony/event-dispatcher": "^7.3", - "symfony/http-foundation": "^7.3", + "symfony/error-handler": "^6.4|^7.0|^8.0", + "symfony/event-dispatcher": "^7.3|^8.0", + "symfony/http-foundation": "^7.3|^8.0", "symfony/polyfill-ctype": "^1.8", "psr/log": "^1|^2|^3" }, "require-dev": { - "symfony/browser-kit": "^6.4|^7.0", - "symfony/clock": "^6.4|^7.0", - "symfony/config": "^6.4|^7.0", - "symfony/console": "^6.4|^7.0", - "symfony/css-selector": "^6.4|^7.0", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/dom-crawler": "^6.4|^7.0", - "symfony/expression-language": "^6.4|^7.0", - "symfony/finder": "^6.4|^7.0", + "symfony/browser-kit": "^6.4|^7.0|^8.0", + "symfony/clock": "^6.4|^7.0|^8.0", + "symfony/config": "^6.4|^7.0|^8.0", + "symfony/console": "^6.4|^7.0|^8.0", + "symfony/css-selector": "^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/dom-crawler": "^6.4|^7.0|^8.0", + "symfony/expression-language": "^6.4|^7.0|^8.0", + "symfony/finder": "^6.4|^7.0|^8.0", "symfony/http-client-contracts": "^2.5|^3", - "symfony/process": "^6.4|^7.0", - "symfony/property-access": "^7.1", - "symfony/routing": "^6.4|^7.0", - "symfony/serializer": "^7.1", - "symfony/stopwatch": "^6.4|^7.0", - "symfony/translation": "^6.4|^7.0", + "symfony/process": "^6.4|^7.0|^8.0", + "symfony/property-access": "^7.1|^8.0", + "symfony/routing": "^6.4|^7.0|^8.0", + "symfony/serializer": "^7.1|^8.0", + "symfony/stopwatch": "^6.4|^7.0|^8.0", + "symfony/translation": "^6.4|^7.0|^8.0", "symfony/translation-contracts": "^2.5|^3", - "symfony/uid": "^6.4|^7.0", - "symfony/validator": "^6.4|^7.0", - "symfony/var-dumper": "^6.4|^7.0", - "symfony/var-exporter": "^6.4|^7.0", + "symfony/uid": "^6.4|^7.0|^8.0", + "symfony/validator": "^6.4|^7.0|^8.0", + "symfony/var-dumper": "^6.4|^7.0|^8.0", + "symfony/var-exporter": "^6.4|^7.0|^8.0", "psr/cache": "^1.0|^2.0|^3.0", "twig/twig": "^3.12" }, diff --git a/src/Symfony/Component/Intl/composer.json b/src/Symfony/Component/Intl/composer.json index b2101cfe5f728..34a948bc0a621 100644 --- a/src/Symfony/Component/Intl/composer.json +++ b/src/Symfony/Component/Intl/composer.json @@ -28,8 +28,8 @@ "symfony/deprecation-contracts": "^2.5|^3" }, "require-dev": { - "symfony/filesystem": "^6.4|^7.0", - "symfony/var-exporter": "^6.4|^7.0" + "symfony/filesystem": "^6.4|^7.0|^8.0", + "symfony/var-exporter": "^6.4|^7.0|^8.0" }, "conflict": { "symfony/string": "<7.1" diff --git a/src/Symfony/Component/JsonPath/composer.json b/src/Symfony/Component/JsonPath/composer.json index 95b02675e7459..b61e388325fb3 100644 --- a/src/Symfony/Component/JsonPath/composer.json +++ b/src/Symfony/Component/JsonPath/composer.json @@ -20,7 +20,7 @@ "symfony/polyfill-mbstring": "~1.0" }, "require-dev": { - "symfony/json-streamer": "^7.3" + "symfony/json-streamer": "^7.3|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\JsonPath\\": "" }, diff --git a/src/Symfony/Component/JsonStreamer/composer.json b/src/Symfony/Component/JsonStreamer/composer.json index a635710bbe5f1..ac3af9ee36b0a 100644 --- a/src/Symfony/Component/JsonStreamer/composer.json +++ b/src/Symfony/Component/JsonStreamer/composer.json @@ -19,14 +19,14 @@ "php": ">=8.2", "psr/container": "^1.1|^2.0", "psr/log": "^1|^2|^3", - "symfony/filesystem": "^7.2", - "symfony/type-info": "^7.2", - "symfony/var-exporter": "^7.2" + "symfony/filesystem": "^7.2|^8.0", + "symfony/type-info": "^7.2|^8.0", + "symfony/var-exporter": "^7.2|^8.0" }, "require-dev": { "phpstan/phpdoc-parser": "^1.0", - "symfony/dependency-injection": "^7.2", - "symfony/http-kernel": "^7.2" + "symfony/dependency-injection": "^7.2|^8.0", + "symfony/http-kernel": "^7.2|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\JsonStreamer\\": "" }, diff --git a/src/Symfony/Component/Ldap/composer.json b/src/Symfony/Component/Ldap/composer.json index 535ad186207ec..32a9ab27552d7 100644 --- a/src/Symfony/Component/Ldap/composer.json +++ b/src/Symfony/Component/Ldap/composer.json @@ -18,11 +18,11 @@ "require": { "php": ">=8.2", "ext-ldap": "*", - "symfony/options-resolver": "^7.3" + "symfony/options-resolver": "^7.3|^8.0" }, "require-dev": { - "symfony/security-core": "^6.4|^7.0", - "symfony/security-http": "^6.4|^7.0" + "symfony/security-core": "^6.4|^7.0|^8.0", + "symfony/security-http": "^6.4|^7.0|^8.0" }, "conflict": { "symfony/security-core": "<6.4" diff --git a/src/Symfony/Component/Mailer/Bridge/AhaSend/composer.json b/src/Symfony/Component/Mailer/Bridge/AhaSend/composer.json index 65fae0816c89d..3eeaa278a962d 100644 --- a/src/Symfony/Component/Mailer/Bridge/AhaSend/composer.json +++ b/src/Symfony/Component/Mailer/Bridge/AhaSend/composer.json @@ -18,11 +18,11 @@ "require": { "php": ">=8.2", "psr/event-dispatcher": "^1", - "symfony/mailer": "^7.3" + "symfony/mailer": "^7.3|^8.0" }, "require-dev": { - "symfony/http-client": "^6.4|^7.0", - "symfony/webhook": "^6.4|^7.0" + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/webhook": "^6.4|^7.0|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Mailer\\Bridge\\AhaSend\\": "" }, diff --git a/src/Symfony/Component/Mailer/Bridge/Amazon/composer.json b/src/Symfony/Component/Mailer/Bridge/Amazon/composer.json index 3b8cd7cd49cb9..323b03519608e 100644 --- a/src/Symfony/Component/Mailer/Bridge/Amazon/composer.json +++ b/src/Symfony/Component/Mailer/Bridge/Amazon/composer.json @@ -18,10 +18,10 @@ "require": { "php": ">=8.2", "async-aws/ses": "^1.8", - "symfony/mailer": "^7.2" + "symfony/mailer": "^7.2|^8.0" }, "require-dev": { - "symfony/http-client": "^6.4|^7.0" + "symfony/http-client": "^6.4|^7.0|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Mailer\\Bridge\\Amazon\\": "" }, diff --git a/src/Symfony/Component/Mailer/Bridge/Azure/composer.json b/src/Symfony/Component/Mailer/Bridge/Azure/composer.json index c8396c21913e0..2772c273ef38e 100644 --- a/src/Symfony/Component/Mailer/Bridge/Azure/composer.json +++ b/src/Symfony/Component/Mailer/Bridge/Azure/composer.json @@ -17,10 +17,10 @@ ], "require": { "php": ">=8.2", - "symfony/mailer": "^7.2" + "symfony/mailer": "^7.2|^8.0" }, "require-dev": { - "symfony/http-client": "^6.4|^7.0" + "symfony/http-client": "^6.4|^7.0|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Mailer\\Bridge\\Azure\\": "" }, diff --git a/src/Symfony/Component/Mailer/Bridge/Brevo/composer.json b/src/Symfony/Component/Mailer/Bridge/Brevo/composer.json index 441dada9ef97d..2fa9bfa4905be 100644 --- a/src/Symfony/Component/Mailer/Bridge/Brevo/composer.json +++ b/src/Symfony/Component/Mailer/Bridge/Brevo/composer.json @@ -16,12 +16,12 @@ } ], "require": { - "php": ">=8.1", - "symfony/mailer": "^7.2" + "php": ">=8.2", + "symfony/mailer": "^7.2|^8.0" }, "require-dev": { - "symfony/http-client": "^6.3|^7.0", - "symfony/webhook": "^6.3|^7.0" + "symfony/http-client": "^6.3|^7.0|^8.0", + "symfony/webhook": "^6.3|^7.0|^8.0" }, "conflict": { "symfony/mime": "<6.2" diff --git a/src/Symfony/Component/Mailer/Bridge/Google/composer.json b/src/Symfony/Component/Mailer/Bridge/Google/composer.json index 13ba43762d942..c60576d8fb9d4 100644 --- a/src/Symfony/Component/Mailer/Bridge/Google/composer.json +++ b/src/Symfony/Component/Mailer/Bridge/Google/composer.json @@ -17,10 +17,10 @@ ], "require": { "php": ">=8.2", - "symfony/mailer": "^7.2" + "symfony/mailer": "^7.2|^8.0" }, "require-dev": { - "symfony/http-client": "^6.4|^7.0" + "symfony/http-client": "^6.4|^7.0|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Mailer\\Bridge\\Google\\": "" }, diff --git a/src/Symfony/Component/Mailer/Bridge/Infobip/composer.json b/src/Symfony/Component/Mailer/Bridge/Infobip/composer.json index e15a7a3d17f4a..5d94ecc9e8c80 100644 --- a/src/Symfony/Component/Mailer/Bridge/Infobip/composer.json +++ b/src/Symfony/Component/Mailer/Bridge/Infobip/composer.json @@ -21,11 +21,11 @@ ], "require": { "php": ">=8.2", - "symfony/mailer": "^7.2", - "symfony/mime": "^6.4|^7.0" + "symfony/mailer": "^7.2|^8.0", + "symfony/mime": "^6.4|^7.0|^8.0" }, "require-dev": { - "symfony/http-client": "^6.4|^7.0" + "symfony/http-client": "^6.4|^7.0|^8.0" }, "conflict": { "symfony/mime": "<6.4" diff --git a/src/Symfony/Component/Mailer/Bridge/MailPace/composer.json b/src/Symfony/Component/Mailer/Bridge/MailPace/composer.json index 9e962e28fc17f..77332cf2cc438 100644 --- a/src/Symfony/Component/Mailer/Bridge/MailPace/composer.json +++ b/src/Symfony/Component/Mailer/Bridge/MailPace/composer.json @@ -22,10 +22,10 @@ "require": { "php": ">=8.2", "psr/event-dispatcher": "^1", - "symfony/mailer": "^7.2" + "symfony/mailer": "^7.2|^8.0" }, "require-dev": { - "symfony/http-client": "^6.4|^7.0" + "symfony/http-client": "^6.4|^7.0|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Mailer\\Bridge\\MailPace\\": "" }, diff --git a/src/Symfony/Component/Mailer/Bridge/Mailchimp/composer.json b/src/Symfony/Component/Mailer/Bridge/Mailchimp/composer.json index 081b5998e6206..29ffb27b889b1 100644 --- a/src/Symfony/Component/Mailer/Bridge/Mailchimp/composer.json +++ b/src/Symfony/Component/Mailer/Bridge/Mailchimp/composer.json @@ -17,11 +17,11 @@ ], "require": { "php": ">=8.2", - "symfony/mailer": "^7.2" + "symfony/mailer": "^7.2|^8.0" }, "require-dev": { - "symfony/http-client": "^6.4|^7.0", - "symfony/webhook": "^7.2" + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/webhook": "^7.2|^8.0" }, "conflict": { "symfony/webhook": "<7.2" diff --git a/src/Symfony/Component/Mailer/Bridge/MailerSend/composer.json b/src/Symfony/Component/Mailer/Bridge/MailerSend/composer.json index 96357327da187..23831dc41c80e 100644 --- a/src/Symfony/Component/Mailer/Bridge/MailerSend/composer.json +++ b/src/Symfony/Component/Mailer/Bridge/MailerSend/composer.json @@ -21,11 +21,11 @@ ], "require": { "php": ">=8.2", - "symfony/mailer": "^7.2" + "symfony/mailer": "^7.2|^8.0" }, "require-dev": { - "symfony/http-client": "^6.4|^7.0", - "symfony/webhook": "^6.3|^7.0" + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/webhook": "^6.3|^7.0|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Mailer\\Bridge\\MailerSend\\": "" }, diff --git a/src/Symfony/Component/Mailer/Bridge/Mailgun/composer.json b/src/Symfony/Component/Mailer/Bridge/Mailgun/composer.json index 3a5a475e3e44b..b68dbb7152fae 100644 --- a/src/Symfony/Component/Mailer/Bridge/Mailgun/composer.json +++ b/src/Symfony/Component/Mailer/Bridge/Mailgun/composer.json @@ -17,11 +17,11 @@ ], "require": { "php": ">=8.2", - "symfony/mailer": "^7.2" + "symfony/mailer": "^7.2|^8.0" }, "require-dev": { - "symfony/http-client": "^6.4|^7.0", - "symfony/webhook": "^6.4|^7.0" + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/webhook": "^6.4|^7.0|^8.0" }, "conflict": { "symfony/http-foundation": "<6.4" diff --git a/src/Symfony/Component/Mailer/Bridge/Mailjet/composer.json b/src/Symfony/Component/Mailer/Bridge/Mailjet/composer.json index 3abc7eb31c135..f4877458b212a 100644 --- a/src/Symfony/Component/Mailer/Bridge/Mailjet/composer.json +++ b/src/Symfony/Component/Mailer/Bridge/Mailjet/composer.json @@ -17,11 +17,11 @@ ], "require": { "php": ">=8.2", - "symfony/mailer": "^7.3" + "symfony/mailer": "^7.3|^8.0" }, "require-dev": { - "symfony/http-client": "^6.4|^7.0", - "symfony/webhook": "^6.4|^7.0" + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/webhook": "^6.4|^7.0|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Mailer\\Bridge\\Mailjet\\": "" }, diff --git a/src/Symfony/Component/Mailer/Bridge/Mailomat/composer.json b/src/Symfony/Component/Mailer/Bridge/Mailomat/composer.json index 2d4cc3f1c8515..dd8e043a2a9c2 100644 --- a/src/Symfony/Component/Mailer/Bridge/Mailomat/composer.json +++ b/src/Symfony/Component/Mailer/Bridge/Mailomat/composer.json @@ -17,12 +17,12 @@ ], "require": { "php": ">=8.2", - "symfony/mailer": "^7.2" + "symfony/mailer": "^7.2|^8.0" }, "require-dev": { - "symfony/http-client": "^6.4|^7.0", - "symfony/http-foundation": "^7.1", - "symfony/webhook": "^6.4|^7.0" + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/http-foundation": "^7.1|^8.0", + "symfony/webhook": "^6.4|^7.0|^8.0" }, "conflict": { "symfony/http-foundation": "<7.1" diff --git a/src/Symfony/Component/Mailer/Bridge/Mailtrap/composer.json b/src/Symfony/Component/Mailer/Bridge/Mailtrap/composer.json index 7d448e7c40768..3fa19c63a89ed 100644 --- a/src/Symfony/Component/Mailer/Bridge/Mailtrap/composer.json +++ b/src/Symfony/Component/Mailer/Bridge/Mailtrap/composer.json @@ -18,11 +18,11 @@ "require": { "php": ">=8.2", "psr/event-dispatcher": "^1", - "symfony/mailer": "^7.2" + "symfony/mailer": "^7.2|^8.0" }, "require-dev": { - "symfony/http-client": "^6.4|^7.0", - "symfony/webhook": "^7.2" + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/webhook": "^7.2|^8.0" }, "conflict": { "symfony/webhook": "<7.2" diff --git a/src/Symfony/Component/Mailer/Bridge/Postal/composer.json b/src/Symfony/Component/Mailer/Bridge/Postal/composer.json index 8c3d3dfe8eda4..62fa6bf19db95 100644 --- a/src/Symfony/Component/Mailer/Bridge/Postal/composer.json +++ b/src/Symfony/Component/Mailer/Bridge/Postal/composer.json @@ -17,10 +17,10 @@ ], "require": { "php": ">=8.2", - "symfony/mailer": "^7.2" + "symfony/mailer": "^7.2|^8.0" }, "require-dev": { - "symfony/http-client": "^6.4|^7.0" + "symfony/http-client": "^6.4|^7.0|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Mailer\\Bridge\\Postal\\": "" }, diff --git a/src/Symfony/Component/Mailer/Bridge/Postmark/composer.json b/src/Symfony/Component/Mailer/Bridge/Postmark/composer.json index 0451fec7f96ce..45bc2c17b6630 100644 --- a/src/Symfony/Component/Mailer/Bridge/Postmark/composer.json +++ b/src/Symfony/Component/Mailer/Bridge/Postmark/composer.json @@ -18,11 +18,11 @@ "require": { "php": ">=8.2", "psr/event-dispatcher": "^1", - "symfony/mailer": "^7.2" + "symfony/mailer": "^7.2|^8.0" }, "require-dev": { - "symfony/http-client": "^6.4|^7.0", - "symfony/webhook": "^6.4|^7.0" + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/webhook": "^6.4|^7.0|^8.0" }, "conflict": { "symfony/http-foundation": "<6.4" diff --git a/src/Symfony/Component/Mailer/Bridge/Resend/composer.json b/src/Symfony/Component/Mailer/Bridge/Resend/composer.json index 0fe9a6f79df3c..66cdd2efbaa27 100644 --- a/src/Symfony/Component/Mailer/Bridge/Resend/composer.json +++ b/src/Symfony/Component/Mailer/Bridge/Resend/composer.json @@ -16,13 +16,13 @@ } ], "require": { - "php": ">=8.1", - "symfony/mailer": "^7.2" + "php": ">=8.2", + "symfony/mailer": "^7.2|^8.0" }, "require-dev": { - "symfony/http-client": "^6.4|^7.0", - "symfony/http-foundation": "^7.1", - "symfony/webhook": "^6.4|^7.0" + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/http-foundation": "^7.1|^8.0", + "symfony/webhook": "^6.4|^7.0|^8.0" }, "conflict": { "symfony/http-foundation": "<7.1" diff --git a/src/Symfony/Component/Mailer/Bridge/Scaleway/composer.json b/src/Symfony/Component/Mailer/Bridge/Scaleway/composer.json index 1ad65e470f641..f4c3e825d86d1 100644 --- a/src/Symfony/Component/Mailer/Bridge/Scaleway/composer.json +++ b/src/Symfony/Component/Mailer/Bridge/Scaleway/composer.json @@ -16,11 +16,11 @@ } ], "require": { - "php": ">=8.1", - "symfony/mailer": "^7.2" + "php": ">=8.2", + "symfony/mailer": "^7.2|^8.0" }, "require-dev": { - "symfony/http-client": "^6.4|^7.0" + "symfony/http-client": "^6.4|^7.0|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Mailer\\Bridge\\Scaleway\\": "" }, diff --git a/src/Symfony/Component/Mailer/Bridge/Sendgrid/composer.json b/src/Symfony/Component/Mailer/Bridge/Sendgrid/composer.json index 700aabef20a7d..899b4f6d9d4d0 100644 --- a/src/Symfony/Component/Mailer/Bridge/Sendgrid/composer.json +++ b/src/Symfony/Component/Mailer/Bridge/Sendgrid/composer.json @@ -17,11 +17,11 @@ ], "require": { "php": ">=8.2", - "symfony/mailer": "^7.2" + "symfony/mailer": "^7.2|^8.0" }, "require-dev": { - "symfony/http-client": "^6.4|^7.0", - "symfony/webhook": "^7.2" + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/webhook": "^7.2|^8.0" }, "conflict": { "symfony/mime": "<6.4", diff --git a/src/Symfony/Component/Mailer/Bridge/Sweego/composer.json b/src/Symfony/Component/Mailer/Bridge/Sweego/composer.json index 4fbe23334d574..4db381b4a9816 100644 --- a/src/Symfony/Component/Mailer/Bridge/Sweego/composer.json +++ b/src/Symfony/Component/Mailer/Bridge/Sweego/composer.json @@ -16,13 +16,13 @@ } ], "require": { - "php": ">=8.1", - "symfony/mailer": "^7.2" + "php": ">=8.2", + "symfony/mailer": "^7.2|^8.0" }, "require-dev": { - "symfony/http-client": "^6.4|^7.0", - "symfony/http-foundation": "^7.1", - "symfony/webhook": "^6.4|^7.0" + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/http-foundation": "^7.1|^8.0", + "symfony/webhook": "^6.4|^7.0|^8.0" }, "conflict": { "symfony/http-foundation": "<7.1" diff --git a/src/Symfony/Component/Mailer/composer.json b/src/Symfony/Component/Mailer/composer.json index 4336e725133fc..105fa39cd46bb 100644 --- a/src/Symfony/Component/Mailer/composer.json +++ b/src/Symfony/Component/Mailer/composer.json @@ -20,15 +20,15 @@ "egulias/email-validator": "^2.1.10|^3|^4", "psr/event-dispatcher": "^1", "psr/log": "^1|^2|^3", - "symfony/event-dispatcher": "^6.4|^7.0", - "symfony/mime": "^7.2", + "symfony/event-dispatcher": "^6.4|^7.0|^8.0", + "symfony/mime": "^7.2|^8.0", "symfony/service-contracts": "^2.5|^3" }, "require-dev": { - "symfony/console": "^6.4|^7.0", - "symfony/http-client": "^6.4|^7.0", - "symfony/messenger": "^6.4|^7.0", - "symfony/twig-bridge": "^6.4|^7.0" + "symfony/console": "^6.4|^7.0|^8.0", + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/messenger": "^6.4|^7.0|^8.0", + "symfony/twig-bridge": "^6.4|^7.0|^8.0" }, "conflict": { "symfony/http-client-contracts": "<2.5", diff --git a/src/Symfony/Component/Messenger/Bridge/AmazonSqs/composer.json b/src/Symfony/Component/Messenger/Bridge/AmazonSqs/composer.json index f334cd349c969..9e6904978670d 100644 --- a/src/Symfony/Component/Messenger/Bridge/AmazonSqs/composer.json +++ b/src/Symfony/Component/Messenger/Bridge/AmazonSqs/composer.json @@ -19,14 +19,14 @@ "php": ">=8.2", "async-aws/core": "^1.7", "async-aws/sqs": "^1.0|^2.0", - "symfony/messenger": "^7.3", + "symfony/messenger": "^7.3|^8.0", "symfony/service-contracts": "^2.5|^3", "psr/log": "^1|^2|^3" }, "require-dev": { "symfony/http-client-contracts": "^2.5|^3", - "symfony/property-access": "^6.4|^7.0", - "symfony/serializer": "^6.4|^7.0" + "symfony/property-access": "^6.4|^7.0|^8.0", + "symfony/serializer": "^6.4|^7.0|^8.0" }, "conflict": { "symfony/http-client-contracts": "<2.5" diff --git a/src/Symfony/Component/Messenger/Bridge/Amqp/composer.json b/src/Symfony/Component/Messenger/Bridge/Amqp/composer.json index 7e078f524fb9f..fcc2ceba9906e 100644 --- a/src/Symfony/Component/Messenger/Bridge/Amqp/composer.json +++ b/src/Symfony/Component/Messenger/Bridge/Amqp/composer.json @@ -18,13 +18,13 @@ "require": { "php": ">=8.2", "ext-amqp": "*", - "symfony/messenger": "^7.3" + "symfony/messenger": "^7.3|^8.0" }, "require-dev": { - "symfony/event-dispatcher": "^6.4|^7.0", - "symfony/process": "^6.4|^7.0", - "symfony/property-access": "^6.4|^7.0", - "symfony/serializer": "^6.4|^7.0" + "symfony/event-dispatcher": "^6.4|^7.0|^8.0", + "symfony/process": "^6.4|^7.0|^8.0", + "symfony/property-access": "^6.4|^7.0|^8.0", + "symfony/serializer": "^6.4|^7.0|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Messenger\\Bridge\\Amqp\\": "" }, diff --git a/src/Symfony/Component/Messenger/Bridge/Beanstalkd/composer.json b/src/Symfony/Component/Messenger/Bridge/Beanstalkd/composer.json index bed9817cedab3..a96066c79790b 100644 --- a/src/Symfony/Component/Messenger/Bridge/Beanstalkd/composer.json +++ b/src/Symfony/Component/Messenger/Bridge/Beanstalkd/composer.json @@ -14,11 +14,11 @@ "require": { "php": ">=8.2", "pda/pheanstalk": "^5.1|^7.0", - "symfony/messenger": "^7.3" + "symfony/messenger": "^7.3|^8.0" }, "require-dev": { - "symfony/property-access": "^6.4|^7.0", - "symfony/serializer": "^6.4|^7.0" + "symfony/property-access": "^6.4|^7.0|^8.0", + "symfony/serializer": "^6.4|^7.0|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Messenger\\Bridge\\Beanstalkd\\": "" }, diff --git a/src/Symfony/Component/Messenger/Bridge/Doctrine/composer.json b/src/Symfony/Component/Messenger/Bridge/Doctrine/composer.json index eabf0a9138c91..8f98bfc979092 100644 --- a/src/Symfony/Component/Messenger/Bridge/Doctrine/composer.json +++ b/src/Symfony/Component/Messenger/Bridge/Doctrine/composer.json @@ -18,13 +18,13 @@ "require": { "php": ">=8.2", "doctrine/dbal": "^3.6|^4", - "symfony/messenger": "^7.2", + "symfony/messenger": "^7.2|^8.0", "symfony/service-contracts": "^2.5|^3" }, "require-dev": { "doctrine/persistence": "^1.3|^2|^3", - "symfony/property-access": "^6.4|^7.0", - "symfony/serializer": "^6.4|^7.0" + "symfony/property-access": "^6.4|^7.0|^8.0", + "symfony/serializer": "^6.4|^7.0|^8.0" }, "conflict": { "doctrine/persistence": "<1.3" diff --git a/src/Symfony/Component/Messenger/Bridge/Redis/composer.json b/src/Symfony/Component/Messenger/Bridge/Redis/composer.json index 050211bb2d36a..d02f4ec0df1be 100644 --- a/src/Symfony/Component/Messenger/Bridge/Redis/composer.json +++ b/src/Symfony/Component/Messenger/Bridge/Redis/composer.json @@ -18,11 +18,11 @@ "require": { "php": ">=8.2", "ext-redis": "*", - "symfony/messenger": "^7.3" + "symfony/messenger": "^7.3|^8.0" }, "require-dev": { - "symfony/property-access": "^6.4|^7.0", - "symfony/serializer": "^6.4|^7.0" + "symfony/property-access": "^6.4|^7.0|^8.0", + "symfony/serializer": "^6.4|^7.0|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Messenger\\Bridge\\Redis\\": "" }, diff --git a/src/Symfony/Component/Messenger/composer.json b/src/Symfony/Component/Messenger/composer.json index 94de9f2439c95..523513be77651 100644 --- a/src/Symfony/Component/Messenger/composer.json +++ b/src/Symfony/Component/Messenger/composer.json @@ -18,24 +18,24 @@ "require": { "php": ">=8.2", "psr/log": "^1|^2|^3", - "symfony/clock": "^6.4|^7.0", + "symfony/clock": "^6.4|^7.0|^8.0", "symfony/deprecation-contracts": "^2.5|^3" }, "require-dev": { "psr/cache": "^1.0|^2.0|^3.0", - "symfony/console": "^7.2", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/event-dispatcher": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/process": "^6.4|^7.0", - "symfony/property-access": "^6.4|^7.0", - "symfony/lock": "^6.4|^7.0", - "symfony/rate-limiter": "^6.4|^7.0", - "symfony/routing": "^6.4|^7.0", - "symfony/serializer": "^6.4|^7.0", + "symfony/console": "^7.2|^8.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/event-dispatcher": "^6.4|^7.0|^8.0", + "symfony/http-kernel": "^6.4|^7.0|^8.0", + "symfony/process": "^6.4|^7.0|^8.0", + "symfony/property-access": "^6.4|^7.0|^8.0", + "symfony/lock": "^6.4|^7.0|^8.0", + "symfony/rate-limiter": "^6.4|^7.0|^8.0", + "symfony/routing": "^6.4|^7.0|^8.0", + "symfony/serializer": "^6.4|^7.0|^8.0", "symfony/service-contracts": "^2.5|^3", - "symfony/stopwatch": "^6.4|^7.0", - "symfony/validator": "^6.4|^7.0" + "symfony/stopwatch": "^6.4|^7.0|^8.0", + "symfony/validator": "^6.4|^7.0|^8.0" }, "conflict": { "symfony/console": "<7.2", diff --git a/src/Symfony/Component/Mime/composer.json b/src/Symfony/Component/Mime/composer.json index 5304bdf36d90b..e5cbc3cb651a4 100644 --- a/src/Symfony/Component/Mime/composer.json +++ b/src/Symfony/Component/Mime/composer.json @@ -24,11 +24,11 @@ "egulias/email-validator": "^2.1.10|^3.1|^4", "league/html-to-markdown": "^5.0", "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/process": "^6.4|^7.0", - "symfony/property-access": "^6.4|^7.0", - "symfony/property-info": "^6.4|^7.0", - "symfony/serializer": "^6.4.3|^7.0.3" + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/process": "^6.4|^7.0|^8.0", + "symfony/property-access": "^6.4|^7.0|^8.0", + "symfony/property-info": "^6.4|^7.0|^8.0", + "symfony/serializer": "^6.4.3|^7.0.3|^8.0" }, "conflict": { "egulias/email-validator": "~3.0.0", diff --git a/src/Symfony/Component/Notifier/Bridge/AllMySms/composer.json b/src/Symfony/Component/Notifier/Bridge/AllMySms/composer.json index cdfed6cfc1f8c..d3e2a3756b51f 100644 --- a/src/Symfony/Component/Notifier/Bridge/AllMySms/composer.json +++ b/src/Symfony/Component/Notifier/Bridge/AllMySms/composer.json @@ -17,8 +17,8 @@ ], "require": { "php": ">=8.2", - "symfony/http-client": "^6.4|^7.0", - "symfony/notifier": "^7.3" + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/notifier": "^7.3|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Notifier\\Bridge\\AllMySms\\": "" }, diff --git a/src/Symfony/Component/Notifier/Bridge/AmazonSns/composer.json b/src/Symfony/Component/Notifier/Bridge/AmazonSns/composer.json index 7c75c725424f1..8bbd2e750db1e 100644 --- a/src/Symfony/Component/Notifier/Bridge/AmazonSns/composer.json +++ b/src/Symfony/Component/Notifier/Bridge/AmazonSns/composer.json @@ -17,8 +17,8 @@ ], "require": { "php": ">=8.2", - "symfony/http-client": "^6.4|^7.0", - "symfony/notifier": "^7.2", + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/notifier": "^7.2|^8.0", "async-aws/sns": "^1.0" }, "autoload": { diff --git a/src/Symfony/Component/Notifier/Bridge/Bandwidth/composer.json b/src/Symfony/Component/Notifier/Bridge/Bandwidth/composer.json index 3dae426e42b46..4255e3d08a571 100644 --- a/src/Symfony/Component/Notifier/Bridge/Bandwidth/composer.json +++ b/src/Symfony/Component/Notifier/Bridge/Bandwidth/composer.json @@ -20,11 +20,11 @@ ], "require": { "php": ">=8.2", - "symfony/http-client": "^6.4|^7.0", - "symfony/notifier": "^7.2" + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/notifier": "^7.2|^8.0" }, "require-dev": { - "symfony/event-dispatcher": "^6.4|^7.0" + "symfony/event-dispatcher": "^6.4|^7.0|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Notifier\\Bridge\\Bandwidth\\": "" }, diff --git a/src/Symfony/Component/Notifier/Bridge/Bluesky/composer.json b/src/Symfony/Component/Notifier/Bridge/Bluesky/composer.json index b6f2a542b6258..82aab39f5f248 100644 --- a/src/Symfony/Component/Notifier/Bridge/Bluesky/composer.json +++ b/src/Symfony/Component/Notifier/Bridge/Bluesky/composer.json @@ -22,13 +22,13 @@ "require": { "php": ">=8.2", "psr/log": "^1|^2|^3", - "symfony/clock": "^6.4|^7.0", - "symfony/http-client": "^6.4|^7.0", - "symfony/notifier": "^7.3", - "symfony/string": "^6.4|^7.0" + "symfony/clock": "^6.4|^7.0|^8.0", + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/notifier": "^7.3|^8.0", + "symfony/string": "^6.4|^7.0|^8.0" }, "require-dev": { - "symfony/mime": "^6.4|^7.0" + "symfony/mime": "^6.4|^7.0|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Notifier\\Bridge\\Bluesky\\": "" }, diff --git a/src/Symfony/Component/Notifier/Bridge/Brevo/composer.json b/src/Symfony/Component/Notifier/Bridge/Brevo/composer.json index 96da9a51281de..fa530a5ebadab 100644 --- a/src/Symfony/Component/Notifier/Bridge/Brevo/composer.json +++ b/src/Symfony/Component/Notifier/Bridge/Brevo/composer.json @@ -17,11 +17,11 @@ ], "require": { "php": ">=8.2", - "symfony/http-client": "^6.4|^7.0", - "symfony/notifier": "^7.3" + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/notifier": "^7.3|^8.0" }, "require-dev": { - "symfony/event-dispatcher": "^5.4|^6.0|^7.0" + "symfony/event-dispatcher": "^6.4|^7.0|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Notifier\\Bridge\\Brevo\\": "" }, diff --git a/src/Symfony/Component/Notifier/Bridge/Chatwork/composer.json b/src/Symfony/Component/Notifier/Bridge/Chatwork/composer.json index c1cbe7a01adaa..edc0c6395dc06 100644 --- a/src/Symfony/Component/Notifier/Bridge/Chatwork/composer.json +++ b/src/Symfony/Component/Notifier/Bridge/Chatwork/composer.json @@ -17,8 +17,8 @@ ], "require": { "php": ">=8.2", - "symfony/http-client": "^6.4|^7.0", - "symfony/notifier": "^7.2" + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/notifier": "^7.2|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Notifier\\Bridge\\Chatwork\\": "" }, diff --git a/src/Symfony/Component/Notifier/Bridge/ClickSend/composer.json b/src/Symfony/Component/Notifier/Bridge/ClickSend/composer.json index 1676fea9e458f..5f264d6403adf 100644 --- a/src/Symfony/Component/Notifier/Bridge/ClickSend/composer.json +++ b/src/Symfony/Component/Notifier/Bridge/ClickSend/composer.json @@ -20,11 +20,11 @@ ], "require": { "php": ">=8.2", - "symfony/http-client": "^6.4|^7.0", - "symfony/notifier": "^7.2" + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/notifier": "^7.2|^8.0" }, "require-dev": { - "symfony/event-dispatcher": "^6.4|^7.0" + "symfony/event-dispatcher": "^6.4|^7.0|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Notifier\\Bridge\\ClickSend\\": "" }, diff --git a/src/Symfony/Component/Notifier/Bridge/Clickatell/composer.json b/src/Symfony/Component/Notifier/Bridge/Clickatell/composer.json index 020ce41f9ca12..1f7c9d08b1605 100644 --- a/src/Symfony/Component/Notifier/Bridge/Clickatell/composer.json +++ b/src/Symfony/Component/Notifier/Bridge/Clickatell/composer.json @@ -21,8 +21,8 @@ ], "require": { "php": ">=8.2", - "symfony/http-client": "^6.4|^7.0", - "symfony/notifier": "^7.2" + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/notifier": "^7.2|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Notifier\\Bridge\\Clickatell\\": "" }, diff --git a/src/Symfony/Component/Notifier/Bridge/ContactEveryone/composer.json b/src/Symfony/Component/Notifier/Bridge/ContactEveryone/composer.json index 6e18ed4424747..3ccdab9f9ebc4 100644 --- a/src/Symfony/Component/Notifier/Bridge/ContactEveryone/composer.json +++ b/src/Symfony/Component/Notifier/Bridge/ContactEveryone/composer.json @@ -17,8 +17,8 @@ ], "require": { "php": ">=8.2", - "symfony/http-client": "^6.4|^7.0", - "symfony/notifier": "^7.2" + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/notifier": "^7.2|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Notifier\\Bridge\\ContactEveryone\\": "" }, diff --git a/src/Symfony/Component/Notifier/Bridge/Discord/composer.json b/src/Symfony/Component/Notifier/Bridge/Discord/composer.json index 4567a41f14f65..76ac74d512119 100644 --- a/src/Symfony/Component/Notifier/Bridge/Discord/composer.json +++ b/src/Symfony/Component/Notifier/Bridge/Discord/composer.json @@ -17,8 +17,8 @@ ], "require": { "php": ">=8.2", - "symfony/http-client": "^6.4|^7.0", - "symfony/notifier": "^7.2", + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/notifier": "^7.2|^8.0", "symfony/polyfill-mbstring": "^1.0" }, "autoload": { diff --git a/src/Symfony/Component/Notifier/Bridge/Engagespot/composer.json b/src/Symfony/Component/Notifier/Bridge/Engagespot/composer.json index 917b8304e9636..dd9be4b9bba3f 100644 --- a/src/Symfony/Component/Notifier/Bridge/Engagespot/composer.json +++ b/src/Symfony/Component/Notifier/Bridge/Engagespot/composer.json @@ -17,8 +17,8 @@ ], "require": { "php": ">=8.2", - "symfony/http-client": "^6.4|^7.0", - "symfony/notifier": "^7.2" + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/notifier": "^7.2|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Notifier\\Bridge\\Engagespot\\": "" }, diff --git a/src/Symfony/Component/Notifier/Bridge/Esendex/composer.json b/src/Symfony/Component/Notifier/Bridge/Esendex/composer.json index a7beb52075fa3..584c309000367 100644 --- a/src/Symfony/Component/Notifier/Bridge/Esendex/composer.json +++ b/src/Symfony/Component/Notifier/Bridge/Esendex/composer.json @@ -17,8 +17,8 @@ ], "require": { "php": ">=8.2", - "symfony/http-client": "^6.4|^7.0", - "symfony/notifier": "^7.2" + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/notifier": "^7.2|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Notifier\\Bridge\\Esendex\\": "" }, diff --git a/src/Symfony/Component/Notifier/Bridge/Expo/composer.json b/src/Symfony/Component/Notifier/Bridge/Expo/composer.json index 002a08c0152a2..015e98d1f6c03 100644 --- a/src/Symfony/Component/Notifier/Bridge/Expo/composer.json +++ b/src/Symfony/Component/Notifier/Bridge/Expo/composer.json @@ -17,8 +17,8 @@ ], "require": { "php": ">=8.2", - "symfony/http-client": "^6.4|^7.0", - "symfony/notifier": "^7.2" + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/notifier": "^7.2|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Notifier\\Bridge\\Expo\\": "" }, diff --git a/src/Symfony/Component/Notifier/Bridge/FakeChat/composer.json b/src/Symfony/Component/Notifier/Bridge/FakeChat/composer.json index 24e05807ec32d..447436ba0fd71 100644 --- a/src/Symfony/Component/Notifier/Bridge/FakeChat/composer.json +++ b/src/Symfony/Component/Notifier/Bridge/FakeChat/composer.json @@ -22,12 +22,12 @@ ], "require": { "php": ">=8.2", - "symfony/http-client": "^6.4|^7.0", - "symfony/notifier": "^7.2" + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/notifier": "^7.2|^8.0" }, "require-dev": { "psr/log": "^1|^2|^3", - "symfony/mailer": "^6.4|^7.0" + "symfony/mailer": "^6.4|^7.0|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Notifier\\Bridge\\FakeChat\\": "" }, diff --git a/src/Symfony/Component/Notifier/Bridge/FakeSms/composer.json b/src/Symfony/Component/Notifier/Bridge/FakeSms/composer.json index 366ec1b8c48fb..4b5a022065e0f 100644 --- a/src/Symfony/Component/Notifier/Bridge/FakeSms/composer.json +++ b/src/Symfony/Component/Notifier/Bridge/FakeSms/composer.json @@ -22,12 +22,12 @@ ], "require": { "php": ">=8.2", - "symfony/http-client": "^6.4|^7.0", - "symfony/notifier": "^7.2" + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/notifier": "^7.2|^8.0" }, "require-dev": { "psr/log": "^1|^2|^3", - "symfony/mailer": "^6.4|^7.0" + "symfony/mailer": "^6.4|^7.0|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Notifier\\Bridge\\FakeSms\\": "" }, diff --git a/src/Symfony/Component/Notifier/Bridge/Firebase/composer.json b/src/Symfony/Component/Notifier/Bridge/Firebase/composer.json index fa18127a3f874..af23aabbec7b8 100644 --- a/src/Symfony/Component/Notifier/Bridge/Firebase/composer.json +++ b/src/Symfony/Component/Notifier/Bridge/Firebase/composer.json @@ -17,8 +17,8 @@ ], "require": { "php": ">=8.2", - "symfony/http-client": "^6.4|^7.0", - "symfony/notifier": "^7.2" + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/notifier": "^7.2|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Notifier\\Bridge\\Firebase\\": "" }, diff --git a/src/Symfony/Component/Notifier/Bridge/FortySixElks/composer.json b/src/Symfony/Component/Notifier/Bridge/FortySixElks/composer.json index 05a1311febf52..83991430bca7c 100644 --- a/src/Symfony/Component/Notifier/Bridge/FortySixElks/composer.json +++ b/src/Symfony/Component/Notifier/Bridge/FortySixElks/composer.json @@ -17,8 +17,8 @@ ], "require": { "php": ">=8.2", - "symfony/http-client": "^6.4|^7.0", - "symfony/notifier": "^7.2" + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/notifier": "^7.2|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Notifier\\Bridge\\FortySixElks\\": "" }, diff --git a/src/Symfony/Component/Notifier/Bridge/FreeMobile/composer.json b/src/Symfony/Component/Notifier/Bridge/FreeMobile/composer.json index 8067f44f261f9..1853af7e319c7 100644 --- a/src/Symfony/Component/Notifier/Bridge/FreeMobile/composer.json +++ b/src/Symfony/Component/Notifier/Bridge/FreeMobile/composer.json @@ -18,8 +18,8 @@ ], "require": { "php": ">=8.2", - "symfony/http-client": "^6.4|^7.0", - "symfony/notifier": "^7.2" + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/notifier": "^7.2|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Notifier\\Bridge\\FreeMobile\\": "" }, diff --git a/src/Symfony/Component/Notifier/Bridge/GatewayApi/composer.json b/src/Symfony/Component/Notifier/Bridge/GatewayApi/composer.json index 7abffe9ba4581..1a2f8290944f4 100644 --- a/src/Symfony/Component/Notifier/Bridge/GatewayApi/composer.json +++ b/src/Symfony/Component/Notifier/Bridge/GatewayApi/composer.json @@ -21,8 +21,8 @@ ], "require": { "php": ">=8.2", - "symfony/http-client": "^6.4|^7.0", - "symfony/notifier": "^7.2" + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/notifier": "^7.2|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Notifier\\Bridge\\GatewayApi\\": "" }, diff --git a/src/Symfony/Component/Notifier/Bridge/GoIp/composer.json b/src/Symfony/Component/Notifier/Bridge/GoIp/composer.json index 166675db8ca9b..a643c08361450 100644 --- a/src/Symfony/Component/Notifier/Bridge/GoIp/composer.json +++ b/src/Symfony/Component/Notifier/Bridge/GoIp/composer.json @@ -17,8 +17,8 @@ ], "require": { "php": ">=8.2", - "symfony/http-client": "^6.4|^7.0", - "symfony/notifier": "^7.3" + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/notifier": "^7.3|^8.0" }, "autoload": { "psr-4": { diff --git a/src/Symfony/Component/Notifier/Bridge/GoogleChat/composer.json b/src/Symfony/Component/Notifier/Bridge/GoogleChat/composer.json index 645b5320b552a..37ab7ee264bbe 100644 --- a/src/Symfony/Component/Notifier/Bridge/GoogleChat/composer.json +++ b/src/Symfony/Component/Notifier/Bridge/GoogleChat/composer.json @@ -17,8 +17,8 @@ ], "require": { "php": ">=8.2", - "symfony/http-client": "^6.4|^7.0", - "symfony/notifier": "^7.3" + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/notifier": "^7.3|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Notifier\\Bridge\\GoogleChat\\": "" }, diff --git a/src/Symfony/Component/Notifier/Bridge/Infobip/composer.json b/src/Symfony/Component/Notifier/Bridge/Infobip/composer.json index a76a85aefd36b..15b41d40a2cd1 100644 --- a/src/Symfony/Component/Notifier/Bridge/Infobip/composer.json +++ b/src/Symfony/Component/Notifier/Bridge/Infobip/composer.json @@ -21,8 +21,8 @@ ], "require": { "php": ">=8.2", - "symfony/http-client": "^6.4|^7.0", - "symfony/notifier": "^7.2" + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/notifier": "^7.2|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Notifier\\Bridge\\Infobip\\": "" }, diff --git a/src/Symfony/Component/Notifier/Bridge/Iqsms/composer.json b/src/Symfony/Component/Notifier/Bridge/Iqsms/composer.json index d36db5d2bebf3..f18db7b4f44f8 100644 --- a/src/Symfony/Component/Notifier/Bridge/Iqsms/composer.json +++ b/src/Symfony/Component/Notifier/Bridge/Iqsms/composer.json @@ -21,8 +21,8 @@ ], "require": { "php": ">=8.2", - "symfony/http-client": "^6.4|^7.0", - "symfony/notifier": "^7.2" + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/notifier": "^7.2|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Notifier\\Bridge\\Iqsms\\": "" }, diff --git a/src/Symfony/Component/Notifier/Bridge/Isendpro/composer.json b/src/Symfony/Component/Notifier/Bridge/Isendpro/composer.json index b7ee9fdbf95f1..efa8ecc0dde24 100644 --- a/src/Symfony/Component/Notifier/Bridge/Isendpro/composer.json +++ b/src/Symfony/Component/Notifier/Bridge/Isendpro/composer.json @@ -21,11 +21,11 @@ ], "require": { "php": ">=8.2", - "symfony/http-client": "^6.4|^7.0", - "symfony/notifier": "^7.3" + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/notifier": "^7.3|^8.0" }, "require-dev": { - "symfony/event-dispatcher": "^6.4|^7.0" + "symfony/event-dispatcher": "^6.4|^7.0|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Notifier\\Bridge\\Isendpro\\": "" }, diff --git a/src/Symfony/Component/Notifier/Bridge/JoliNotif/composer.json b/src/Symfony/Component/Notifier/Bridge/JoliNotif/composer.json index e6512df786dc0..66e34613f96b2 100644 --- a/src/Symfony/Component/Notifier/Bridge/JoliNotif/composer.json +++ b/src/Symfony/Component/Notifier/Bridge/JoliNotif/composer.json @@ -23,8 +23,8 @@ "require": { "php": ">=8.2", "jolicode/jolinotif": "^2.7.2|^3.0", - "symfony/http-client": "^7.2", - "symfony/notifier": "^7.3" + "symfony/http-client": "^7.2|^8.0", + "symfony/notifier": "^7.3|^8.0" }, "autoload": { "psr-4": { diff --git a/src/Symfony/Component/Notifier/Bridge/KazInfoTeh/composer.json b/src/Symfony/Component/Notifier/Bridge/KazInfoTeh/composer.json index aa2a2d126bf4c..38ea6acc5535b 100644 --- a/src/Symfony/Component/Notifier/Bridge/KazInfoTeh/composer.json +++ b/src/Symfony/Component/Notifier/Bridge/KazInfoTeh/composer.json @@ -19,8 +19,8 @@ "require": { "php": ">=8.2", "ext-simplexml": "*", - "symfony/http-client": "^6.4|^7.0", - "symfony/notifier": "^7.2" + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/notifier": "^7.2|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Notifier\\Bridge\\KazInfoTeh\\": "" }, diff --git a/src/Symfony/Component/Notifier/Bridge/LightSms/composer.json b/src/Symfony/Component/Notifier/Bridge/LightSms/composer.json index 18a3d52027894..9cb0e2e092ff6 100644 --- a/src/Symfony/Component/Notifier/Bridge/LightSms/composer.json +++ b/src/Symfony/Component/Notifier/Bridge/LightSms/composer.json @@ -17,8 +17,8 @@ ], "require": { "php": ">=8.2", - "symfony/http-client": "^6.4|^7.0", - "symfony/notifier": "^7.2" + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/notifier": "^7.2|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Notifier\\Bridge\\LightSms\\": "" }, diff --git a/src/Symfony/Component/Notifier/Bridge/LineBot/composer.json b/src/Symfony/Component/Notifier/Bridge/LineBot/composer.json index 7e200237c7cfa..f5bb1102e4f13 100644 --- a/src/Symfony/Component/Notifier/Bridge/LineBot/composer.json +++ b/src/Symfony/Component/Notifier/Bridge/LineBot/composer.json @@ -17,11 +17,11 @@ ], "require": { "php": ">=8.2", - "symfony/http-client": "^6.4|^7.0", - "symfony/notifier": "^7.2" + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/notifier": "^7.2|^8.0" }, "require-dev": { - "symfony/event-dispatcher": "^6.4|^7.0" + "symfony/event-dispatcher": "^6.4|^7.0|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Notifier\\Bridge\\LineBot\\": "" }, diff --git a/src/Symfony/Component/Notifier/Bridge/LineNotify/composer.json b/src/Symfony/Component/Notifier/Bridge/LineNotify/composer.json index c7af719ead66d..93aceb6388d60 100644 --- a/src/Symfony/Component/Notifier/Bridge/LineNotify/composer.json +++ b/src/Symfony/Component/Notifier/Bridge/LineNotify/composer.json @@ -17,11 +17,11 @@ ], "require": { "php": ">=8.2", - "symfony/http-client": "^6.4|^7.0", - "symfony/notifier": "^7.2" + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/notifier": "^7.2|^8.0" }, "require-dev": { - "symfony/event-dispatcher": "^6.4|^7.0" + "symfony/event-dispatcher": "^6.4|^7.0|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Notifier\\Bridge\\LineNotify\\": "" }, diff --git a/src/Symfony/Component/Notifier/Bridge/LinkedIn/composer.json b/src/Symfony/Component/Notifier/Bridge/LinkedIn/composer.json index 2886f0eba9b68..dea4cd68b967e 100644 --- a/src/Symfony/Component/Notifier/Bridge/LinkedIn/composer.json +++ b/src/Symfony/Component/Notifier/Bridge/LinkedIn/composer.json @@ -17,8 +17,8 @@ ], "require": { "php": ">=8.2", - "symfony/http-client": "^6.4|^7.0", - "symfony/notifier": "^7.3" + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/notifier": "^7.3|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Notifier\\Bridge\\LinkedIn\\": "" }, diff --git a/src/Symfony/Component/Notifier/Bridge/Lox24/composer.json b/src/Symfony/Component/Notifier/Bridge/Lox24/composer.json index 98f09a409937d..3664936585b35 100644 --- a/src/Symfony/Component/Notifier/Bridge/Lox24/composer.json +++ b/src/Symfony/Component/Notifier/Bridge/Lox24/composer.json @@ -10,12 +10,12 @@ "homepage": "https://symfony.com", "license": "MIT", "require": { - "php": ">=8.1", - "symfony/http-client": "^6.4|^7.0", - "symfony/notifier": "^7.2" + "php": ">=8.2", + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/notifier": "^7.2|^8.0" }, "require-dev": { - "symfony/webhook": "^6.4|^7.0" + "symfony/webhook": "^6.4|^7.0|^8.0" }, "autoload": { "psr-4": { diff --git a/src/Symfony/Component/Notifier/Bridge/Mailjet/composer.json b/src/Symfony/Component/Notifier/Bridge/Mailjet/composer.json index 9aa215e815fb2..fdf8269bf2360 100644 --- a/src/Symfony/Component/Notifier/Bridge/Mailjet/composer.json +++ b/src/Symfony/Component/Notifier/Bridge/Mailjet/composer.json @@ -21,8 +21,8 @@ ], "require": { "php": ">=8.2", - "symfony/http-client": "^6.4|^7.0", - "symfony/notifier": "^7.2" + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/notifier": "^7.2|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Notifier\\Bridge\\Mailjet\\": "" }, diff --git a/src/Symfony/Component/Notifier/Bridge/Mastodon/composer.json b/src/Symfony/Component/Notifier/Bridge/Mastodon/composer.json index d09d403fc7b36..190e279e84f4d 100644 --- a/src/Symfony/Component/Notifier/Bridge/Mastodon/composer.json +++ b/src/Symfony/Component/Notifier/Bridge/Mastodon/composer.json @@ -17,11 +17,11 @@ ], "require": { "php": ">=8.2", - "symfony/http-client": "^6.4|^7.0", - "symfony/notifier": "^7.2" + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/notifier": "^7.2|^8.0" }, "require-dev": { - "symfony/mime": "^6.4|^7.0" + "symfony/mime": "^6.4|^7.0|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Notifier\\Bridge\\Mastodon\\": "" }, diff --git a/src/Symfony/Component/Notifier/Bridge/Matrix/composer.json b/src/Symfony/Component/Notifier/Bridge/Matrix/composer.json index 22ce807af3364..f51c3804bfae7 100644 --- a/src/Symfony/Component/Notifier/Bridge/Matrix/composer.json +++ b/src/Symfony/Component/Notifier/Bridge/Matrix/composer.json @@ -17,9 +17,9 @@ ], "require": { "php": ">=8.2", - "symfony/notifier": "^7.3", - "symfony/uid": "^7.2", - "symfony/http-client": "^6.4|^7.0" + "symfony/notifier": "^7.3|^8.0", + "symfony/uid": "^7.2|^8.0", + "symfony/http-client": "^6.4|^7.0|^8.0" }, "autoload": { "psr-4": { diff --git a/src/Symfony/Component/Notifier/Bridge/Mattermost/composer.json b/src/Symfony/Component/Notifier/Bridge/Mattermost/composer.json index 958d64f42f865..0a0a72c535669 100644 --- a/src/Symfony/Component/Notifier/Bridge/Mattermost/composer.json +++ b/src/Symfony/Component/Notifier/Bridge/Mattermost/composer.json @@ -17,8 +17,8 @@ ], "require": { "php": ">=8.2", - "symfony/http-client": "^6.4|^7.0", - "symfony/notifier": "^7.2" + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/notifier": "^7.2|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Notifier\\Bridge\\Mattermost\\": "" }, diff --git a/src/Symfony/Component/Notifier/Bridge/Mercure/composer.json b/src/Symfony/Component/Notifier/Bridge/Mercure/composer.json index ac965af31ca78..9920fe60f2abd 100644 --- a/src/Symfony/Component/Notifier/Bridge/Mercure/composer.json +++ b/src/Symfony/Component/Notifier/Bridge/Mercure/composer.json @@ -18,7 +18,7 @@ "require": { "php": ">=8.2", "symfony/mercure": "^0.5.2|^0.6", - "symfony/notifier": "^7.3", + "symfony/notifier": "^7.3|^8.0", "symfony/service-contracts": "^2.5|^3" }, "autoload": { diff --git a/src/Symfony/Component/Notifier/Bridge/MessageBird/composer.json b/src/Symfony/Component/Notifier/Bridge/MessageBird/composer.json index c1729e047f3fd..bffc9b345c13a 100644 --- a/src/Symfony/Component/Notifier/Bridge/MessageBird/composer.json +++ b/src/Symfony/Component/Notifier/Bridge/MessageBird/composer.json @@ -17,8 +17,8 @@ ], "require": { "php": ">=8.2", - "symfony/http-client": "^6.4|^7.0", - "symfony/notifier": "^7.2" + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/notifier": "^7.2|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Notifier\\Bridge\\MessageBird\\": "" }, diff --git a/src/Symfony/Component/Notifier/Bridge/MessageMedia/composer.json b/src/Symfony/Component/Notifier/Bridge/MessageMedia/composer.json index 187f9ed1fde88..e46a9e69de6fa 100644 --- a/src/Symfony/Component/Notifier/Bridge/MessageMedia/composer.json +++ b/src/Symfony/Component/Notifier/Bridge/MessageMedia/composer.json @@ -17,8 +17,8 @@ ], "require": { "php": ">=8.2", - "symfony/http-client": "^6.4|^7.0", - "symfony/notifier": "^7.2" + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/notifier": "^7.2|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Notifier\\Bridge\\MessageMedia\\": "" }, diff --git a/src/Symfony/Component/Notifier/Bridge/MicrosoftTeams/composer.json b/src/Symfony/Component/Notifier/Bridge/MicrosoftTeams/composer.json index 37722b03ec16a..28b83814f49ee 100644 --- a/src/Symfony/Component/Notifier/Bridge/MicrosoftTeams/composer.json +++ b/src/Symfony/Component/Notifier/Bridge/MicrosoftTeams/composer.json @@ -21,8 +21,8 @@ ], "require": { "php": ">=8.2", - "symfony/http-client": "^6.4|^7.0", - "symfony/notifier": "^7.2" + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/notifier": "^7.2|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Notifier\\Bridge\\MicrosoftTeams\\": "" }, diff --git a/src/Symfony/Component/Notifier/Bridge/Mobyt/composer.json b/src/Symfony/Component/Notifier/Bridge/Mobyt/composer.json index 1317236985478..1f14286f128a6 100644 --- a/src/Symfony/Component/Notifier/Bridge/Mobyt/composer.json +++ b/src/Symfony/Component/Notifier/Bridge/Mobyt/composer.json @@ -17,8 +17,8 @@ ], "require": { "php": ">=8.2", - "symfony/http-client": "^6.4|^7.0", - "symfony/notifier": "^7.2" + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/notifier": "^7.2|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Notifier\\Bridge\\Mobyt\\": "" }, diff --git a/src/Symfony/Component/Notifier/Bridge/Novu/composer.json b/src/Symfony/Component/Notifier/Bridge/Novu/composer.json index 999320b21523b..7a303afdb4aca 100644 --- a/src/Symfony/Component/Notifier/Bridge/Novu/composer.json +++ b/src/Symfony/Component/Notifier/Bridge/Novu/composer.json @@ -16,9 +16,9 @@ } ], "require": { - "php": ">=8.1", - "symfony/http-client": "^5.4|^6.0|^7.0", - "symfony/notifier": "^7.2" + "php": ">=8.2", + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/notifier": "^7.2|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Notifier\\Bridge\\Novu\\": "" }, diff --git a/src/Symfony/Component/Notifier/Bridge/Ntfy/composer.json b/src/Symfony/Component/Notifier/Bridge/Ntfy/composer.json index e15e8d511b973..6e7c25249dd27 100644 --- a/src/Symfony/Component/Notifier/Bridge/Ntfy/composer.json +++ b/src/Symfony/Component/Notifier/Bridge/Ntfy/composer.json @@ -17,9 +17,9 @@ ], "require": { "php": ">=8.2", - "symfony/clock": "^6.4|^7.0", - "symfony/http-client": "^6.4|^7.0", - "symfony/notifier": "^7.3" + "symfony/clock": "^6.4|^7.0|^8.0", + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/notifier": "^7.3|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Notifier\\Bridge\\Ntfy\\": "" }, diff --git a/src/Symfony/Component/Notifier/Bridge/Octopush/composer.json b/src/Symfony/Component/Notifier/Bridge/Octopush/composer.json index d081b539bc179..91f9e9fc6d7a0 100644 --- a/src/Symfony/Component/Notifier/Bridge/Octopush/composer.json +++ b/src/Symfony/Component/Notifier/Bridge/Octopush/composer.json @@ -17,8 +17,8 @@ ], "require": { "php": ">=8.2", - "symfony/http-client": "^6.4|^7.0", - "symfony/notifier": "^7.2" + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/notifier": "^7.2|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Notifier\\Bridge\\Octopush\\": "" }, diff --git a/src/Symfony/Component/Notifier/Bridge/OneSignal/composer.json b/src/Symfony/Component/Notifier/Bridge/OneSignal/composer.json index 2d3d243cf3884..35be562c547d6 100644 --- a/src/Symfony/Component/Notifier/Bridge/OneSignal/composer.json +++ b/src/Symfony/Component/Notifier/Bridge/OneSignal/composer.json @@ -17,8 +17,8 @@ ], "require": { "php": ">=8.2", - "symfony/http-client": "^6.4|^7.0", - "symfony/notifier": "^7.2" + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/notifier": "^7.2|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Notifier\\Bridge\\OneSignal\\": "" }, diff --git a/src/Symfony/Component/Notifier/Bridge/OrangeSms/composer.json b/src/Symfony/Component/Notifier/Bridge/OrangeSms/composer.json index 24923f1bc0bb9..a26866587b53b 100644 --- a/src/Symfony/Component/Notifier/Bridge/OrangeSms/composer.json +++ b/src/Symfony/Component/Notifier/Bridge/OrangeSms/composer.json @@ -17,8 +17,8 @@ ], "require": { "php": ">=8.2", - "symfony/http-client": "^6.4|^7.0", - "symfony/notifier": "^7.2" + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/notifier": "^7.2|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Notifier\\Bridge\\OrangeSms\\": "" }, diff --git a/src/Symfony/Component/Notifier/Bridge/OvhCloud/composer.json b/src/Symfony/Component/Notifier/Bridge/OvhCloud/composer.json index c105fcccdf9e0..ae82ed77dcc81 100644 --- a/src/Symfony/Component/Notifier/Bridge/OvhCloud/composer.json +++ b/src/Symfony/Component/Notifier/Bridge/OvhCloud/composer.json @@ -17,8 +17,8 @@ ], "require": { "php": ">=8.2", - "symfony/http-client": "^6.4|^7.0", - "symfony/notifier": "^7.3" + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/notifier": "^7.3|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Notifier\\Bridge\\OvhCloud\\": "" }, diff --git a/src/Symfony/Component/Notifier/Bridge/PagerDuty/composer.json b/src/Symfony/Component/Notifier/Bridge/PagerDuty/composer.json index b75ee3960c62a..f1f14ae047d52 100644 --- a/src/Symfony/Component/Notifier/Bridge/PagerDuty/composer.json +++ b/src/Symfony/Component/Notifier/Bridge/PagerDuty/composer.json @@ -17,8 +17,8 @@ ], "require": { "php": ">=8.2", - "symfony/http-client": "^6.4|^7.0", - "symfony/notifier": "^7.2" + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/notifier": "^7.2|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Notifier\\Bridge\\PagerDuty\\": "" }, diff --git a/src/Symfony/Component/Notifier/Bridge/Plivo/composer.json b/src/Symfony/Component/Notifier/Bridge/Plivo/composer.json index 4a4c3cb13fd21..ead7c057ae552 100644 --- a/src/Symfony/Component/Notifier/Bridge/Plivo/composer.json +++ b/src/Symfony/Component/Notifier/Bridge/Plivo/composer.json @@ -20,11 +20,11 @@ ], "require": { "php": ">=8.2", - "symfony/http-client": "^6.4|^7.0", - "symfony/notifier": "^7.2" + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/notifier": "^7.2|^8.0" }, "require-dev": { - "symfony/event-dispatcher": "^6.4|^7.0" + "symfony/event-dispatcher": "^6.4|^7.0|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Notifier\\Bridge\\Plivo\\": "" }, diff --git a/src/Symfony/Component/Notifier/Bridge/Primotexto/composer.json b/src/Symfony/Component/Notifier/Bridge/Primotexto/composer.json index e89e378e144e0..094a05b1e321d 100644 --- a/src/Symfony/Component/Notifier/Bridge/Primotexto/composer.json +++ b/src/Symfony/Component/Notifier/Bridge/Primotexto/composer.json @@ -17,8 +17,8 @@ ], "require": { "php": ">=8.2", - "symfony/http-client": "^6.4|^7.0", - "symfony/notifier": "^7.2" + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/notifier": "^7.2|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Notifier\\Bridge\\Primotexto\\": "" }, diff --git a/src/Symfony/Component/Notifier/Bridge/Pushover/composer.json b/src/Symfony/Component/Notifier/Bridge/Pushover/composer.json index 926267eee9dc8..70c14694afe0a 100644 --- a/src/Symfony/Component/Notifier/Bridge/Pushover/composer.json +++ b/src/Symfony/Component/Notifier/Bridge/Pushover/composer.json @@ -20,11 +20,11 @@ ], "require": { "php": ">=8.2", - "symfony/http-client": "^6.4|^7.0", - "symfony/notifier": "^7.2" + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/notifier": "^7.2|^8.0" }, "require-dev": { - "symfony/event-dispatcher": "^6.4|^7.0" + "symfony/event-dispatcher": "^6.4|^7.0|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Notifier\\Bridge\\Pushover\\": "" }, diff --git a/src/Symfony/Component/Notifier/Bridge/Pushy/composer.json b/src/Symfony/Component/Notifier/Bridge/Pushy/composer.json index e207e4b3a2811..e774ee4c52b71 100644 --- a/src/Symfony/Component/Notifier/Bridge/Pushy/composer.json +++ b/src/Symfony/Component/Notifier/Bridge/Pushy/composer.json @@ -21,11 +21,11 @@ ], "require": { "php": ">=8.2", - "symfony/http-client": "^6.4|^7.0", - "symfony/notifier": "^7.2" + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/notifier": "^7.2|^8.0" }, "require-dev": { - "symfony/event-dispatcher": "^6.4|^7.0" + "symfony/event-dispatcher": "^6.4|^7.0|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Notifier\\Bridge\\Pushy\\": "" }, diff --git a/src/Symfony/Component/Notifier/Bridge/Redlink/composer.json b/src/Symfony/Component/Notifier/Bridge/Redlink/composer.json index e56215f7b36ba..6398c1ea913ef 100644 --- a/src/Symfony/Component/Notifier/Bridge/Redlink/composer.json +++ b/src/Symfony/Component/Notifier/Bridge/Redlink/composer.json @@ -17,8 +17,8 @@ ], "require": { "php": ">=8.2", - "symfony/http-client": "^6.4|^7.0", - "symfony/notifier": "^7.2" + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/notifier": "^7.2|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Notifier\\Bridge\\Redlink\\": "" }, diff --git a/src/Symfony/Component/Notifier/Bridge/RingCentral/composer.json b/src/Symfony/Component/Notifier/Bridge/RingCentral/composer.json index df9f13c56189c..c05948b79acdb 100644 --- a/src/Symfony/Component/Notifier/Bridge/RingCentral/composer.json +++ b/src/Symfony/Component/Notifier/Bridge/RingCentral/composer.json @@ -20,11 +20,11 @@ ], "require": { "php": ">=8.2", - "symfony/http-client": "^6.4|^7.0", - "symfony/notifier": "^7.2" + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/notifier": "^7.2|^8.0" }, "require-dev": { - "symfony/event-dispatcher": "^6.4|^7.0" + "symfony/event-dispatcher": "^6.4|^7.0|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Notifier\\Bridge\\RingCentral\\": "" }, diff --git a/src/Symfony/Component/Notifier/Bridge/RocketChat/composer.json b/src/Symfony/Component/Notifier/Bridge/RocketChat/composer.json index bc7bd923340a8..31e312222c67d 100644 --- a/src/Symfony/Component/Notifier/Bridge/RocketChat/composer.json +++ b/src/Symfony/Component/Notifier/Bridge/RocketChat/composer.json @@ -17,8 +17,8 @@ ], "require": { "php": ">=8.2", - "symfony/http-client": "^6.4|^7.0", - "symfony/notifier": "^7.2" + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/notifier": "^7.2|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Notifier\\Bridge\\RocketChat\\": "" }, diff --git a/src/Symfony/Component/Notifier/Bridge/Sendberry/composer.json b/src/Symfony/Component/Notifier/Bridge/Sendberry/composer.json index 56a9e2163023e..2dcbd77c51b2b 100644 --- a/src/Symfony/Component/Notifier/Bridge/Sendberry/composer.json +++ b/src/Symfony/Component/Notifier/Bridge/Sendberry/composer.json @@ -17,8 +17,8 @@ ], "require": { "php": ">=8.2", - "symfony/http-client": "^6.4|^7.0", - "symfony/notifier": "^7.2" + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/notifier": "^7.2|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Notifier\\Bridge\\Sendberry\\": "" }, diff --git a/src/Symfony/Component/Notifier/Bridge/Sevenio/composer.json b/src/Symfony/Component/Notifier/Bridge/Sevenio/composer.json index c2b6d0b5264c7..2c489b47fb50b 100644 --- a/src/Symfony/Component/Notifier/Bridge/Sevenio/composer.json +++ b/src/Symfony/Component/Notifier/Bridge/Sevenio/composer.json @@ -17,8 +17,8 @@ ], "require": { "php": ">=8.2", - "symfony/http-client": "^6.4|^7.0", - "symfony/notifier": "^7.2" + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/notifier": "^7.2|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Notifier\\Bridge\\Sevenio\\": "" }, diff --git a/src/Symfony/Component/Notifier/Bridge/SimpleTextin/composer.json b/src/Symfony/Component/Notifier/Bridge/SimpleTextin/composer.json index f27e41c7b090a..8e1e6799135bb 100644 --- a/src/Symfony/Component/Notifier/Bridge/SimpleTextin/composer.json +++ b/src/Symfony/Component/Notifier/Bridge/SimpleTextin/composer.json @@ -20,11 +20,11 @@ ], "require": { "php": ">=8.2", - "symfony/http-client": "^6.4|^7.0", - "symfony/notifier": "^7.2" + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/notifier": "^7.2|^8.0" }, "require-dev": { - "symfony/event-dispatcher": "^6.4|^7.0" + "symfony/event-dispatcher": "^6.4|^7.0|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Notifier\\Bridge\\SimpleTextin\\": "" }, diff --git a/src/Symfony/Component/Notifier/Bridge/Sinch/composer.json b/src/Symfony/Component/Notifier/Bridge/Sinch/composer.json index 296393553b02d..8128c5bfa780d 100644 --- a/src/Symfony/Component/Notifier/Bridge/Sinch/composer.json +++ b/src/Symfony/Component/Notifier/Bridge/Sinch/composer.json @@ -17,8 +17,8 @@ ], "require": { "php": ">=8.2", - "symfony/http-client": "^6.4|^7.0", - "symfony/notifier": "^7.2" + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/notifier": "^7.2|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Notifier\\Bridge\\Sinch\\": "" }, diff --git a/src/Symfony/Component/Notifier/Bridge/Sipgate/composer.json b/src/Symfony/Component/Notifier/Bridge/Sipgate/composer.json index bba4c1bb1b652..12ffb1f792d82 100644 --- a/src/Symfony/Component/Notifier/Bridge/Sipgate/composer.json +++ b/src/Symfony/Component/Notifier/Bridge/Sipgate/composer.json @@ -17,8 +17,8 @@ ], "require": { "php": ">=8.2", - "symfony/http-client": "^6.4|^7.0", - "symfony/notifier": "^7.2" + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/notifier": "^7.2|^8.0" }, "autoload": { "psr-4": { diff --git a/src/Symfony/Component/Notifier/Bridge/Slack/composer.json b/src/Symfony/Component/Notifier/Bridge/Slack/composer.json index 8507a4d041254..bb6752dd37080 100644 --- a/src/Symfony/Component/Notifier/Bridge/Slack/composer.json +++ b/src/Symfony/Component/Notifier/Bridge/Slack/composer.json @@ -17,8 +17,8 @@ ], "require": { "php": ">=8.2", - "symfony/http-client": "^6.4|^7.0", - "symfony/notifier": "^7.2" + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/notifier": "^7.2|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Notifier\\Bridge\\Slack\\": "" }, diff --git a/src/Symfony/Component/Notifier/Bridge/Sms77/composer.json b/src/Symfony/Component/Notifier/Bridge/Sms77/composer.json index 8dd642e151321..25def742454a1 100644 --- a/src/Symfony/Component/Notifier/Bridge/Sms77/composer.json +++ b/src/Symfony/Component/Notifier/Bridge/Sms77/composer.json @@ -18,8 +18,8 @@ "require": { "php": ">=8.2", "symfony/deprecation-contracts": "^2.5|^3", - "symfony/http-client": "^6.4|^7.0", - "symfony/notifier": "^7.2" + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/notifier": "^7.2|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Notifier\\Bridge\\Sms77\\": "" }, diff --git a/src/Symfony/Component/Notifier/Bridge/SmsBiuras/composer.json b/src/Symfony/Component/Notifier/Bridge/SmsBiuras/composer.json index cbba623e99aa4..feff4ced7eede 100644 --- a/src/Symfony/Component/Notifier/Bridge/SmsBiuras/composer.json +++ b/src/Symfony/Component/Notifier/Bridge/SmsBiuras/composer.json @@ -17,8 +17,8 @@ ], "require": { "php": ">=8.2", - "symfony/http-client": "^6.4|^7.0", - "symfony/notifier": "^7.3" + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/notifier": "^7.3|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Notifier\\Bridge\\SmsBiuras\\": "" }, diff --git a/src/Symfony/Component/Notifier/Bridge/SmsFactor/composer.json b/src/Symfony/Component/Notifier/Bridge/SmsFactor/composer.json index b530771b49dce..5496b1185c5eb 100644 --- a/src/Symfony/Component/Notifier/Bridge/SmsFactor/composer.json +++ b/src/Symfony/Component/Notifier/Bridge/SmsFactor/composer.json @@ -17,8 +17,8 @@ ], "require": { "php": ">=8.2", - "symfony/http-client": "^6.4|^7.0", - "symfony/notifier": "^7.2" + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/notifier": "^7.2|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Notifier\\Bridge\\SmsFactor\\": "" }, diff --git a/src/Symfony/Component/Notifier/Bridge/SmsSluzba/composer.json b/src/Symfony/Component/Notifier/Bridge/SmsSluzba/composer.json index c4256019769a0..fd419bf91fcf7 100644 --- a/src/Symfony/Component/Notifier/Bridge/SmsSluzba/composer.json +++ b/src/Symfony/Component/Notifier/Bridge/SmsSluzba/composer.json @@ -16,10 +16,10 @@ } ], "require": { - "php": ">=8.1", - "symfony/http-client": "^6.4|^7.1", - "symfony/notifier": "^7.2", - "symfony/serializer": "^6.4|^7.1" + "php": ">=8.2", + "symfony/http-client": "^6.4|^7.1|^8.0", + "symfony/notifier": "^7.2|^8.0", + "symfony/serializer": "^6.4|^7.1|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Notifier\\Bridge\\SmsSluzba\\": "" }, diff --git a/src/Symfony/Component/Notifier/Bridge/Smsapi/composer.json b/src/Symfony/Component/Notifier/Bridge/Smsapi/composer.json index 40e2710c4dff7..481ac4538c99f 100644 --- a/src/Symfony/Component/Notifier/Bridge/Smsapi/composer.json +++ b/src/Symfony/Component/Notifier/Bridge/Smsapi/composer.json @@ -17,8 +17,8 @@ ], "require": { "php": ">=8.2", - "symfony/http-client": "^6.4|^7.0", - "symfony/notifier": "^7.3" + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/notifier": "^7.3|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Notifier\\Bridge\\Smsapi\\": "" }, diff --git a/src/Symfony/Component/Notifier/Bridge/Smsbox/composer.json b/src/Symfony/Component/Notifier/Bridge/Smsbox/composer.json index 0b1907fb71f15..2c6d8e9cc0242 100644 --- a/src/Symfony/Component/Notifier/Bridge/Smsbox/composer.json +++ b/src/Symfony/Component/Notifier/Bridge/Smsbox/composer.json @@ -25,13 +25,13 @@ ], "require": { "php": ">=8.2", - "symfony/clock": "^6.4|^7.0", - "symfony/http-client": "^6.4|^7.0", - "symfony/notifier": "^7.2", + "symfony/clock": "^6.4|^7.0|^8.0", + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/notifier": "^7.2|^8.0", "symfony/polyfill-php83": "^1.28" }, "require-dev": { - "symfony/webhook": "^6.4|^7.0" + "symfony/webhook": "^6.4|^7.0|^8.0" }, "autoload": { "psr-4": { diff --git a/src/Symfony/Component/Notifier/Bridge/Smsc/composer.json b/src/Symfony/Component/Notifier/Bridge/Smsc/composer.json index 2a5bded5aea9b..f057349e0e65a 100644 --- a/src/Symfony/Component/Notifier/Bridge/Smsc/composer.json +++ b/src/Symfony/Component/Notifier/Bridge/Smsc/composer.json @@ -17,8 +17,8 @@ ], "require": { "php": ">=8.2", - "symfony/http-client": "^6.4|^7.0", - "symfony/notifier": "^7.2" + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/notifier": "^7.2|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Notifier\\Bridge\\Smsc\\": "" }, diff --git a/src/Symfony/Component/Notifier/Bridge/Smsense/composer.json b/src/Symfony/Component/Notifier/Bridge/Smsense/composer.json index 4002648bd504b..f80bd05867d3c 100644 --- a/src/Symfony/Component/Notifier/Bridge/Smsense/composer.json +++ b/src/Symfony/Component/Notifier/Bridge/Smsense/composer.json @@ -17,8 +17,8 @@ ], "require": { "php": ">=8.2", - "symfony/http-client": "^6.4|^7.0", - "symfony/notifier": "^7.2" + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/notifier": "^7.2|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Notifier\\Bridge\\Smsense\\": "" }, diff --git a/src/Symfony/Component/Notifier/Bridge/Smsmode/composer.json b/src/Symfony/Component/Notifier/Bridge/Smsmode/composer.json index a9131f75ed3ad..f975c19833ccd 100644 --- a/src/Symfony/Component/Notifier/Bridge/Smsmode/composer.json +++ b/src/Symfony/Component/Notifier/Bridge/Smsmode/composer.json @@ -20,11 +20,11 @@ ], "require": { "php": ">=8.2", - "symfony/http-client": "^6.4|^7.0", - "symfony/notifier": "^7.2" + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/notifier": "^7.2|^8.0" }, "require-dev": { - "symfony/event-dispatcher": "^6.4|^7.0" + "symfony/event-dispatcher": "^6.4|^7.0|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Notifier\\Bridge\\Smsmode\\": "" }, diff --git a/src/Symfony/Component/Notifier/Bridge/SpotHit/composer.json b/src/Symfony/Component/Notifier/Bridge/SpotHit/composer.json index a9b66ba3636b4..491df32dedded 100644 --- a/src/Symfony/Component/Notifier/Bridge/SpotHit/composer.json +++ b/src/Symfony/Component/Notifier/Bridge/SpotHit/composer.json @@ -21,8 +21,8 @@ ], "require": { "php": ">=8.2", - "symfony/http-client": "^6.4|^7.0", - "symfony/notifier": "^7.2" + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/notifier": "^7.2|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Notifier\\Bridge\\SpotHit\\": "" }, diff --git a/src/Symfony/Component/Notifier/Bridge/Sweego/composer.json b/src/Symfony/Component/Notifier/Bridge/Sweego/composer.json index 006d739b86151..f9c3dc1d26f18 100644 --- a/src/Symfony/Component/Notifier/Bridge/Sweego/composer.json +++ b/src/Symfony/Component/Notifier/Bridge/Sweego/composer.json @@ -17,11 +17,11 @@ ], "require": { "php": ">=8.2", - "symfony/http-client": "^6.4|^7.0", - "symfony/notifier": "^7.2" + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/notifier": "^7.2|^8.0" }, "require-dev": { - "symfony/webhook": "^6.4|^7.0" + "symfony/webhook": "^6.4|^7.0|^8.0" }, "conflict": { "symfony/http-foundation": "<7.1" diff --git a/src/Symfony/Component/Notifier/Bridge/Telegram/composer.json b/src/Symfony/Component/Notifier/Bridge/Telegram/composer.json index 435641839410a..e046bcfd320e5 100644 --- a/src/Symfony/Component/Notifier/Bridge/Telegram/composer.json +++ b/src/Symfony/Component/Notifier/Bridge/Telegram/composer.json @@ -17,9 +17,9 @@ ], "require": { "php": ">=8.2", - "symfony/http-client": "^6.4|^7.0", - "symfony/mime": "^6.4|^7.0", - "symfony/notifier": "^7.2" + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/mime": "^6.4|^7.0|^8.0", + "symfony/notifier": "^7.2|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Notifier\\Bridge\\Telegram\\": "" }, diff --git a/src/Symfony/Component/Notifier/Bridge/Telnyx/composer.json b/src/Symfony/Component/Notifier/Bridge/Telnyx/composer.json index 3b53e750bd395..860c0f7f9efd2 100644 --- a/src/Symfony/Component/Notifier/Bridge/Telnyx/composer.json +++ b/src/Symfony/Component/Notifier/Bridge/Telnyx/composer.json @@ -17,8 +17,8 @@ ], "require": { "php": ">=8.2", - "symfony/http-client": "^6.4|^7.0", - "symfony/notifier": "^7.2" + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/notifier": "^7.2|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Notifier\\Bridge\\Telnyx\\": "" }, diff --git a/src/Symfony/Component/Notifier/Bridge/Termii/composer.json b/src/Symfony/Component/Notifier/Bridge/Termii/composer.json index 31ed79a368071..57d397d206c9b 100644 --- a/src/Symfony/Component/Notifier/Bridge/Termii/composer.json +++ b/src/Symfony/Component/Notifier/Bridge/Termii/composer.json @@ -20,11 +20,11 @@ ], "require": { "php": ">=8.2", - "symfony/http-client": "^6.4|^7.0", - "symfony/notifier": "^7.2" + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/notifier": "^7.2|^8.0" }, "require-dev": { - "symfony/event-dispatcher": "^6.4|^7.0" + "symfony/event-dispatcher": "^6.4|^7.0|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Notifier\\Bridge\\Termii\\": "" }, diff --git a/src/Symfony/Component/Notifier/Bridge/TurboSms/composer.json b/src/Symfony/Component/Notifier/Bridge/TurboSms/composer.json index 36c6def23ae2d..d90400dad8aca 100644 --- a/src/Symfony/Component/Notifier/Bridge/TurboSms/composer.json +++ b/src/Symfony/Component/Notifier/Bridge/TurboSms/composer.json @@ -17,8 +17,8 @@ ], "require": { "php": ">=8.2", - "symfony/http-client": "^6.4|^7.0", - "symfony/notifier": "^7.2", + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/notifier": "^7.2|^8.0", "symfony/polyfill-mbstring": "^1.0" }, "autoload": { diff --git a/src/Symfony/Component/Notifier/Bridge/Twilio/composer.json b/src/Symfony/Component/Notifier/Bridge/Twilio/composer.json index ee1872491bdfb..f2ae0c75429ca 100644 --- a/src/Symfony/Component/Notifier/Bridge/Twilio/composer.json +++ b/src/Symfony/Component/Notifier/Bridge/Twilio/composer.json @@ -17,11 +17,11 @@ ], "require": { "php": ">=8.2", - "symfony/http-client": "^6.4|^7.0", - "symfony/notifier": "^7.2" + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/notifier": "^7.2|^8.0" }, "require-dev": { - "symfony/webhook": "^6.4|^7.0" + "symfony/webhook": "^6.4|^7.0|^8.0" }, "conflict": { "symfony/http-foundation": "<6.4" diff --git a/src/Symfony/Component/Notifier/Bridge/Twitter/composer.json b/src/Symfony/Component/Notifier/Bridge/Twitter/composer.json index f50531a1448ed..2f96a28349f40 100644 --- a/src/Symfony/Component/Notifier/Bridge/Twitter/composer.json +++ b/src/Symfony/Component/Notifier/Bridge/Twitter/composer.json @@ -17,11 +17,11 @@ ], "require": { "php": ">=8.2", - "symfony/http-client": "^6.4|^7.0", - "symfony/notifier": "^7.2" + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/notifier": "^7.2|^8.0" }, "require-dev": { - "symfony/mime": "^6.4|^7.0" + "symfony/mime": "^6.4|^7.0|^8.0" }, "conflict": { "symfony/mime": "<6.4" diff --git a/src/Symfony/Component/Notifier/Bridge/Unifonic/composer.json b/src/Symfony/Component/Notifier/Bridge/Unifonic/composer.json index 48fbbdf2db84b..30cad613f85af 100644 --- a/src/Symfony/Component/Notifier/Bridge/Unifonic/composer.json +++ b/src/Symfony/Component/Notifier/Bridge/Unifonic/composer.json @@ -21,8 +21,8 @@ ], "require": { "php": ">=8.2", - "symfony/http-client": "^6.4|^7.0", - "symfony/notifier": "^7.2" + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/notifier": "^7.2|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Notifier\\Bridge\\Unifonic\\": "" }, diff --git a/src/Symfony/Component/Notifier/Bridge/Vonage/composer.json b/src/Symfony/Component/Notifier/Bridge/Vonage/composer.json index 243f0903155fe..a8939fb564e88 100644 --- a/src/Symfony/Component/Notifier/Bridge/Vonage/composer.json +++ b/src/Symfony/Component/Notifier/Bridge/Vonage/composer.json @@ -17,11 +17,11 @@ ], "require": { "php": ">=8.2", - "symfony/http-client": "^6.4|^7.0", - "symfony/notifier": "^7.2" + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/notifier": "^7.2|^8.0" }, "require-dev": { - "symfony/webhook": "^6.4|^7.0" + "symfony/webhook": "^6.4|^7.0|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Notifier\\Bridge\\Vonage\\": "" }, diff --git a/src/Symfony/Component/Notifier/Bridge/Yunpian/composer.json b/src/Symfony/Component/Notifier/Bridge/Yunpian/composer.json index 58366edc8eb74..4bf05c1afc0cd 100644 --- a/src/Symfony/Component/Notifier/Bridge/Yunpian/composer.json +++ b/src/Symfony/Component/Notifier/Bridge/Yunpian/composer.json @@ -17,8 +17,8 @@ ], "require": { "php": ">=8.2", - "symfony/http-client": "^6.4|^7.0", - "symfony/notifier": "^7.2" + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/notifier": "^7.2|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Notifier\\Bridge\\Yunpian\\": "" }, diff --git a/src/Symfony/Component/Notifier/Bridge/Zendesk/composer.json b/src/Symfony/Component/Notifier/Bridge/Zendesk/composer.json index 87044d4d4829e..66eea8847150d 100644 --- a/src/Symfony/Component/Notifier/Bridge/Zendesk/composer.json +++ b/src/Symfony/Component/Notifier/Bridge/Zendesk/composer.json @@ -17,8 +17,8 @@ ], "require": { "php": ">=8.2", - "symfony/http-client": "^6.4|^7.0", - "symfony/notifier": "^7.2" + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/notifier": "^7.2|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Notifier\\Bridge\\Zendesk\\": "" }, diff --git a/src/Symfony/Component/Notifier/Bridge/Zulip/composer.json b/src/Symfony/Component/Notifier/Bridge/Zulip/composer.json index f124c18e7e58b..8fb05ff6b2817 100644 --- a/src/Symfony/Component/Notifier/Bridge/Zulip/composer.json +++ b/src/Symfony/Component/Notifier/Bridge/Zulip/composer.json @@ -17,8 +17,8 @@ ], "require": { "php": ">=8.2", - "symfony/http-client": "^6.4|^7.0", - "symfony/notifier": "^7.2" + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/notifier": "^7.2|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Notifier\\Bridge\\Zulip\\": "" }, diff --git a/src/Symfony/Component/Notifier/composer.json b/src/Symfony/Component/Notifier/composer.json index 3cb8fe7d28073..9cc0495f4d396 100644 --- a/src/Symfony/Component/Notifier/composer.json +++ b/src/Symfony/Component/Notifier/composer.json @@ -22,8 +22,8 @@ "require-dev": { "symfony/event-dispatcher-contracts": "^2.5|^3", "symfony/http-client-contracts": "^2.5|^3", - "symfony/http-foundation": "^6.4|^7.0", - "symfony/messenger": "^6.4|^7.0" + "symfony/http-foundation": "^6.4|^7.0|^8.0", + "symfony/messenger": "^6.4|^7.0|^8.0" }, "conflict": { "symfony/event-dispatcher": "<6.4", diff --git a/src/Symfony/Component/ObjectMapper/composer.json b/src/Symfony/Component/ObjectMapper/composer.json index 6d1b445d92781..1ae9f2740fea2 100644 --- a/src/Symfony/Component/ObjectMapper/composer.json +++ b/src/Symfony/Component/ObjectMapper/composer.json @@ -20,7 +20,7 @@ "psr/container": "^2.0" }, "require-dev": { - "symfony/property-access": "^7.2" + "symfony/property-access": "^7.2|^8.0" }, "autoload": { "psr-4": { diff --git a/src/Symfony/Component/PasswordHasher/composer.json b/src/Symfony/Component/PasswordHasher/composer.json index ebcb51b4b9599..1eb6681722c02 100644 --- a/src/Symfony/Component/PasswordHasher/composer.json +++ b/src/Symfony/Component/PasswordHasher/composer.json @@ -19,8 +19,8 @@ "php": ">=8.2" }, "require-dev": { - "symfony/security-core": "^6.4|^7.0", - "symfony/console": "^6.4|^7.0" + "symfony/security-core": "^6.4|^7.0|^8.0", + "symfony/console": "^6.4|^7.0|^8.0" }, "conflict": { "symfony/security-core": "<6.4" diff --git a/src/Symfony/Component/PropertyAccess/composer.json b/src/Symfony/Component/PropertyAccess/composer.json index 376ee7e1afd0d..2d1a292856460 100644 --- a/src/Symfony/Component/PropertyAccess/composer.json +++ b/src/Symfony/Component/PropertyAccess/composer.json @@ -17,10 +17,10 @@ ], "require": { "php": ">=8.2", - "symfony/property-info": "^6.4|^7.0" + "symfony/property-info": "^6.4|^7.0|^8.0" }, "require-dev": { - "symfony/cache": "^6.4|^7.0" + "symfony/cache": "^6.4|^7.0|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\PropertyAccess\\": "" }, diff --git a/src/Symfony/Component/PropertyInfo/composer.json b/src/Symfony/Component/PropertyInfo/composer.json index f8ae018a40b7f..89c2c6ad01cce 100644 --- a/src/Symfony/Component/PropertyInfo/composer.json +++ b/src/Symfony/Component/PropertyInfo/composer.json @@ -25,13 +25,13 @@ "require": { "php": ">=8.2", "symfony/deprecation-contracts": "^2.5|^3", - "symfony/string": "^6.4|^7.0", - "symfony/type-info": "~7.1.9|^7.2.2" + "symfony/string": "^6.4|^7.0|^8.0", + "symfony/type-info": "~7.1.9|^7.2.2|^8.0" }, "require-dev": { - "symfony/serializer": "^6.4|^7.0", - "symfony/cache": "^6.4|^7.0", - "symfony/dependency-injection": "^6.4|^7.0", + "symfony/serializer": "^6.4|^7.0|^8.0", + "symfony/cache": "^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", "phpdocumentor/reflection-docblock": "^5.2", "phpstan/phpdoc-parser": "^1.0|^2.0" }, diff --git a/src/Symfony/Component/RateLimiter/composer.json b/src/Symfony/Component/RateLimiter/composer.json index fdf0e01c4979b..428ce3480e53f 100644 --- a/src/Symfony/Component/RateLimiter/composer.json +++ b/src/Symfony/Component/RateLimiter/composer.json @@ -17,11 +17,11 @@ ], "require": { "php": ">=8.2", - "symfony/options-resolver": "^7.3" + "symfony/options-resolver": "^7.3|^8.0" }, "require-dev": { "psr/cache": "^1.0|^2.0|^3.0", - "symfony/lock": "^6.4|^7.0" + "symfony/lock": "^6.4|^7.0|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\RateLimiter\\": "" }, diff --git a/src/Symfony/Component/RemoteEvent/composer.json b/src/Symfony/Component/RemoteEvent/composer.json index 292110b3424f5..83b82a71727e7 100644 --- a/src/Symfony/Component/RemoteEvent/composer.json +++ b/src/Symfony/Component/RemoteEvent/composer.json @@ -17,7 +17,7 @@ ], "require": { "php": ">=8.2", - "symfony/messenger": "^6.4|^7.0" + "symfony/messenger": "^6.4|^7.0|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\RemoteEvent\\": "" }, diff --git a/src/Symfony/Component/Routing/composer.json b/src/Symfony/Component/Routing/composer.json index 59e30bef69611..1fcc24b61606c 100644 --- a/src/Symfony/Component/Routing/composer.json +++ b/src/Symfony/Component/Routing/composer.json @@ -20,11 +20,11 @@ "symfony/deprecation-contracts": "^2.5|^3" }, "require-dev": { - "symfony/config": "^6.4|^7.0", - "symfony/http-foundation": "^6.4|^7.0", - "symfony/yaml": "^6.4|^7.0", - "symfony/expression-language": "^6.4|^7.0", - "symfony/dependency-injection": "^6.4|^7.0", + "symfony/config": "^6.4|^7.0|^8.0", + "symfony/http-foundation": "^6.4|^7.0|^8.0", + "symfony/yaml": "^6.4|^7.0|^8.0", + "symfony/expression-language": "^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", "psr/log": "^1|^2|^3" }, "conflict": { diff --git a/src/Symfony/Component/Runtime/composer.json b/src/Symfony/Component/Runtime/composer.json index fa9c2cb3f58d0..624f90541d30f 100644 --- a/src/Symfony/Component/Runtime/composer.json +++ b/src/Symfony/Component/Runtime/composer.json @@ -21,10 +21,10 @@ }, "require-dev": { "composer/composer": "^2.6", - "symfony/console": "^6.4|^7.0", - "symfony/dotenv": "^6.4|^7.0", - "symfony/http-foundation": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0" + "symfony/console": "^6.4|^7.0|^8.0", + "symfony/dotenv": "^6.4|^7.0|^8.0", + "symfony/http-foundation": "^6.4|^7.0|^8.0", + "symfony/http-kernel": "^6.4|^7.0|^8.0" }, "conflict": { "symfony/dotenv": "<6.4" diff --git a/src/Symfony/Component/Scheduler/composer.json b/src/Symfony/Component/Scheduler/composer.json index e907a79d55dcb..8a5cc60506212 100644 --- a/src/Symfony/Component/Scheduler/composer.json +++ b/src/Symfony/Component/Scheduler/composer.json @@ -21,17 +21,17 @@ ], "require": { "php": ">=8.2", - "symfony/clock": "^6.4|^7.0" + "symfony/clock": "^6.4|^7.0|^8.0" }, "require-dev": { "dragonmantank/cron-expression": "^3.1", - "symfony/cache": "^6.4|^7.0", - "symfony/console": "^6.4|^7.0", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/event-dispatcher": "^6.4|^7.0", - "symfony/lock": "^6.4|^7.0", - "symfony/messenger": "^6.4|^7.0", - "symfony/serializer": "^6.4|^7.1" + "symfony/cache": "^6.4|^7.0|^8.0", + "symfony/console": "^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/event-dispatcher": "^6.4|^7.0|^8.0", + "symfony/lock": "^6.4|^7.0|^8.0", + "symfony/messenger": "^6.4|^7.0|^8.0", + "symfony/serializer": "^6.4|^7.1|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Scheduler\\": "" }, diff --git a/src/Symfony/Component/Security/Core/composer.json b/src/Symfony/Component/Security/Core/composer.json index 0aaff1e3645bf..9d662f7e9eeda 100644 --- a/src/Symfony/Component/Security/Core/composer.json +++ b/src/Symfony/Component/Security/Core/composer.json @@ -20,20 +20,20 @@ "symfony/deprecation-contracts": "^2.5|^3", "symfony/event-dispatcher-contracts": "^2.5|^3", "symfony/service-contracts": "^2.5|^3", - "symfony/password-hasher": "^6.4|^7.0" + "symfony/password-hasher": "^6.4|^7.0|^8.0" }, "require-dev": { "psr/container": "^1.1|^2.0", "psr/cache": "^1.0|^2.0|^3.0", - "symfony/cache": "^6.4|^7.0", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/event-dispatcher": "^6.4|^7.0", - "symfony/expression-language": "^6.4|^7.0", - "symfony/http-foundation": "^6.4|^7.0", - "symfony/ldap": "^6.4|^7.0", - "symfony/string": "^6.4|^7.0", - "symfony/translation": "^6.4.3|^7.0.3", - "symfony/validator": "^6.4|^7.0", + "symfony/cache": "^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/event-dispatcher": "^6.4|^7.0|^8.0", + "symfony/expression-language": "^6.4|^7.0|^8.0", + "symfony/http-foundation": "^6.4|^7.0|^8.0", + "symfony/ldap": "^6.4|^7.0|^8.0", + "symfony/string": "^6.4|^7.0|^8.0", + "symfony/translation": "^6.4.3|^7.0.3|^8.0", + "symfony/validator": "^6.4|^7.0|^8.0", "psr/log": "^1|^2|^3" }, "conflict": { diff --git a/src/Symfony/Component/Security/Csrf/composer.json b/src/Symfony/Component/Security/Csrf/composer.json index c2bfed1de3d7e..6129d76ce8e1b 100644 --- a/src/Symfony/Component/Security/Csrf/composer.json +++ b/src/Symfony/Component/Security/Csrf/composer.json @@ -17,12 +17,12 @@ ], "require": { "php": ">=8.2", - "symfony/security-core": "^6.4|^7.0" + "symfony/security-core": "^6.4|^7.0|^8.0" }, "require-dev": { "psr/log": "^1|^2|^3", - "symfony/http-foundation": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0" + "symfony/http-foundation": "^6.4|^7.0|^8.0", + "symfony/http-kernel": "^6.4|^7.0|^8.0" }, "conflict": { "symfony/http-foundation": "<6.4" diff --git a/src/Symfony/Component/Security/Http/composer.json b/src/Symfony/Component/Security/Http/composer.json index 77f6af87395ec..43312990d22c3 100644 --- a/src/Symfony/Component/Security/Http/composer.json +++ b/src/Symfony/Component/Security/Http/composer.json @@ -18,23 +18,23 @@ "require": { "php": ">=8.2", "symfony/deprecation-contracts": "^2.5|^3", - "symfony/http-foundation": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0", + "symfony/http-foundation": "^6.4|^7.0|^8.0", + "symfony/http-kernel": "^6.4|^7.0|^8.0", "symfony/polyfill-mbstring": "~1.0", - "symfony/property-access": "^6.4|^7.0", - "symfony/security-core": "^7.3", + "symfony/property-access": "^6.4|^7.0|^8.0", + "symfony/security-core": "^7.3|^8.0", "symfony/service-contracts": "^2.5|^3" }, "require-dev": { - "symfony/cache": "^6.4|^7.0", - "symfony/clock": "^6.4|^7.0", - "symfony/expression-language": "^6.4|^7.0", - "symfony/http-client": "^6.4|^7.0", + "symfony/cache": "^6.4|^7.0|^8.0", + "symfony/clock": "^6.4|^7.0|^8.0", + "symfony/expression-language": "^6.4|^7.0|^8.0", + "symfony/http-client": "^6.4|^7.0|^8.0", "symfony/http-client-contracts": "^3.0", - "symfony/rate-limiter": "^6.4|^7.0", - "symfony/routing": "^6.4|^7.0", - "symfony/security-csrf": "^6.4|^7.0", - "symfony/translation": "^6.4|^7.0", + "symfony/rate-limiter": "^6.4|^7.0|^8.0", + "symfony/routing": "^6.4|^7.0|^8.0", + "symfony/security-csrf": "^6.4|^7.0|^8.0", + "symfony/translation": "^6.4|^7.0|^8.0", "psr/log": "^1|^2|^3", "web-token/jwt-library": "^3.3.2|^4.0" }, diff --git a/src/Symfony/Component/Serializer/composer.json b/src/Symfony/Component/Serializer/composer.json index d8809fa079ef9..1a58ba7ee133b 100644 --- a/src/Symfony/Component/Serializer/composer.json +++ b/src/Symfony/Component/Serializer/composer.json @@ -24,26 +24,26 @@ "phpdocumentor/reflection-docblock": "^3.2|^4.0|^5.0", "phpstan/phpdoc-parser": "^1.0|^2.0", "seld/jsonlint": "^1.10", - "symfony/cache": "^6.4|^7.0", - "symfony/config": "^6.4|^7.0", - "symfony/console": "^6.4|^7.0", - "symfony/dependency-injection": "^7.2", - "symfony/error-handler": "^6.4|^7.0", - "symfony/filesystem": "^6.4|^7.0", - "symfony/form": "^6.4|^7.0", - "symfony/http-foundation": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/messenger": "^6.4|^7.0", - "symfony/mime": "^6.4|^7.0", - "symfony/property-access": "^6.4|^7.0", - "symfony/property-info": "^6.4|^7.0", + "symfony/cache": "^6.4|^7.0|^8.0", + "symfony/config": "^6.4|^7.0|^8.0", + "symfony/console": "^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^7.2|^8.0", + "symfony/error-handler": "^6.4|^7.0|^8.0", + "symfony/filesystem": "^6.4|^7.0|^8.0", + "symfony/form": "^6.4|^7.0|^8.0", + "symfony/http-foundation": "^6.4|^7.0|^8.0", + "symfony/http-kernel": "^6.4|^7.0|^8.0", + "symfony/messenger": "^6.4|^7.0|^8.0", + "symfony/mime": "^6.4|^7.0|^8.0", + "symfony/property-access": "^6.4|^7.0|^8.0", + "symfony/property-info": "^6.4|^7.0|^8.0", "symfony/translation-contracts": "^2.5|^3", - "symfony/type-info": "^7.1", - "symfony/uid": "^6.4|^7.0", - "symfony/validator": "^6.4|^7.0", - "symfony/var-dumper": "^6.4|^7.0", - "symfony/var-exporter": "^6.4|^7.0", - "symfony/yaml": "^6.4|^7.0" + "symfony/type-info": "^7.1|^8.0", + "symfony/uid": "^6.4|^7.0|^8.0", + "symfony/validator": "^6.4|^7.0|^8.0", + "symfony/var-dumper": "^6.4|^7.0|^8.0", + "symfony/var-exporter": "^6.4|^7.0|^8.0", + "symfony/yaml": "^6.4|^7.0|^8.0" }, "conflict": { "phpdocumentor/reflection-docblock": "<3.2.2", diff --git a/src/Symfony/Component/String/composer.json b/src/Symfony/Component/String/composer.json index 10d0ee620e4da..e2f31fdb14525 100644 --- a/src/Symfony/Component/String/composer.json +++ b/src/Symfony/Component/String/composer.json @@ -23,12 +23,12 @@ "symfony/polyfill-mbstring": "~1.0" }, "require-dev": { - "symfony/error-handler": "^6.4|^7.0", - "symfony/emoji": "^7.1", - "symfony/http-client": "^6.4|^7.0", - "symfony/intl": "^6.4|^7.0", + "symfony/error-handler": "^6.4|^7.0|^8.0", + "symfony/emoji": "^7.1|^8.0", + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/intl": "^6.4|^7.0|^8.0", "symfony/translation-contracts": "^2.5|^3.0", - "symfony/var-exporter": "^6.4|^7.0" + "symfony/var-exporter": "^6.4|^7.0|^8.0" }, "conflict": { "symfony/translation-contracts": "<2.5" diff --git a/src/Symfony/Component/Translation/Bridge/Crowdin/composer.json b/src/Symfony/Component/Translation/Bridge/Crowdin/composer.json index d2f60819d6b9b..700733a7d8c6a 100644 --- a/src/Symfony/Component/Translation/Bridge/Crowdin/composer.json +++ b/src/Symfony/Component/Translation/Bridge/Crowdin/composer.json @@ -21,9 +21,9 @@ ], "require": { "php": ">=8.2", - "symfony/config": "^6.4|^7.0", - "symfony/http-client": "^6.4|^7.0", - "symfony/translation": "^7.2" + "symfony/config": "^6.4|^7.0|^8.0", + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/translation": "^7.2|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Translation\\Bridge\\Crowdin\\": "" }, diff --git a/src/Symfony/Component/Translation/Bridge/Loco/composer.json b/src/Symfony/Component/Translation/Bridge/Loco/composer.json index 40eb6f753d363..a98c6f91595b8 100644 --- a/src/Symfony/Component/Translation/Bridge/Loco/composer.json +++ b/src/Symfony/Component/Translation/Bridge/Loco/composer.json @@ -17,9 +17,9 @@ ], "require": { "php": ">=8.2", - "symfony/http-client": "^6.4|^7.0", - "symfony/config": "^6.4|^7.0", - "symfony/translation": "^7.2" + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/config": "^6.4|^7.0|^8.0", + "symfony/translation": "^7.2|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Translation\\Bridge\\Loco\\": "" }, diff --git a/src/Symfony/Component/Translation/Bridge/Lokalise/composer.json b/src/Symfony/Component/Translation/Bridge/Lokalise/composer.json index 78be5ea3c89cc..6f9a8c915e20b 100644 --- a/src/Symfony/Component/Translation/Bridge/Lokalise/composer.json +++ b/src/Symfony/Component/Translation/Bridge/Lokalise/composer.json @@ -17,9 +17,9 @@ ], "require": { "php": ">=8.2", - "symfony/config": "^6.4|^7.0", - "symfony/http-client": "^6.4|^7.0", - "symfony/translation": "^7.2" + "symfony/config": "^6.4|^7.0|^8.0", + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/translation": "^7.2|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Translation\\Bridge\\Lokalise\\": "" }, diff --git a/src/Symfony/Component/Translation/Bridge/Phrase/composer.json b/src/Symfony/Component/Translation/Bridge/Phrase/composer.json index 2d3105037f7c6..3cd63db74b5a9 100644 --- a/src/Symfony/Component/Translation/Bridge/Phrase/composer.json +++ b/src/Symfony/Component/Translation/Bridge/Phrase/composer.json @@ -18,9 +18,9 @@ "require": { "php": ">=8.2", "psr/cache": "^3.0", - "symfony/http-client": "^6.4|^7.0", - "symfony/mime": "^6.4|^7.0", - "symfony/translation": "^7.2" + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/mime": "^6.4|^7.0|^8.0", + "symfony/translation": "^7.2|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Translation\\Bridge\\Phrase\\": "" }, diff --git a/src/Symfony/Component/Translation/composer.json b/src/Symfony/Component/Translation/composer.json index ce9a7bf48c61b..69a2998af9390 100644 --- a/src/Symfony/Component/Translation/composer.json +++ b/src/Symfony/Component/Translation/composer.json @@ -23,17 +23,17 @@ }, "require-dev": { "nikic/php-parser": "^5.0", - "symfony/config": "^6.4|^7.0", - "symfony/console": "^6.4|^7.0", - "symfony/dependency-injection": "^6.4|^7.0", + "symfony/config": "^6.4|^7.0|^8.0", + "symfony/console": "^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", "symfony/http-client-contracts": "^2.5|^3.0", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/intl": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0|^8.0", + "symfony/intl": "^6.4|^7.0|^8.0", "symfony/polyfill-intl-icu": "^1.21", - "symfony/routing": "^6.4|^7.0", + "symfony/routing": "^6.4|^7.0|^8.0", "symfony/service-contracts": "^2.5|^3", - "symfony/yaml": "^6.4|^7.0", - "symfony/finder": "^6.4|^7.0", + "symfony/yaml": "^6.4|^7.0|^8.0", + "symfony/finder": "^6.4|^7.0|^8.0", "psr/log": "^1|^2|^3" }, "conflict": { diff --git a/src/Symfony/Component/Uid/composer.json b/src/Symfony/Component/Uid/composer.json index 9843341c6d174..a3dd9f4401c94 100644 --- a/src/Symfony/Component/Uid/composer.json +++ b/src/Symfony/Component/Uid/composer.json @@ -24,7 +24,7 @@ "symfony/polyfill-uuid": "^1.15" }, "require-dev": { - "symfony/console": "^6.4|^7.0" + "symfony/console": "^6.4|^7.0|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Uid\\": "" }, diff --git a/src/Symfony/Component/Validator/composer.json b/src/Symfony/Component/Validator/composer.json index 0eaac5f6bf735..899808727470e 100644 --- a/src/Symfony/Component/Validator/composer.json +++ b/src/Symfony/Component/Validator/composer.json @@ -24,23 +24,23 @@ "symfony/translation-contracts": "^2.5|^3" }, "require-dev": { - "symfony/console": "^6.4|^7.0", - "symfony/finder": "^6.4|^7.0", - "symfony/http-client": "^6.4|^7.0", - "symfony/http-foundation": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/intl": "^6.4|^7.0", - "symfony/yaml": "^6.4|^7.0", - "symfony/config": "^6.4|^7.0", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/expression-language": "^6.4|^7.0", - "symfony/cache": "^6.4|^7.0", - "symfony/mime": "^6.4|^7.0", - "symfony/property-access": "^6.4|^7.0", - "symfony/property-info": "^6.4|^7.0", - "symfony/string": "^6.4|^7.0", - "symfony/translation": "^6.4.3|^7.0.3", - "symfony/type-info": "^7.1", + "symfony/console": "^6.4|^7.0|^8.0", + "symfony/finder": "^6.4|^7.0|^8.0", + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/http-foundation": "^6.4|^7.0|^8.0", + "symfony/http-kernel": "^6.4|^7.0|^8.0", + "symfony/intl": "^6.4|^7.0|^8.0", + "symfony/yaml": "^6.4|^7.0|^8.0", + "symfony/config": "^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/expression-language": "^6.4|^7.0|^8.0", + "symfony/cache": "^6.4|^7.0|^8.0", + "symfony/mime": "^6.4|^7.0|^8.0", + "symfony/property-access": "^6.4|^7.0|^8.0", + "symfony/property-info": "^6.4|^7.0|^8.0", + "symfony/string": "^6.4|^7.0|^8.0", + "symfony/translation": "^6.4.3|^7.0.3|^8.0", + "symfony/type-info": "^7.1|^8.0", "egulias/email-validator": "^2.1.10|^3|^4" }, "conflict": { diff --git a/src/Symfony/Component/VarDumper/composer.json b/src/Symfony/Component/VarDumper/composer.json index ed312c5288b88..23c982098d053 100644 --- a/src/Symfony/Component/VarDumper/composer.json +++ b/src/Symfony/Component/VarDumper/composer.json @@ -22,10 +22,10 @@ }, "require-dev": { "ext-iconv": "*", - "symfony/console": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/process": "^6.4|^7.0", - "symfony/uid": "^6.4|^7.0", + "symfony/console": "^6.4|^7.0|^8.0", + "symfony/http-kernel": "^6.4|^7.0|^8.0", + "symfony/process": "^6.4|^7.0|^8.0", + "symfony/uid": "^6.4|^7.0|^8.0", "twig/twig": "^3.12" }, "conflict": { diff --git a/src/Symfony/Component/VarExporter/composer.json b/src/Symfony/Component/VarExporter/composer.json index 215d3ee56a836..36f1b422ff267 100644 --- a/src/Symfony/Component/VarExporter/composer.json +++ b/src/Symfony/Component/VarExporter/composer.json @@ -20,9 +20,9 @@ "symfony/deprecation-contracts": "^2.5|^3" }, "require-dev": { - "symfony/property-access": "^6.4|^7.0", - "symfony/serializer": "^6.4|^7.0", - "symfony/var-dumper": "^6.4|^7.0" + "symfony/property-access": "^6.4|^7.0|^8.0", + "symfony/serializer": "^6.4|^7.0|^8.0", + "symfony/var-dumper": "^6.4|^7.0|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\VarExporter\\": "" }, diff --git a/src/Symfony/Component/WebLink/composer.json b/src/Symfony/Component/WebLink/composer.json index 3203f6fa83163..0d7ca7857629a 100644 --- a/src/Symfony/Component/WebLink/composer.json +++ b/src/Symfony/Component/WebLink/composer.json @@ -23,7 +23,7 @@ "psr/link": "^1.1|^2.0" }, "require-dev": { - "symfony/http-kernel": "^6.4|^7.0" + "symfony/http-kernel": "^6.4|^7.0|^8.0" }, "conflict": { "symfony/http-kernel": "<6.4" diff --git a/src/Symfony/Component/Webhook/composer.json b/src/Symfony/Component/Webhook/composer.json index 46ce35b5d90cb..035817b066383 100644 --- a/src/Symfony/Component/Webhook/composer.json +++ b/src/Symfony/Component/Webhook/composer.json @@ -17,14 +17,14 @@ ], "require": { "php": ">=8.2", - "symfony/http-foundation": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/messenger": "^6.4|^7.0", - "symfony/remote-event": "^6.4|^7.0" + "symfony/http-foundation": "^6.4|^7.0|^8.0", + "symfony/http-kernel": "^6.4|^7.0|^8.0", + "symfony/messenger": "^6.4|^7.0|^8.0", + "symfony/remote-event": "^6.4|^7.0|^8.0" }, "require-dev": { - "symfony/http-client": "^6.4|^7.0", - "symfony/serializer": "^6.4|^7.0" + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/serializer": "^6.4|^7.0|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\Webhook\\": "" }, diff --git a/src/Symfony/Component/Workflow/composer.json b/src/Symfony/Component/Workflow/composer.json index 3e2c50a38cffd..ff8561caa1c88 100644 --- a/src/Symfony/Component/Workflow/composer.json +++ b/src/Symfony/Component/Workflow/composer.json @@ -25,15 +25,15 @@ }, "require-dev": { "psr/log": "^1|^2|^3", - "symfony/config": "^6.4|^7.0", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/error-handler": "^6.4|^7.0", - "symfony/event-dispatcher": "^6.4|^7.0", - "symfony/expression-language": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/security-core": "^6.4|^7.0", - "symfony/stopwatch": "^6.4|^7.0", - "symfony/validator": "^6.4|^7.0" + "symfony/config": "^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/error-handler": "^6.4|^7.0|^8.0", + "symfony/event-dispatcher": "^6.4|^7.0|^8.0", + "symfony/expression-language": "^6.4|^7.0|^8.0", + "symfony/http-kernel": "^6.4|^7.0|^8.0", + "symfony/security-core": "^6.4|^7.0|^8.0", + "symfony/stopwatch": "^6.4|^7.0|^8.0", + "symfony/validator": "^6.4|^7.0|^8.0" }, "conflict": { "symfony/event-dispatcher": "<6.4" diff --git a/src/Symfony/Component/Yaml/composer.json b/src/Symfony/Component/Yaml/composer.json index 2ceac94665037..8f31f2e4de031 100644 --- a/src/Symfony/Component/Yaml/composer.json +++ b/src/Symfony/Component/Yaml/composer.json @@ -21,7 +21,7 @@ "symfony/polyfill-ctype": "^1.8" }, "require-dev": { - "symfony/console": "^6.4|^7.0" + "symfony/console": "^6.4|^7.0|^8.0" }, "conflict": { "symfony/console": "<6.4" From 16c8a943488e61452c9203108f7dfb3f07cf0da3 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Mon, 2 Jun 2025 18:05:30 +0200 Subject: [PATCH 41/80] use STARTTLS for SMTP with MailerSend --- .../Bridge/MailerSend/Transport/MailerSendSmtpTransport.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/Mailer/Bridge/MailerSend/Transport/MailerSendSmtpTransport.php b/src/Symfony/Component/Mailer/Bridge/MailerSend/Transport/MailerSendSmtpTransport.php index 84e2553a627cc..e5bfb4daddc2e 100644 --- a/src/Symfony/Component/Mailer/Bridge/MailerSend/Transport/MailerSendSmtpTransport.php +++ b/src/Symfony/Component/Mailer/Bridge/MailerSend/Transport/MailerSendSmtpTransport.php @@ -22,7 +22,7 @@ final class MailerSendSmtpTransport extends EsmtpTransport { public function __construct(string $username, #[\SensitiveParameter] string $password, ?EventDispatcherInterface $dispatcher = null, ?LoggerInterface $logger = null) { - parent::__construct('smtp.mailersend.net', 587, true, $dispatcher, $logger); + parent::__construct('smtp.mailersend.net', 587, false, $dispatcher, $logger); $this->setUsername($username); $this->setPassword($password); From 9e0413f8f770d8536fdc7dbb6a382acaed6edd5b Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Tue, 3 Jun 2025 08:37:35 +0200 Subject: [PATCH 42/80] also test patched components with 6.4 --- .github/workflows/unit-tests.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index 803f6b51cbb22..95eff42344ac7 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -142,7 +142,7 @@ jobs: echo SYMFONY_VERSION=$SYMFONY_VERSION >> $GITHUB_ENV echo COMPOSER_ROOT_VERSION=$SYMFONY_VERSION.x-dev >> $GITHUB_ENV - echo SYMFONY_REQUIRE=">=$([ '${{ matrix.mode }}' = low-deps ] && echo 5.4 || echo $SYMFONY_VERSION)" >> $GITHUB_ENV + echo SYMFONY_REQUIRE=">=$([ '${{ matrix.mode }}' = low-deps ] && echo 6.4 || echo $SYMFONY_VERSION)" >> $GITHUB_ENV [[ "${{ matrix.mode }}" = *-deps ]] && mv composer.json.phpunit composer.json || true - name: Install dependencies @@ -214,8 +214,8 @@ jobs: # get a list of the patched components (relies on .github/build-packages.php being called in the previous step) PATCHED_COMPONENTS=$(git diff --name-only src/ | grep composer.json || true) - # for 6.4 LTS, checkout and test previous major with the patched components (only for patched components) - if [[ $PATCHED_COMPONENTS && $SYMFONY_VERSION = 6.4 ]]; then + # for 7.4 LTS, checkout and test previous major with the patched components (only for patched components) + if [[ $PATCHED_COMPONENTS && $SYMFONY_VERSION = 7.4 ]]; then export FLIP='^' SYMFONY_VERSION=$(echo $SYMFONY_VERSION | awk '{print $1 - 1}') echo -e "\\n\\e[33;1mChecking out Symfony $SYMFONY_VERSION and running tests with patched components as deps\\e[0m" From c2f9a7ca5774ce2de291f193238814887015a489 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 3 Jun 2025 08:46:12 +0200 Subject: [PATCH 43/80] [Yaml] fix support for years outside of the 32b range on x86 arch on PHP 8.4 --- src/Symfony/Component/Yaml/Inline.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/Yaml/Inline.php b/src/Symfony/Component/Yaml/Inline.php index e1553f9d24e0a..0394c09b46221 100644 --- a/src/Symfony/Component/Yaml/Inline.php +++ b/src/Symfony/Component/Yaml/Inline.php @@ -737,7 +737,7 @@ private static function evaluateScalar(string $scalar, int $flags, array &$refer if (false !== $scalar = $time->getTimestamp()) { return $scalar; } - } catch (\ValueError) { + } catch (\DateRangeError|\ValueError) { // no-op } From 2070c3340d228f986708a10afdb5f3fdbddc7d73 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 3 Jun 2025 08:50:18 +0200 Subject: [PATCH 44/80] [VarDumper] Fix tests on PHP 8.4 --- .../VarDumper/Tests/Caster/ResourceCasterTest.php | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/Symfony/Component/VarDumper/Tests/Caster/ResourceCasterTest.php b/src/Symfony/Component/VarDumper/Tests/Caster/ResourceCasterTest.php index 029f7fb0d6876..946db1dd828a6 100644 --- a/src/Symfony/Component/VarDumper/Tests/Caster/ResourceCasterTest.php +++ b/src/Symfony/Component/VarDumper/Tests/Caster/ResourceCasterTest.php @@ -69,14 +69,11 @@ public function testCastDbaPriorToPhp84() } /** - * @requires PHP 8.4 + * @requires PHP 8.4.2 + * @requires extension dba */ public function testCastDba() { - if (\PHP_VERSION_ID < 80402) { - $this->markTestSkipped('The test cannot be run on PHP 8.4.0 and PHP 8.4.1, see https://github.com/php/php-src/issues/16990'); - } - $dba = dba_open(sys_get_temp_dir().'/test.db', 'c'); $this->assertDumpMatchesFormat( @@ -89,6 +86,7 @@ public function testCastDba() /** * @requires PHP 8.4 + * @requires extension dba */ public function testCastDbaOnBuggyPhp84() { From 591f89384b6ca1418ac11ffc8d45f4257530cfaf Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Tue, 3 Jun 2025 08:37:35 +0200 Subject: [PATCH 45/80] fix low deps build --- src/Symfony/Component/PropertyAccess/composer.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/PropertyAccess/composer.json b/src/Symfony/Component/PropertyAccess/composer.json index 2d1a292856460..906e432b03f99 100644 --- a/src/Symfony/Component/PropertyAccess/composer.json +++ b/src/Symfony/Component/PropertyAccess/composer.json @@ -20,7 +20,8 @@ "symfony/property-info": "^6.4|^7.0|^8.0" }, "require-dev": { - "symfony/cache": "^6.4|^7.0|^8.0" + "symfony/cache": "^6.4|^7.0|^8.0", + "symfony/var-exporter": "^6.4.1|^7.0.1|^8.0" }, "autoload": { "psr-4": { "Symfony\\Component\\PropertyAccess\\": "" }, From 593ff77de56b08b9ff1b7812349399625bb7933f Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Tue, 3 Jun 2025 08:30:01 +0200 Subject: [PATCH 46/80] don't register SchedulerTriggerNormalizer without symfony/serializer --- .../FrameworkBundle/DependencyInjection/FrameworkExtension.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index 64d27bca9d63b..05504af2e15a7 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -2297,7 +2297,7 @@ private function registerSchedulerConfiguration(ContainerBuilder $container, Php } // BC layer Scheduler < 7.3 - if (!class_exists(SchedulerTriggerNormalizer::class)) { + if (!ContainerBuilder::willBeAvailable('symfony/serializer', DenormalizerInterface::class, ['symfony/framework-bundle', 'symfony/scheduler']) || !class_exists(SchedulerTriggerNormalizer::class)) { $container->removeDefinition('serializer.normalizer.scheduler_trigger'); } } From bf9786c47f6fd0f6f48853221f01f2629be46aee Mon Sep 17 00:00:00 2001 From: Carlos Quintana Date: Tue, 27 May 2025 14:57:57 +0200 Subject: [PATCH 47/80] [FrameworkBundle] ensureKernelShutdown in tearDownAfterClass --- src/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.php b/src/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.php index ee67fa7af9728..e87ac48244e24 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.php +++ b/src/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.php @@ -11,6 +11,7 @@ namespace Symfony\Bundle\FrameworkBundle\Test; +use PHPUnit\Framework\Attributes\AfterClass; use PHPUnit\Framework\TestCase; use Symfony\Component\DependencyInjection\Container; use Symfony\Component\DependencyInjection\ContainerInterface; @@ -120,8 +121,11 @@ protected static function createKernel(array $options = []): KernelInterface /** * Shuts the kernel down if it was used in the test - called by the tearDown method by default. + * + * @afterClass */ - protected static function ensureKernelShutdown() + #[AfterClass] + public static function ensureKernelShutdown() { if (null !== static::$kernel) { static::$kernel->boot(); From 36974c32de4169f96cc7e00052c79b8cf775f7ac Mon Sep 17 00:00:00 2001 From: HypeMC Date: Tue, 3 Jun 2025 06:05:56 +0200 Subject: [PATCH 48/80] [PhpUnitBridge] Skip bootstrap for PHPUnit >=10 --- src/Symfony/Bridge/PhpUnit/bootstrap.php | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Bridge/PhpUnit/bootstrap.php b/src/Symfony/Bridge/PhpUnit/bootstrap.php index f11b7ab7f4945..50157aec4ad0f 100644 --- a/src/Symfony/Bridge/PhpUnit/bootstrap.php +++ b/src/Symfony/Bridge/PhpUnit/bootstrap.php @@ -14,7 +14,11 @@ use Symfony\Bridge\PhpUnit\DeprecationErrorHandler; // Detect if we need to serialize deprecations to a file. -if (in_array(\PHP_SAPI, ['cli', 'phpdbg'], true) && $file = getenv('SYMFONY_DEPRECATIONS_SERIALIZE')) { +if ( + // Skip if we're using PHPUnit >=10 + !class_exists(PHPUnit\Metadata\Metadata::class) + && in_array(\PHP_SAPI, ['cli', 'phpdbg'], true) && $file = getenv('SYMFONY_DEPRECATIONS_SERIALIZE') +) { DeprecationErrorHandler::collectDeprecations($file); return; @@ -46,6 +50,10 @@ } } -if ('disabled' !== getenv('SYMFONY_DEPRECATIONS_HELPER')) { +if ( + // Skip if we're using PHPUnit >=10 + !class_exists(PHPUnit\Metadata\Metadata::class, false) + && 'disabled' !== getenv('SYMFONY_DEPRECATIONS_HELPER') +) { DeprecationErrorHandler::register(getenv('SYMFONY_DEPRECATIONS_HELPER')); } From 4457633ce59336269e513c8b60b68e885afc1789 Mon Sep 17 00:00:00 2001 From: Mathias Arlaud Date: Tue, 3 Jun 2025 12:32:17 +0200 Subject: [PATCH 49/80] [TypeInfo] Handle `key-of` and `value-of` types --- .../TypeResolver/StringTypeResolverTest.php | 17 +++++++++++++- .../TypeResolver/StringTypeResolver.php | 23 +++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/TypeInfo/Tests/TypeResolver/StringTypeResolverTest.php b/src/Symfony/Component/TypeInfo/Tests/TypeResolver/StringTypeResolverTest.php index 9320987c6baed..1ea0390339004 100644 --- a/src/Symfony/Component/TypeInfo/Tests/TypeResolver/StringTypeResolverTest.php +++ b/src/Symfony/Component/TypeInfo/Tests/TypeResolver/StringTypeResolverTest.php @@ -152,6 +152,9 @@ public static function resolveDataProvider(): iterable yield [Type::generic(Type::object(\DateTime::class), Type::string(), Type::bool()), \DateTime::class.'']; yield [Type::generic(Type::object(\DateTime::class), Type::generic(Type::object(\Stringable::class), Type::bool())), \sprintf('%s<%s>', \DateTime::class, \Stringable::class)]; yield [Type::int(), 'int<0, 100>']; + yield [Type::string(), \sprintf('value-of<%s>', DummyBackedEnum::class)]; + yield [Type::int(), 'key-of>']; + yield [Type::bool(), 'value-of>']; // union yield [Type::union(Type::int(), Type::string()), 'int|string']; @@ -207,9 +210,21 @@ public function testCannotResolveParentWithoutTypeContext() $this->resolver->resolve('parent'); } - public function testCannotUnknownIdentifier() + public function testCannotResolveUnknownIdentifier() { $this->expectException(UnsupportedException::class); $this->resolver->resolve('unknown'); } + + public function testCannotResolveKeyOfInvalidType() + { + $this->expectException(UnsupportedException::class); + $this->resolver->resolve('key-of'); + } + + public function testCannotResolveValueOfInvalidType() + { + $this->expectException(UnsupportedException::class); + $this->resolver->resolve('value-of'); + } } diff --git a/src/Symfony/Component/TypeInfo/TypeResolver/StringTypeResolver.php b/src/Symfony/Component/TypeInfo/TypeResolver/StringTypeResolver.php index a172d388a8722..f219824dee1cf 100644 --- a/src/Symfony/Component/TypeInfo/TypeResolver/StringTypeResolver.php +++ b/src/Symfony/Component/TypeInfo/TypeResolver/StringTypeResolver.php @@ -38,6 +38,7 @@ use Symfony\Component\TypeInfo\Exception\InvalidArgumentException; use Symfony\Component\TypeInfo\Exception\UnsupportedException; use Symfony\Component\TypeInfo\Type; +use Symfony\Component\TypeInfo\Type\BackedEnumType; use Symfony\Component\TypeInfo\Type\BuiltinType; use Symfony\Component\TypeInfo\Type\CollectionType; use Symfony\Component\TypeInfo\Type\GenericType; @@ -182,6 +183,28 @@ private function getTypeFromNode(TypeNode $node, ?TypeContext $typeContext): Typ } if ($node instanceof GenericTypeNode) { + if ($node->type instanceof IdentifierTypeNode && 'value-of' === $node->type->name) { + $type = $this->getTypeFromNode($node->genericTypes[0], $typeContext); + if ($type instanceof BackedEnumType) { + return $type->getBackingType(); + } + + if ($type instanceof CollectionType) { + return $type->getCollectionValueType(); + } + + throw new \DomainException(\sprintf('"%s" is not a valid type for "value-of".', $node->genericTypes[0])); + } + + if ($node->type instanceof IdentifierTypeNode && 'key-of' === $node->type->name) { + $type = $this->getTypeFromNode($node->genericTypes[0], $typeContext); + if ($type instanceof CollectionType) { + return $type->getCollectionKeyType(); + } + + throw new \DomainException(\sprintf('"%s" is not a valid type for "key-of".', $node->genericTypes[0])); + } + $type = $this->getTypeFromNode($node->type, $typeContext); // handle integer ranges as simple integers From 73ecbcab3a4d6f93357d3feac73050707069669b Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Tue, 3 Jun 2025 21:58:06 +0200 Subject: [PATCH 50/80] re-add accidentally removed changelog examples --- src/Symfony/Component/Validator/CHANGELOG.md | 48 ++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/src/Symfony/Component/Validator/CHANGELOG.md b/src/Symfony/Component/Validator/CHANGELOG.md index a7363d7f59c19..e8146d2a50683 100644 --- a/src/Symfony/Component/Validator/CHANGELOG.md +++ b/src/Symfony/Component/Validator/CHANGELOG.md @@ -6,7 +6,55 @@ CHANGELOG * Add the `filenameCharset` and `filenameCountUnit` options to the `File` constraint * Deprecate defining custom constraints not supporting named arguments + + Before: + + ```php + use Symfony\Component\Validator\Constraint; + + class CustomConstraint extends Constraint + { + public function __construct(array $options) + { + // ... + } + } + ``` + + After: + + ```php + use Symfony\Component\Validator\Attribute\HasNamedArguments; + use Symfony\Component\Validator\Constraint; + + class CustomConstraint extends Constraint + { + #[HasNamedArguments] + public function __construct($option1, $option2, $groups, $payload) + { + // ... + } + } + ``` * Deprecate passing an array of options to the constructors of the constraint classes, pass each option as a dedicated argument instead + + Before: + + ```php + new NotNull([ + 'groups' => ['foo', 'bar'], + 'message' => 'a custom constraint violation message', + ]) + ``` + + After: + + ```php + new NotNull( + groups: ['foo', 'bar'], + message: 'a custom constraint violation message', + ) + ``` * Add support for ratio checks for SVG files to the `Image` constraint * Add support for the `otherwise` option in the `When` constraint * Add support for multiple fields containing nested constraints in `Composite` constraints From 2f1408ff0f66c453677b7740a3d81429c88156d9 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Tue, 3 Jun 2025 22:16:57 +0200 Subject: [PATCH 51/80] implicitly run all Composer commands non-interactively --- src/Symfony/Bridge/PhpUnit/bin/simple-phpunit.php | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit.php b/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit.php index ad6da8a2e9237..c021a4e8ee832 100644 --- a/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit.php +++ b/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit.php @@ -135,6 +135,7 @@ 'COMPOSER' => 'composer.json', 'COMPOSER_VENDOR_DIR' => 'vendor', 'COMPOSER_BIN_DIR' => 'bin', + 'COMPOSER_NO_INTERACTION' => '1', 'SYMFONY_SIMPLE_PHPUNIT_BIN_DIR' => __DIR__, ]; @@ -231,13 +232,13 @@ @copy("$PHPUNIT_VERSION_DIR/phpunit.xsd", 'phpunit.xsd'); chdir("$PHPUNIT_VERSION_DIR"); if ($SYMFONY_PHPUNIT_REMOVE) { - $passthruOrFail("$COMPOSER remove --no-update --no-interaction ".$SYMFONY_PHPUNIT_REMOVE); + $passthruOrFail("$COMPOSER remove --no-update ".$SYMFONY_PHPUNIT_REMOVE); } if ($SYMFONY_PHPUNIT_REQUIRE) { - $passthruOrFail("$COMPOSER require --no-update --no-interaction ".$SYMFONY_PHPUNIT_REQUIRE); + $passthruOrFail("$COMPOSER require --no-update ".$SYMFONY_PHPUNIT_REQUIRE); } if (5.1 <= $PHPUNIT_VERSION && $PHPUNIT_VERSION < 5.4) { - $passthruOrFail("$COMPOSER require --no-update --no-interaction phpunit/phpunit-mock-objects \"~3.1.0\""); + $passthruOrFail("$COMPOSER require --no-update phpunit/phpunit-mock-objects \"~3.1.0\""); } if (preg_match('{\^((\d++\.)\d++)[\d\.]*$}', $info['requires']['php'], $phpVersion) && version_compare($phpVersion[2].'99', \PHP_VERSION, '<')) { @@ -253,13 +254,13 @@ if (realpath($p) === realpath($path)) { $path = $p; } - $passthruOrFail("$COMPOSER require --no-update --no-interaction symfony/phpunit-bridge \"*@dev\""); + $passthruOrFail("$COMPOSER require --no-update symfony/phpunit-bridge \"*@dev\""); $passthruOrFail("$COMPOSER config repositories.phpunit-bridge path ".escapeshellarg(str_replace('/', \DIRECTORY_SEPARATOR, $path))); if ('\\' === \DIRECTORY_SEPARATOR) { file_put_contents('composer.json', preg_replace('/^( {8})"phpunit-bridge": \{$/m', "$0\n$1 ".'"options": {"symlink": false},', file_get_contents('composer.json'))); } } else { - $passthruOrFail("$COMPOSER require --no-update --no-interaction symfony/phpunit-bridge \"*\""); + $passthruOrFail("$COMPOSER require --no-update symfony/phpunit-bridge \"*\""); } $prevRoot = getenv('COMPOSER_ROOT_VERSION'); putenv("COMPOSER_ROOT_VERSION=$PHPUNIT_VERSION.99"); From 442970b9f13dba80307a952094e2ea2f68c4dc5a Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Tue, 3 Jun 2025 18:11:58 +0200 Subject: [PATCH 52/80] [JsonPath] Always use brackets notation with `JsonPath::key()` --- src/Symfony/Component/JsonPath/JsonPath.php | 25 +++++++- .../JsonPath/Tests/JsonCrawlerTest.php | 62 +++++++++++++++++++ .../Component/JsonPath/Tests/JsonPathTest.php | 53 ++++++++++++++-- 3 files changed, 133 insertions(+), 7 deletions(-) diff --git a/src/Symfony/Component/JsonPath/JsonPath.php b/src/Symfony/Component/JsonPath/JsonPath.php index 1009369b0a56d..e716167eb3f64 100644 --- a/src/Symfony/Component/JsonPath/JsonPath.php +++ b/src/Symfony/Component/JsonPath/JsonPath.php @@ -30,7 +30,9 @@ public function __construct( public function key(string $key): static { - return new self($this->path.(str_ends_with($this->path, '..') ? '' : '.').$key); + $escaped = $this->escapeKey($key); + + return new self($this->path.'["'.$escaped.'"]'); } public function index(int $index): static @@ -80,4 +82,25 @@ public function __toString(): string { return $this->path; } + + private function escapeKey(string $key): string + { + $key = strtr($key, [ + '\\' => '\\\\', + '"' => '\\"', + "\n" => '\\n', + "\r" => '\\r', + "\t" => '\\t', + "\b" => '\\b', + "\f" => '\\f' + ]); + + for ($i = 0; $i <= 31; $i++) { + if ($i < 8 || $i > 13) { + $key = str_replace(chr($i), sprintf('\\u%04x', $i), $key); + } + } + + return $key; + } } diff --git a/src/Symfony/Component/JsonPath/Tests/JsonCrawlerTest.php b/src/Symfony/Component/JsonPath/Tests/JsonCrawlerTest.php index 6871a56511890..66ccfc2642141 100644 --- a/src/Symfony/Component/JsonPath/Tests/JsonCrawlerTest.php +++ b/src/Symfony/Component/JsonPath/Tests/JsonCrawlerTest.php @@ -49,6 +49,19 @@ public function testAllAuthors() ], $result); } + public function testAllAuthorsWithBrackets() + { + $result = self::getBookstoreCrawler()->find('$..["author"]'); + + $this->assertCount(4, $result); + $this->assertSame([ + 'Nigel Rees', + 'Evelyn Waugh', + 'Herman Melville', + 'J. R. R. Tolkien', + ], $result); + } + public function testAllThingsInStore() { $result = self::getBookstoreCrawler()->find('$.store.*'); @@ -58,6 +71,15 @@ public function testAllThingsInStore() $this->assertArrayHasKey('color', $result[1]); } + public function testAllThingsInStoreWithBrackets() + { + $result = self::getBookstoreCrawler()->find('$["store"][*]'); + + $this->assertCount(2, $result); + $this->assertCount(4, $result[0]); + $this->assertArrayHasKey('color', $result[1]); + } + public function testEscapedDoubleQuotesInFieldName() { $crawler = new JsonCrawler(<<assertSame('Nigel Rees', $result[0]['author']); } + public function testBasicNameSelectorWithBrackts() + { + $result = self::getBookstoreCrawler()->find('$["store"]["book"]')[0]; + + $this->assertCount(4, $result); + $this->assertSame('Nigel Rees', $result[0]['author']); + } + public function testAllPrices() { $result = self::getBookstoreCrawler()->find('$.store..price'); @@ -121,6 +151,17 @@ public function testBooksWithIsbn() ], [$result[0]['isbn'], $result[1]['isbn']]); } + public function testBooksWithBracketsAndFilter() + { + $result = self::getBookstoreCrawler()->find('$..["book"][?(@.isbn)]'); + + $this->assertCount(2, $result); + $this->assertSame([ + '0-553-21311-3', + '0-395-19395-8', + ], [$result[0]['isbn'], $result[1]['isbn']]); + } + public function testBooksLessThanTenDollars() { $result = self::getBookstoreCrawler()->find('$..book[?(@.price < 10)]'); @@ -216,6 +257,14 @@ public function testEverySecondElementReverseSlice() $this->assertSame([6, 2, 5], $result); } + public function testEverySecondElementReverseSliceAndBrackets() + { + $crawler = self::getSimpleCollectionCrawler(); + + $result = $crawler->find('$["a"][::-2]'); + $this->assertSame([6, 2, 5], $result); + } + public function testEmptyResults() { $crawler = self::getSimpleCollectionCrawler(); @@ -404,6 +453,19 @@ public function testAcceptsJsonPath() $this->assertSame('red', $result[0]['color']); } + public function testStarAsKey() + { + $crawler = new JsonCrawler(<<find('$["*"]'); + + $this->assertCount(1, $result); + $this->assertSame(['a' => 1, 'b' => 2], $result[0]); + } + + private static function getBookstoreCrawler(): JsonCrawler { return new JsonCrawler(<<index(0) ->key('address'); - $this->assertSame('$.users[0].address', (string) $path); - $this->assertSame('$.users[0].address..city', (string) $path->deepScan()->key('city')); + $this->assertSame('$["users"][0]["address"]', (string) $path); + $this->assertSame('$["users"][0]["address"]..["city"]', (string) $path->deepScan()->key('city')); } public function testBuildWithFilter() @@ -33,7 +33,7 @@ public function testBuildWithFilter() $path = $path->key('users') ->filter('@.age > 18'); - $this->assertSame('$.users[?(@.age > 18)]', (string) $path); + $this->assertSame('$["users"][?(@.age > 18)]', (string) $path); } public function testAll() @@ -42,7 +42,7 @@ public function testAll() $path = $path->key('users') ->all(); - $this->assertSame('$.users[*]', (string) $path); + $this->assertSame('$["users"][*]', (string) $path); } public function testFirst() @@ -51,7 +51,7 @@ public function testFirst() $path = $path->key('users') ->first(); - $this->assertSame('$.users[0]', (string) $path); + $this->assertSame('$["users"][0]', (string) $path); } public function testLast() @@ -60,6 +60,47 @@ public function testLast() $path = $path->key('users') ->last(); - $this->assertSame('$.users[-1]', (string) $path); + $this->assertSame('$["users"][-1]', (string) $path); + } + + /** + * @dataProvider provideKeysToEscape + */ + public function testEscapedKey(string $key, string $expectedPath) + { + $path = new JsonPath(); + $path = $path->key($key); + + $this->assertSame($expectedPath, (string) $path); + } + + public static function provideKeysToEscape(): iterable + { + yield ['simple_key', '$["simple_key"]']; + yield ['key"with"quotes', '$["key\\"with\\"quotes"]']; + yield ['path\\backslash', '$["path\\backslash"]']; + yield ['mixed\\"case', '$["mixed\\\\\\"case"]']; + yield ['unicode_🔑', '$["unicode_🔑"]']; + yield ['"quotes_only"', '$["\\"quotes_only\\""]']; + yield ['\\\\multiple\\\\backslashes', '$["\\\\\\\\multiple\\\\\\backslashes"]']; + yield ["control\x00\x1f\x1echar", '$["control\u0000\u001f\u001echar"]']; + + yield ['key"with\\"mixed', '$["key\\"with\\\\\\"mixed"]']; + yield ['\\"complex\\"case\\"', '$["\\\\\\"complex\\\\\\"case\\\\\\""]']; + yield ['json_like":{"value":"test"}', '$["json_like\\":{\\"value\\":\\"test\\"}"]']; + yield ['C:\\Program Files\\"App Name"', '$["C:\\\\Program Files\\\\\\"App Name\\""]']; + + yield ['key_with_é_accents', '$["key_with_é_accents"]']; + yield ['unicode_→_arrows', '$["unicode_→_arrows"]']; + yield ['chinese_中文_key', '$["chinese_中文_key"]']; + + yield ['', '$[""]']; + yield [' ', '$[" "]']; + yield [' spaces ', '$[" spaces "]']; + yield ["\t\n\r", '$["\\t\\n\\r"]']; + yield ["control\x00char", '$["control\u0000char"]']; + yield ["newline\nkey", '$["newline\\nkey"]']; + yield ["tab\tkey", '$["tab\\tkey"]']; + yield ["carriage\rreturn", '$["carriage\\rreturn"]']; } } From 51205c8b376ebe1db4151d1fadacfd43a2f2f016 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 4 Jun 2025 09:39:44 +0200 Subject: [PATCH 53/80] Fix building packages in the CI --- .github/build-packages.php | 32 ++++++++++++++------------------ .github/composer.json | 5 ----- .github/workflows/unit-tests.yml | 10 ++-------- 3 files changed, 16 insertions(+), 31 deletions(-) delete mode 100644 .github/composer.json diff --git a/.github/build-packages.php b/.github/build-packages.php index dda58049ab842..4793b8483d7ed 100644 --- a/.github/build-packages.php +++ b/.github/build-packages.php @@ -1,8 +1,14 @@ '__unset' !== $v); + }, []); + + return $expandedVersions ?? []; +} if (3 > $_SERVER['argc']) { echo "Usage: branch version dir1 dir2 ... dirN\n"; @@ -56,24 +62,14 @@ $packages[$package->name][$package->version] = $package; - if (false !== $taggedReleases = @file_get_contents('https://repo.packagist.org/p2/'.$package->name.'.json')) { - $versions = MetadataMinifier::expand(json_decode($taggedReleases, true)['packages'][$package->name]); + foreach (['.json', '~dev.json'] as $ext) { + $versions = @file_get_contents('https://repo.packagist.org/p2/'.$package->name.$ext) ?: '[]'; + $versions = json_decode($versions, true)['packages'][$package->name] ?? []; - foreach ($versions as $v => $p) { - $packages[$package->name] += [$v => $p]; + foreach (expandComposerMetadata($versions) as $p) { + $packages[$package->name] += [$p['version'] => $p]; } } - - if (false !== $devReleases = @file_get_contents('https://repo.packagist.org/p2/'.$package->name.'~dev.json')) { - $versions = MetadataMinifier::expand(json_decode($taggedReleases, true)['packages'][$package->name]); - } else { - $versions = sprintf('{"packages":{"%s":{"%s":%s}}}', $package->name, $package->version, file_get_contents($dir.'/composer.json')); - $versions = json_decode($versions, true)['packages'][$package->name]; - } - - foreach ($versions as $v => $p) { - $packages[$package->name] += [$v => $p]; - } } file_put_contents('packages.json', json_encode(compact('packages'), $flags)); diff --git a/.github/composer.json b/.github/composer.json deleted file mode 100644 index 5bd3935482174..0000000000000 --- a/.github/composer.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "require": { - "composer/metadata-minifier": "^1.0" - } -} diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index a8b46ce823cc0..8e4c8516dad81 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -92,18 +92,12 @@ jobs: # Create local composer packages for each patched components and reference them in composer.json files when cross-testing components if [[ ! "${{ matrix.mode }}" = *-deps ]]; then - cd .github - composer install - php ./build-packages.php HEAD^ $SYMFONY_VERSION src/Symfony/Bridge/PhpUnit - cd .. + php .github/build-packages.php HEAD^ $SYMFONY_VERSION src/Symfony/Bridge/PhpUnit else echo SYMFONY_DEPRECATIONS_HELPER=weak >> $GITHUB_ENV cp composer.json composer.json.orig echo -e '{\n"require":{'"$(grep phpunit-bridge composer.json)"'"php":"*"},"minimum-stability":"dev"}' > composer.json - cd .github - composer install - php ./build-packages.php HEAD^ $SYMFONY_VERSION $(find src/Symfony -mindepth 2 -type f -name composer.json -printf '%h\n' | grep -v src/Symfony/Component/Intl/Resources/emoji) - cd .. + php .github/build-packages.php HEAD^ $SYMFONY_VERSION $(find src/Symfony -mindepth 2 -type f -name composer.json -printf '%h\n' | grep -v src/Symfony/Component/Intl/Resources/emoji) mv composer.json composer.json.phpunit mv composer.json.orig composer.json fi From db646c72569d2d851129ea682f523610e80cbf9a Mon Sep 17 00:00:00 2001 From: Jiri Korenek Date: Tue, 3 Jun 2025 20:49:38 +0200 Subject: [PATCH 54/80] [Validator] review cs tran --- .../Validator/Resources/translations/validators.cs.xlf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.cs.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.cs.xlf index 2a2e559b95238..d5f48f0ae7ff2 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.cs.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.cs.xlf @@ -468,7 +468,7 @@ This value is not a valid Twig template. - Tato hodnota není platná šablona Twig. + Tato hodnota není platná Twig šablona. From b778a80c81b2cc4f22fb3f4ff2cde17b9c2a9b63 Mon Sep 17 00:00:00 2001 From: Mathias Arlaud Date: Mon, 2 Jun 2025 18:31:59 +0200 Subject: [PATCH 55/80] [TypeInfo] Fix type alias resolving --- .../Tests/Fixtures/DummyWithTypeAliases.php | 26 ++++++ .../TypeContext/TypeContextFactoryTest.php | 33 ++++++++ .../TypeContext/TypeContextFactory.php | 79 ++++++++++++++++--- 3 files changed, 125 insertions(+), 13 deletions(-) diff --git a/src/Symfony/Component/TypeInfo/Tests/Fixtures/DummyWithTypeAliases.php b/src/Symfony/Component/TypeInfo/Tests/Fixtures/DummyWithTypeAliases.php index 0b65137e4cdda..7f73190df1549 100644 --- a/src/Symfony/Component/TypeInfo/Tests/Fixtures/DummyWithTypeAliases.php +++ b/src/Symfony/Component/TypeInfo/Tests/Fixtures/DummyWithTypeAliases.php @@ -12,11 +12,15 @@ namespace Symfony\Component\TypeInfo\Tests\Fixtures; /** + * @phpstan-type CustomArray = array{0: CustomInt, 1: CustomString, 2: bool} * @phpstan-type CustomString = string + * * @phpstan-import-type CustomInt from DummyWithPhpDoc * @phpstan-import-type CustomInt from DummyWithPhpDoc as AliasedCustomInt * + * @psalm-type PsalmCustomArray = array{0: PsalmCustomInt, 1: PsalmCustomString, 2: bool} * @psalm-type PsalmCustomString = string + * * @psalm-import-type PsalmCustomInt from DummyWithPhpDoc * @psalm-import-type PsalmCustomInt from DummyWithPhpDoc as PsalmAliasedCustomInt */ @@ -53,9 +57,31 @@ final class DummyWithTypeAliases public mixed $psalmOtherAliasedExternalAlias; } +/** + * @phpstan-type Foo = array{0: Bar} + * @phpstan-type Bar = array{0: Foo} + */ +final class DummyWithRecursiveTypeAliases +{ +} + +/** + * @phpstan-type Invalid = SomethingInvalid + */ +final class DummyWithInvalidTypeAlias +{ +} + /** * @phpstan-import-type Invalid from DummyWithTypeAliases */ final class DummyWithInvalidTypeAliasImport { } + +/** + * @phpstan-import-type Invalid from int + */ +final class DummyWithTypeAliasImportedFromInvalidClassName +{ +} diff --git a/src/Symfony/Component/TypeInfo/Tests/TypeContext/TypeContextFactoryTest.php b/src/Symfony/Component/TypeInfo/Tests/TypeContext/TypeContextFactoryTest.php index e7794e4f114b6..7d6725ed26743 100644 --- a/src/Symfony/Component/TypeInfo/Tests/TypeContext/TypeContextFactoryTest.php +++ b/src/Symfony/Component/TypeInfo/Tests/TypeContext/TypeContextFactoryTest.php @@ -15,9 +15,12 @@ use Symfony\Component\TypeInfo\Exception\LogicException; use Symfony\Component\TypeInfo\Tests\Fixtures\AbstractDummy; use Symfony\Component\TypeInfo\Tests\Fixtures\Dummy; +use Symfony\Component\TypeInfo\Tests\Fixtures\DummyWithInvalidTypeAlias; use Symfony\Component\TypeInfo\Tests\Fixtures\DummyWithInvalidTypeAliasImport; +use Symfony\Component\TypeInfo\Tests\Fixtures\DummyWithRecursiveTypeAliases; use Symfony\Component\TypeInfo\Tests\Fixtures\DummyWithTemplates; use Symfony\Component\TypeInfo\Tests\Fixtures\DummyWithTypeAliases; +use Symfony\Component\TypeInfo\Tests\Fixtures\DummyWithTypeAliasImportedFromInvalidClassName; use Symfony\Component\TypeInfo\Tests\Fixtures\DummyWithUses; use Symfony\Component\TypeInfo\Type; use Symfony\Component\TypeInfo\TypeContext\TypeContextFactory; @@ -128,27 +131,33 @@ public function testCollectTypeAliases() $this->assertEquals([ 'CustomString' => Type::string(), 'CustomInt' => Type::int(), + 'CustomArray' => Type::arrayShape([0 => Type::int(), 1 => Type::string(), 2 => Type::bool()]), 'AliasedCustomInt' => Type::int(), 'PsalmCustomString' => Type::string(), 'PsalmCustomInt' => Type::int(), + 'PsalmCustomArray' => Type::arrayShape([0 => Type::int(), 1 => Type::string(), 2 => Type::bool()]), 'PsalmAliasedCustomInt' => Type::int(), ], $this->typeContextFactory->createFromClassName(DummyWithTypeAliases::class)->typeAliases); $this->assertEquals([ 'CustomString' => Type::string(), 'CustomInt' => Type::int(), + 'CustomArray' => Type::arrayShape([0 => Type::int(), 1 => Type::string(), 2 => Type::bool()]), 'AliasedCustomInt' => Type::int(), 'PsalmCustomString' => Type::string(), 'PsalmCustomInt' => Type::int(), + 'PsalmCustomArray' => Type::arrayShape([0 => Type::int(), 1 => Type::string(), 2 => Type::bool()]), 'PsalmAliasedCustomInt' => Type::int(), ], $this->typeContextFactory->createFromReflection(new \ReflectionClass(DummyWithTypeAliases::class))->typeAliases); $this->assertEquals([ 'CustomString' => Type::string(), 'CustomInt' => Type::int(), + 'CustomArray' => Type::arrayShape([0 => Type::int(), 1 => Type::string(), 2 => Type::bool()]), 'AliasedCustomInt' => Type::int(), 'PsalmCustomString' => Type::string(), 'PsalmCustomInt' => Type::int(), + 'PsalmCustomArray' => Type::arrayShape([0 => Type::int(), 1 => Type::string(), 2 => Type::bool()]), 'PsalmAliasedCustomInt' => Type::int(), ], $this->typeContextFactory->createFromReflection(new \ReflectionProperty(DummyWithTypeAliases::class, 'localAlias'))->typeAliases); } @@ -167,4 +176,28 @@ public function testThrowWhenImportingInvalidAlias() $this->typeContextFactory->createFromClassName(DummyWithInvalidTypeAliasImport::class); } + + public function testThrowWhenCannotResolveTypeAlias() + { + $this->expectException(LogicException::class); + $this->expectExceptionMessage('Cannot resolve "Invalid" type alias.'); + + $this->typeContextFactory->createFromClassName(DummyWithInvalidTypeAlias::class); + } + + public function testThrowWhenTypeAliasNotImportedFromValidClassName() + { + $this->expectException(LogicException::class); + $this->expectExceptionMessage('Type alias "Invalid" is not imported from a valid class name.'); + + $this->typeContextFactory->createFromClassName(DummyWithTypeAliasImportedFromInvalidClassName::class); + } + + public function testThrowWhenImportingRecursiveTypeAliases() + { + $this->expectException(LogicException::class); + $this->expectExceptionMessage('Cannot resolve "Bar" type alias.'); + + $this->typeContextFactory->createFromClassName(DummyWithRecursiveTypeAliases::class)->typeAliases; + } } diff --git a/src/Symfony/Component/TypeInfo/TypeContext/TypeContextFactory.php b/src/Symfony/Component/TypeInfo/TypeContext/TypeContextFactory.php index d268c85fe49b0..a149a52249ba7 100644 --- a/src/Symfony/Component/TypeInfo/TypeContext/TypeContextFactory.php +++ b/src/Symfony/Component/TypeInfo/TypeContext/TypeContextFactory.php @@ -199,32 +199,85 @@ private function collectTypeAliases(\ReflectionClass $reflection, TypeContext $t } $aliases = []; - foreach ($this->getPhpDocNode($rawDocNode)->getTagsByName('@psalm-type') + $this->getPhpDocNode($rawDocNode)->getTagsByName('@phpstan-type') as $tag) { - if (!$tag->value instanceof TypeAliasTagValueNode) { + $resolvedAliases = []; + + foreach ($this->getPhpDocNode($rawDocNode)->getTagsByName('@psalm-import-type') + $this->getPhpDocNode($rawDocNode)->getTagsByName('@phpstan-import-type') as $tag) { + if (!$tag->value instanceof TypeAliasImportTagValueNode) { continue; } - $aliases[$tag->value->alias] = $this->stringTypeResolver->resolve((string) $tag->value->type, $typeContext); + $importedFromType = $this->stringTypeResolver->resolve((string) $tag->value->importedFrom, $typeContext); + if (!$importedFromType instanceof ObjectType) { + throw new LogicException(\sprintf('Type alias "%s" is not imported from a valid class name.', $tag->value->importedAlias)); + } + + $importedFromContext = $this->createFromClassName($importedFromType->getClassName()); + + $typeAlias = $importedFromContext->typeAliases[$tag->value->importedAlias] ?? null; + if (!$typeAlias) { + throw new LogicException(\sprintf('Cannot find any "%s" type alias in "%s".', $tag->value->importedAlias, $importedFromType->getClassName())); + } + + $resolvedAliases[$tag->value->importedAs ?? $tag->value->importedAlias] = $typeAlias; } - foreach ($this->getPhpDocNode($rawDocNode)->getTagsByName('@psalm-import-type') + $this->getPhpDocNode($rawDocNode)->getTagsByName('@phpstan-import-type') as $tag) { - if (!$tag->value instanceof TypeAliasImportTagValueNode) { + foreach ($this->getPhpDocNode($rawDocNode)->getTagsByName('@psalm-type') + $this->getPhpDocNode($rawDocNode)->getTagsByName('@phpstan-type') as $tag) { + if (!$tag->value instanceof TypeAliasTagValueNode) { continue; } - /** @var ObjectType $importedType */ - $importedType = $this->stringTypeResolver->resolve((string) $tag->value->importedFrom, $typeContext); - $importedTypeContext = $this->createFromClassName($importedType->getClassName()); + $aliases[$tag->value->alias] = (string) $tag->value->type; + } - $typeAlias = $importedTypeContext->typeAliases[$tag->value->importedAlias] ?? null; - if (!$typeAlias) { - throw new LogicException(\sprintf('Cannot find any "%s" type alias in "%s".', $tag->value->importedAlias, $importedType->getClassName())); + return $this->resolveTypeAliases($aliases, $resolvedAliases, $typeContext); + } + + /** + * @param array $toResolve + * @param array $resolved + * + * @return array + */ + private function resolveTypeAliases(array $toResolve, array $resolved, TypeContext $typeContext): array + { + if (!$toResolve) { + return []; + } + + $typeContext = new TypeContext( + $typeContext->calledClassName, + $typeContext->declaringClassName, + $typeContext->namespace, + $typeContext->uses, + $typeContext->templates, + $typeContext->typeAliases + $resolved, + ); + + $succeeded = false; + $lastFailure = null; + $lastFailingAlias = null; + + foreach ($toResolve as $alias => $type) { + try { + $resolved[$alias] = $this->stringTypeResolver->resolve($type, $typeContext); + unset($toResolve[$alias]); + $succeeded = true; + } catch (UnsupportedException $lastFailure) { + $lastFailingAlias = $alias; } + } + + // nothing has succeeded, the result won't be different from the + // previous one, we can stop here. + if (!$succeeded) { + throw new LogicException(\sprintf('Cannot resolve "%s" type alias.', $lastFailingAlias), 0, $lastFailure); + } - $aliases[$tag->value->importedAs ?? $tag->value->importedAlias] = $typeAlias; + if ($toResolve) { + return $this->resolveTypeAliases($toResolve, $resolved, $typeContext); } - return $aliases; + return $resolved; } private function getPhpDocNode(string $rawDocNode): PhpDocNode From 11ad92285af13418fe4ab1fbf232271a50ac4741 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 4 Jun 2025 12:06:03 +0200 Subject: [PATCH 56/80] [JsonStreamer] lazyGhostsDir should be optional --- src/Symfony/Component/JsonStreamer/JsonStreamReader.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/JsonStreamer/JsonStreamReader.php b/src/Symfony/Component/JsonStreamer/JsonStreamReader.php index b2f2fabaa3dad..e813f4a8a5408 100644 --- a/src/Symfony/Component/JsonStreamer/JsonStreamReader.php +++ b/src/Symfony/Component/JsonStreamer/JsonStreamReader.php @@ -45,7 +45,7 @@ public function __construct( private ContainerInterface $valueTransformers, PropertyMetadataLoaderInterface $propertyMetadataLoader, string $streamReadersDir, - string $lazyGhostsDir, + ?string $lazyGhostsDir = null, ) { $this->streamReaderGenerator = new StreamReaderGenerator($propertyMetadataLoader, $streamReadersDir); $this->instantiator = new Instantiator(); From 3e4098b5c91468d21f5bf59b7e783862fb3ad9f5 Mon Sep 17 00:00:00 2001 From: Matthias Schmidt Date: Tue, 3 Jun 2025 20:12:23 +0200 Subject: [PATCH 57/80] [JsonPath] Fix typo in comment in JsonCrawler --- src/Symfony/Component/JsonPath/JsonCrawler.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/JsonPath/JsonCrawler.php b/src/Symfony/Component/JsonPath/JsonCrawler.php index 75c61e14f79d7..b1d7ef0bf94d8 100644 --- a/src/Symfony/Component/JsonPath/JsonCrawler.php +++ b/src/Symfony/Component/JsonPath/JsonCrawler.php @@ -222,7 +222,7 @@ private function evaluateBracket(string $expr, mixed $value): array throw new JsonCrawlerException($expr, 'Invalid filter expression'); } - // remove outrer filter parentheses + // remove outer filter parentheses $innerExpr = substr(substr($filterExpr, 1), 0, -1); return $this->evaluateFilter($innerExpr, $value); From 8092ffd3a7e829d0c33e94372df146aae7870bc3 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 4 Jun 2025 14:09:48 +0200 Subject: [PATCH 58/80] [Security] Keep roles when serializing tokens --- .../Authentication/Token/AbstractToken.php | 23 ++++--------------- 1 file changed, 4 insertions(+), 19 deletions(-) diff --git a/src/Symfony/Component/Security/Core/Authentication/Token/AbstractToken.php b/src/Symfony/Component/Security/Core/Authentication/Token/AbstractToken.php index b2e18a29efe51..683e46d4e0eb8 100644 --- a/src/Symfony/Component/Security/Core/Authentication/Token/AbstractToken.php +++ b/src/Symfony/Component/Security/Core/Authentication/Token/AbstractToken.php @@ -32,16 +32,12 @@ abstract class AbstractToken implements TokenInterface, \Serializable */ public function __construct(array $roles = []) { - $this->roleNames = []; - - foreach ($roles as $role) { - $this->roleNames[] = (string) $role; - } + $this->roleNames = $roles; } public function getRoleNames(): array { - return $this->roleNames ??= self::__construct($this->user->getRoles()) ?? $this->roleNames; + return $this->roleNames ??= $this->user?->getRoles() ?? []; } public function getUserIdentifier(): string @@ -90,13 +86,7 @@ public function eraseCredentials(): void */ public function __serialize(): array { - $data = [$this->user, true, null, $this->attributes]; - - if (!$this->user instanceof EquatableInterface) { - $data[] = $this->roleNames; - } - - return $data; + return [$this->user, true, null, $this->attributes, $this->getRoleNames()]; } /** @@ -160,12 +150,7 @@ public function __toString(): string $class = static::class; $class = substr($class, strrpos($class, '\\') + 1); - $roles = []; - foreach ($this->roleNames as $role) { - $roles[] = $role; - } - - return \sprintf('%s(user="%s", roles="%s")', $class, $this->getUserIdentifier(), implode(', ', $roles)); + return \sprintf('%s(user="%s", roles="%s")', $class, $this->getUserIdentifier(), implode(', ', $this->getRoleNames())); } /** From d8dc8573cfd39048fac45e5d182360c8efc8dd6d Mon Sep 17 00:00:00 2001 From: Jack Worman Date: Tue, 3 Jun 2025 21:54:08 -0400 Subject: [PATCH 59/80] [Process] Improve typing for process callback --- src/Symfony/Component/Process/PhpProcess.php | 3 ++ .../Component/Process/PhpSubprocess.php | 3 ++ src/Symfony/Component/Process/Process.php | 41 +++++++++++-------- 3 files changed, 31 insertions(+), 16 deletions(-) diff --git a/src/Symfony/Component/Process/PhpProcess.php b/src/Symfony/Component/Process/PhpProcess.php index 0e7ff84647fb8..930f591f0d399 100644 --- a/src/Symfony/Component/Process/PhpProcess.php +++ b/src/Symfony/Component/Process/PhpProcess.php @@ -55,6 +55,9 @@ public static function fromShellCommandline(string $command, ?string $cwd = null throw new LogicException(\sprintf('The "%s()" method cannot be called when using "%s".', __METHOD__, self::class)); } + /** + * @param (callable('out'|'err', string):void)|null $callback + */ public function start(?callable $callback = null, array $env = []): void { if (null === $this->getCommandLine()) { diff --git a/src/Symfony/Component/Process/PhpSubprocess.php b/src/Symfony/Component/Process/PhpSubprocess.php index bdd4173c2a053..8282f93cd47ea 100644 --- a/src/Symfony/Component/Process/PhpSubprocess.php +++ b/src/Symfony/Component/Process/PhpSubprocess.php @@ -78,6 +78,9 @@ public static function fromShellCommandline(string $command, ?string $cwd = null throw new LogicException(\sprintf('The "%s()" method cannot be called when using "%s".', __METHOD__, self::class)); } + /** + * @param (callable('out'|'err', string):void)|null $callback + */ public function start(?callable $callback = null, array $env = []): void { if (null === $this->getCommandLine()) { diff --git a/src/Symfony/Component/Process/Process.php b/src/Symfony/Component/Process/Process.php index a8beb93d44988..d52db23ac6afb 100644 --- a/src/Symfony/Component/Process/Process.php +++ b/src/Symfony/Component/Process/Process.php @@ -51,6 +51,9 @@ class Process implements \IteratorAggregate public const ITER_SKIP_OUT = 4; // Use this flag to skip STDOUT while iterating public const ITER_SKIP_ERR = 8; // Use this flag to skip STDERR while iterating + /** + * @var \Closure('out'|'err', string)|null + */ private ?\Closure $callback = null; private array|string $commandline; private ?string $cwd; @@ -231,8 +234,8 @@ public function __clone() * The STDOUT and STDERR are also available after the process is finished * via the getOutput() and getErrorOutput() methods. * - * @param callable|null $callback A PHP callback to run whenever there is some - * output available on STDOUT or STDERR + * @param (callable('out'|'err', string):void)|null $callback A PHP callback to run whenever there is some + * output available on STDOUT or STDERR * * @return int The exit status code * @@ -257,6 +260,9 @@ public function run(?callable $callback = null, array $env = []): int * This is identical to run() except that an exception is thrown if the process * exits with a non-zero exit code. * + * @param (callable('out'|'err', string):void)|null $callback A PHP callback to run whenever there is some + * output available on STDOUT or STDERR + * * @return $this * * @throws ProcessFailedException if the process didn't terminate successfully @@ -284,8 +290,8 @@ public function mustRun(?callable $callback = null, array $env = []): static * the output in real-time while writing the standard input to the process. * It allows to have feedback from the independent process during execution. * - * @param callable|null $callback A PHP callback to run whenever there is some - * output available on STDOUT or STDERR + * @param (callable('out'|'err', string):void)|null $callback A PHP callback to run whenever there is some + * output available on STDOUT or STDERR * * @throws ProcessStartFailedException When process can't be launched * @throws RuntimeException When process is already running @@ -395,8 +401,8 @@ public function start(?callable $callback = null, array $env = []): void * * Be warned that the process is cloned before being started. * - * @param callable|null $callback A PHP callback to run whenever there is some - * output available on STDOUT or STDERR + * @param (callable('out'|'err', string):void)|null $callback A PHP callback to run whenever there is some + * output available on STDOUT or STDERR * * @throws ProcessStartFailedException When process can't be launched * @throws RuntimeException When process is already running @@ -424,7 +430,8 @@ public function restart(?callable $callback = null, array $env = []): static * from the output in real-time while writing the standard input to the process. * It allows to have feedback from the independent process during execution. * - * @param callable|null $callback A valid PHP callback + * @param (callable('out'|'err', string):void)|null $callback A PHP callback to run whenever there is some + * output available on STDOUT or STDERR * * @return int The exitcode of the process * @@ -471,6 +478,9 @@ public function wait(?callable $callback = null): int * from the output in real-time while writing the standard input to the process. * It allows to have feedback from the independent process during execution. * + * @param (callable('out'|'err', string):bool)|null $callback A PHP callback to run whenever there is some + * output available on STDOUT or STDERR + * * @throws RuntimeException When process timed out * @throws LogicException When process is not yet started * @throws ProcessTimedOutException In case the timeout was reached @@ -1291,7 +1301,9 @@ private function getDescriptors(bool $hasCallback): array * The callbacks adds all occurred output to the specific buffer and calls * the user callback (if present) with the received output. * - * @param callable|null $callback The user defined PHP callback + * @param callable('out'|'err', string)|null $callback + * + * @return \Closure('out'|'err', string):bool */ protected function buildCallback(?callable $callback = null): \Closure { @@ -1299,14 +1311,11 @@ protected function buildCallback(?callable $callback = null): \Closure return fn ($type, $data): bool => null !== $callback && $callback($type, $data); } - $out = self::OUT; - - return function ($type, $data) use ($callback, $out): bool { - if ($out == $type) { - $this->addOutput($data); - } else { - $this->addErrorOutput($data); - } + return function ($type, $data) use ($callback): bool { + match ($type) { + self::OUT => $this->addOutput($data), + self::ERR => $this->addErrorOutput($data), + }; return null !== $callback && $callback($type, $data); }; From 0291ea140a91daf0a0b7ff4516fce4c60905b637 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 4 Jun 2025 15:57:42 +0200 Subject: [PATCH 60/80] Revert "bug #60564 [FrameworkBundle] ensureKernelShutdown in tearDownAfterClass (cquintana92)" This reverts commit ec76ab4f28454ebfbcf14287b2aac1351f00df79, reversing changes made to bc886008906f022a8fbf9796b943af928b64d86c. --- src/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.php | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.php b/src/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.php index e87ac48244e24..ee67fa7af9728 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.php +++ b/src/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.php @@ -11,7 +11,6 @@ namespace Symfony\Bundle\FrameworkBundle\Test; -use PHPUnit\Framework\Attributes\AfterClass; use PHPUnit\Framework\TestCase; use Symfony\Component\DependencyInjection\Container; use Symfony\Component\DependencyInjection\ContainerInterface; @@ -121,11 +120,8 @@ protected static function createKernel(array $options = []): KernelInterface /** * Shuts the kernel down if it was used in the test - called by the tearDown method by default. - * - * @afterClass */ - #[AfterClass] - public static function ensureKernelShutdown() + protected static function ensureKernelShutdown() { if (null !== static::$kernel) { static::$kernel->boot(); From c193b98678b125c94b9de24292c51b31d219aa69 Mon Sep 17 00:00:00 2001 From: Carlos Quintana Date: Tue, 27 May 2025 14:57:57 +0200 Subject: [PATCH 61/80] [FrameworkBundle] ensureKernelShutdown in tearDownAfterClass --- .../Bundle/FrameworkBundle/Test/KernelTestCase.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.php b/src/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.php index ee67fa7af9728..1312f6592176d 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.php +++ b/src/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.php @@ -45,6 +45,14 @@ protected function tearDown(): void static::$booted = false; } + public static function tearDownAfterClass(): void + { + static::ensureKernelShutdown(); + static::$class = null; + static::$kernel = null; + static::$booted = false; + } + /** * @throws \RuntimeException * @throws \LogicException From 90d9afb1b0fc5ac7ed8b12f27ec76ea5e32e45c5 Mon Sep 17 00:00:00 2001 From: llupa Date: Wed, 4 Jun 2025 17:20:03 +0200 Subject: [PATCH 62/80] [Intl] Add missing currency (NOK) localization (en_NO) --- .../Component/Intl/Resources/data/currencies/en_NO.php | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 src/Symfony/Component/Intl/Resources/data/currencies/en_NO.php diff --git a/src/Symfony/Component/Intl/Resources/data/currencies/en_NO.php b/src/Symfony/Component/Intl/Resources/data/currencies/en_NO.php new file mode 100644 index 0000000000000..dc28340678e53 --- /dev/null +++ b/src/Symfony/Component/Intl/Resources/data/currencies/en_NO.php @@ -0,0 +1,10 @@ + [ + 'NOK' => [ + 'kr', + 'Norwegian Krone', + ], + ], +]; From ad7c6b992d382bd240f9ab369fed35d4d85dac32 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 5 Jun 2025 10:47:29 +0200 Subject: [PATCH 63/80] Tweak return type declarations and related CI checks --- .github/expected-missing-return-types.diff | 41 +++++++++++++++---- .github/patch-types.php | 34 +++++++-------- .github/workflows/unit-tests.yml | 5 ++- .../Test/Traits/RuntimeLoaderProvider.php | 3 ++ .../Tests/Kernel/MicroKernelTraitTest.php | 25 ----------- .../Tests/Kernel/MinimalKernel.php | 39 ++++++++++++++++++ .../VarDumper/Test/VarDumperTestTrait.php | 6 +++ 7 files changed, 100 insertions(+), 53 deletions(-) create mode 100644 src/Symfony/Bundle/FrameworkBundle/Tests/Kernel/MinimalKernel.php diff --git a/.github/expected-missing-return-types.diff b/.github/expected-missing-return-types.diff index 9faed9a44dd73..1979bba26f58c 100644 --- a/.github/expected-missing-return-types.diff +++ b/.github/expected-missing-return-types.diff @@ -7,6 +7,16 @@ git checkout src/Symfony/Contracts/Service/ResetInterface.php (echo "$head" && echo && git diff -U2 src/ | grep '^index ' -v) > .github/expected-missing-return-types.diff git checkout composer.json src/ +diff --git a/src/Symfony/Bridge/Twig/Test/Traits/RuntimeLoaderProvider.php b/src/Symfony/Bridge/Twig/Test/Traits/RuntimeLoaderProvider.php +--- a/src/Symfony/Bridge/Twig/Test/Traits/RuntimeLoaderProvider.php ++++ b/src/Symfony/Bridge/Twig/Test/Traits/RuntimeLoaderProvider.php +@@ -21,5 +21,5 @@ trait RuntimeLoaderProvider + * @return void + */ +- protected function registerTwigRuntimeLoader(Environment $environment, FormRenderer $renderer) ++ protected function registerTwigRuntimeLoader(Environment $environment, FormRenderer $renderer): void + { + $loader = $this->createMock(RuntimeLoaderInterface::class); diff --git a/src/Symfony/Component/BrowserKit/AbstractBrowser.php b/src/Symfony/Component/BrowserKit/AbstractBrowser.php --- a/src/Symfony/Component/BrowserKit/AbstractBrowser.php +++ b/src/Symfony/Component/BrowserKit/AbstractBrowser.php @@ -48,7 +58,7 @@ diff --git a/src/Symfony/Component/BrowserKit/AbstractBrowser.php b/src/Symfony/ diff --git a/src/Symfony/Component/Config/Definition/Builder/NodeDefinition.php b/src/Symfony/Component/Config/Definition/Builder/NodeDefinition.php --- a/src/Symfony/Component/Config/Definition/Builder/NodeDefinition.php +++ b/src/Symfony/Component/Config/Definition/Builder/NodeDefinition.php -@@ -94,5 +94,5 @@ abstract class NodeDefinition implements NodeParentInterface +@@ -115,5 +115,5 @@ abstract class NodeDefinition implements NodeParentInterface * @return NodeParentInterface|NodeBuilder|self|ArrayNodeDefinition|VariableNodeDefinition */ - public function end(): NodeParentInterface @@ -58,21 +68,21 @@ diff --git a/src/Symfony/Component/Config/Definition/Builder/NodeDefinition.php diff --git a/src/Symfony/Component/Console/Command/Command.php b/src/Symfony/Component/Console/Command/Command.php --- a/src/Symfony/Component/Console/Command/Command.php +++ b/src/Symfony/Component/Console/Command/Command.php -@@ -163,5 +163,5 @@ class Command +@@ -201,5 +201,5 @@ class Command implements SignalableCommandInterface * @return void */ - protected function configure() + protected function configure(): void { } -@@ -195,5 +195,5 @@ class Command +@@ -233,5 +233,5 @@ class Command implements SignalableCommandInterface * @return void */ - protected function interact(InputInterface $input, OutputInterface $output) + protected function interact(InputInterface $input, OutputInterface $output): void { } -@@ -211,5 +211,5 @@ class Command +@@ -249,5 +249,5 @@ class Command implements SignalableCommandInterface * @return void */ - protected function initialize(InputInterface $input, OutputInterface $output) @@ -474,14 +484,14 @@ diff --git a/src/Symfony/Component/HttpKernel/KernelInterface.php b/src/Symfony/ diff --git a/src/Symfony/Component/Routing/Loader/AttributeClassLoader.php b/src/Symfony/Component/Routing/Loader/AttributeClassLoader.php --- a/src/Symfony/Component/Routing/Loader/AttributeClassLoader.php +++ b/src/Symfony/Component/Routing/Loader/AttributeClassLoader.php -@@ -253,5 +253,5 @@ abstract class AttributeClassLoader implements LoaderInterface +@@ -277,5 +277,5 @@ abstract class AttributeClassLoader implements LoaderInterface * @return string */ - protected function getDefaultRouteName(\ReflectionClass $class, \ReflectionMethod $method) + protected function getDefaultRouteName(\ReflectionClass $class, \ReflectionMethod $method): string { $name = str_replace('\\', '_', $class->name).'_'.$method->name; -@@ -355,5 +355,5 @@ abstract class AttributeClassLoader implements LoaderInterface +@@ -379,5 +379,5 @@ abstract class AttributeClassLoader implements LoaderInterface * @return void */ - abstract protected function configureRoute(Route $route, \ReflectionClass $class, \ReflectionMethod $method, object $attr); @@ -578,7 +588,7 @@ diff --git a/src/Symfony/Component/Translation/Extractor/ExtractorInterface.php diff --git a/src/Symfony/Component/TypeInfo/Tests/Fixtures/DummyWithPhpDoc.php b/src/Symfony/Component/TypeInfo/Tests/Fixtures/DummyWithPhpDoc.php --- a/src/Symfony/Component/TypeInfo/Tests/Fixtures/DummyWithPhpDoc.php +++ b/src/Symfony/Component/TypeInfo/Tests/Fixtures/DummyWithPhpDoc.php -@@ -15,5 +15,5 @@ final class DummyWithPhpDoc +@@ -50,5 +50,5 @@ final class DummyWithPhpDoc * @return Dummy */ - public function getNextDummy(mixed $dummy): mixed @@ -610,6 +620,23 @@ diff --git a/src/Symfony/Component/VarDumper/Dumper/DataDumperInterface.php b/sr - public function dump(Data $data); + public function dump(Data $data): ?string; } +diff --git a/src/Symfony/Component/VarDumper/Test/VarDumperTestTrait.php b/src/Symfony/Component/VarDumper/Test/VarDumperTestTrait.php +--- a/src/Symfony/Component/VarDumper/Test/VarDumperTestTrait.php ++++ b/src/Symfony/Component/VarDumper/Test/VarDumperTestTrait.php +@@ -49,5 +49,5 @@ trait VarDumperTestTrait + * @return void + */ +- public function assertDumpEquals(mixed $expected, mixed $data, int $filter = 0, string $message = '') ++ public function assertDumpEquals(mixed $expected, mixed $data, int $filter = 0, string $message = ''): void + { + $this->assertSame($this->prepareExpectation($expected, $filter), $this->getDump($data, null, $filter), $message); +@@ -57,5 +57,5 @@ trait VarDumperTestTrait + * @return void + */ +- public function assertDumpMatchesFormat(mixed $expected, mixed $data, int $filter = 0, string $message = '') ++ public function assertDumpMatchesFormat(mixed $expected, mixed $data, int $filter = 0, string $message = ''): void + { + $this->assertStringMatchesFormat($this->prepareExpectation($expected, $filter), $this->getDump($data, null, $filter), $message); diff --git a/src/Symfony/Contracts/Translation/LocaleAwareInterface.php b/src/Symfony/Contracts/Translation/LocaleAwareInterface.php --- a/src/Symfony/Contracts/Translation/LocaleAwareInterface.php +++ b/src/Symfony/Contracts/Translation/LocaleAwareInterface.php diff --git a/.github/patch-types.php b/.github/patch-types.php index fc6be71995397..0a25ef95af146 100644 --- a/.github/patch-types.php +++ b/.github/patch-types.php @@ -1,5 +1,7 @@ getMethods() as $method) { if ( $method->getReturnType() - || str_contains($method->getDocComment(), '@return') + || (str_contains($method->getDocComment(), '@return') && str_contains($method->getDocComment(), 'resource')) || '__construct' === $method->getName() || '__destruct' === $method->getName() || '__clone' === $method->getName() || $method->getDeclaringClass()->getName() !== $class - || str_contains($method->getDeclaringClass()->getName(), '\\Test\\') + || str_contains($method->getDeclaringClass()->getName(), '\\Tests\\') + || str_contains($method->getDeclaringClass()->getName(), '\\Test\\') && str_starts_with($method->getName(), 'test') ) { continue; } @@ -95,6 +90,7 @@ class_exists($class); if ($missingReturnTypes) { echo \count($missingReturnTypes)." missing return types on interfaces\n\n"; echo implode("\n", $missingReturnTypes); + echo "\n"; exit(1); } diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index 36441f141bd65..c661b7b25f3f7 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -101,7 +101,7 @@ jobs: # Create local composer packages for each patched components and reference them in composer.json files when cross-testing components if [[ ! "${{ matrix.mode }}" = *-deps ]]; then - php .github/build-packages.php HEAD^ $SYMFONY_VERSION src/Symfony/Bridge/PhpUnit + php .github/build-packages.php HEAD^ $SYMFONY_VERSION src/Symfony/Bridge/PhpUnit else echo SYMFONY_DEPRECATIONS_HELPER=weak >> $GITHUB_ENV cp composer.json composer.json.orig @@ -154,9 +154,10 @@ jobs: run: | patch -sp1 < .github/expected-missing-return-types.diff git add . + sed -i 's/ *"\*\*\/Tests\/",//' composer.json composer install -q --optimize-autoloader || composer install --optimize-autoloader SYMFONY_PATCH_TYPE_DECLARATIONS='force=2&php=8.2' php .github/patch-types.php - git checkout src/Symfony/Contracts/Service/ResetInterface.php + git checkout composer.json src/Symfony/Contracts/Service/ResetInterface.php SYMFONY_PATCH_TYPE_DECLARATIONS='force=2&php=8.2' php .github/patch-types.php # ensure the script is idempotent git checkout src/Symfony/Contracts/Service/ResetInterface.php git diff --exit-code diff --git a/src/Symfony/Bridge/Twig/Test/Traits/RuntimeLoaderProvider.php b/src/Symfony/Bridge/Twig/Test/Traits/RuntimeLoaderProvider.php index 52f84a7d8f23b..5aa37c8bd0fe7 100644 --- a/src/Symfony/Bridge/Twig/Test/Traits/RuntimeLoaderProvider.php +++ b/src/Symfony/Bridge/Twig/Test/Traits/RuntimeLoaderProvider.php @@ -17,6 +17,9 @@ trait RuntimeLoaderProvider { + /** + * @return void + */ protected function registerTwigRuntimeLoader(Environment $environment, FormRenderer $renderer) { $loader = $this->createMock(RuntimeLoaderInterface::class); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Kernel/MicroKernelTraitTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Kernel/MicroKernelTraitTest.php index 5c7161124bda5..159dd21eb2690 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Kernel/MicroKernelTraitTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Kernel/MicroKernelTraitTest.php @@ -14,7 +14,6 @@ use PHPUnit\Framework\TestCase; use Psr\Log\NullLogger; use Symfony\Bundle\FrameworkBundle\Console\Application; -use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait; use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Input\ArrayInput; use Symfony\Component\Console\Output\BufferedOutput; @@ -186,27 +185,3 @@ public function testDefaultKernel() $this->assertSame('OK', $response->getContent()); } } - -abstract class MinimalKernel extends Kernel -{ - use MicroKernelTrait; - - private string $cacheDir; - - public function __construct(string $cacheDir) - { - parent::__construct('test', false); - - $this->cacheDir = sys_get_temp_dir().'/'.$cacheDir; - } - - public function getCacheDir(): string - { - return $this->cacheDir; - } - - public function getLogDir(): string - { - return $this->cacheDir; - } -} diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Kernel/MinimalKernel.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Kernel/MinimalKernel.php new file mode 100644 index 0000000000000..df2c97e6a0be8 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Kernel/MinimalKernel.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Kernel; + +use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait; +use Symfony\Component\HttpKernel\Kernel; + +abstract class MinimalKernel extends Kernel +{ + use MicroKernelTrait; + + private string $cacheDir; + + public function __construct(string $cacheDir) + { + parent::__construct('test', false); + + $this->cacheDir = sys_get_temp_dir().'/'.$cacheDir; + } + + public function getCacheDir(): string + { + return $this->cacheDir; + } + + public function getLogDir(): string + { + return $this->cacheDir; + } +} diff --git a/src/Symfony/Component/VarDumper/Test/VarDumperTestTrait.php b/src/Symfony/Component/VarDumper/Test/VarDumperTestTrait.php index e29121a306cde..f50adb13fc679 100644 --- a/src/Symfony/Component/VarDumper/Test/VarDumperTestTrait.php +++ b/src/Symfony/Component/VarDumper/Test/VarDumperTestTrait.php @@ -45,11 +45,17 @@ protected function tearDownVarDumper(): void $this->varDumperConfig['flags'] = null; } + /** + * @return void + */ public function assertDumpEquals(mixed $expected, mixed $data, int $filter = 0, string $message = '') { $this->assertSame($this->prepareExpectation($expected, $filter), $this->getDump($data, null, $filter), $message); } + /** + * @return void + */ public function assertDumpMatchesFormat(mixed $expected, mixed $data, int $filter = 0, string $message = '') { $this->assertStringMatchesFormat($this->prepareExpectation($expected, $filter), $this->getDump($data, null, $filter), $message); From 6156095b84894d9538859d2ec7259e4253862e5c Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 5 Jun 2025 11:00:17 +0200 Subject: [PATCH 64/80] cs tweak --- .github/workflows/unit-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index 8e4c8516dad81..c58f9aae078d0 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -92,7 +92,7 @@ jobs: # Create local composer packages for each patched components and reference them in composer.json files when cross-testing components if [[ ! "${{ matrix.mode }}" = *-deps ]]; then - php .github/build-packages.php HEAD^ $SYMFONY_VERSION src/Symfony/Bridge/PhpUnit + php .github/build-packages.php HEAD^ $SYMFONY_VERSION src/Symfony/Bridge/PhpUnit else echo SYMFONY_DEPRECATIONS_HELPER=weak >> $GITHUB_ENV cp composer.json composer.json.orig From 25479463e2c6ecf691f86a0f6eb9843fa715e219 Mon Sep 17 00:00:00 2001 From: Dave Heineman Date: Thu, 5 Jun 2025 11:26:03 +0200 Subject: [PATCH 65/80] [WebProfilerBundle] Fix typos in routing config deprecation messages --- .../WebProfilerBundle/Resources/config/routing/profiler.php | 2 +- .../Bundle/WebProfilerBundle/Resources/config/routing/wdt.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/config/routing/profiler.php b/src/Symfony/Bundle/WebProfilerBundle/Resources/config/routing/profiler.php index 46175d1d1f82e..09e022be922b0 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/config/routing/profiler.php +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/config/routing/profiler.php @@ -16,7 +16,7 @@ foreach (debug_backtrace(\DEBUG_BACKTRACE_PROVIDE_OBJECT) as $trace) { if (isset($trace['object']) && $trace['object'] instanceof XmlFileLoader && 'doImport' === $trace['function']) { if (__DIR__ === dirname(realpath($trace['args'][3]))) { - trigger_deprecation('symfony/routing', '7.3', 'The "profiler.xml" routing configuration file is deprecated, import "profile.php" instead.'); + trigger_deprecation('symfony/routing', '7.3', 'The "profiler.xml" routing configuration file is deprecated, import "profiler.php" instead.'); break; } diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/config/routing/wdt.php b/src/Symfony/Bundle/WebProfilerBundle/Resources/config/routing/wdt.php index 81b471d228c05..d0383ee8fbef9 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/config/routing/wdt.php +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/config/routing/wdt.php @@ -16,7 +16,7 @@ foreach (debug_backtrace(\DEBUG_BACKTRACE_PROVIDE_OBJECT) as $trace) { if (isset($trace['object']) && $trace['object'] instanceof XmlFileLoader && 'doImport' === $trace['function']) { if (__DIR__ === dirname(realpath($trace['args'][3]))) { - trigger_deprecation('symfony/routing', '7.3', 'The "xdt.xml" routing configuration file is deprecated, import "xdt.php" instead.'); + trigger_deprecation('symfony/routing', '7.3', 'The "wdt.xml" routing configuration file is deprecated, import "wdt.php" instead.'); break; } From 052bc4f8846d77b6ce8450736400f1e7859ceb7c Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 5 Jun 2025 11:54:04 +0200 Subject: [PATCH 66/80] [HttpClient] Deprecate using amphp/http-client < 5 --- UPGRADE-7.4.md | 14 ++++++++++++++ src/Symfony/Component/HttpClient/AmpHttpClient.php | 3 +++ src/Symfony/Component/HttpClient/CHANGELOG.md | 5 +++++ src/Symfony/Component/HttpClient/HttpClient.php | 2 +- 4 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 UPGRADE-7.4.md diff --git a/UPGRADE-7.4.md b/UPGRADE-7.4.md new file mode 100644 index 0000000000000..6623f1f6cd2bb --- /dev/null +++ b/UPGRADE-7.4.md @@ -0,0 +1,14 @@ +UPGRADE FROM 7.3 to 7.4 +======================= + +Symfony 7.4 is a minor release. According to the Symfony release process, there should be no significant +backward compatibility breaks. Minor backward compatibility breaks are prefixed in this document with +`[BC BREAK]`, make sure your code is compatible with these entries before upgrading. +Read more about this in the [Symfony documentation](https://symfony.com/doc/7.4/setup/upgrade_minor.html). + +If you're upgrading from a version below 7.3, follow the [7.3 upgrade guide](UPGRADE-7.3.md) first. + +HttpClient +---------- + + * Deprecate using amphp/http-client < 5 diff --git a/src/Symfony/Component/HttpClient/AmpHttpClient.php b/src/Symfony/Component/HttpClient/AmpHttpClient.php index 4c73fbaf3db24..1420ed2b0c4bd 100644 --- a/src/Symfony/Component/HttpClient/AmpHttpClient.php +++ b/src/Symfony/Component/HttpClient/AmpHttpClient.php @@ -78,6 +78,9 @@ public function __construct(array $defaultOptions = [], ?callable $clientConfigu if (is_subclass_of(Request::class, HttpMessage::class)) { $this->multi = new AmpClientStateV5($clientConfigurator, $maxHostConnections, $maxPendingPushes, $this->logger); } else { + if (\PHP_VERSION_ID >= 80400) { + trigger_deprecation('symfony/http-client', '7.4', 'Using amphp/http-client < 5 is deprecated. Try running "composer require amphp/http-client:^5".'); + } $this->multi = new AmpClientStateV4($clientConfigurator, $maxHostConnections, $maxPendingPushes, $this->logger); } } diff --git a/src/Symfony/Component/HttpClient/CHANGELOG.md b/src/Symfony/Component/HttpClient/CHANGELOG.md index 40dc2ec5d5445..8a44989783c8d 100644 --- a/src/Symfony/Component/HttpClient/CHANGELOG.md +++ b/src/Symfony/Component/HttpClient/CHANGELOG.md @@ -1,6 +1,11 @@ CHANGELOG ========= +7.4 +--- + + * Deprecate using amphp/http-client < 5 + 7.3 --- diff --git a/src/Symfony/Component/HttpClient/HttpClient.php b/src/Symfony/Component/HttpClient/HttpClient.php index 3eb3665614fd7..27659358bce4c 100644 --- a/src/Symfony/Component/HttpClient/HttpClient.php +++ b/src/Symfony/Component/HttpClient/HttpClient.php @@ -62,7 +62,7 @@ public static function create(array $defaultOptions = [], int $maxHostConnection return new AmpHttpClient($defaultOptions, null, $maxHostConnections, $maxPendingPushes); } - @trigger_error((\extension_loaded('curl') ? 'Upgrade' : 'Install').' the curl extension or run "composer require amphp/http-client:^4.2.1" to perform async HTTP operations, including full HTTP/2 support', \E_USER_NOTICE); + @trigger_error((\extension_loaded('curl') ? 'Upgrade' : 'Install').' the curl extension or run "composer require amphp/http-client:^5" to perform async HTTP operations, including full HTTP/2 support', \E_USER_NOTICE); return new NativeHttpClient($defaultOptions, $maxHostConnections); } From 7d67017dfbe61a6a6c5ff6484f6595c46fd3b15f Mon Sep 17 00:00:00 2001 From: llupa Date: Thu, 5 Jun 2025 14:51:53 +0200 Subject: [PATCH 67/80] [Intl] Ensure data consistency between alpha and numeric codes --- .../Data/Generator/RegionDataGenerator.php | 8 ++- .../Intl/Resources/data/regions/meta.php | 72 ------------------- .../Component/Intl/Tests/CountriesTest.php | 51 ++++--------- 3 files changed, 20 insertions(+), 111 deletions(-) diff --git a/src/Symfony/Component/Intl/Data/Generator/RegionDataGenerator.php b/src/Symfony/Component/Intl/Data/Generator/RegionDataGenerator.php index b03f56614c1ed..59c86ddc5c266 100644 --- a/src/Symfony/Component/Intl/Data/Generator/RegionDataGenerator.php +++ b/src/Symfony/Component/Intl/Data/Generator/RegionDataGenerator.php @@ -160,7 +160,7 @@ protected function generateDataForMeta(BundleEntryReaderInterface $reader, strin $alpha3ToAlpha2 = array_flip($alpha2ToAlpha3); asort($alpha3ToAlpha2); - $alpha2ToNumeric = $this->generateAlpha2ToNumericMapping($metadataBundle); + $alpha2ToNumeric = $this->generateAlpha2ToNumericMapping(array_flip($this->regionCodes), $metadataBundle); $numericToAlpha2 = []; foreach ($alpha2ToNumeric as $alpha2 => $numeric) { // Add underscore prefix to force keys with leading zeros to remain as string keys. @@ -231,7 +231,7 @@ private function generateAlpha2ToAlpha3Mapping(array $countries, ArrayAccessible return $alpha2ToAlpha3; } - private function generateAlpha2ToNumericMapping(ArrayAccessibleResourceBundle $metadataBundle): array + private function generateAlpha2ToNumericMapping(array $countries, ArrayAccessibleResourceBundle $metadataBundle): array { $aliases = iterator_to_array($metadataBundle['alias']['territory']); @@ -250,6 +250,10 @@ private function generateAlpha2ToNumericMapping(ArrayAccessibleResourceBundle $m continue; } + if (!isset($countries[$data['replacement']])) { + continue; + } + if ('deprecated' === $data['reason']) { continue; } diff --git a/src/Symfony/Component/Intl/Resources/data/regions/meta.php b/src/Symfony/Component/Intl/Resources/data/regions/meta.php index 1c9f233273af7..e0a99ccb7f5a8 100644 --- a/src/Symfony/Component/Intl/Resources/data/regions/meta.php +++ b/src/Symfony/Component/Intl/Resources/data/regions/meta.php @@ -755,7 +755,6 @@ 'ZWE' => 'ZW', ], 'Alpha2ToNumeric' => [ - 'AA' => '958', 'AD' => '020', 'AE' => '784', 'AF' => '004', @@ -943,18 +942,6 @@ 'PW' => '585', 'PY' => '600', 'QA' => '634', - 'QM' => '959', - 'QN' => '960', - 'QP' => '962', - 'QQ' => '963', - 'QR' => '964', - 'QS' => '965', - 'QT' => '966', - 'QV' => '968', - 'QW' => '969', - 'QX' => '970', - 'QY' => '971', - 'QZ' => '972', 'RE' => '638', 'RO' => '642', 'RS' => '688', @@ -1012,29 +999,6 @@ 'VU' => '548', 'WF' => '876', 'WS' => '882', - 'XC' => '975', - 'XD' => '976', - 'XE' => '977', - 'XF' => '978', - 'XG' => '979', - 'XH' => '980', - 'XI' => '981', - 'XJ' => '982', - 'XL' => '984', - 'XM' => '985', - 'XN' => '986', - 'XO' => '987', - 'XP' => '988', - 'XQ' => '989', - 'XR' => '990', - 'XS' => '991', - 'XT' => '992', - 'XU' => '993', - 'XV' => '994', - 'XW' => '995', - 'XX' => '996', - 'XY' => '997', - 'XZ' => '998', 'YE' => '887', 'YT' => '175', 'ZA' => '710', @@ -1042,7 +1006,6 @@ 'ZW' => '716', ], 'NumericToAlpha2' => [ - '_958' => 'AA', '_020' => 'AD', '_784' => 'AE', '_004' => 'AF', @@ -1230,18 +1193,6 @@ '_585' => 'PW', '_600' => 'PY', '_634' => 'QA', - '_959' => 'QM', - '_960' => 'QN', - '_962' => 'QP', - '_963' => 'QQ', - '_964' => 'QR', - '_965' => 'QS', - '_966' => 'QT', - '_968' => 'QV', - '_969' => 'QW', - '_970' => 'QX', - '_971' => 'QY', - '_972' => 'QZ', '_638' => 'RE', '_642' => 'RO', '_688' => 'RS', @@ -1299,29 +1250,6 @@ '_548' => 'VU', '_876' => 'WF', '_882' => 'WS', - '_975' => 'XC', - '_976' => 'XD', - '_977' => 'XE', - '_978' => 'XF', - '_979' => 'XG', - '_980' => 'XH', - '_981' => 'XI', - '_982' => 'XJ', - '_984' => 'XL', - '_985' => 'XM', - '_986' => 'XN', - '_987' => 'XO', - '_988' => 'XP', - '_989' => 'XQ', - '_990' => 'XR', - '_991' => 'XS', - '_992' => 'XT', - '_993' => 'XU', - '_994' => 'XV', - '_995' => 'XW', - '_996' => 'XX', - '_997' => 'XY', - '_998' => 'XZ', '_887' => 'YE', '_175' => 'YT', '_710' => 'ZA', diff --git a/src/Symfony/Component/Intl/Tests/CountriesTest.php b/src/Symfony/Component/Intl/Tests/CountriesTest.php index 7b921036b2a00..01f0f76f2e40a 100644 --- a/src/Symfony/Component/Intl/Tests/CountriesTest.php +++ b/src/Symfony/Component/Intl/Tests/CountriesTest.php @@ -527,7 +527,6 @@ class CountriesTest extends ResourceBundleTestCase ]; private const ALPHA2_TO_NUMERIC = [ - 'AA' => '958', 'AD' => '020', 'AE' => '784', 'AF' => '004', @@ -715,18 +714,6 @@ class CountriesTest extends ResourceBundleTestCase 'PW' => '585', 'PY' => '600', 'QA' => '634', - 'QM' => '959', - 'QN' => '960', - 'QP' => '962', - 'QQ' => '963', - 'QR' => '964', - 'QS' => '965', - 'QT' => '966', - 'QV' => '968', - 'QW' => '969', - 'QX' => '970', - 'QY' => '971', - 'QZ' => '972', 'RE' => '638', 'RO' => '642', 'RS' => '688', @@ -784,29 +771,6 @@ class CountriesTest extends ResourceBundleTestCase 'VU' => '548', 'WF' => '876', 'WS' => '882', - 'XC' => '975', - 'XD' => '976', - 'XE' => '977', - 'XF' => '978', - 'XG' => '979', - 'XH' => '980', - 'XI' => '981', - 'XJ' => '982', - 'XL' => '984', - 'XM' => '985', - 'XN' => '986', - 'XO' => '987', - 'XP' => '988', - 'XQ' => '989', - 'XR' => '990', - 'XS' => '991', - 'XT' => '992', - 'XU' => '993', - 'XV' => '994', - 'XW' => '995', - 'XX' => '996', - 'XY' => '997', - 'XZ' => '998', 'YE' => '887', 'YT' => '175', 'ZA' => '710', @@ -814,6 +778,19 @@ class CountriesTest extends ResourceBundleTestCase 'ZW' => '716', ]; + public function testAllGettersGenerateTheSameDataSetCount() + { + $alpha2Count = count(Countries::getCountryCodes()); + $alpha3Count = count(Countries::getAlpha3Codes()); + $numericCodesCount = count(Countries::getNumericCodes()); + $namesCount = count(Countries::getNames()); + + // we base all on Name count since it is the first to be generated + $this->assertEquals($namesCount, $alpha2Count, 'Alpha 2 count does not match'); + $this->assertEquals($namesCount, $alpha3Count, 'Alpha 3 count does not match'); + $this->assertEquals($namesCount, $numericCodesCount, 'Numeric codes count does not match'); + } + public function testGetCountryCodes() { $this->assertSame(self::COUNTRIES, Countries::getCountryCodes()); @@ -992,7 +969,7 @@ public function testGetNumericCode() public function testNumericCodeExists() { $this->assertTrue(Countries::numericCodeExists('250')); - $this->assertTrue(Countries::numericCodeExists('982')); + $this->assertTrue(Countries::numericCodeExists('008')); $this->assertTrue(Countries::numericCodeExists('716')); $this->assertTrue(Countries::numericCodeExists('036')); $this->assertFalse(Countries::numericCodeExists('667')); From dc09be9cff03cc083784e6fc9f4cc0f8f4e4cd44 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 5 Jun 2025 17:01:05 +0200 Subject: [PATCH 68/80] Fix leftover --- src/Symfony/Component/HttpClient/AmpHttpClient.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/HttpClient/AmpHttpClient.php b/src/Symfony/Component/HttpClient/AmpHttpClient.php index 1420ed2b0c4bd..b45229fa8d6b9 100644 --- a/src/Symfony/Component/HttpClient/AmpHttpClient.php +++ b/src/Symfony/Component/HttpClient/AmpHttpClient.php @@ -33,7 +33,7 @@ use Symfony\Contracts\Service\ResetInterface; if (!interface_exists(DelegateHttpClient::class)) { - throw new \LogicException('You cannot use "Symfony\Component\HttpClient\AmpHttpClient" as the "amphp/http-client" package is not installed. Try running "composer require amphp/http-client:^4.2.1".'); + throw new \LogicException('You cannot use "Symfony\Component\HttpClient\AmpHttpClient" as the "amphp/http-client" package is not installed. Try running "composer require amphp/http-client:^5".'); } if (\PHP_VERSION_ID < 80400 && is_subclass_of(Request::class, HttpMessage::class)) { From 542cc71af2bd01ab7eb53c668e8a47db761e458e Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 5 Jun 2025 17:31:40 +0200 Subject: [PATCH 69/80] [Security] conflict with event-subscriber v8 --- src/Symfony/Component/Security/Http/composer.json | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Symfony/Component/Security/Http/composer.json b/src/Symfony/Component/Security/Http/composer.json index 43312990d22c3..a6c2626da5873 100644 --- a/src/Symfony/Component/Security/Http/composer.json +++ b/src/Symfony/Component/Security/Http/composer.json @@ -18,6 +18,7 @@ "require": { "php": ">=8.2", "symfony/deprecation-contracts": "^2.5|^3", + "symfony/event-dispatcher": "^6.4|^7.0", "symfony/http-foundation": "^6.4|^7.0|^8.0", "symfony/http-kernel": "^6.4|^7.0|^8.0", "symfony/polyfill-mbstring": "~1.0", From 851c22c28dd7ee459bc3ba55252dcbb7fc55ea26 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 5 Jun 2025 17:28:28 +0200 Subject: [PATCH 70/80] [HttpClient] Suggest amphp/http-client v5 by default --- src/Symfony/Component/HttpClient/AmpHttpClient.php | 2 +- src/Symfony/Component/HttpClient/HttpClient.php | 2 +- src/Symfony/Component/HttpClient/composer.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Symfony/Component/HttpClient/AmpHttpClient.php b/src/Symfony/Component/HttpClient/AmpHttpClient.php index 4c73fbaf3db24..0bfa824a9a9a5 100644 --- a/src/Symfony/Component/HttpClient/AmpHttpClient.php +++ b/src/Symfony/Component/HttpClient/AmpHttpClient.php @@ -33,7 +33,7 @@ use Symfony\Contracts\Service\ResetInterface; if (!interface_exists(DelegateHttpClient::class)) { - throw new \LogicException('You cannot use "Symfony\Component\HttpClient\AmpHttpClient" as the "amphp/http-client" package is not installed. Try running "composer require amphp/http-client:^4.2.1".'); + throw new \LogicException('You cannot use "Symfony\Component\HttpClient\AmpHttpClient" as the "amphp/http-client" package is not installed. Try running "composer require amphp/http-client:^5".'); } if (\PHP_VERSION_ID < 80400 && is_subclass_of(Request::class, HttpMessage::class)) { diff --git a/src/Symfony/Component/HttpClient/HttpClient.php b/src/Symfony/Component/HttpClient/HttpClient.php index 3eb3665614fd7..27659358bce4c 100644 --- a/src/Symfony/Component/HttpClient/HttpClient.php +++ b/src/Symfony/Component/HttpClient/HttpClient.php @@ -62,7 +62,7 @@ public static function create(array $defaultOptions = [], int $maxHostConnection return new AmpHttpClient($defaultOptions, null, $maxHostConnections, $maxPendingPushes); } - @trigger_error((\extension_loaded('curl') ? 'Upgrade' : 'Install').' the curl extension or run "composer require amphp/http-client:^4.2.1" to perform async HTTP operations, including full HTTP/2 support', \E_USER_NOTICE); + @trigger_error((\extension_loaded('curl') ? 'Upgrade' : 'Install').' the curl extension or run "composer require amphp/http-client:^5" to perform async HTTP operations, including full HTTP/2 support', \E_USER_NOTICE); return new NativeHttpClient($defaultOptions, $maxHostConnections); } diff --git a/src/Symfony/Component/HttpClient/composer.json b/src/Symfony/Component/HttpClient/composer.json index 7ca008fd01f13..39e43f50b4fcd 100644 --- a/src/Symfony/Component/HttpClient/composer.json +++ b/src/Symfony/Component/HttpClient/composer.json @@ -31,7 +31,6 @@ "require-dev": { "amphp/http-client": "^4.2.1|^5.0", "amphp/http-tunnel": "^1.0|^2.0", - "amphp/socket": "^1.1", "guzzlehttp/promises": "^1.4|^2.0", "nyholm/psr7": "^1.0", "php-http/httplug": "^1.0|^2.0", @@ -46,6 +45,7 @@ }, "conflict": { "amphp/amp": "<2.5", + "amphp/socket": "<1.1", "php-http/discovery": "<1.15", "symfony/http-foundation": "<6.4" }, From fc9491e50234106e87fae8292f936777d99cdbd7 Mon Sep 17 00:00:00 2001 From: jprivet-dev Date: Thu, 15 May 2025 08:45:41 +0200 Subject: [PATCH 71/80] [PhpUnitBridge] Add `strtotime()` to `ClockMock` --- src/Symfony/Bridge/PhpUnit/CHANGELOG.md | 5 +++++ src/Symfony/Bridge/PhpUnit/ClockMock.php | 17 +++++++++++++++++ .../Bridge/PhpUnit/Tests/ClockMockTest.php | 5 +++++ 3 files changed, 27 insertions(+) diff --git a/src/Symfony/Bridge/PhpUnit/CHANGELOG.md b/src/Symfony/Bridge/PhpUnit/CHANGELOG.md index 0b139af321f5d..579fd88af71cf 100644 --- a/src/Symfony/Bridge/PhpUnit/CHANGELOG.md +++ b/src/Symfony/Bridge/PhpUnit/CHANGELOG.md @@ -1,6 +1,11 @@ CHANGELOG ========= +7.4 +--- + + * Add support for mocking the `strtotime()` function + 7.3 --- diff --git a/src/Symfony/Bridge/PhpUnit/ClockMock.php b/src/Symfony/Bridge/PhpUnit/ClockMock.php index 4cca8fc26cfc6..7c76596f3a50a 100644 --- a/src/Symfony/Bridge/PhpUnit/ClockMock.php +++ b/src/Symfony/Bridge/PhpUnit/ClockMock.php @@ -109,6 +109,18 @@ public static function hrtime($asNumber = false) return [(int) self::$now, (int) $ns]; } + /** + * @return false|int + */ + public static function strtotime(string $datetime, ?int $timestamp = null) + { + if (null === $timestamp) { + $timestamp = self::time(); + } + + return \strtotime($datetime, $timestamp); + } + public static function register($class): void { $self = static::class; @@ -161,6 +173,11 @@ function hrtime(\$asNumber = false) { return \\$self::hrtime(\$asNumber); } + +function strtotime(\$datetime, \$timestamp = null) +{ + return \\$self::strtotime(\$datetime, \$timestamp); +} EOPHP ); } diff --git a/src/Symfony/Bridge/PhpUnit/Tests/ClockMockTest.php b/src/Symfony/Bridge/PhpUnit/Tests/ClockMockTest.php index 7df7865d1c9be..84241081f7ba0 100644 --- a/src/Symfony/Bridge/PhpUnit/Tests/ClockMockTest.php +++ b/src/Symfony/Bridge/PhpUnit/Tests/ClockMockTest.php @@ -79,4 +79,9 @@ public function testHrTimeAsNumber() { $this->assertSame(1234567890125000000, hrtime(true)); } + + public function testStrToTime() + { + $this->assertSame(1234567890, strtotime('now')); + } } From f623d3a1eb8597e7dbe8dcf9609807105bc0ef6e Mon Sep 17 00:00:00 2001 From: "Nathanael d. Noblet" Date: Tue, 3 Jun 2025 14:24:16 -0600 Subject: [PATCH 72/80] Allow NumberToLocalizedStringTransformer empty values --- .../DataTransformer/MoneyToLocalizedStringTransformer.php | 4 ++-- .../DataTransformer/NumberToLocalizedStringTransformer.php | 4 ++-- .../MoneyToLocalizedStringTransformerTest.php | 7 +++++++ .../NumberToLocalizedStringTransformerTest.php | 1 + 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/Symfony/Component/Form/Extension/Core/DataTransformer/MoneyToLocalizedStringTransformer.php b/src/Symfony/Component/Form/Extension/Core/DataTransformer/MoneyToLocalizedStringTransformer.php index 7a8aacac6975c..d862b885d890b 100644 --- a/src/Symfony/Component/Form/Extension/Core/DataTransformer/MoneyToLocalizedStringTransformer.php +++ b/src/Symfony/Component/Form/Extension/Core/DataTransformer/MoneyToLocalizedStringTransformer.php @@ -33,14 +33,14 @@ public function __construct(?int $scale = 2, ?bool $grouping = true, ?int $round /** * Transforms a normalized format into a localized money string. * - * @param int|float|null $value Normalized number + * @param int|float|string|null $value Normalized number * * @throws TransformationFailedException if the given value is not numeric or * if the value cannot be transformed */ public function transform(mixed $value): string { - if (null !== $value && 1 !== $this->divisor) { + if (null !== $value && '' !== $value && 1 !== $this->divisor) { if (!is_numeric($value)) { throw new TransformationFailedException('Expected a numeric.'); } diff --git a/src/Symfony/Component/Form/Extension/Core/DataTransformer/NumberToLocalizedStringTransformer.php b/src/Symfony/Component/Form/Extension/Core/DataTransformer/NumberToLocalizedStringTransformer.php index 71d225e58b40b..2bff37ad3f6ca 100644 --- a/src/Symfony/Component/Form/Extension/Core/DataTransformer/NumberToLocalizedStringTransformer.php +++ b/src/Symfony/Component/Form/Extension/Core/DataTransformer/NumberToLocalizedStringTransformer.php @@ -43,14 +43,14 @@ public function __construct(?int $scale = null, ?bool $grouping = false, ?int $r /** * Transforms a number type into localized number. * - * @param int|float|null $value Number value + * @param int|float|string|null $value Number value * * @throws TransformationFailedException if the given value is not numeric * or if the value cannot be transformed */ public function transform(mixed $value): string { - if (null === $value) { + if (null === $value || '' === $value) { return ''; } diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/MoneyToLocalizedStringTransformerTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/MoneyToLocalizedStringTransformerTest.php index 2d43e9533298d..f25d49981cd3d 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/MoneyToLocalizedStringTransformerTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/MoneyToLocalizedStringTransformerTest.php @@ -54,6 +54,13 @@ public function testTransformExpectsNumeric() $transformer->transform('abcd'); } + public function testTransformEmptyString() + { + $transformer = new MoneyToLocalizedStringTransformer(null, null, null, 100); + + $this->assertSame('', $transformer->transform('')); + } + public function testTransformEmpty() { $transformer = new MoneyToLocalizedStringTransformer(); diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/NumberToLocalizedStringTransformerTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/NumberToLocalizedStringTransformerTest.php index 37448db51030a..c0344b9f232ea 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/NumberToLocalizedStringTransformerTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/NumberToLocalizedStringTransformerTest.php @@ -49,6 +49,7 @@ public static function provideTransformations() { return [ [null, '', 'de_AT'], + ['', '', 'de_AT'], [1, '1', 'de_AT'], [1.5, '1,5', 'de_AT'], [1234.5, '1234,5', 'de_AT'], From dba505a344147111e7fe142bb40be386e7ab4371 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 5 Jun 2025 18:55:09 +0200 Subject: [PATCH 73/80] Test AssetMapper with and without ext-brotli/ext-zstd in one job --- .github/workflows/unit-tests.yml | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index efe0e92a14595..578b225ea6f17 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -21,7 +21,7 @@ jobs: name: Unit Tests env: - extensions: amqp,apcu,igbinary,intl,mbstring,memcached,redis,relay + extensions: amqp,apcu,brotli,igbinary,intl,mbstring,memcached,redis,relay,zstd strategy: matrix: @@ -33,9 +33,6 @@ jobs: mode: low-deps - php: '8.3' - php: '8.4' - # brotli and zstd extensions are optional, when not present the commands will be used instead, - # we must test both scenarios - extensions: amqp,apcu,brotli,igbinary,intl,mbstring,memcached,redis,relay,zstd - php: '8.5' #mode: experimental fail-fast: false @@ -233,6 +230,12 @@ jobs: run: | script -e -c './phpunit --group tty' /dev/null + - name: Run AssetMapper without ext-brotli nor ext-zstd + if: "! matrix.mode" + run: | + sudo rm /etc/php/*/cli/conf.d/*-{brotli,zstd}.ini + ./phpunit src/Symfony/Component/AssetMapper + - name: Run tests with SIGCHLD enabled PHP if: "matrix.php == '8.2' && ! matrix.mode" run: | From 56554c27054db8488c30e5f33fb5d770c9b81d27 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Fri, 6 Jun 2025 08:38:09 +0200 Subject: [PATCH 74/80] [VarDumper] Fix dumping LazyObjectState when using VarExporter v8 --- .../Component/VarDumper/Caster/SymfonyCaster.php | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/Symfony/Component/VarDumper/Caster/SymfonyCaster.php b/src/Symfony/Component/VarDumper/Caster/SymfonyCaster.php index ebc00f90ec8ab..676d95b98b02c 100644 --- a/src/Symfony/Component/VarDumper/Caster/SymfonyCaster.php +++ b/src/Symfony/Component/VarDumper/Caster/SymfonyCaster.php @@ -90,12 +90,14 @@ public static function castLazyObjectState($state, array $a, Stub $stub, bool $i $instance = $a['realInstance'] ?? null; - $a = ['status' => new ConstStub(match ($a['status']) { - LazyObjectState::STATUS_INITIALIZED_FULL => 'INITIALIZED_FULL', - LazyObjectState::STATUS_INITIALIZED_PARTIAL => 'INITIALIZED_PARTIAL', - LazyObjectState::STATUS_UNINITIALIZED_FULL => 'UNINITIALIZED_FULL', - LazyObjectState::STATUS_UNINITIALIZED_PARTIAL => 'UNINITIALIZED_PARTIAL', - }, $a['status'])]; + if (isset($a['status'])) { // forward-compat with Symfony 8 + $a = ['status' => new ConstStub(match ($a['status']) { + LazyObjectState::STATUS_INITIALIZED_FULL => 'INITIALIZED_FULL', + LazyObjectState::STATUS_INITIALIZED_PARTIAL => 'INITIALIZED_PARTIAL', + LazyObjectState::STATUS_UNINITIALIZED_FULL => 'UNINITIALIZED_FULL', + LazyObjectState::STATUS_UNINITIALIZED_PARTIAL => 'UNINITIALIZED_PARTIAL', + }, $a['status'])]; + } if ($instance) { $a['realInstance'] = $instance; From d56b14862e4d2a01073634362862cf2a22c14135 Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Wed, 4 Jun 2025 10:02:35 +0200 Subject: [PATCH 75/80] [JsonPath] Better handling of Unicode chars in expressions --- .../Component/JsonPath/JsonCrawler.php | 4 +- .../JsonPath/JsonCrawlerInterface.php | 2 +- src/Symfony/Component/JsonPath/JsonPath.php | 6 +- .../Component/JsonPath/JsonPathUtils.php | 74 +++++ .../JsonPath/Tests/JsonCrawlerTest.php | 269 ++++++++++++++++++ .../Test/JsonPathAssertionsTraitTest.php | 9 + src/Symfony/Component/JsonPath/composer.json | 1 + 7 files changed, 359 insertions(+), 6 deletions(-) diff --git a/src/Symfony/Component/JsonPath/JsonCrawler.php b/src/Symfony/Component/JsonPath/JsonCrawler.php index b1d7ef0bf94d8..492f56e77bba7 100644 --- a/src/Symfony/Component/JsonPath/JsonCrawler.php +++ b/src/Symfony/Component/JsonPath/JsonCrawler.php @@ -230,7 +230,7 @@ private function evaluateBracket(string $expr, mixed $value): array // quoted strings for object keys if (preg_match('/^([\'"])(.*)\1$/', $expr, $matches)) { - $key = stripslashes($matches[2]); + $key = JsonPathUtils::unescapeString($matches[2], $matches[1]); return \array_key_exists($key, $value) ? [$value[$key]] : []; } @@ -335,7 +335,7 @@ private function evaluateScalar(string $expr, array $context): mixed // string literals if (preg_match('/^([\'"])(.*)\1$/', $expr, $matches)) { - return $matches[2]; + return JsonPathUtils::unescapeString($matches[2], $matches[1]); } // current node references diff --git a/src/Symfony/Component/JsonPath/JsonCrawlerInterface.php b/src/Symfony/Component/JsonPath/JsonCrawlerInterface.php index 3e8a222f0ba8e..4859c2bde076b 100644 --- a/src/Symfony/Component/JsonPath/JsonCrawlerInterface.php +++ b/src/Symfony/Component/JsonPath/JsonCrawlerInterface.php @@ -25,7 +25,7 @@ interface JsonCrawlerInterface * @return list * * @throws InvalidArgumentException When the JSON string provided to the crawler cannot be decoded - * @throws JsonCrawlerException When a syntax error occurs in the provided JSON path + * @throws JsonCrawlerException When a syntax error occurs in the provided JSON path */ public function find(string|JsonPath $query): array; } diff --git a/src/Symfony/Component/JsonPath/JsonPath.php b/src/Symfony/Component/JsonPath/JsonPath.php index e716167eb3f64..e36fc9ffd2ef1 100644 --- a/src/Symfony/Component/JsonPath/JsonPath.php +++ b/src/Symfony/Component/JsonPath/JsonPath.php @@ -92,12 +92,12 @@ private function escapeKey(string $key): string "\r" => '\\r', "\t" => '\\t', "\b" => '\\b', - "\f" => '\\f' + "\f" => '\\f', ]); - for ($i = 0; $i <= 31; $i++) { + for ($i = 0; $i <= 31; ++$i) { if ($i < 8 || $i > 13) { - $key = str_replace(chr($i), sprintf('\\u%04x', $i), $key); + $key = str_replace(\chr($i), \sprintf('\\u%04x', $i), $key); } } diff --git a/src/Symfony/Component/JsonPath/JsonPathUtils.php b/src/Symfony/Component/JsonPath/JsonPathUtils.php index b5ac2ae6b8d0a..6f971d20115b2 100644 --- a/src/Symfony/Component/JsonPath/JsonPathUtils.php +++ b/src/Symfony/Component/JsonPath/JsonPathUtils.php @@ -85,4 +85,78 @@ public static function findSmallestDeserializableStringAndPath(array $tokens, mi 'tokens' => $remainingTokens, ]; } + + public static function unescapeString(string $str, string $quoteChar): string + { + if ('"' === $quoteChar) { + // try JSON decoding first for unicode sequences + $jsonStr = '"'.$str.'"'; + $decoded = json_decode($jsonStr, true); + + if (null !== $decoded) { + return $decoded; + } + } + + $result = ''; + $length = \strlen($str); + + for ($i = 0; $i < $length; ++$i) { + if ('\\' === $str[$i] && $i + 1 < $length) { + $result .= match ($str[$i + 1]) { + '"' => '"', + "'" => "'", + '\\' => '\\', + '/' => '/', + 'b' => "\b", + 'f' => "\f", + 'n' => "\n", + 'r' => "\r", + 't' => "\t", + 'u' => self::unescapeUnicodeSequence($str, $length, $i), + default => $str[$i].$str[$i + 1], // keep the backslash + }; + + ++$i; + } else { + $result .= $str[$i]; + } + } + + return $result; + } + + private static function unescapeUnicodeSequence(string $str, int $length, int &$i): string + { + if ($i + 5 >= $length) { + // not enough characters for Unicode escape, treat as literal + return $str[$i]; + } + + $hex = substr($str, $i + 2, 4); + if (!ctype_xdigit($hex)) { + // invalid hex, treat as literal + return $str[$i]; + } + + $codepoint = hexdec($hex); + // looks like a valid Unicode codepoint, string length is sufficient and it starts with \u + if (0xD800 <= $codepoint && $codepoint <= 0xDBFF && $i + 11 < $length && '\\' === $str[$i + 6] && 'u' === $str[$i + 7]) { + $lowHex = substr($str, $i + 8, 4); + if (ctype_xdigit($lowHex)) { + $lowSurrogate = hexdec($lowHex); + if (0xDC00 <= $lowSurrogate && $lowSurrogate <= 0xDFFF) { + $codepoint = 0x10000 + (($codepoint & 0x3FF) << 10) + ($lowSurrogate & 0x3FF); + $i += 10; // skip surrogate pair + + return mb_chr($codepoint, 'UTF-8'); + } + } + } + + // single Unicode character or invalid surrogate, skip the sequence + $i += 4; + + return mb_chr($codepoint, 'UTF-8'); + } } diff --git a/src/Symfony/Component/JsonPath/Tests/JsonCrawlerTest.php b/src/Symfony/Component/JsonPath/Tests/JsonCrawlerTest.php index 66ccfc2642141..213ae06afa7db 100644 --- a/src/Symfony/Component/JsonPath/Tests/JsonCrawlerTest.php +++ b/src/Symfony/Component/JsonPath/Tests/JsonCrawlerTest.php @@ -465,6 +465,251 @@ public function testStarAsKey() $this->assertSame(['a' => 1, 'b' => 2], $result[0]); } + /** + * @dataProvider provideUnicodeEscapeSequencesProvider + */ + public function testUnicodeEscapeSequences(string $jsonPath, array $expected) + { + $this->assertSame($expected, self::getUnicodeDocumentCrawler()->find($jsonPath)); + } + + public static function provideUnicodeEscapeSequencesProvider(): array + { + return [ + [ + '$["caf\u00e9"]', + ['coffee'], + ], + [ + '$["\u65e5\u672c"]', + ['Japan'], + ], + [ + '$["M\u00fcller"]', + [], + ], + [ + '$["emoji\ud83d\ude00"]', + ['smiley'], + ], + [ + '$["tab\there"]', + ['with tab'], + ], + [ + '$["new\nline"]', + ['with newline'], + ], + [ + '$["quote\"here"]', + ['with quote'], + ], + [ + '$["backslash\\\\here"]', + ['with backslash'], + ], + [ + '$["apostrophe\'here"]', + ['with apostrophe'], + ], + [ + '$["control\u0001char"]', + ['with control char'], + ], + [ + '$["\u0063af\u00e9"]', + ['coffee'], + ], + ]; + } + + /** + * @dataProvider provideSingleQuotedStringProvider + */ + public function testSingleQuotedStrings(string $jsonPath, array $expected) + { + $this->assertSame($expected, self::getUnicodeDocumentCrawler()->find($jsonPath)); + } + + public static function provideSingleQuotedStringProvider(): array + { + return [ + [ + "$['caf\\u00e9']", + ['coffee'], + ], + [ + "$['\\u65e5\\u672c']", + ['Japan'], + ], + [ + "$['quote\"here']", + ['with quote'], + ], + [ + "$['M\\u00fcller']", + [], + ], + [ + "$['emoji\\ud83d\\ude00']", + ['smiley'], + ], + [ + "$['tab\\there']", + ['with tab'], + ], + [ + "$['quote\\\"here']", + ['with quote'], + ], + [ + "$['backslash\\\\here']", + ['with backslash'], + ], + [ + "$['apostrophe\\'here']", + ['with apostrophe'], + ], + [ + "$['control\\u0001char']", + ['with control char'], + ], + [ + "$['\\u0063af\\u00e9']", + ['coffee'], + ], + ]; + } + + /** + * @dataProvider provideFilterWithUnicodeProvider + */ + public function testFilterWithUnicodeStrings(string $jsonPath, int $expectedCount, string $expectedCountry) + { + $result = self::getUnicodeDocumentCrawler()->find($jsonPath); + + $this->assertCount($expectedCount, $result); + + if ($expectedCount > 0) { + $this->assertSame($expectedCountry, $result[0]['country']); + } + } + + public static function provideFilterWithUnicodeProvider(): array + { + return [ + [ + '$.users[?(@.name == "caf\u00e9")]', + 1, + 'France', + ], + [ + '$.users[?(@.name == "\u65e5\u672c\u592a\u90ce")]', + 1, + 'Japan', + ], + [ + '$.users[?(@.name == "Jos\u00e9")]', + 1, + 'Spain', + ], + [ + '$.users[?(@.name == "John")]', + 1, + 'USA', + ], + [ + '$.users[?(@.name == "NonExistent\u0020Name")]', + 0, + '', + ], + ]; + } + + /** + * @dataProvider provideInvalidUnicodeSequenceProvider + */ + public function testInvalidUnicodeSequencesAreProcessedAsLiterals(string $jsonPath) + { + $this->assertIsArray(self::getUnicodeDocumentCrawler()->find($jsonPath), 'invalid unicode sequence should be treated as literal and not throw'); + } + + public static function provideInvalidUnicodeSequenceProvider(): array + { + return [ + [ + '$["test\uZZZZ"]', + ], + [ + '$["test\u123"]', + ], + [ + '$["test\u"]', + ], + ]; + } + + /** + * @dataProvider provideComplexUnicodePath + */ + public function testComplexUnicodePaths(string $jsonPath, array $expected) + { + $complexJson = [ + 'データ' => [ + 'ユーザー' => [ + ['名前' => 'テスト', 'ID' => 1], + ['名前' => 'サンプル', 'ID' => 2], + ], + ], + 'special🔑' => [ + 'value💎' => 'treasure', + ], + ]; + + $crawler = new JsonCrawler(json_encode($complexJson)); + + $this->assertSame($expected, $crawler->find($jsonPath)); + } + + public static function provideComplexUnicodePath(): array + { + return [ + [ + '$["\u30c7\u30fc\u30bf"]["\u30e6\u30fc\u30b6\u30fc"][0]["\u540d\u524d"]', + ['テスト'], + ], + [ + '$["special\ud83d\udd11"]["value\ud83d\udc8e"]', + ['treasure'], + ], + [ + '$["\u30c7\u30fc\u30bf"]["\u30e6\u30fc\u30b6\u30fc"][*]["\u540d\u524d"]', + ['テスト', 'サンプル'], + ], + ]; + } + + public function testSurrogatePairHandling() + { + $json = ['𝒽𝑒𝓁𝓁𝑜' => 'mathematical script hello']; + $crawler = new JsonCrawler(json_encode($json)); + + // mathematical script "hello" requires surrogate pairs for each character + $result = $crawler->find('$["\ud835\udcbd\ud835\udc52\ud835\udcc1\ud835\udcc1\ud835\udc5c"]'); + $this->assertSame(['mathematical script hello'], $result); + } + + public function testMixedQuoteTypes() + { + $json = ['key"with"quotes' => 'value1', "key'with'apostrophes" => 'value2']; + $crawler = new JsonCrawler(json_encode($json)); + + $result = $crawler->find('$[\'key"with"quotes\']'); + $this->assertSame(['value1'], $result); + + $result = $crawler->find('$["key\'with\'apostrophes"]'); + $this->assertSame(['value2'], $result); + } private static function getBookstoreCrawler(): JsonCrawler { @@ -515,4 +760,28 @@ private static function getSimpleCollectionCrawler(): JsonCrawler {"a": [3, 5, 1, 2, 4, 6]} JSON); } + + private static function getUnicodeDocumentCrawler(): JsonCrawler + { + $json = [ + 'café' => 'coffee', + '日本' => 'Japan', + 'emoji😀' => 'smiley', + 'tab here' => 'with tab', + "new\nline" => 'with newline', + 'quote"here' => 'with quote', + 'backslash\\here' => 'with backslash', + 'apostrophe\'here' => 'with apostrophe', + "control\x01char" => 'with control char', + 'users' => [ + ['name' => 'café', 'country' => 'France'], + ['name' => '日本太郎', 'country' => 'Japan'], + ['name' => 'John', 'country' => 'USA'], + ['name' => 'Müller', 'country' => 'Germany'], + ['name' => 'José', 'country' => 'Spain'], + ], + ]; + + return new JsonCrawler(json_encode($json)); + } } diff --git a/src/Symfony/Component/JsonPath/Tests/Test/JsonPathAssertionsTraitTest.php b/src/Symfony/Component/JsonPath/Tests/Test/JsonPathAssertionsTraitTest.php index 62d64b53e1e8d..1044e7658672b 100644 --- a/src/Symfony/Component/JsonPath/Tests/Test/JsonPathAssertionsTraitTest.php +++ b/src/Symfony/Component/JsonPath/Tests/Test/JsonPathAssertionsTraitTest.php @@ -1,5 +1,14 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace Symfony\Component\JsonPath\Tests\Test; use PHPUnit\Framework\AssertionFailedError; diff --git a/src/Symfony/Component/JsonPath/composer.json b/src/Symfony/Component/JsonPath/composer.json index fe8ddf84dd82d..feb8158aa5be2 100644 --- a/src/Symfony/Component/JsonPath/composer.json +++ b/src/Symfony/Component/JsonPath/composer.json @@ -17,6 +17,7 @@ ], "require": { "php": ">=8.2", + "symfony/polyfill-ctype": "^1.8", "symfony/polyfill-mbstring": "~1.0" }, "require-dev": { From 78be7ebea2b3d5029cbf6eaae265808ac34ed196 Mon Sep 17 00:00:00 2001 From: Mathias Arlaud Date: Fri, 6 Jun 2025 15:43:35 +0200 Subject: [PATCH 76/80] [JsonStreamer] Add PHPDoc to generated code --- src/Symfony/Component/JsonStreamer/Read/PhpGenerator.php | 6 ++++++ .../Tests/Fixtures/stream_reader/backed_enum.php | 3 +++ .../Tests/Fixtures/stream_reader/backed_enum.stream.php | 3 +++ .../JsonStreamer/Tests/Fixtures/stream_reader/dict.php | 3 +++ .../Tests/Fixtures/stream_reader/dict.stream.php | 3 +++ .../JsonStreamer/Tests/Fixtures/stream_reader/iterable.php | 3 +++ .../Tests/Fixtures/stream_reader/iterable.stream.php | 3 +++ .../JsonStreamer/Tests/Fixtures/stream_reader/list.php | 3 +++ .../Tests/Fixtures/stream_reader/list.stream.php | 3 +++ .../JsonStreamer/Tests/Fixtures/stream_reader/mixed.php | 3 +++ .../Tests/Fixtures/stream_reader/mixed.stream.php | 3 +++ .../JsonStreamer/Tests/Fixtures/stream_reader/null.php | 3 +++ .../Tests/Fixtures/stream_reader/null.stream.php | 3 +++ .../Tests/Fixtures/stream_reader/nullable_backed_enum.php | 3 +++ .../Fixtures/stream_reader/nullable_backed_enum.stream.php | 3 +++ .../Tests/Fixtures/stream_reader/nullable_object.php | 3 +++ .../Tests/Fixtures/stream_reader/nullable_object.stream.php | 3 +++ .../Tests/Fixtures/stream_reader/nullable_object_dict.php | 3 +++ .../Fixtures/stream_reader/nullable_object_dict.stream.php | 3 +++ .../Tests/Fixtures/stream_reader/nullable_object_list.php | 3 +++ .../Fixtures/stream_reader/nullable_object_list.stream.php | 3 +++ .../JsonStreamer/Tests/Fixtures/stream_reader/object.php | 3 +++ .../Tests/Fixtures/stream_reader/object.stream.php | 3 +++ .../Tests/Fixtures/stream_reader/object_dict.php | 3 +++ .../Tests/Fixtures/stream_reader/object_dict.stream.php | 3 +++ .../Tests/Fixtures/stream_reader/object_in_object.php | 3 +++ .../Fixtures/stream_reader/object_in_object.stream.php | 3 +++ .../Tests/Fixtures/stream_reader/object_iterable.php | 3 +++ .../Tests/Fixtures/stream_reader/object_iterable.stream.php | 3 +++ .../Tests/Fixtures/stream_reader/object_list.php | 3 +++ .../Tests/Fixtures/stream_reader/object_list.stream.php | 3 +++ .../stream_reader/object_with_nullable_properties.php | 3 +++ .../object_with_nullable_properties.stream.php | 3 +++ .../Tests/Fixtures/stream_reader/object_with_union.php | 3 +++ .../Fixtures/stream_reader/object_with_union.stream.php | 3 +++ .../stream_reader/object_with_value_transformer.php | 3 +++ .../stream_reader/object_with_value_transformer.stream.php | 3 +++ .../JsonStreamer/Tests/Fixtures/stream_reader/scalar.php | 3 +++ .../Tests/Fixtures/stream_reader/scalar.stream.php | 3 +++ .../JsonStreamer/Tests/Fixtures/stream_reader/union.php | 3 +++ .../Tests/Fixtures/stream_reader/union.stream.php | 3 +++ .../Tests/Fixtures/stream_writer/backed_enum.php | 3 +++ .../JsonStreamer/Tests/Fixtures/stream_writer/bool.php | 3 +++ .../JsonStreamer/Tests/Fixtures/stream_writer/bool_list.php | 3 +++ .../JsonStreamer/Tests/Fixtures/stream_writer/dict.php | 3 +++ .../JsonStreamer/Tests/Fixtures/stream_writer/iterable.php | 3 +++ .../JsonStreamer/Tests/Fixtures/stream_writer/list.php | 3 +++ .../JsonStreamer/Tests/Fixtures/stream_writer/mixed.php | 3 +++ .../JsonStreamer/Tests/Fixtures/stream_writer/null.php | 3 +++ .../JsonStreamer/Tests/Fixtures/stream_writer/null_list.php | 3 +++ .../Tests/Fixtures/stream_writer/nullable_backed_enum.php | 3 +++ .../Tests/Fixtures/stream_writer/nullable_object.php | 3 +++ .../Tests/Fixtures/stream_writer/nullable_object_dict.php | 3 +++ .../Tests/Fixtures/stream_writer/nullable_object_list.php | 3 +++ .../JsonStreamer/Tests/Fixtures/stream_writer/object.php | 3 +++ .../Tests/Fixtures/stream_writer/object_dict.php | 3 +++ .../Tests/Fixtures/stream_writer/object_in_object.php | 3 +++ .../Tests/Fixtures/stream_writer/object_iterable.php | 3 +++ .../Tests/Fixtures/stream_writer/object_list.php | 3 +++ .../Tests/Fixtures/stream_writer/object_with_union.php | 3 +++ .../stream_writer/object_with_value_transformer.php | 3 +++ .../JsonStreamer/Tests/Fixtures/stream_writer/scalar.php | 3 +++ .../Fixtures/stream_writer/self_referencing_object.php | 3 +++ .../JsonStreamer/Tests/Fixtures/stream_writer/union.php | 3 +++ src/Symfony/Component/JsonStreamer/Write/PhpGenerator.php | 3 +++ 65 files changed, 198 insertions(+) diff --git a/src/Symfony/Component/JsonStreamer/Read/PhpGenerator.php b/src/Symfony/Component/JsonStreamer/Read/PhpGenerator.php index 28a9cc9200121..399030226da6a 100644 --- a/src/Symfony/Component/JsonStreamer/Read/PhpGenerator.php +++ b/src/Symfony/Component/JsonStreamer/Read/PhpGenerator.php @@ -51,6 +51,9 @@ public function generate(DataModelNodeInterface $dataModel, bool $decodeFromStre if ($decodeFromStream) { return $this->line('line('', $context) + .$this->line('/**', $context) + .$this->line(' * @return '.$dataModel->getType(), $context) + .$this->line(' */', $context) .$this->line('return static function (mixed $stream, \\'.ContainerInterface::class.' $valueTransformers, \\'.LazyInstantiator::class.' $instantiator, array $options): mixed {', $context) .$providers .($this->canBeDecodedWithJsonDecode($dataModel, $decodeFromStream) @@ -61,6 +64,9 @@ public function generate(DataModelNodeInterface $dataModel, bool $decodeFromStre return $this->line('line('', $context) + .$this->line('/**', $context) + .$this->line(' * @return '.$dataModel->getType(), $context) + .$this->line(' */', $context) .$this->line('return static function (string|\\Stringable $string, \\'.ContainerInterface::class.' $valueTransformers, \\'.Instantiator::class.' $instantiator, array $options): mixed {', $context) .$providers .($this->canBeDecodedWithJsonDecode($dataModel, $decodeFromStream) diff --git a/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_reader/backed_enum.php b/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_reader/backed_enum.php index 6c994dd39fbed..2395fea69823f 100644 --- a/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_reader/backed_enum.php +++ b/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_reader/backed_enum.php @@ -1,5 +1,8 @@ + */ return static function (string|\Stringable $string, \Psr\Container\ContainerInterface $valueTransformers, \Symfony\Component\JsonStreamer\Read\Instantiator $instantiator, array $options): mixed { return \Symfony\Component\JsonStreamer\Read\Decoder::decodeString((string) $string); }; diff --git a/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_reader/dict.stream.php b/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_reader/dict.stream.php index 36729b8cec658..183b77955ddd9 100644 --- a/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_reader/dict.stream.php +++ b/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_reader/dict.stream.php @@ -1,5 +1,8 @@ + */ return static function (mixed $stream, \Psr\Container\ContainerInterface $valueTransformers, \Symfony\Component\JsonStreamer\Read\LazyInstantiator $instantiator, array $options): mixed { $providers['array'] = static function ($stream, $offset, $length) use ($options, $valueTransformers, $instantiator, &$providers) { $data = \Symfony\Component\JsonStreamer\Read\Splitter::splitDict($stream, $offset, $length); diff --git a/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_reader/iterable.php b/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_reader/iterable.php index a6fedcbd99ba0..45458cd2df0cb 100644 --- a/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_reader/iterable.php +++ b/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_reader/iterable.php @@ -1,5 +1,8 @@ + */ return static function (string|\Stringable $string, \Psr\Container\ContainerInterface $valueTransformers, \Symfony\Component\JsonStreamer\Read\Instantiator $instantiator, array $options): mixed { return \Symfony\Component\JsonStreamer\Read\Decoder::decodeString((string) $string); }; diff --git a/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_reader/list.stream.php b/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_reader/list.stream.php index 2fa9a0a668dbd..35c1d921aeae5 100644 --- a/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_reader/list.stream.php +++ b/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_reader/list.stream.php @@ -1,5 +1,8 @@ + */ return static function (mixed $stream, \Psr\Container\ContainerInterface $valueTransformers, \Symfony\Component\JsonStreamer\Read\LazyInstantiator $instantiator, array $options): mixed { $providers['array'] = static function ($stream, $offset, $length) use ($options, $valueTransformers, $instantiator, &$providers) { $data = \Symfony\Component\JsonStreamer\Read\Splitter::splitList($stream, $offset, $length); diff --git a/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_reader/mixed.php b/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_reader/mixed.php index a6fedcbd99ba0..0d68447374ff6 100644 --- a/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_reader/mixed.php +++ b/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_reader/mixed.php @@ -1,5 +1,8 @@ instantiate(\Symfony\Component\JsonStreamer\Tests\Fixtures\Model\ClassicDummy::class, \array_filter(['id' => $data['id'] ?? '_symfony_missing_value', 'name' => $data['name'] ?? '_symfony_missing_value'], static function ($v) { diff --git a/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_reader/nullable_object.stream.php b/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_reader/nullable_object.stream.php index b6af2cc29630a..ee8a34a2f8b8a 100644 --- a/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_reader/nullable_object.stream.php +++ b/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_reader/nullable_object.stream.php @@ -1,5 +1,8 @@ |null + */ return static function (string|\Stringable $string, \Psr\Container\ContainerInterface $valueTransformers, \Symfony\Component\JsonStreamer\Read\Instantiator $instantiator, array $options): mixed { $providers['array'] = static function ($data) use ($options, $valueTransformers, $instantiator, &$providers) { $iterable = static function ($data) use ($options, $valueTransformers, $instantiator, &$providers) { diff --git a/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_reader/nullable_object_dict.stream.php b/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_reader/nullable_object_dict.stream.php index fe3be40f02c7e..93addc49d5b29 100644 --- a/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_reader/nullable_object_dict.stream.php +++ b/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_reader/nullable_object_dict.stream.php @@ -1,5 +1,8 @@ |null + */ return static function (mixed $stream, \Psr\Container\ContainerInterface $valueTransformers, \Symfony\Component\JsonStreamer\Read\LazyInstantiator $instantiator, array $options): mixed { $providers['array'] = static function ($stream, $offset, $length) use ($options, $valueTransformers, $instantiator, &$providers) { $data = \Symfony\Component\JsonStreamer\Read\Splitter::splitDict($stream, $offset, $length); diff --git a/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_reader/nullable_object_list.php b/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_reader/nullable_object_list.php index 031d3dc609fac..1213ee6600297 100644 --- a/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_reader/nullable_object_list.php +++ b/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_reader/nullable_object_list.php @@ -1,5 +1,8 @@ |null + */ return static function (string|\Stringable $string, \Psr\Container\ContainerInterface $valueTransformers, \Symfony\Component\JsonStreamer\Read\Instantiator $instantiator, array $options): mixed { $providers['array'] = static function ($data) use ($options, $valueTransformers, $instantiator, &$providers) { $iterable = static function ($data) use ($options, $valueTransformers, $instantiator, &$providers) { diff --git a/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_reader/nullable_object_list.stream.php b/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_reader/nullable_object_list.stream.php index 558e1eac1c4e1..717d645bfb8e0 100644 --- a/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_reader/nullable_object_list.stream.php +++ b/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_reader/nullable_object_list.stream.php @@ -1,5 +1,8 @@ |null + */ return static function (mixed $stream, \Psr\Container\ContainerInterface $valueTransformers, \Symfony\Component\JsonStreamer\Read\LazyInstantiator $instantiator, array $options): mixed { $providers['array'] = static function ($stream, $offset, $length) use ($options, $valueTransformers, $instantiator, &$providers) { $data = \Symfony\Component\JsonStreamer\Read\Splitter::splitList($stream, $offset, $length); diff --git a/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_reader/object.php b/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_reader/object.php index 4bfffaea57b8c..e7fbe5f057954 100644 --- a/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_reader/object.php +++ b/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_reader/object.php @@ -1,5 +1,8 @@ instantiate(\Symfony\Component\JsonStreamer\Tests\Fixtures\Model\ClassicDummy::class, \array_filter(['id' => $data['id'] ?? '_symfony_missing_value', 'name' => $data['name'] ?? '_symfony_missing_value'], static function ($v) { diff --git a/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_reader/object.stream.php b/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_reader/object.stream.php index 97489cf36f414..afdbe35d9089c 100644 --- a/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_reader/object.stream.php +++ b/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_reader/object.stream.php @@ -1,5 +1,8 @@ + */ return static function (string|\Stringable $string, \Psr\Container\ContainerInterface $valueTransformers, \Symfony\Component\JsonStreamer\Read\Instantiator $instantiator, array $options): mixed { $providers['array'] = static function ($data) use ($options, $valueTransformers, $instantiator, &$providers) { $iterable = static function ($data) use ($options, $valueTransformers, $instantiator, &$providers) { diff --git a/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_reader/object_dict.stream.php b/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_reader/object_dict.stream.php index 0baba407dc54b..cd38d41659421 100644 --- a/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_reader/object_dict.stream.php +++ b/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_reader/object_dict.stream.php @@ -1,5 +1,8 @@ + */ return static function (mixed $stream, \Psr\Container\ContainerInterface $valueTransformers, \Symfony\Component\JsonStreamer\Read\LazyInstantiator $instantiator, array $options): mixed { $providers['array'] = static function ($stream, $offset, $length) use ($options, $valueTransformers, $instantiator, &$providers) { $data = \Symfony\Component\JsonStreamer\Read\Splitter::splitDict($stream, $offset, $length); diff --git a/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_reader/object_in_object.php b/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_reader/object_in_object.php index bbba349a3ca93..11efc401589e9 100644 --- a/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_reader/object_in_object.php +++ b/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_reader/object_in_object.php @@ -1,5 +1,8 @@ instantiate(\Symfony\Component\JsonStreamer\Tests\Fixtures\Model\DummyWithOtherDummies::class, \array_filter(['name' => $data['name'] ?? '_symfony_missing_value', 'otherDummyOne' => \array_key_exists('otherDummyOne', $data) ? $providers['Symfony\Component\JsonStreamer\Tests\Fixtures\Model\DummyWithNameAttributes']($data['otherDummyOne']) : '_symfony_missing_value', 'otherDummyTwo' => \array_key_exists('otherDummyTwo', $data) ? $providers['Symfony\Component\JsonStreamer\Tests\Fixtures\Model\ClassicDummy']($data['otherDummyTwo']) : '_symfony_missing_value'], static function ($v) { diff --git a/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_reader/object_in_object.stream.php b/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_reader/object_in_object.stream.php index df1596179e8e1..1c95a99555fc8 100644 --- a/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_reader/object_in_object.stream.php +++ b/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_reader/object_in_object.stream.php @@ -1,5 +1,8 @@ + */ return static function (string|\Stringable $string, \Psr\Container\ContainerInterface $valueTransformers, \Symfony\Component\JsonStreamer\Read\Instantiator $instantiator, array $options): mixed { $providers['iterable'] = static function ($data) use ($options, $valueTransformers, $instantiator, &$providers) { $iterable = static function ($data) use ($options, $valueTransformers, $instantiator, &$providers) { diff --git a/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_reader/object_iterable.stream.php b/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_reader/object_iterable.stream.php index 144749d14959b..9fb08d04a4002 100644 --- a/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_reader/object_iterable.stream.php +++ b/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_reader/object_iterable.stream.php @@ -1,5 +1,8 @@ + */ return static function (mixed $stream, \Psr\Container\ContainerInterface $valueTransformers, \Symfony\Component\JsonStreamer\Read\LazyInstantiator $instantiator, array $options): mixed { $providers['iterable'] = static function ($stream, $offset, $length) use ($options, $valueTransformers, $instantiator, &$providers) { $data = \Symfony\Component\JsonStreamer\Read\Splitter::splitDict($stream, $offset, $length); diff --git a/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_reader/object_list.php b/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_reader/object_list.php index a243d0c95a76f..84999c8823dae 100644 --- a/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_reader/object_list.php +++ b/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_reader/object_list.php @@ -1,5 +1,8 @@ + */ return static function (string|\Stringable $string, \Psr\Container\ContainerInterface $valueTransformers, \Symfony\Component\JsonStreamer\Read\Instantiator $instantiator, array $options): mixed { $providers['array'] = static function ($data) use ($options, $valueTransformers, $instantiator, &$providers) { $iterable = static function ($data) use ($options, $valueTransformers, $instantiator, &$providers) { diff --git a/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_reader/object_list.stream.php b/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_reader/object_list.stream.php index 14bb63a2a1dfc..73be0c3639c8a 100644 --- a/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_reader/object_list.stream.php +++ b/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_reader/object_list.stream.php @@ -1,5 +1,8 @@ + */ return static function (mixed $stream, \Psr\Container\ContainerInterface $valueTransformers, \Symfony\Component\JsonStreamer\Read\LazyInstantiator $instantiator, array $options): mixed { $providers['array'] = static function ($stream, $offset, $length) use ($options, $valueTransformers, $instantiator, &$providers) { $data = \Symfony\Component\JsonStreamer\Read\Splitter::splitList($stream, $offset, $length); diff --git a/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_reader/object_with_nullable_properties.php b/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_reader/object_with_nullable_properties.php index 647a3aeb923bb..91923525f1d32 100644 --- a/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_reader/object_with_nullable_properties.php +++ b/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_reader/object_with_nullable_properties.php @@ -1,5 +1,8 @@ instantiate(\Symfony\Component\JsonStreamer\Tests\Fixtures\Model\DummyWithNullableProperties::class, \array_filter(['name' => $data['name'] ?? '_symfony_missing_value', 'enum' => \array_key_exists('enum', $data) ? $providers['Symfony\Component\JsonStreamer\Tests\Fixtures\Enum\DummyBackedEnum|null']($data['enum']) : '_symfony_missing_value'], static function ($v) { diff --git a/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_reader/object_with_nullable_properties.stream.php b/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_reader/object_with_nullable_properties.stream.php index 9266447cd53f3..c05e0f05d84cf 100644 --- a/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_reader/object_with_nullable_properties.stream.php +++ b/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_reader/object_with_nullable_properties.stream.php @@ -1,5 +1,8 @@ instantiate(\Symfony\Component\JsonStreamer\Tests\Fixtures\Model\DummyWithUnionProperties::class, \array_filter(['value' => \array_key_exists('value', $data) ? $providers['Symfony\Component\JsonStreamer\Tests\Fixtures\Enum\DummyBackedEnum|null|string']($data['value']) : '_symfony_missing_value'], static function ($v) { diff --git a/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_reader/object_with_union.stream.php b/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_reader/object_with_union.stream.php index ef7dc5791c666..1ccf17a7b0bf2 100644 --- a/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_reader/object_with_union.stream.php +++ b/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_reader/object_with_union.stream.php @@ -1,5 +1,8 @@ instantiate(\Symfony\Component\JsonStreamer\Tests\Fixtures\Model\DummyWithValueTransformerAttributes::class, \array_filter(['id' => $valueTransformers->get('Symfony\Component\JsonStreamer\Tests\Fixtures\ValueTransformer\DivideStringAndCastToIntValueTransformer')->transform($data['id'] ?? '_symfony_missing_value', $options), 'active' => $valueTransformers->get('Symfony\Component\JsonStreamer\Tests\Fixtures\ValueTransformer\StringToBooleanValueTransformer')->transform($data['active'] ?? '_symfony_missing_value', $options), 'name' => strtoupper($data['name'] ?? '_symfony_missing_value'), 'range' => Symfony\Component\JsonStreamer\Tests\Fixtures\Model\DummyWithValueTransformerAttributes::explodeRange($data['range'] ?? '_symfony_missing_value', $options)], static function ($v) { diff --git a/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_reader/object_with_value_transformer.stream.php b/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_reader/object_with_value_transformer.stream.php index a6898aeb9bf6e..7904bc2d3a3b6 100644 --- a/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_reader/object_with_value_transformer.stream.php +++ b/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_reader/object_with_value_transformer.stream.php @@ -1,5 +1,8 @@ |int + */ return static function (string|\Stringable $string, \Psr\Container\ContainerInterface $valueTransformers, \Symfony\Component\JsonStreamer\Read\Instantiator $instantiator, array $options): mixed { $providers['array'] = static function ($data) use ($options, $valueTransformers, $instantiator, &$providers) { $iterable = static function ($data) use ($options, $valueTransformers, $instantiator, &$providers) { diff --git a/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_reader/union.stream.php b/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_reader/union.stream.php index db8d2cffb283e..a5f19897b3dbe 100644 --- a/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_reader/union.stream.php +++ b/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_reader/union.stream.php @@ -1,5 +1,8 @@ |int + */ return static function (mixed $stream, \Psr\Container\ContainerInterface $valueTransformers, \Symfony\Component\JsonStreamer\Read\LazyInstantiator $instantiator, array $options): mixed { $providers['array'] = static function ($stream, $offset, $length) use ($options, $valueTransformers, $instantiator, &$providers) { $data = \Symfony\Component\JsonStreamer\Read\Splitter::splitList($stream, $offset, $length); diff --git a/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_writer/backed_enum.php b/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_writer/backed_enum.php index cd64125f0a71e..0793dda9f82f2 100644 --- a/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_writer/backed_enum.php +++ b/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_writer/backed_enum.php @@ -1,5 +1,8 @@ value, \JSON_THROW_ON_ERROR, 512); diff --git a/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_writer/bool.php b/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_writer/bool.php index f645b7c3cc391..79888d618436c 100644 --- a/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_writer/bool.php +++ b/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_writer/bool.php @@ -1,5 +1,8 @@ $data + */ return static function (mixed $data, \Psr\Container\ContainerInterface $valueTransformers, array $options): \Traversable { try { yield \json_encode($data, \JSON_THROW_ON_ERROR, 512); diff --git a/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_writer/dict.php b/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_writer/dict.php index cd6e53ba38da1..ca7218ad63810 100644 --- a/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_writer/dict.php +++ b/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_writer/dict.php @@ -1,5 +1,8 @@ $data + */ return static function (mixed $data, \Psr\Container\ContainerInterface $valueTransformers, array $options): \Traversable { try { yield \json_encode($data, \JSON_THROW_ON_ERROR, 512); diff --git a/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_writer/iterable.php b/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_writer/iterable.php index cd6e53ba38da1..a0ecc71c74555 100644 --- a/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_writer/iterable.php +++ b/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_writer/iterable.php @@ -1,5 +1,8 @@ $data + */ return static function (mixed $data, \Psr\Container\ContainerInterface $valueTransformers, array $options): \Traversable { try { yield \json_encode($data, \JSON_THROW_ON_ERROR, 512); diff --git a/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_writer/mixed.php b/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_writer/mixed.php index cd6e53ba38da1..e121bf57929b0 100644 --- a/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_writer/mixed.php +++ b/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_writer/mixed.php @@ -1,5 +1,8 @@ $data + */ return static function (mixed $data, \Psr\Container\ContainerInterface $valueTransformers, array $options): \Traversable { try { yield \json_encode($data, \JSON_THROW_ON_ERROR, 512); diff --git a/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_writer/nullable_backed_enum.php b/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_writer/nullable_backed_enum.php index 42f62c6037f05..76ed43bba41f5 100644 --- a/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_writer/nullable_backed_enum.php +++ b/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_writer/nullable_backed_enum.php @@ -1,5 +1,8 @@ |null $data + */ return static function (mixed $data, \Psr\Container\ContainerInterface $valueTransformers, array $options): \Traversable { try { if (\is_array($data)) { diff --git a/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_writer/nullable_object_list.php b/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_writer/nullable_object_list.php index f891ae0a649bc..1d06cf77b3e2e 100644 --- a/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_writer/nullable_object_list.php +++ b/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_writer/nullable_object_list.php @@ -1,5 +1,8 @@ |null $data + */ return static function (mixed $data, \Psr\Container\ContainerInterface $valueTransformers, array $options): \Traversable { try { if (\is_array($data)) { diff --git a/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_writer/object.php b/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_writer/object.php index 36499b3d3035c..7fbc49cf96edc 100644 --- a/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_writer/object.php +++ b/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_writer/object.php @@ -1,5 +1,8 @@ $data + */ return static function (mixed $data, \Psr\Container\ContainerInterface $valueTransformers, array $options): \Traversable { try { yield '{'; diff --git a/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_writer/object_in_object.php b/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_writer/object_in_object.php index 3f6dc691cbba9..1e04f6b1d8e6a 100644 --- a/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_writer/object_in_object.php +++ b/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_writer/object_in_object.php @@ -1,5 +1,8 @@ $data + */ return static function (mixed $data, \Psr\Container\ContainerInterface $valueTransformers, array $options): \Traversable { try { yield '{'; diff --git a/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_writer/object_list.php b/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_writer/object_list.php index bb4a6a45d0a46..3b691fa350048 100644 --- a/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_writer/object_list.php +++ b/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_writer/object_list.php @@ -1,5 +1,8 @@ $data + */ return static function (mixed $data, \Psr\Container\ContainerInterface $valueTransformers, array $options): \Traversable { try { yield '['; diff --git a/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_writer/object_with_union.php b/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_writer/object_with_union.php index bc069637c4e42..cd99dd4630fe7 100644 --- a/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_writer/object_with_union.php +++ b/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_writer/object_with_union.php @@ -1,5 +1,8 @@ = 512) { diff --git a/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_writer/union.php b/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_writer/union.php index edb5e5c46fe7c..0043bb1872233 100644 --- a/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_writer/union.php +++ b/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_writer/union.php @@ -1,5 +1,8 @@ |int $data + */ return static function (mixed $data, \Psr\Container\ContainerInterface $valueTransformers, array $options): \Traversable { try { if (\is_array($data)) { diff --git a/src/Symfony/Component/JsonStreamer/Write/PhpGenerator.php b/src/Symfony/Component/JsonStreamer/Write/PhpGenerator.php index 0e79481007a65..f9fb7eb83bd2d 100644 --- a/src/Symfony/Component/JsonStreamer/Write/PhpGenerator.php +++ b/src/Symfony/Component/JsonStreamer/Write/PhpGenerator.php @@ -61,6 +61,9 @@ public function generate(DataModelNodeInterface $dataModel, array $options = [], return $this->line('line('', $context) + .$this->line('/**', $context) + .$this->line(' * @param '.$dataModel->getType().' $data', $context) + .$this->line(' */', $context) .$this->line('return static function (mixed $data, \\'.ContainerInterface::class.' $valueTransformers, array $options): \\Traversable {', $context) .implode('', $generators) .$this->line(' try {', $context) From d8908286fff8ee9d9cb64ea0608717bd9396ade7 Mon Sep 17 00:00:00 2001 From: Larry Garfield Date: Fri, 6 Jun 2025 09:38:13 -0500 Subject: [PATCH 77/80] Improve docblock on compile() --- .../Component/DependencyInjection/ContainerBuilder.php | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php index 5be5b76f586b5..2771defe45134 100644 --- a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php +++ b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php @@ -742,10 +742,11 @@ public function deprecateParameter(string $name, string $package, string $versio * * The parameter bag is frozen; * * Extension loading is disabled. * - * @param bool $resolveEnvPlaceholders Whether %env()% parameters should be resolved using the current - * env vars or be replaced by uniquely identifiable placeholders. - * Set to "true" when you want to use the current ContainerBuilder - * directly, keep to "false" when the container is dumped instead. + * @param bool $resolveEnvPlaceholders Whether %env()% parameters should be resolved at build time using + * the current env var values (true), or be resolved at runtime based + * on the environment (false). In general, this should be set to "true" + * when you want to use the current ContainerBuilder directly, and to + * "false" when the container is dumped instead. * * @return void */ From a73c9d17f0b22a31fdcf6c6749aba878a1dc719c Mon Sep 17 00:00:00 2001 From: matlec Date: Wed, 4 Jun 2025 15:43:26 +0200 Subject: [PATCH 78/80] [DependencyInjection] Fix `ServiceLocatorTagPass` indexes handling --- .../Attribute/AsTaggedItem.php | 4 +- .../Compiler/PriorityTaggedServiceTrait.php | 3 +- .../Compiler/ServiceLocatorTagPass.php | 69 ++++++++++--------- .../Compiler/ServiceLocatorTagPassTest.php | 63 ++++++++++++++++- 4 files changed, 100 insertions(+), 39 deletions(-) diff --git a/src/Symfony/Component/DependencyInjection/Attribute/AsTaggedItem.php b/src/Symfony/Component/DependencyInjection/Attribute/AsTaggedItem.php index 2e649bdeaaadd..6b1a94dd3dd35 100644 --- a/src/Symfony/Component/DependencyInjection/Attribute/AsTaggedItem.php +++ b/src/Symfony/Component/DependencyInjection/Attribute/AsTaggedItem.php @@ -20,8 +20,8 @@ class AsTaggedItem { /** - * @param string|null $index The property or method to use to index the item in the locator - * @param int|null $priority The priority of the item; the higher the number, the earlier the tagged service will be located in the locator + * @param string|null $index The property or method to use to index the item in the iterator/locator + * @param int|null $priority The priority of the item; the higher the number, the earlier the tagged service will be located in the iterator/locator */ public function __construct( public ?string $index = null, diff --git a/src/Symfony/Component/DependencyInjection/Compiler/PriorityTaggedServiceTrait.php b/src/Symfony/Component/DependencyInjection/Compiler/PriorityTaggedServiceTrait.php index 77a1d7ef8ffc2..e3a4eba275a75 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/PriorityTaggedServiceTrait.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/PriorityTaggedServiceTrait.php @@ -87,8 +87,7 @@ private function findAndSortTaggedServices(string|TaggedIteratorArgument $tagNam if (null === $index && null === $defaultIndex && $defaultPriorityMethod && $class) { $defaultIndex = PriorityTaggedServiceUtil::getDefault($container, $serviceId, $class, $defaultIndexMethod ?? 'getDefaultName', $tagName, $indexAttribute, $checkTaggedItem); } - $decorated = $definition->getTag('container.decorator')[0]['id'] ?? null; - $index = $index ?? $defaultIndex ?? $defaultIndex = $decorated ?? $serviceId; + $index ??= $defaultIndex ??= $definition->getTag('container.decorator')[0]['id'] ?? $serviceId; $services[] = [$priority, ++$i, $index, $serviceId, $class]; } diff --git a/src/Symfony/Component/DependencyInjection/Compiler/ServiceLocatorTagPass.php b/src/Symfony/Component/DependencyInjection/Compiler/ServiceLocatorTagPass.php index 81c14ac5cc4d0..eedc0f484243c 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/ServiceLocatorTagPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/ServiceLocatorTagPass.php @@ -54,17 +54,41 @@ protected function processValue(mixed $value, bool $isRoot = false): mixed $value->setClass(ServiceLocator::class); } - $services = $value->getArguments()[0] ?? null; + $values = $value->getArguments()[0] ?? null; + $services = []; - if ($services instanceof TaggedIteratorArgument) { - $services = $this->findAndSortTaggedServices($services, $this->container); - } - - if (!\is_array($services)) { + if ($values instanceof TaggedIteratorArgument) { + foreach ($this->findAndSortTaggedServices($values, $this->container) as $k => $v) { + $services[$k] = new ServiceClosureArgument($v); + } + } elseif (!\is_array($values)) { throw new InvalidArgumentException(\sprintf('Invalid definition for service "%s": an array of references is expected as first argument when the "container.service_locator" tag is set.', $this->currentId)); + } else { + $i = 0; + + foreach ($values as $k => $v) { + if ($v instanceof ServiceClosureArgument) { + $services[$k] = $v; + continue; + } + + if ($i === $k) { + if ($v instanceof Reference) { + $k = (string) $v; + } + ++$i; + } elseif (\is_int($k)) { + $i = null; + } + + $services[$k] = new ServiceClosureArgument($v); + } + if (\count($services) === $i) { + ksort($services); + } } - $value->setArgument(0, self::map($services)); + $value->setArgument(0, $services); $id = '.service_locator.'.ContainerBuilder::hash($value); @@ -83,8 +107,12 @@ protected function processValue(mixed $value, bool $isRoot = false): mixed public static function register(ContainerBuilder $container, array $map, ?string $callerId = null): Reference { + foreach ($map as $k => $v) { + $map[$k] = new ServiceClosureArgument($v); + } + $locator = (new Definition(ServiceLocator::class)) - ->addArgument(self::map($map)) + ->addArgument($map) ->addTag('container.service_locator'); if (null !== $callerId && $container->hasDefinition($callerId)) { @@ -109,29 +137,4 @@ public static function register(ContainerBuilder $container, array $map, ?string return new Reference($id); } - - public static function map(array $services): array - { - $i = 0; - - foreach ($services as $k => $v) { - if ($v instanceof ServiceClosureArgument) { - continue; - } - - if ($i === $k) { - if ($v instanceof Reference) { - unset($services[$k]); - $k = (string) $v; - } - ++$i; - } elseif (\is_int($k)) { - $i = null; - } - - $services[$k] = new ServiceClosureArgument($v); - } - - return $services; - } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ServiceLocatorTagPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ServiceLocatorTagPassTest.php index 812b47c7a6f1f..9a93067756d50 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ServiceLocatorTagPassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ServiceLocatorTagPassTest.php @@ -86,6 +86,26 @@ public function testProcessValue() $this->assertSame(CustomDefinition::class, \get_class($locator('inlines.service'))); } + public function testServiceListIsOrdered() + { + $container = new ContainerBuilder(); + + $container->register('bar', CustomDefinition::class); + $container->register('baz', CustomDefinition::class); + + $container->register('foo', ServiceLocator::class) + ->setArguments([[ + new Reference('baz'), + new Reference('bar'), + ]]) + ->addTag('container.service_locator') + ; + + (new ServiceLocatorTagPass())->process($container); + + $this->assertSame(['bar', 'baz'], array_keys($container->getDefinition('foo')->getArgument(0))); + } + public function testServiceWithKeyOverwritesPreviousInheritedKey() { $container = new ContainerBuilder(); @@ -170,6 +190,27 @@ public function testTaggedServices() $this->assertSame(TestDefinition2::class, $locator('baz')::class); } + public function testTaggedServicesKeysAreKept() + { + $container = new ContainerBuilder(); + + $container->register('bar', TestDefinition1::class)->addTag('test_tag', ['index' => 0]); + $container->register('baz', TestDefinition2::class)->addTag('test_tag', ['index' => 1]); + + $container->register('foo', ServiceLocator::class) + ->setArguments([new TaggedIteratorArgument('test_tag', 'index', null, true)]) + ->addTag('container.service_locator') + ; + + (new ServiceLocatorTagPass())->process($container); + + /** @var ServiceLocator $locator */ + $locator = $container->get('foo'); + + $this->assertSame(TestDefinition1::class, $locator(0)::class); + $this->assertSame(TestDefinition2::class, $locator(1)::class); + } + public function testIndexedByServiceIdWithDecoration() { $container = new ContainerBuilder(); @@ -201,15 +242,33 @@ public function testIndexedByServiceIdWithDecoration() static::assertInstanceOf(DecoratedService::class, $locator->get(Service::class)); } - public function testDefinitionOrderIsTheSame() + public function testServicesKeysAreKept() { $container = new ContainerBuilder(); $container->register('service-1'); $container->register('service-2'); + $container->register('service-3'); $locator = ServiceLocatorTagPass::register($container, [ - new Reference('service-2'), new Reference('service-1'), + 'service-2' => new Reference('service-2'), + 'foo' => new Reference('service-3'), + ]); + $locator = $container->getDefinition($locator); + $factories = $locator->getArguments()[0]; + + static::assertSame([0, 'service-2', 'foo'], array_keys($factories)); + } + + public function testDefinitionOrderIsTheSame() + { + $container = new ContainerBuilder(); + $container->register('service-1'); + $container->register('service-2'); + + $locator = ServiceLocatorTagPass::register($container, [ + 'service-2' => new Reference('service-2'), + 'service-1' => new Reference('service-1'), ]); $locator = $container->getDefinition($locator); $factories = $locator->getArguments()[0]; From aa94da287761d2e466d09a0278d45ed1448b6164 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Fri, 6 Jun 2025 23:23:26 +0200 Subject: [PATCH 79/80] remove no longer needed conflict rule on symfony/event-dispatcher --- src/Symfony/Component/Security/Http/composer.json | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Symfony/Component/Security/Http/composer.json b/src/Symfony/Component/Security/Http/composer.json index a6c2626da5873..2d5ed369a7f57 100644 --- a/src/Symfony/Component/Security/Http/composer.json +++ b/src/Symfony/Component/Security/Http/composer.json @@ -41,7 +41,6 @@ }, "conflict": { "symfony/clock": "<6.4", - "symfony/event-dispatcher": "<6.4", "symfony/http-client-contracts": "<3.0", "symfony/security-bundle": "<6.4", "symfony/security-csrf": "<6.4" From 03d612a5332b97a2511f2de52050755e173814e3 Mon Sep 17 00:00:00 2001 From: HypeMC Date: Sat, 7 Jun 2025 17:59:31 +0200 Subject: [PATCH 80/80] [Console] Fix setting aliases & hidden via name --- src/Symfony/Component/Console/CHANGELOG.md | 5 +++++ src/Symfony/Component/Console/Command/Command.php | 6 +++--- .../Component/Console/Tests/Command/CommandTest.php | 13 +++++++++++++ 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/src/Symfony/Component/Console/CHANGELOG.md b/src/Symfony/Component/Console/CHANGELOG.md index 9f3ae3d7d2326..509a9d03c5707 100644 --- a/src/Symfony/Component/Console/CHANGELOG.md +++ b/src/Symfony/Component/Console/CHANGELOG.md @@ -1,6 +1,11 @@ CHANGELOG ========= +7.4 +--- + + * Allow setting aliases and the hidden flag via the command name passed to the constructor + 7.3 --- diff --git a/src/Symfony/Component/Console/Command/Command.php b/src/Symfony/Component/Console/Command/Command.php index f6cd8499791f1..23e3b662138c8 100644 --- a/src/Symfony/Component/Console/Command/Command.php +++ b/src/Symfony/Component/Console/Command/Command.php @@ -97,13 +97,13 @@ public function __construct(?string $name = null) if (self::class !== (new \ReflectionMethod($this, 'getDefaultName'))->class) { trigger_deprecation('symfony/console', '7.3', 'Overriding "Command::getDefaultName()" in "%s" is deprecated and will be removed in Symfony 8.0, use the #[AsCommand] attribute instead.', static::class); - $defaultName = static::getDefaultName(); + $name = static::getDefaultName(); } else { - $defaultName = $attribute?->name; + $name = $attribute?->name; } } - if (null === $name && null !== $name = $defaultName) { + if (null !== $name) { $aliases = explode('|', $name); if ('' === $name = array_shift($aliases)) { diff --git a/src/Symfony/Component/Console/Tests/Command/CommandTest.php b/src/Symfony/Component/Console/Tests/Command/CommandTest.php index 0db3572fc3476..85442c7b9243e 100644 --- a/src/Symfony/Component/Console/Tests/Command/CommandTest.php +++ b/src/Symfony/Component/Console/Tests/Command/CommandTest.php @@ -205,6 +205,19 @@ public function testGetSetAliases() $this->assertEquals(['name1'], $command->getAliases(), '->setAliases() sets the aliases'); } + /** + * @testWith ["name|alias1|alias2", "name", ["alias1", "alias2"], false] + * ["|alias1|alias2", "alias1", ["alias2"], true] + */ + public function testSetAliasesAndHiddenViaName(string $name, string $expectedName, array $expectedAliases, bool $expectedHidden) + { + $command = new Command($name); + + self::assertSame($expectedName, $command->getName()); + self::assertSame($expectedHidden, $command->isHidden()); + self::assertSame($expectedAliases, $command->getAliases()); + } + public function testGetSynopsis() { $command = new \TestCommand(); 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