diff --git a/.github/workflows/package-tests.yml b/.github/workflows/package-tests.yml index 03cb17cab63bc..928069d04e122 100644 --- a/.github/workflows/package-tests.yml +++ b/.github/workflows/package-tests.yml @@ -21,7 +21,7 @@ jobs: - name: Find packages id: find-packages - run: echo "packages=$(php .github/get-modified-packages.php $(find src/Symfony -mindepth 2 -maxdepth 6 -type f -name composer.json -printf '%h\n' | grep -v src/Symfony/Component/Intl/Resources/emoji |jq -R -s -c 'split("\n")[:-1]') $(git diff --name-only origin/${{ github.base_ref }} HEAD | grep src/ | jq -R -s -c 'split("\n")[:-1]'))" >> $GITHUB_OUTPUT + run: echo "packages=$(php .github/get-modified-packages.php $(find src/Symfony -mindepth 2 -type f -name composer.json -printf '%h\n' | grep -v src/Symfony/Component/Intl/Resources/emoji |jq -R -s -c 'split("\n")[:-1]') $(git diff --name-only origin/${{ github.base_ref }} HEAD | grep src/ | jq -R -s -c 'split("\n")[:-1]'))" >> $GITHUB_OUTPUT - name: Verify meta files are correct run: | diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index 536c8dde31059..327e1ec701b4c 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -93,7 +93,7 @@ jobs: 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 -maxdepth 6 -type f -name composer.json -printf '%h\n' | grep -v src/Symfony/Component/Intl/Resources/emoji) + 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 diff --git a/CHANGELOG-6.0.md b/CHANGELOG-6.0.md index c2328862f0a62..36533d04d2bc3 100644 --- a/CHANGELOG-6.0.md +++ b/CHANGELOG-6.0.md @@ -7,6 +7,11 @@ in 6.0 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.0.0...v6.0.1 +* 6.0.18 (2022-12-29) + + * bug #48823 [Cache] Fix possibly null value passed to preg_match() in RedisTrait (chalasr) + * bug #48816 [Cache] Fix for RedisAdapter without auth parameter (rikvdh) + * 6.0.17 (2022-12-28) * bug #48787 [PhpUnitBridge] Use verbose deprecation output for quiet types only when it reaches the threshold (ogizanagi) diff --git a/CHANGELOG-6.1.md b/CHANGELOG-6.1.md index ec054deff3146..f798e9732f746 100644 --- a/CHANGELOG-6.1.md +++ b/CHANGELOG-6.1.md @@ -7,6 +7,11 @@ in 6.1 minor versions. To get the diff for a specific change, go to https://github.com/symfony/symfony/commit/XXX where XXX is the change hash To get the diff between two versions, go to https://github.com/symfony/symfony/compare/v6.1.0...v6.1.1 +* 6.1.10 (2022-12-29) + + * bug #48823 [Cache] Fix possibly null value passed to preg_match() in RedisTrait (chalasr) + * bug #48816 [Cache] Fix for RedisAdapter without auth parameter (rikvdh) + * 6.1.9 (2022-12-28) * bug #48787 [PhpUnitBridge] Use verbose deprecation output for quiet types only when it reaches the threshold (ogizanagi) diff --git a/CHANGELOG-6.2.md b/CHANGELOG-6.2.md index df3991022ddf0..caa48fbf31992 100644 --- a/CHANGELOG-6.2.md +++ b/CHANGELOG-6.2.md @@ -7,6 +7,39 @@ in 6.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/v6.2.0...v6.2.1 +* 6.2.5 (2023-01-24) + + * bug #49078 [Security/Http] Check tokens before loading users from providers (nicolas-grekas) + * bug #49077 [DependencyInjection] Fix named arguments when using ContainerBuilder before compilation (nicolas-grekas) + * bug #49031 [Cache] fix collecting cache stats when nesting computations (nicolas-grekas) + * bug #49046 Fix for Windows when projects are deployed on junctions/symlinks (nerdgod) + * bug #49025 [Notifier] [OvhCloud] handle invalid receiver (seferov) + * bug #49034 [Security] Return default value instead of deferring to lower prio resolvers when using #[CurrentUser] and no user is found (Seldaek) + * bug #48993 [VarDumper] Fix JS to expand / collapse (nicolas-grekas) + * bug #48983 Fix BC user_identifier support after deprecation username (vtsykun) + * bug #48986 [Validator] Fix Email validator logic (fabpot) + * bug #48969 [PropertyInfo] Fixes constructor extractor for mixed type (michael.kubovic) + * bug #48978 [Serializer] use method_exists() instead of catching reflection exceptions (xabbuh) + * bug #48958 [DependencyInjection] fixes validation of non-scalar attribute values (ju1ius) + * bug #48937 [SecurityBundle] Fix using same handler for multiple authenticators (RobertMe) + * bug #48971 [DependencyInjection] Fix dump order of inlined deps (nicolas-grekas) + * bug #48966 [HttpClient] Let curl handle content-length headers (nicolas-grekas) + * bug #48968 [VarExporter] Fix exporting enums (nicolas-grekas) + * bug #48933 [Validator] Fix bad handling of nulls when the 'fields' option of the Unique constraint is set (plfort) + * bug #48926 [DependencyInjection] Fix support for named arguments on non-autowired services (nicolas-grekas) + * bug #48943 [FrameworkBundle] Fix deprecation when accessing a "container.private" service from the test container (nicolas-grekas) + * bug #48939 [VarExporter] Fix signature of `Lazy*Trait::createLazy*()` (nicolas-grekas) + * bug #48931 [DependencyInjection] Fix dumping inlined withers (nicolas-grekas) + * bug #48898 [HttpClient] Move Http clients data collecting at a late level (pforesi) + * bug #48896 [DoctrineBridge] Fix detecting mapping with one line annotations (franmomu) + * bug #48916 [FrameworkBundle] restore call to addGlobalIgnoredName (alexislefebvre) + * bug #48917 [Config] Fix XML dump when node example is an array (alexandre-daubois) + * bug #48904 [Validator] Allow egulias/email-validator v4 (chalasr) + * bug #48830 [Translation] fix PhpAstExtractor also extracts messages if t() contains both unnamed and named arguments (gassan) + * bug #48846 [Translation] Fix for resolving Constraint Validator FQCN defined as foo.bar.class parameters (gassan) + * bug #48866 [Validator] fix: Case-insensitive extensions in File-Constraint (spackmat) + * bug #48831 [Uid] Fix validating nil and max uuid (fancyweb) + * 6.2.4 (2022-12-29) * bug #48822 [WebProfilerBundle] Fix the usage of web fonts (javiereguiluz) diff --git a/LICENSE b/LICENSE index 88bf75bb4d6a2..008370457251e 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2022 Fabien Potencier +Copyright (c) 2004-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/composer.json b/composer.json index 1b610a05c761e..e30aa58d03a25 100644 --- a/composer.json +++ b/composer.json @@ -128,7 +128,7 @@ "doctrine/data-fixtures": "^1.1", "doctrine/dbal": "^2.13.1|^3.0", "doctrine/orm": "^2.7.4", - "egulias/email-validator": "^2.1.10|^3.1", + "egulias/email-validator": "^2.1.10|^3.1|^4", "guzzlehttp/promises": "^1.4", "league/html-to-markdown": "^5.0", "masterminds/html5": "^2.7.2", @@ -202,15 +202,6 @@ { "type": "path", "url": "src/Symfony/Component/Runtime" - }, - { - "type": "path", - "url": "src/Symfony/Component/Notifier/Bridge/Mercure/Tests/stella-maris-clock", - "options": { - "versions": { - "stella-maris/clock": "0.1.x-dev" - } - } } ], "minimum-stability": "dev" diff --git a/src/Symfony/Bridge/Doctrine/DependencyInjection/AbstractDoctrineExtension.php b/src/Symfony/Bridge/Doctrine/DependencyInjection/AbstractDoctrineExtension.php index ebcf2c06f8d4f..cd5fc91cb32b7 100644 --- a/src/Symfony/Bridge/Doctrine/DependencyInjection/AbstractDoctrineExtension.php +++ b/src/Symfony/Bridge/Doctrine/DependencyInjection/AbstractDoctrineExtension.php @@ -301,8 +301,8 @@ private function detectMappingType(string $directory, ContainerBuilder $containe break; } if ( - preg_match('/^ \* @.*'.$quotedMappingObjectName.'\b/m', $content) || - preg_match('/^ \* @.*Embeddable\b/m', $content) + preg_match('/^(?: \*|\/\*\*) @.*'.$quotedMappingObjectName.'\b/m', $content) || + preg_match('/^(?: \*|\/\*\*) @.*Embeddable\b/m', $content) ) { $type = 'annotation'; break; diff --git a/src/Symfony/Bridge/Doctrine/LICENSE b/src/Symfony/Bridge/Doctrine/LICENSE index 88bf75bb4d6a2..008370457251e 100644 --- a/src/Symfony/Bridge/Doctrine/LICENSE +++ b/src/Symfony/Bridge/Doctrine/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2022 Fabien Potencier +Copyright (c) 2004-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Bridge/Doctrine/Tests/DependencyInjection/DoctrineExtensionTest.php b/src/Symfony/Bridge/Doctrine/Tests/DependencyInjection/DoctrineExtensionTest.php index b481ab9569c2c..2ca6eda3e74b8 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/DependencyInjection/DoctrineExtensionTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/DependencyInjection/DoctrineExtensionTest.php @@ -276,6 +276,7 @@ public function testUnrecognizedCacheDriverException() public function providerBundles() { yield ['AnnotationsBundle', 'annotation', '/Entity']; + yield ['AnnotationsOneLineBundle', 'annotation', '/Entity']; yield ['FullEmbeddableAnnotationsBundle', 'annotation', '/Entity']; yield ['AttributesBundle', 'attribute', '/Entity']; yield ['FullEmbeddableAttributesBundle', 'attribute', '/Entity']; diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/AnnotationsOneLineBundle/AnnotationsOneLineBundle.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/AnnotationsOneLineBundle/AnnotationsOneLineBundle.php new file mode 100644 index 0000000000000..6d401bae4f987 --- /dev/null +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/AnnotationsOneLineBundle/AnnotationsOneLineBundle.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Fixtures\Bundles\AnnotationsOneLineBundle; + +use Symfony\Component\HttpKernel\Bundle\Bundle; + +class AnnotationsOneLineBundle extends Bundle +{ +} diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/AnnotationsOneLineBundle/Entity/Person.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/AnnotationsOneLineBundle/Entity/Person.php new file mode 100644 index 0000000000000..b55fe6f86503b --- /dev/null +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/AnnotationsOneLineBundle/Entity/Person.php @@ -0,0 +1,37 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Fixtures\Bundles\AnnotationsOneLineBundle\Entity; + +use Doctrine\ORM\Mapping\Column; +use Doctrine\ORM\Mapping\Entity; +use Doctrine\ORM\Mapping\Id; + +/** @Entity */ +class Person +{ + /** @Id @Column(type="integer") */ + protected $id; + + /** @Column(type="string") */ + public $name; + + public function __construct($id, $name) + { + $this->id = $id; + $this->name = $name; + } + + public function __toString(): string + { + return (string) $this->name; + } +} diff --git a/src/Symfony/Bridge/Monolog/LICENSE b/src/Symfony/Bridge/Monolog/LICENSE index 88bf75bb4d6a2..008370457251e 100644 --- a/src/Symfony/Bridge/Monolog/LICENSE +++ b/src/Symfony/Bridge/Monolog/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2022 Fabien Potencier +Copyright (c) 2004-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Bridge/PhpUnit/LICENSE b/src/Symfony/Bridge/PhpUnit/LICENSE index a843ec124ea70..72412a62b2029 100644 --- a/src/Symfony/Bridge/PhpUnit/LICENSE +++ b/src/Symfony/Bridge/PhpUnit/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2014-2022 Fabien Potencier +Copyright (c) 2014-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Bridge/ProxyManager/LICENSE b/src/Symfony/Bridge/ProxyManager/LICENSE index 88bf75bb4d6a2..008370457251e 100644 --- a/src/Symfony/Bridge/ProxyManager/LICENSE +++ b/src/Symfony/Bridge/ProxyManager/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2022 Fabien Potencier +Copyright (c) 2004-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Bridge/Twig/LICENSE b/src/Symfony/Bridge/Twig/LICENSE index 88bf75bb4d6a2..008370457251e 100644 --- a/src/Symfony/Bridge/Twig/LICENSE +++ b/src/Symfony/Bridge/Twig/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2022 Fabien Potencier +Copyright (c) 2004-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Bridge/Twig/composer.json b/src/Symfony/Bridge/Twig/composer.json index afcd87d37096d..c1a45f918f878 100644 --- a/src/Symfony/Bridge/Twig/composer.json +++ b/src/Symfony/Bridge/Twig/composer.json @@ -22,7 +22,7 @@ }, "require-dev": { "doctrine/annotations": "^1.12|^2", - "egulias/email-validator": "^2.1.10|^3", + "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": "^5.4|^6.0", diff --git a/src/Symfony/Bundle/DebugBundle/LICENSE b/src/Symfony/Bundle/DebugBundle/LICENSE index a843ec124ea70..72412a62b2029 100644 --- a/src/Symfony/Bundle/DebugBundle/LICENSE +++ b/src/Symfony/Bundle/DebugBundle/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2014-2022 Fabien Potencier +Copyright (c) 2014-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/TestServiceContainerRealRefPass.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/TestServiceContainerRealRefPass.php index 222b5c7b75af0..942eb635b26f3 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/TestServiceContainerRealRefPass.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/TestServiceContainerRealRefPass.php @@ -11,6 +11,7 @@ namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler; +use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Reference; @@ -38,6 +39,16 @@ public function process(ContainerBuilder $container) } } + foreach ($container->getAliases() as $id => $target) { + while ($container->hasAlias($target = (string) $target)) { + $target = $container->getAlias($target); + } + + if ($definitions[$target]->hasTag('container.private')) { + $privateServices[$id] = new ServiceClosureArgument(new Reference($target)); + } + } + $privateContainer->replaceArgument(0, $privateServices); } } diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index 66f8048863968..dadd84d8e6c92 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -1635,11 +1635,14 @@ private function registerAnnotationsConfiguration(array $config, ContainerBuilde $loader->load('annotations.php'); + // registerUniqueLoader exists since doctrine/annotations v1.6 if (!method_exists(AnnotationRegistry::class, 'registerUniqueLoader')) { + // registerLoader exists only in doctrine/annotations v1 if (method_exists(AnnotationRegistry::class, 'registerLoader')) { $container->getDefinition('annotations.dummy_registry') ->setMethodCalls([['registerLoader', ['class_exists']]]); } else { + // remove the dummy registry when doctrine/annotations v2 is used $container->removeDefinition('annotations.dummy_registry'); } } diff --git a/src/Symfony/Bundle/FrameworkBundle/LICENSE b/src/Symfony/Bundle/FrameworkBundle/LICENSE index 88bf75bb4d6a2..008370457251e 100644 --- a/src/Symfony/Bundle/FrameworkBundle/LICENSE +++ b/src/Symfony/Bundle/FrameworkBundle/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2022 Fabien Potencier +Copyright (c) 2004-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/annotations.php b/src/Symfony/Bundle/FrameworkBundle/Resources/config/annotations.php index 65a3f4e8ffd90..ec86fed495498 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/annotations.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/annotations.php @@ -25,7 +25,7 @@ ->set('annotations.reader', AnnotationReader::class) ->call('addGlobalIgnoredName', [ 'required', - service('annotations.dummy_registry')->ignoreOnInvalid(), // dummy arg to register class_exists as annotation loader only when required + service('annotations.dummy_registry')->nullOnInvalid(), // dummy arg to register class_exists as annotation loader only when required ]) ->set('annotations.dummy_registry', AnnotationRegistry::class) diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/TestServiceContainerRefPassesTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/TestServiceContainerRefPassesTest.php index 7dc9e6f59ec99..355b1527d64bf 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/TestServiceContainerRefPassesTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/TestServiceContainerRefPassesTest.php @@ -43,6 +43,13 @@ public function testProcess() ->setPublic(true) ->addTag('container.private', ['package' => 'foo/bar', 'version' => '1.42']) ; + $container->register('Test\soon_private_service_decorated') + ->setPublic(true) + ->addTag('container.private', ['package' => 'foo/bar', 'version' => '1.42']) + ; + $container->register('Test\soon_private_service_decorator') + ->setDecoratedService('Test\soon_private_service_decorated') + ->setArguments(['Test\soon_private_service_decorator.inner']); $container->register('Test\private_used_shared_service'); $container->register('Test\private_unused_shared_service'); @@ -55,6 +62,8 @@ public function testProcess() 'Test\private_used_shared_service' => new ServiceClosureArgument(new Reference('Test\private_used_shared_service')), 'Test\private_used_non_shared_service' => new ServiceClosureArgument(new Reference('Test\private_used_non_shared_service')), 'Test\soon_private_service' => new ServiceClosureArgument(new Reference('.container.private.Test\soon_private_service')), + 'Test\soon_private_service_decorator' => new ServiceClosureArgument(new Reference('.container.private.Test\soon_private_service_decorated')), + 'Test\soon_private_service_decorated' => new ServiceClosureArgument(new Reference('.container.private.Test\soon_private_service_decorated')), ]; $privateServices = $container->getDefinition('test.private_services_locator')->getArgument(0); diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/AbstractFactory.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/AbstractFactory.php index b0c6b5c0ecb79..6a34fe7efb703 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/AbstractFactory.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/AbstractFactory.php @@ -80,7 +80,7 @@ protected function createAuthenticationSuccessHandler(ContainerBuilder $containe if (isset($config['success_handler'])) { $successHandler = $container->setDefinition($successHandlerId, new ChildDefinition('security.authentication.custom_success_handler')); - $successHandler->replaceArgument(0, new Reference($config['success_handler'])); + $successHandler->replaceArgument(0, new ChildDefinition($config['success_handler'])); $successHandler->replaceArgument(1, $options); $successHandler->replaceArgument(2, $id); } else { @@ -99,7 +99,7 @@ protected function createAuthenticationFailureHandler(ContainerBuilder $containe if (isset($config['failure_handler'])) { $failureHandler = $container->setDefinition($id, new ChildDefinition('security.authentication.custom_failure_handler')); - $failureHandler->replaceArgument(0, new Reference($config['failure_handler'])); + $failureHandler->replaceArgument(0, new ChildDefinition($config['failure_handler'])); $failureHandler->replaceArgument(1, $options); } else { $failureHandler = $container->setDefinition($id, new ChildDefinition('security.authentication.failure_handler')); diff --git a/src/Symfony/Bundle/SecurityBundle/LICENSE b/src/Symfony/Bundle/SecurityBundle/LICENSE index 88bf75bb4d6a2..008370457251e 100644 --- a/src/Symfony/Bundle/SecurityBundle/LICENSE +++ b/src/Symfony/Bundle/SecurityBundle/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2022 Fabien Potencier +Copyright (c) 2004-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Security/Factory/AbstractFactoryTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Security/Factory/AbstractFactoryTest.php index ff4108d25cdc9..a0d9353b70483 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Security/Factory/AbstractFactoryTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Security/Factory/AbstractFactoryTest.php @@ -13,6 +13,7 @@ use PHPUnit\Framework\TestCase; use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\AbstractFactory; +use Symfony\Component\DependencyInjection\ChildDefinition; use Symfony\Component\DependencyInjection\ContainerBuilder; class AbstractFactoryTest extends TestCase @@ -43,12 +44,19 @@ public function testDefaultFailureHandler($serviceId, $defaultHandlerInjection) $failureHandler = $this->container->getDefinition('security.authentication.failure_handler.foo.stub'); + $expectedFailureHandlerOptions = ['login_path' => '/bar']; $methodCalls = $failureHandler->getMethodCalls(); if ($defaultHandlerInjection) { $this->assertEquals('setOptions', $methodCalls[0][0]); - $this->assertEquals(['login_path' => '/bar'], $methodCalls[0][1][0]); + $this->assertEquals($expectedFailureHandlerOptions, $methodCalls[0][1][0]); } else { $this->assertCount(0, $methodCalls); + $this->assertInstanceOf(ChildDefinition::class, $failureHandler); + $this->assertEquals('security.authentication.custom_failure_handler', $failureHandler->getParent()); + $failureHandlerArguments = $failureHandler->getArguments(); + $this->assertInstanceOf(ChildDefinition::class, $failureHandlerArguments['index_0']); + $this->assertEquals($serviceId, $failureHandlerArguments['index_0']->getParent()); + $this->assertEquals($expectedFailureHandlerOptions, $failureHandlerArguments['index_1']); } } @@ -80,13 +88,22 @@ public function testDefaultSuccessHandler($serviceId, $defaultHandlerInjection) $successHandler = $this->container->getDefinition('security.authentication.success_handler.foo.stub'); $methodCalls = $successHandler->getMethodCalls(); + $expectedSuccessHandlerOptions = ['default_target_path' => '/bar']; + $expectedFirewallName = 'foo'; if ($defaultHandlerInjection) { $this->assertEquals('setOptions', $methodCalls[0][0]); - $this->assertEquals(['default_target_path' => '/bar'], $methodCalls[0][1][0]); + $this->assertEquals($expectedSuccessHandlerOptions, $methodCalls[0][1][0]); $this->assertEquals('setFirewallName', $methodCalls[1][0]); - $this->assertEquals(['foo'], $methodCalls[1][1]); + $this->assertEquals($expectedFirewallName, $methodCalls[1][1][0]); } else { $this->assertCount(0, $methodCalls); + $this->assertInstanceOf(ChildDefinition::class, $successHandler); + $this->assertEquals('security.authentication.custom_success_handler', $successHandler->getParent()); + $successHandlerArguments = $successHandler->getArguments(); + $this->assertInstanceOf(ChildDefinition::class, $successHandlerArguments['index_0']); + $this->assertEquals($serviceId, $successHandlerArguments['index_0']->getParent()); + $this->assertEquals($expectedSuccessHandlerOptions, $successHandlerArguments['index_1']); + $this->assertEquals($expectedFirewallName, $successHandlerArguments['index_2']); } } diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/AuthenticatorTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/AuthenticatorTest.php index 10eeb39ca8c5e..970a9dc9ae746 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/AuthenticatorTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/AuthenticatorTest.php @@ -102,4 +102,38 @@ public function testMultipleFirewalls() $client->request('GET', '/firewall2/profile'); $this->assertResponseRedirects('http://localhost/login'); } + + public function testCustomSuccessHandler() + { + $client = $this->createClient(['test_case' => 'Authenticator', 'root_config' => 'custom_handlers.yml']); + + $client->request('POST', '/firewall1/login', [ + '_username' => 'jane@example.org', + '_password' => 'test', + ]); + $this->assertResponseRedirects('http://localhost/firewall1/test'); + + $client->request('POST', '/firewall1/dummy_login', [ + '_username' => 'jane@example.org', + '_password' => 'test', + ]); + $this->assertResponseRedirects('http://localhost/firewall1/dummy'); + } + + public function testCustomFailureHandler() + { + $client = $this->createClient(['test_case' => 'Authenticator', 'root_config' => 'custom_handlers.yml']); + + $client->request('POST', '/firewall1/login', [ + '_username' => 'jane@example.org', + '_password' => '', + ]); + $this->assertResponseRedirects('http://localhost/firewall1/login'); + + $client->request('POST', '/firewall1/dummy_login', [ + '_username' => 'jane@example.org', + '_password' => '', + ]); + $this->assertResponseRedirects('http://localhost/firewall1/dummy_login'); + } } diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/AuthenticatorBundle/AuthenticatorBundle.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/AuthenticatorBundle/AuthenticatorBundle.php new file mode 100644 index 0000000000000..730974f17f169 --- /dev/null +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/AuthenticatorBundle/AuthenticatorBundle.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\Bundle\SecurityBundle\Tests\Functional\Bundle\AuthenticatorBundle; + +use Symfony\Bundle\SecurityBundle\DependencyInjection\SecurityExtension; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\HttpKernel\Bundle\Bundle; + +class AuthenticatorBundle extends Bundle +{ + public function build(ContainerBuilder $container) + { + parent::build($container); + + $this->configureSecurityExtension($container); + } + + private function configureSecurityExtension(ContainerBuilder $container): void + { + /** @var SecurityExtension $extension */ + $extension = $container->getExtension('security'); + + $extension->addAuthenticatorFactory(new DummyFormLoginFactory()); + } +} diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/AuthenticatorBundle/DummyFormLoginFactory.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/AuthenticatorBundle/DummyFormLoginFactory.php new file mode 100644 index 0000000000000..fc728fe5c6f4e --- /dev/null +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/AuthenticatorBundle/DummyFormLoginFactory.php @@ -0,0 +1,43 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\AuthenticatorBundle; + +use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\FormLoginFactory; +use Symfony\Component\DependencyInjection\ChildDefinition; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; + +class DummyFormLoginFactory extends FormLoginFactory +{ + public function getKey(): string + { + return 'dummy_form_login'; + } + + public function createAuthenticator(ContainerBuilder $container, string $firewallName, array $config, string $userProviderId): string + { + $authenticatorId = 'security.authenticator.dummy_form_login.'.$firewallName; + $options = array_intersect_key($config, $this->options); + $authenticator = $container + ->setDefinition($authenticatorId, new ChildDefinition('security.authenticator.form_login')) + ->replaceArgument(1, new Reference($userProviderId)) + ->replaceArgument(2, new Reference($this->createAuthenticationSuccessHandler($container, $firewallName, $config))) + ->replaceArgument(3, new Reference($this->createAuthenticationFailureHandler($container, $firewallName, $config))) + ->replaceArgument(4, $options); + + if ($options['use_forward'] ?? false) { + $authenticator->addMethodCall('setHttpKernel', [new Reference('http_kernel')]); + } + + return $authenticatorId; + } +} diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Authenticator/bundles.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Authenticator/bundles.php index d1e9eb7e0d36a..372700495208f 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Authenticator/bundles.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Authenticator/bundles.php @@ -12,4 +12,5 @@ return [ new Symfony\Bundle\FrameworkBundle\FrameworkBundle(), new Symfony\Bundle\SecurityBundle\SecurityBundle(), + new Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\AuthenticatorBundle\AuthenticatorBundle(), ]; diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Authenticator/custom_handlers.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Authenticator/custom_handlers.yml new file mode 100644 index 0000000000000..df25ce3fa602d --- /dev/null +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Authenticator/custom_handlers.yml @@ -0,0 +1,33 @@ +imports: + - { resource: ./config.yml } + - { resource: ./security.yml } + +security: + firewalls: + firewall1: + pattern: /firewall1 + provider: in_memory + entry_point: form_login + form_login: + check_path: /firewall1/login + success_handler: success_handler + failure_handler: failure_handler + default_target_path: /firewall1/test + login_path: /firewall1/login + dummy_form_login: + check_path: /firewall1/dummy_login + success_handler: success_handler + failure_handler: failure_handler + default_target_path: /firewall1/dummy + login_path: /firewall1/dummy_login + +services: + success_handler: + class: Symfony\Component\Security\Http\Authentication\DefaultAuthenticationSuccessHandler + arguments: + - '@security.http_utils' + failure_handler: + class: Symfony\Component\Security\Http\Authentication\DefaultAuthenticationFailureHandler + arguments: + - '@http_kernel' + - '@security.http_utils' diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Authenticator/routing.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Authenticator/routing.yml index 4796a3f6bc00c..76d894d9de904 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Authenticator/routing.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Authenticator/routing.yml @@ -22,6 +22,9 @@ security_custom_profile: firewall1_login: path: /firewall1/login +firewall_dummy_login: + path: /firewall1/dummy_login + firewall2_profile: path: /firewall2/profile defaults: diff --git a/src/Symfony/Bundle/TwigBundle/LICENSE b/src/Symfony/Bundle/TwigBundle/LICENSE index 88bf75bb4d6a2..008370457251e 100644 --- a/src/Symfony/Bundle/TwigBundle/LICENSE +++ b/src/Symfony/Bundle/TwigBundle/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2022 Fabien Potencier +Copyright (c) 2004-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Bundle/WebProfilerBundle/LICENSE b/src/Symfony/Bundle/WebProfilerBundle/LICENSE index 88bf75bb4d6a2..008370457251e 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/LICENSE +++ b/src/Symfony/Bundle/WebProfilerBundle/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2022 Fabien Potencier +Copyright (c) 2004-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Asset/LICENSE b/src/Symfony/Component/Asset/LICENSE index 88bf75bb4d6a2..008370457251e 100644 --- a/src/Symfony/Component/Asset/LICENSE +++ b/src/Symfony/Component/Asset/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2022 Fabien Potencier +Copyright (c) 2004-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/BrowserKit/LICENSE b/src/Symfony/Component/BrowserKit/LICENSE index 88bf75bb4d6a2..008370457251e 100644 --- a/src/Symfony/Component/BrowserKit/LICENSE +++ b/src/Symfony/Component/BrowserKit/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2022 Fabien Potencier +Copyright (c) 2004-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Cache/DataCollector/CacheDataCollector.php b/src/Symfony/Component/Cache/DataCollector/CacheDataCollector.php index af5d687927887..1571e39c4e328 100644 --- a/src/Symfony/Component/Cache/DataCollector/CacheDataCollector.php +++ b/src/Symfony/Component/Cache/DataCollector/CacheDataCollector.php @@ -115,7 +115,7 @@ private function calculateStatistics(): array /** @var TraceableAdapterEvent $call */ foreach ($calls as $call) { ++$statistics[$name]['calls']; - $statistics[$name]['time'] += $call->end - $call->start; + $statistics[$name]['time'] += ($call->end ?? microtime(true)) - $call->start; if ('get' === $call->name) { ++$statistics[$name]['reads']; if ($call->hits) { @@ -137,10 +137,8 @@ private function calculateStatistics(): array $statistics[$name]['misses'] += $call->misses; } elseif ('hasItem' === $call->name) { ++$statistics[$name]['reads']; - if (false === $call->result) { - ++$statistics[$name]['misses']; - } else { - ++$statistics[$name]['hits']; + foreach ($call->result ?? [] as $result) { + ++$statistics[$name][$result ? 'hits' : 'misses']; } } elseif ('save' === $call->name) { ++$statistics[$name]['writes']; diff --git a/src/Symfony/Component/Cache/LICENSE b/src/Symfony/Component/Cache/LICENSE index 7fa9539054928..f2345234aa9ea 100644 --- a/src/Symfony/Component/Cache/LICENSE +++ b/src/Symfony/Component/Cache/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2016-2022 Fabien Potencier +Copyright (c) 2016-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Cache/Tests/DataCollector/CacheDataCollectorTest.php b/src/Symfony/Component/Cache/Tests/DataCollector/CacheDataCollectorTest.php index 6aa94c2c63383..a00954b6cb828 100644 --- a/src/Symfony/Component/Cache/Tests/DataCollector/CacheDataCollectorTest.php +++ b/src/Symfony/Component/Cache/Tests/DataCollector/CacheDataCollectorTest.php @@ -12,6 +12,7 @@ namespace Symfony\Component\Cache\Tests\DataCollector; use PHPUnit\Framework\TestCase; +use Symfony\Component\Cache\Adapter\NullAdapter; use Symfony\Component\Cache\Adapter\TraceableAdapter; use Symfony\Component\Cache\DataCollector\CacheDataCollector; use Symfony\Component\HttpFoundation\Request; @@ -55,7 +56,7 @@ public function testHitedEventDataCollector() $traceableAdapterEvent->name = 'hasItem'; $traceableAdapterEvent->start = 0; $traceableAdapterEvent->end = 0; - $traceableAdapterEvent->hits = 1; + $traceableAdapterEvent->hits = 0; $traceableAdapterEvent->misses = 0; $traceableAdapterEvent->result = ['foo' => false]; @@ -63,8 +64,8 @@ public function testHitedEventDataCollector() $this->assertEquals($statistics[self::INSTANCE_NAME]['calls'], 1, 'calls'); $this->assertEquals($statistics[self::INSTANCE_NAME]['reads'], 1, 'reads'); - $this->assertEquals($statistics[self::INSTANCE_NAME]['hits'], 1, 'hits'); - $this->assertEquals($statistics[self::INSTANCE_NAME]['misses'], 0, 'misses'); + $this->assertEquals($statistics[self::INSTANCE_NAME]['hits'], 0, 'hits'); + $this->assertEquals($statistics[self::INSTANCE_NAME]['misses'], 1, 'misses'); $this->assertEquals($statistics[self::INSTANCE_NAME]['writes'], 0, 'writes'); } @@ -84,6 +85,25 @@ public function testSavedEventDataCollector() $this->assertEquals($statistics[self::INSTANCE_NAME]['writes'], 1, 'writes'); } + public function testCollectBeforeEnd() + { + $adapter = new TraceableAdapter(new NullAdapter()); + + $collector = new CacheDataCollector(); + $collector->addInstance(self::INSTANCE_NAME, $adapter); + + $adapter->get('foo', function () use ($collector) { + $collector->collect(new Request(), new Response()); + + return 123; + }); + + $stats = $collector->getStatistics(); + $this->assertGreaterThan(0, $stats[self::INSTANCE_NAME]['time']); + $this->assertEquals($stats[self::INSTANCE_NAME]['hits'], 0, 'hits'); + $this->assertEquals($stats[self::INSTANCE_NAME]['misses'], 1, 'misses'); + } + private function getCacheDataCollectorStatisticsFromEvents(array $traceableAdapterEvents) { $traceableAdapterMock = $this->createMock(TraceableAdapter::class); diff --git a/src/Symfony/Component/Clock/LICENSE b/src/Symfony/Component/Clock/LICENSE index 0ece8964f767d..074eb2b39259e 100644 --- a/src/Symfony/Component/Clock/LICENSE +++ b/src/Symfony/Component/Clock/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2022 Fabien Potencier +Copyright (c) 2022-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Config/Definition/Dumper/XmlReferenceDumper.php b/src/Symfony/Component/Config/Definition/Dumper/XmlReferenceDumper.php index 8dee2d394aeb9..6520d2fac6ba6 100644 --- a/src/Symfony/Component/Config/Definition/Dumper/XmlReferenceDumper.php +++ b/src/Symfony/Component/Config/Definition/Dumper/XmlReferenceDumper.php @@ -138,7 +138,7 @@ private function writeNode(NodeInterface $node, int $depth = 0, bool $root = fal } if ($child instanceof BaseNode && $example = $child->getExample()) { - $comments[] = 'Example: '.$example; + $comments[] = 'Example: '.(\is_array($example) ? implode(', ', $example) : $example); } if ($child->isRequired()) { diff --git a/src/Symfony/Component/Config/LICENSE b/src/Symfony/Component/Config/LICENSE index 88bf75bb4d6a2..008370457251e 100644 --- a/src/Symfony/Component/Config/LICENSE +++ b/src/Symfony/Component/Config/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2022 Fabien Potencier +Copyright (c) 2004-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Config/Tests/Definition/Dumper/XmlReferenceDumperTest.php b/src/Symfony/Component/Config/Tests/Definition/Dumper/XmlReferenceDumperTest.php index 8d84ae50babee..520d25666a1c0 100644 --- a/src/Symfony/Component/Config/Tests/Definition/Dumper/XmlReferenceDumperTest.php +++ b/src/Symfony/Component/Config/Tests/Definition/Dumper/XmlReferenceDumperTest.php @@ -42,6 +42,7 @@ private function getConfigurationAsString() + diff --git a/src/Symfony/Component/Config/Tests/Definition/Dumper/YamlReferenceDumperTest.php b/src/Symfony/Component/Config/Tests/Definition/Dumper/YamlReferenceDumperTest.php index 88340d1afbada..7d8c2d951897f 100644 --- a/src/Symfony/Component/Config/Tests/Definition/Dumper/YamlReferenceDumperTest.php +++ b/src/Symfony/Component/Config/Tests/Definition/Dumper/YamlReferenceDumperTest.php @@ -114,6 +114,11 @@ enum: ~ # One of "this"; "that" # which should be indented child3: ~ # Example: 'example setting' scalar_prototyped: [] + variable: + + # Examples: + - foo + - bar parameters: # Prototype: Parameter name diff --git a/src/Symfony/Component/Config/Tests/Fixtures/Configuration/ExampleConfiguration.php b/src/Symfony/Component/Config/Tests/Fixtures/Configuration/ExampleConfiguration.php index 919240f1f7acd..126008831796a 100644 --- a/src/Symfony/Component/Config/Tests/Fixtures/Configuration/ExampleConfiguration.php +++ b/src/Symfony/Component/Config/Tests/Fixtures/Configuration/ExampleConfiguration.php @@ -58,6 +58,9 @@ public function getConfigTreeBuilder(): TreeBuilder ->arrayNode('scalar_prototyped') ->prototype('scalar')->end() ->end() + ->variableNode('variable') + ->example(['foo', 'bar']) + ->end() ->arrayNode('parameters') ->useAttributeAsKey('name') ->prototype('scalar')->info('Parameter name')->end() diff --git a/src/Symfony/Component/Console/LICENSE b/src/Symfony/Component/Console/LICENSE index 88bf75bb4d6a2..008370457251e 100644 --- a/src/Symfony/Component/Console/LICENSE +++ b/src/Symfony/Component/Console/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2022 Fabien Potencier +Copyright (c) 2004-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/CssSelector/LICENSE b/src/Symfony/Component/CssSelector/LICENSE index 88bf75bb4d6a2..008370457251e 100644 --- a/src/Symfony/Component/CssSelector/LICENSE +++ b/src/Symfony/Component/CssSelector/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2022 Fabien Potencier +Copyright (c) 2004-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/DependencyInjection/Compiler/CheckDefinitionValidityPass.php b/src/Symfony/Component/DependencyInjection/Compiler/CheckDefinitionValidityPass.php index 68c42ae4811d1..47b6c1518fbba 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/CheckDefinitionValidityPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/CheckDefinitionValidityPass.php @@ -62,11 +62,7 @@ public function process(ContainerBuilder $container) // tag attribute values must be scalars foreach ($definition->getTags() as $name => $tags) { foreach ($tags as $attributes) { - foreach ($attributes as $attribute => $value) { - if (!\is_scalar($value) && null !== $value) { - throw new RuntimeException(sprintf('A "tags" attribute must be of a scalar-type for service "%s", tag "%s", attribute "%s".', $id, $name, $attribute)); - } - } + $this->validateAttributes($id, $name, $attributes); } } @@ -87,4 +83,16 @@ public function process(ContainerBuilder $container) } } } + + private function validateAttributes(string $id, string $tag, array $attributes, array $path = []): void + { + foreach ($attributes as $name => $value) { + if (\is_array($value)) { + $this->validateAttributes($id, $tag, $value, [...$path, $name]); + } elseif (!\is_scalar($value) && null !== $value) { + $name = implode('.', [...$path, $name]); + throw new RuntimeException(sprintf('A "tags" attribute must be of a scalar-type for service "%s", tag "%s", attribute "%s".', $id, $tag, $name)); + } + } + } } diff --git a/src/Symfony/Component/DependencyInjection/Compiler/ResolveNamedArgumentsPass.php b/src/Symfony/Component/DependencyInjection/Compiler/ResolveNamedArgumentsPass.php index 28e4389de296c..12fe6a6b0bb0f 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/ResolveNamedArgumentsPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/ResolveNamedArgumentsPass.php @@ -40,6 +40,7 @@ protected function processValue(mixed $value, bool $isRoot = false): mixed foreach ($calls as $i => $call) { [$method, $arguments] = $call; $parameters = null; + $resolvedKeys = []; $resolvedArguments = []; foreach ($arguments as $key => $argument) { @@ -48,6 +49,7 @@ protected function processValue(mixed $value, bool $isRoot = false): mixed } if (\is_int($key)) { + $resolvedKeys[$key] = $key; $resolvedArguments[$key] = $argument; continue; } @@ -68,9 +70,11 @@ protected function processValue(mixed $value, bool $isRoot = false): mixed if ($key === '$'.$p->name) { if ($p->isVariadic() && \is_array($argument)) { foreach ($argument as $variadicArgument) { + $resolvedKeys[$j] = $j; $resolvedArguments[$j++] = $variadicArgument; } } else { + $resolvedKeys[$j] = $p->name; $resolvedArguments[$j] = $argument; } @@ -88,6 +92,7 @@ protected function processValue(mixed $value, bool $isRoot = false): mixed $typeFound = false; foreach ($parameters as $j => $p) { if (!\array_key_exists($j, $resolvedArguments) && ProxyHelper::exportType($p, true) === $key) { + $resolvedKeys[$j] = $p->name; $resolvedArguments[$j] = $argument; $typeFound = true; } @@ -100,6 +105,12 @@ protected function processValue(mixed $value, bool $isRoot = false): mixed if ($resolvedArguments !== $call[1]) { ksort($resolvedArguments); + + if (!$value->isAutowired() && !array_is_list($resolvedArguments)) { + ksort($resolvedKeys); + $resolvedArguments = array_combine($resolvedKeys, $resolvedArguments); + } + $calls[$i][1] = $resolvedArguments; } } diff --git a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php index ae7ca22ee5035..4ac916b18f638 100644 --- a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php +++ b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php @@ -1033,6 +1033,10 @@ private function createService(Definition $definition, array &$inlineServices, b return $this->services[$id] ?? $this->privates[$id]; } + if (!array_is_list($arguments)) { + $arguments = array_combine(array_map(function ($k) { return preg_replace('/^.*\\$/', '', $k); }, array_keys($arguments)), $arguments); + } + if (null !== $factory) { $service = $factory(...$arguments); @@ -1048,12 +1052,12 @@ private function createService(Definition $definition, array &$inlineServices, b if (\is_object($tryProxy)) { if ($r->getConstructor()) { - $tryProxy->__construct(...array_values($arguments)); + $tryProxy->__construct(...$arguments); } $service = $tryProxy; } else { - $service = $r->getConstructor() ? $r->newInstanceArgs(array_values($arguments)) : $r->newInstance(); + $service = $r->getConstructor() ? $r->newInstanceArgs($arguments) : $r->newInstance(); } if (!$definition->isDeprecated() && 0 < strpos($r->getDocComment(), "\n * @deprecated ")) { diff --git a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php index 62c3f253ad23c..074a1427f63a9 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php @@ -476,7 +476,7 @@ private function collectCircularReferences(string $sourceId, array $edges, array $loop = []; } } - $this->addCircularReferences($first, $loop, true); + $this->addCircularReferences($first, $loop, $loopByConstructor); break; } } @@ -734,7 +734,7 @@ private function addServiceMethodCalls(Definition $definition, string $variableN $witherAssignation = ''; if ($call[2] ?? false) { - if (null !== $sharedNonLazyId && $lastWitherIndex === $k) { + if (null !== $sharedNonLazyId && $lastWitherIndex === $k && 'instance' === $variableName) { $witherAssignation = sprintf('$this->%s[\'%s\'] = ', $definition->isPublic() ? 'services' : 'privates', $sharedNonLazyId); } $witherAssignation .= sprintf('$%s = ', $variableName); diff --git a/src/Symfony/Component/DependencyInjection/LICENSE b/src/Symfony/Component/DependencyInjection/LICENSE index 88bf75bb4d6a2..008370457251e 100644 --- a/src/Symfony/Component/DependencyInjection/LICENSE +++ b/src/Symfony/Component/DependencyInjection/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2022 Fabien Potencier +Copyright (c) 2004-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/DependencyInjection/Loader/Configurator/DefaultsConfigurator.php b/src/Symfony/Component/DependencyInjection/Loader/Configurator/DefaultsConfigurator.php index 223233f88072e..2236cd77a8802 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/Configurator/DefaultsConfigurator.php +++ b/src/Symfony/Component/DependencyInjection/Loader/Configurator/DefaultsConfigurator.php @@ -63,13 +63,14 @@ final public function instanceof(string $fqcn): InstanceofConfigurator return $this->parent->instanceof($fqcn); } - private function validateAttributes(string $tagName, array $attributes, string $prefix = ''): void + private function validateAttributes(string $tag, array $attributes, array $path = []): void { - foreach ($attributes as $attribute => $value) { + foreach ($attributes as $name => $value) { if (\is_array($value)) { - $this->validateAttributes($tagName, $value, $attribute.'.'); + $this->validateAttributes($tag, $value, [...$path, $name]); } elseif (!\is_scalar($value ?? '')) { - throw new InvalidArgumentException(sprintf('Tag "%s", attribute "%s" in "_defaults" must be of a scalar-type or an array of scalar-type.', $tagName, $prefix.$attribute)); + $name = implode('.', [...$path, $name]); + throw new InvalidArgumentException(sprintf('Tag "%s", attribute "%s" in "_defaults" must be of a scalar-type or an array of scalar-type.', $tag, $name)); } } } diff --git a/src/Symfony/Component/DependencyInjection/Loader/Configurator/Traits/TagTrait.php b/src/Symfony/Component/DependencyInjection/Loader/Configurator/Traits/TagTrait.php index 6c98cb76199b9..a38d04a83c505 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/Configurator/Traits/TagTrait.php +++ b/src/Symfony/Component/DependencyInjection/Loader/Configurator/Traits/TagTrait.php @@ -33,13 +33,14 @@ final public function tag(string $name, array $attributes = []): static return $this; } - private function validateAttributes(string $tagName, array $attributes, string $prefix = ''): void + private function validateAttributes(string $tag, array $attributes, array $path = []): void { - foreach ($attributes as $attribute => $value) { + foreach ($attributes as $name => $value) { if (\is_array($value)) { - $this->validateAttributes($tagName, $value, $attribute.'.'); + $this->validateAttributes($tag, $value, [...$path, $name]); } elseif (!\is_scalar($value ?? '')) { - throw new InvalidArgumentException(sprintf('A tag attribute must be of a scalar-type or an array of scalar-types for service "%s", tag "%s", attribute "%s".', $this->id, $tagName, $prefix.$attribute)); + $name = implode('.', [...$path, $name]); + throw new InvalidArgumentException(sprintf('A tag attribute must be of a scalar-type or an array of scalar-types for service "%s", tag "%s", attribute "%s".', $this->id, $tag, $name)); } } } diff --git a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php index 80a58d39f4f6b..b95e88af4dd89 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php @@ -937,13 +937,14 @@ private function checkDefinition(string $id, array $definition, string $file) } } - private function validateAttributes(string $message, array $attributes, string $prefix = ''): void + private function validateAttributes(string $message, array $attributes, array $path = []): void { - foreach ($attributes as $attribute => $value) { + foreach ($attributes as $name => $value) { if (\is_array($value)) { - $this->validateAttributes($message, $value, $attribute.'.'); + $this->validateAttributes($message, $value, [...$path, $name]); } elseif (!\is_scalar($value ?? '')) { - throw new InvalidArgumentException(sprintf($message, $prefix.$attribute)); + $name = implode('.', [...$path, $name]); + throw new InvalidArgumentException(sprintf($message, $name)); } } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/CheckDefinitionValidityPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/CheckDefinitionValidityPassTest.php index c683fdbbc118a..ed8ba2376b208 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/CheckDefinitionValidityPassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/CheckDefinitionValidityPassTest.php @@ -80,21 +80,46 @@ public function testValidTags() $container->register('b', 'class')->addTag('foo', ['bar' => null]); $container->register('c', 'class')->addTag('foo', ['bar' => 1]); $container->register('d', 'class')->addTag('foo', ['bar' => 1.1]); + $container->register('d', 'class')->addTag('foo', ['bar' => ['baz' => 'baz']]); + $container->register('e', 'class')->addTag('foo', ['deep' => ['foo' => ['bar' => 'baz']]]); $this->process($container); $this->addToAssertionCount(1); } - public function testInvalidTags() + /** + * @dataProvider provideInvalidTags + */ + public function testInvalidTags(string $name, array $attributes, string $message) { $this->expectException(RuntimeException::class); + $this->expectExceptionMessage($message); $container = new ContainerBuilder(); - $container->register('a', 'class')->addTag('foo', ['bar' => ['baz' => 'baz']]); - + $container->register('a', 'class')->addTag($name, $attributes); $this->process($container); } + public static function provideInvalidTags(): iterable + { + $message = 'A "tags" attribute must be of a scalar-type for service "a", tag "%s", attribute "%s".'; + yield 'object attribute value' => [ + 'foo', + ['bar' => new class() {}], + sprintf($message, 'foo', 'bar'), + ]; + yield 'nested object attribute value' => [ + 'foo', + ['bar' => ['baz' => new class() {}]], + sprintf($message, 'foo', 'bar.baz'), + ]; + yield 'deeply nested object attribute value' => [ + 'foo', + ['bar' => ['baz' => ['qux' => new class() {}]]], + sprintf($message, 'foo', 'bar.baz.qux'), + ]; + } + public function testDynamicPublicServiceName() { $this->expectException(EnvParameterException::class); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveNamedArgumentsPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveNamedArgumentsPassTest.php index 4e9973bb30e9c..2c28949486f24 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveNamedArgumentsPassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveNamedArgumentsPassTest.php @@ -165,7 +165,7 @@ public function testInterfaceTypedArgument() $pass = new ResolveNamedArgumentsPass(); $pass->process($container); - $this->assertSame($expected, $definition->getArgument(3)); + $this->assertSame($expected, $definition->getArgument('container')); } public function testResolvesMultipleArgumentsOfTheSameType() diff --git a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php index 9ec5d0631fe61..40040f3f4e66b 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php @@ -1829,6 +1829,33 @@ public function testFindTags() $this->assertSame(['tag1', 'tag2', 'tag3'], $container->findTags()); } + + public function testNamedArgumentAfterCompile() + { + $container = new ContainerBuilder(); + $container->register(E::class) + ->setPublic(true) + ->setArguments(['$second' => 2]); + + $container->compile(); + + $e = $container->get(E::class); + + $this->assertSame('', $e->first); + $this->assertSame(2, $e->second); + } + + public function testNamedArgumentBeforeCompile() + { + $container = new ContainerBuilder(); + $container->register(E::class, E::class) + ->setPublic(true) + ->setArguments(['$first' => 1]); + + $e = $container->get(E::class); + + $this->assertSame(1, $e->first); + } } class FooClass @@ -1857,3 +1884,15 @@ class C implements X class D implements X { } + +class E +{ + public $first; + public $second; + + public function __construct($first = '', $second = '') + { + $this->first = $first; + $this->second = $second; + } +} diff --git a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php index d752c27abecc6..1397da968571c 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php @@ -1405,7 +1405,8 @@ public function testAliasCanBeFoundInTheDumpedContainerWhenBothTheAliasAndTheSer public function testWither() { $container = new ContainerBuilder(); - $container->register(Foo::class); + $container->register(Foo::class) + ->setAutowired(true); $container ->register('wither', Wither::class) diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/autowiring_classes.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/autowiring_classes.php index 4c81725bc824f..87f5ab65a1277 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/autowiring_classes.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/autowiring_classes.php @@ -13,6 +13,13 @@ class Foo { + /** + * @required + */ + public function cloneFoo(): static + { + return clone $this; + } } class Bar diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_almost_circular_private.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_almost_circular_private.php index ec41d87a99987..bd2a0cc4e3c83 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_almost_circular_private.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_almost_circular_private.php @@ -419,17 +419,16 @@ protected function getMonologInline_LoggerService() */ protected function getPAService() { - $a = new \stdClass(); - - $b = ($this->privates['pC'] ?? $this->getPCService()); + $a = ($this->privates['pC'] ?? $this->getPCService()); if (isset($this->services['pA'])) { return $this->services['pA']; } + $b = new \stdClass(); - $this->services['pA'] = $instance = new \stdClass($a, $b); + $this->services['pA'] = $instance = new \stdClass($b, $a); - $a->d = ($this->privates['pD'] ?? $this->getPDService()); + $b->d = ($this->privates['pD'] ?? $this->getPDService()); return $instance; } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_wither.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_wither.php index 3be9833ea75ea..61ba3d17adc14 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_wither.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_wither.php @@ -53,6 +53,7 @@ protected function getWitherService() $instance = new \Symfony\Component\DependencyInjection\Tests\Compiler\Wither(); $a = new \Symfony\Component\DependencyInjection\Tests\Compiler\Foo(); + $a = $a->cloneFoo(); $instance = $instance->withFoo1($a); $this->services['wither'] = $instance = $instance->withFoo2($a); diff --git a/src/Symfony/Component/DomCrawler/LICENSE b/src/Symfony/Component/DomCrawler/LICENSE index 88bf75bb4d6a2..008370457251e 100644 --- a/src/Symfony/Component/DomCrawler/LICENSE +++ b/src/Symfony/Component/DomCrawler/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2022 Fabien Potencier +Copyright (c) 2004-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/DomCrawler/Tests/Html5ParserCrawlerTest.php b/src/Symfony/Component/DomCrawler/Tests/Html5ParserCrawlerTest.php index 7990f5bb48ff5..05a1e79b7fcbf 100644 --- a/src/Symfony/Component/DomCrawler/Tests/Html5ParserCrawlerTest.php +++ b/src/Symfony/Component/DomCrawler/Tests/Html5ParserCrawlerTest.php @@ -48,7 +48,7 @@ public function testHtml5ParserWithInvalidHeadedContent(string $content) public function validHtml5Provider(): iterable { - $html = $this->getDoctype().'

Foo

'; + $html = static::getDoctype().'

Foo

'; $BOM = \chr(0xEF).\chr(0xBB).\chr(0xBF); yield 'BOM first' => [$BOM.$html]; @@ -56,12 +56,12 @@ public function validHtml5Provider(): iterable yield 'Multiline comment' => ["".$html]; yield 'Several comments' => [' '.$html]; yield 'Whitespaces' => [' '.$html]; - yield 'All together' => [$BOM.' '.''.$html]; + yield 'All together' => [$BOM.' '.$html]; } public function invalidHtml5Provider(): iterable { - $html = $this->getDoctype().'

Foo

'; + $html = static::getDoctype().'

Foo

'; yield 'Text' => ['hello world'.$html]; yield 'Text between comments' => [' test '.$html]; diff --git a/src/Symfony/Component/Dotenv/LICENSE b/src/Symfony/Component/Dotenv/LICENSE index 7fa9539054928..f2345234aa9ea 100644 --- a/src/Symfony/Component/Dotenv/LICENSE +++ b/src/Symfony/Component/Dotenv/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2016-2022 Fabien Potencier +Copyright (c) 2016-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/ErrorHandler/LICENSE b/src/Symfony/Component/ErrorHandler/LICENSE index 9c907a46a6218..5c7ba0551cb65 100644 --- a/src/Symfony/Component/ErrorHandler/LICENSE +++ b/src/Symfony/Component/ErrorHandler/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2019-2022 Fabien Potencier +Copyright (c) 2019-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/EventDispatcher/LICENSE b/src/Symfony/Component/EventDispatcher/LICENSE index 88bf75bb4d6a2..008370457251e 100644 --- a/src/Symfony/Component/EventDispatcher/LICENSE +++ b/src/Symfony/Component/EventDispatcher/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2022 Fabien Potencier +Copyright (c) 2004-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/ExpressionLanguage/LICENSE b/src/Symfony/Component/ExpressionLanguage/LICENSE index 88bf75bb4d6a2..008370457251e 100644 --- a/src/Symfony/Component/ExpressionLanguage/LICENSE +++ b/src/Symfony/Component/ExpressionLanguage/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2022 Fabien Potencier +Copyright (c) 2004-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/ExpressionLanguage/Tests/ExpressionLanguageTest.php b/src/Symfony/Component/ExpressionLanguage/Tests/ExpressionLanguageTest.php index 2efa7a3be4722..c5dc30cbfb72a 100644 --- a/src/Symfony/Component/ExpressionLanguage/Tests/ExpressionLanguageTest.php +++ b/src/Symfony/Component/ExpressionLanguage/Tests/ExpressionLanguageTest.php @@ -121,8 +121,19 @@ public function testParseThrowsInsteadOfNotice() public function shortCircuitProviderEvaluate() { - $object = $this->getMockBuilder(\stdClass::class)->setMethods(['foo'])->getMock(); - $object->expects($this->never())->method('foo'); + $object = new class(\Closure::fromCallable([static::class, 'fail'])) { + private $fail; + + public function __construct(callable $fail) + { + $this->fail = $fail; + } + + public function foo() + { + ($this->fail)(); + } + }; return [ ['false and object.foo()', ['object' => $object], false], diff --git a/src/Symfony/Component/Filesystem/LICENSE b/src/Symfony/Component/Filesystem/LICENSE index 88bf75bb4d6a2..008370457251e 100644 --- a/src/Symfony/Component/Filesystem/LICENSE +++ b/src/Symfony/Component/Filesystem/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2022 Fabien Potencier +Copyright (c) 2004-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Filesystem/Tests/PathTest.php b/src/Symfony/Component/Filesystem/Tests/PathTest.php index 2f04c790c396a..3d0f2beb11087 100644 --- a/src/Symfony/Component/Filesystem/Tests/PathTest.php +++ b/src/Symfony/Component/Filesystem/Tests/PathTest.php @@ -458,11 +458,30 @@ public function providePathTests(): \Generator yield ['..', '/webmozart/symfony', '/webmozart']; } + private static function getPathTests(): \Generator + { + yield from [ + // relative to absolute path + ['css/style.css', '/webmozart/symfony', '/webmozart/symfony/css/style.css'], + ['../css/style.css', '/webmozart/symfony', '/webmozart/css/style.css'], + ['../../css/style.css', '/webmozart/symfony', '/css/style.css'], + + // relative to root + ['css/style.css', '/', '/css/style.css'], + ['css/style.css', 'C:', 'C:/css/style.css'], + ['css/style.css', 'C:/', 'C:/css/style.css'], + + // same sub directories in different base directories + ['../../symfony/css/style.css', '/webmozart/css', '/symfony/css/style.css'], + + ['', '/webmozart/symfony', '/webmozart/symfony'], + ['..', '/webmozart/symfony', '/webmozart'], + ]; + } + public function provideMakeAbsoluteTests(): \Generator { - foreach ($this->providePathTests() as $set) { - yield $set; - } + yield from static::getPathTests(); // collapse dots yield ['css/./style.css', '/webmozart/symfony', '/webmozart/symfony/css/style.css']; @@ -589,7 +608,7 @@ public function testMakeAbsoluteDoesNotFailIfDifferentRoot(string $basePath, str public function provideMakeRelativeTests(): \Generator { - foreach ($this->providePathTests() as $set) { + foreach (static::getPathTests() as $set) { yield [$set[2], $set[1], $set[0]]; } diff --git a/src/Symfony/Component/Finder/LICENSE b/src/Symfony/Component/Finder/LICENSE index 88bf75bb4d6a2..008370457251e 100644 --- a/src/Symfony/Component/Finder/LICENSE +++ b/src/Symfony/Component/Finder/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2022 Fabien Potencier +Copyright (c) 2004-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Finder/Tests/Iterator/DepthRangeFilterIteratorTest.php b/src/Symfony/Component/Finder/Tests/Iterator/DepthRangeFilterIteratorTest.php index e4660695c2734..69feccc0f8cc3 100644 --- a/src/Symfony/Component/Finder/Tests/Iterator/DepthRangeFilterIteratorTest.php +++ b/src/Symfony/Component/Finder/Tests/Iterator/DepthRangeFilterIteratorTest.php @@ -95,11 +95,11 @@ public function getAcceptData() ]; return [ - [0, 0, $this->toAbsolute($lessThan1)], - [0, 1, $this->toAbsolute($lessThanOrEqualTo1)], + [0, 0, static::toAbsolute($lessThan1)], + [0, 1, static::toAbsolute($lessThanOrEqualTo1)], [2, \PHP_INT_MAX, []], - [1, \PHP_INT_MAX, $this->toAbsolute($graterThanOrEqualTo1)], - [1, 1, $this->toAbsolute($equalTo1)], + [1, \PHP_INT_MAX, static::toAbsolute($graterThanOrEqualTo1)], + [1, 1, static::toAbsolute($equalTo1)], ]; } } diff --git a/src/Symfony/Component/Finder/Tests/Iterator/ExcludeDirectoryFilterIteratorTest.php b/src/Symfony/Component/Finder/Tests/Iterator/ExcludeDirectoryFilterIteratorTest.php index 2978fb0fc4339..519ff03768106 100644 --- a/src/Symfony/Component/Finder/Tests/Iterator/ExcludeDirectoryFilterIteratorTest.php +++ b/src/Symfony/Component/Finder/Tests/Iterator/ExcludeDirectoryFilterIteratorTest.php @@ -105,9 +105,9 @@ public function getAcceptData() ]; return [ - [['foo'], $this->toAbsolute($foo)], - [['fo'], $this->toAbsolute($fo)], - [['toto/'], $this->toAbsolute($toto)], + [['foo'], static::toAbsolute($foo)], + [['fo'], static::toAbsolute($fo)], + [['toto/'], static::toAbsolute($toto)], ]; } } diff --git a/src/Symfony/Component/Finder/Tests/Iterator/FileTypeFilterIteratorTest.php b/src/Symfony/Component/Finder/Tests/Iterator/FileTypeFilterIteratorTest.php index fda381b4ee8a1..3a70697a4844c 100644 --- a/src/Symfony/Component/Finder/Tests/Iterator/FileTypeFilterIteratorTest.php +++ b/src/Symfony/Component/Finder/Tests/Iterator/FileTypeFilterIteratorTest.php @@ -59,8 +59,8 @@ public function getAcceptData() ]; return [ - [FileTypeFilterIterator::ONLY_FILES, $this->toAbsolute($onlyFiles)], - [FileTypeFilterIterator::ONLY_DIRECTORIES, $this->toAbsolute($onlyDirectories)], + [FileTypeFilterIterator::ONLY_FILES, static::toAbsolute($onlyFiles)], + [FileTypeFilterIterator::ONLY_DIRECTORIES, static::toAbsolute($onlyDirectories)], ]; } } diff --git a/src/Symfony/Component/Finder/Tests/Iterator/SortableIteratorTest.php b/src/Symfony/Component/Finder/Tests/Iterator/SortableIteratorTest.php index 182e58fbfe9c7..6cc31890d5034 100644 --- a/src/Symfony/Component/Finder/Tests/Iterator/SortableIteratorTest.php +++ b/src/Symfony/Component/Finder/Tests/Iterator/SortableIteratorTest.php @@ -261,13 +261,13 @@ public function getAcceptData() ]; return [ - [SortableIterator::SORT_BY_NAME, $this->toAbsolute($sortByName)], - [SortableIterator::SORT_BY_TYPE, $this->toAbsolute($sortByType)], - [SortableIterator::SORT_BY_ACCESSED_TIME, $this->toAbsolute($sortByAccessedTime)], - [SortableIterator::SORT_BY_CHANGED_TIME, $this->toAbsolute($sortByChangedTime)], - [SortableIterator::SORT_BY_MODIFIED_TIME, $this->toAbsolute($sortByModifiedTime)], - [SortableIterator::SORT_BY_NAME_NATURAL, $this->toAbsolute($sortByNameNatural)], - [function (\SplFileInfo $a, \SplFileInfo $b) { return strcmp($a->getRealPath(), $b->getRealPath()); }, $this->toAbsolute($customComparison)], + [SortableIterator::SORT_BY_NAME, static::toAbsolute($sortByName)], + [SortableIterator::SORT_BY_TYPE, static::toAbsolute($sortByType)], + [SortableIterator::SORT_BY_ACCESSED_TIME, static::toAbsolute($sortByAccessedTime)], + [SortableIterator::SORT_BY_CHANGED_TIME, static::toAbsolute($sortByChangedTime)], + [SortableIterator::SORT_BY_MODIFIED_TIME, static::toAbsolute($sortByModifiedTime)], + [SortableIterator::SORT_BY_NAME_NATURAL, static::toAbsolute($sortByNameNatural)], + [function (\SplFileInfo $a, \SplFileInfo $b) { return strcmp($a->getRealPath(), $b->getRealPath()); }, static::toAbsolute($customComparison)], ]; } } diff --git a/src/Symfony/Component/Form/LICENSE b/src/Symfony/Component/Form/LICENSE index 88bf75bb4d6a2..008370457251e 100644 --- a/src/Symfony/Component/Form/LICENSE +++ b/src/Symfony/Component/Form/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2022 Fabien Potencier +Copyright (c) 2004-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/HtmlSanitizer/LICENSE b/src/Symfony/Component/HtmlSanitizer/LICENSE index 48d17c4fb34f1..d354b95ffee02 100644 --- a/src/Symfony/Component/HtmlSanitizer/LICENSE +++ b/src/Symfony/Component/HtmlSanitizer/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2021-2022 Fabien Potencier +Copyright (c) 2021-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/HttpClient/CurlHttpClient.php b/src/Symfony/Component/HttpClient/CurlHttpClient.php index c1e3dd9fa6e48..1fc33c7a02ff2 100644 --- a/src/Symfony/Component/HttpClient/CurlHttpClient.php +++ b/src/Symfony/Component/HttpClient/CurlHttpClient.php @@ -197,8 +197,14 @@ public function request(string $method, string $url, array $options = []): Respo if (\extension_loaded('zlib') && !isset($options['normalized_headers']['accept-encoding'])) { $options['headers'][] = 'Accept-Encoding: gzip'; // Expose only one encoding, some servers mess up when more are provided } + $body = $options['body']; - foreach ($options['headers'] as $header) { + foreach ($options['headers'] as $i => $header) { + if (\is_string($body) && '' !== $body && 0 === stripos($header, 'Content-Length: ')) { + // Let curl handle Content-Length headers + unset($options['headers'][$i]); + continue; + } if (':' === $header[-2] && \strlen($header) - 2 === strpos($header, ': ')) { // curl requires a special syntax to send empty headers $curlopts[\CURLOPT_HTTPHEADER][] = substr_replace($header, ';', -2); @@ -214,7 +220,7 @@ public function request(string $method, string $url, array $options = []): Respo } } - if (!\is_string($body = $options['body'])) { + if (!\is_string($body)) { if (\is_resource($body)) { $curlopts[\CURLOPT_INFILE] = $body; } else { @@ -226,15 +232,16 @@ public function request(string $method, string $url, array $options = []): Respo } if (isset($options['normalized_headers']['content-length'][0])) { - $curlopts[\CURLOPT_INFILESIZE] = substr($options['normalized_headers']['content-length'][0], \strlen('Content-Length: ')); - } elseif (!isset($options['normalized_headers']['transfer-encoding'])) { - $curlopts[\CURLOPT_HTTPHEADER][] = 'Transfer-Encoding: chunked'; // Enable chunked request bodies + $curlopts[\CURLOPT_INFILESIZE] = (int) substr($options['normalized_headers']['content-length'][0], \strlen('Content-Length: ')); + } + if (!isset($options['normalized_headers']['transfer-encoding'])) { + $curlopts[\CURLOPT_HTTPHEADER][] = 'Transfer-Encoding:'.(isset($curlopts[\CURLOPT_INFILESIZE]) ? '' : ' chunked'); } if ('POST' !== $method) { $curlopts[\CURLOPT_UPLOAD] = true; - if (!isset($options['normalized_headers']['content-type'])) { + if (!isset($options['normalized_headers']['content-type']) && 0 !== ($curlopts[\CURLOPT_INFILESIZE] ?? null)) { $curlopts[\CURLOPT_HTTPHEADER][] = 'Content-Type: application/x-www-form-urlencoded'; } } diff --git a/src/Symfony/Component/HttpClient/DataCollector/HttpClientDataCollector.php b/src/Symfony/Component/HttpClient/DataCollector/HttpClientDataCollector.php index 781be5df1b4a3..4878dba990372 100644 --- a/src/Symfony/Component/HttpClient/DataCollector/HttpClientDataCollector.php +++ b/src/Symfony/Component/HttpClient/DataCollector/HttpClientDataCollector.php @@ -37,6 +37,10 @@ public function registerClient(string $name, TraceableHttpClient $client) } public function collect(Request $request, Response $response, \Throwable $exception = null) + { + } + + public function lateCollect() { $this->reset(); @@ -50,12 +54,7 @@ public function collect(Request $request, Response $response, \Throwable $except $this->data['request_count'] += \count($traces); $this->data['error_count'] += $errorCount; - } - } - public function lateCollect() - { - foreach ($this->clients as $client) { $client->reset(); } } diff --git a/src/Symfony/Component/HttpClient/LICENSE b/src/Symfony/Component/HttpClient/LICENSE index 74cdc2dbf6dbe..99757d517117d 100644 --- a/src/Symfony/Component/HttpClient/LICENSE +++ b/src/Symfony/Component/HttpClient/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2018-2022 Fabien Potencier +Copyright (c) 2018-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/HttpClient/Tests/DataCollector/HttpClientDataCollectorTest.php b/src/Symfony/Component/HttpClient/Tests/DataCollector/HttpClientDataCollectorTest.php index 46a1177befeaa..f84c043fdcc80 100755 --- a/src/Symfony/Component/HttpClient/Tests/DataCollector/HttpClientDataCollectorTest.php +++ b/src/Symfony/Component/HttpClient/Tests/DataCollector/HttpClientDataCollectorTest.php @@ -15,8 +15,6 @@ use Symfony\Component\HttpClient\DataCollector\HttpClientDataCollector; use Symfony\Component\HttpClient\NativeHttpClient; use Symfony\Component\HttpClient\TraceableHttpClient; -use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\HttpFoundation\Response; use Symfony\Contracts\HttpClient\Test\TestHttpServer; class HttpClientDataCollectorTest extends TestCase @@ -50,7 +48,7 @@ public function testItCollectsRequestCount() $sut->registerClient('http_client2', $httpClient2); $sut->registerClient('http_client3', $httpClient3); $this->assertEquals(0, $sut->getRequestCount()); - $sut->collect(new Request(), new Response()); + $sut->lateCollect(); $this->assertEquals(3, $sut->getRequestCount()); } @@ -79,7 +77,7 @@ public function testItCollectsErrorCount() $sut->registerClient('http_client2', $httpClient2); $sut->registerClient('http_client3', $httpClient3); $this->assertEquals(0, $sut->getErrorCount()); - $sut->collect(new Request(), new Response()); + $sut->lateCollect(); $this->assertEquals(1, $sut->getErrorCount()); } @@ -108,7 +106,7 @@ public function testItCollectsErrorCountByClient() $sut->registerClient('http_client2', $httpClient2); $sut->registerClient('http_client3', $httpClient3); $this->assertEquals([], $sut->getClients()); - $sut->collect(new Request(), new Response()); + $sut->lateCollect(); $collectedData = $sut->getClients(); $this->assertEquals(0, $collectedData['http_client1']['error_count']); $this->assertEquals(1, $collectedData['http_client2']['error_count']); @@ -140,7 +138,7 @@ public function testItCollectsTracesByClient() $sut->registerClient('http_client2', $httpClient2); $sut->registerClient('http_client3', $httpClient3); $this->assertEquals([], $sut->getClients()); - $sut->collect(new Request(), new Response()); + $sut->lateCollect(); $collectedData = $sut->getClients(); $this->assertCount(2, $collectedData['http_client1']['traces']); $this->assertCount(1, $collectedData['http_client2']['traces']); @@ -157,7 +155,7 @@ public function testItIsEmptyAfterReset() ]); $sut = new HttpClientDataCollector(); $sut->registerClient('http_client1', $httpClient1); - $sut->collect(new Request(), new Response()); + $sut->lateCollect(); $collectedData = $sut->getClients(); $this->assertCount(1, $collectedData['http_client1']['traces']); $sut->reset(); @@ -174,7 +172,7 @@ public function testItGeneratesCurlCommandsAsExpected(array $request, string $ex { $sut = new HttpClientDataCollector(); $sut->registerClient('http_client', $this->httpClientThatHasTracedRequests([$request])); - $sut->collect(new Request(), new Response()); + $sut->lateCollect(); $collectedData = $sut->getClients(); self::assertCount(1, $collectedData['http_client']['traces']); $curlCommand = $collectedData['http_client']['traces'][0]['curlCommand']; @@ -358,7 +356,7 @@ public function testItDoesNotFollowRedirectionsWhenGeneratingCurlCommands() ], ], ])); - $sut->collect(new Request(), new Response()); + $sut->lateCollect(); $collectedData = $sut->getClients(); self::assertCount(1, $collectedData['http_client']['traces']); $curlCommand = $collectedData['http_client']['traces'][0]['curlCommand']; @@ -388,7 +386,7 @@ public function testItDoesNotGeneratesCurlCommandsForUnsupportedBodyType() ], ], ])); - $sut->collect(new Request(), new Response()); + $sut->lateCollect(); $collectedData = $sut->getClients(); self::assertCount(1, $collectedData['http_client']['traces']); $curlCommand = $collectedData['http_client']['traces'][0]['curlCommand']; @@ -410,7 +408,7 @@ public function testItDoesNotGeneratesCurlCommandsForNotEncodableBody() ], ], ])); - $sut->collect(new Request(), new Response()); + $sut->lateCollect(); $collectedData = $sut->getClients(); self::assertCount(1, $collectedData['http_client']['traces']); $curlCommand = $collectedData['http_client']['traces'][0]['curlCommand']; @@ -432,7 +430,7 @@ public function testItDoesNotGeneratesCurlCommandsForTooBigData() ], ], ])); - $sut->collect(new Request(), new Response()); + $sut->lateCollect(); $collectedData = $sut->getClients(); self::assertCount(1, $collectedData['http_client']['traces']); $curlCommand = $collectedData['http_client']['traces'][0]['curlCommand']; diff --git a/src/Symfony/Component/HttpFoundation/LICENSE b/src/Symfony/Component/HttpFoundation/LICENSE index 88bf75bb4d6a2..008370457251e 100644 --- a/src/Symfony/Component/HttpFoundation/LICENSE +++ b/src/Symfony/Component/HttpFoundation/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2022 Fabien Potencier +Copyright (c) 2004-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index 846e12d284216..f6b86a8e3aef1 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -75,11 +75,11 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl */ private static array $freshCache = []; - public const VERSION = '6.2.4'; - public const VERSION_ID = 60204; + public const VERSION = '6.2.5'; + public const VERSION_ID = 60205; public const MAJOR_VERSION = 6; public const MINOR_VERSION = 2; - public const RELEASE_VERSION = 4; + public const RELEASE_VERSION = 5; public const EXTRA_VERSION = ''; public const END_OF_MAINTENANCE = '07/2023'; diff --git a/src/Symfony/Component/HttpKernel/LICENSE b/src/Symfony/Component/HttpKernel/LICENSE index 88bf75bb4d6a2..008370457251e 100644 --- a/src/Symfony/Component/HttpKernel/LICENSE +++ b/src/Symfony/Component/HttpKernel/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2022 Fabien Potencier +Copyright (c) 2004-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/HttpKernel/Tests/Controller/ArgumentResolver/DateTimeValueResolverTest.php b/src/Symfony/Component/HttpKernel/Tests/Controller/ArgumentResolver/DateTimeValueResolverTest.php index 770324d59b7e0..982d48231f80e 100644 --- a/src/Symfony/Component/HttpKernel/Tests/Controller/ArgumentResolver/DateTimeValueResolverTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/Controller/ArgumentResolver/DateTimeValueResolverTest.php @@ -35,8 +35,8 @@ protected function tearDown(): void public static function getTimeZones() { yield ['UTC']; - yield ['Etc/GMT+9']; - yield ['Etc/GMT-14']; + yield ['Pacific/Honolulu']; + yield ['America/Toronto']; } public static function getClasses() diff --git a/src/Symfony/Component/HttpKernel/Tests/EventListener/ErrorListenerTest.php b/src/Symfony/Component/HttpKernel/Tests/EventListener/ErrorListenerTest.php index e988fe6914956..e62b80c79647d 100644 --- a/src/Symfony/Component/HttpKernel/Tests/EventListener/ErrorListenerTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/EventListener/ErrorListenerTest.php @@ -207,7 +207,7 @@ public function controllerProvider() }]; yield [function ($exception) { - $this->assertInstanceOf(FlattenException::class, $exception); + static::assertInstanceOf(FlattenException::class, $exception); return new Response('OK: '.$exception->getMessage()); }]; diff --git a/src/Symfony/Component/Intl/LICENSE b/src/Symfony/Component/Intl/LICENSE index 88bf75bb4d6a2..008370457251e 100644 --- a/src/Symfony/Component/Intl/LICENSE +++ b/src/Symfony/Component/Intl/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2022 Fabien Potencier +Copyright (c) 2004-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Intl/Resources/emoji/Makefile b/src/Symfony/Component/Intl/Resources/emoji/Makefile index 8eb0a3cc70bea..8e3e900f29b4b 100644 --- a/src/Symfony/Component/Intl/Resources/emoji/Makefile +++ b/src/Symfony/Component/Intl/Resources/emoji/Makefile @@ -5,6 +5,7 @@ update: ## Update sources @composer update @curl https://api.github.com/emojis > vendor/github-emojis.json @curl https://raw.githubusercontent.com/iamcal/emoji-data/master/emoji.json > vendor/slack-emojis.json + @curl https://unicode.org/Public/emoji/latest/emoji-test.txt > vendor/emoji-test.txt build: ## Build rules @./build.php diff --git a/src/Symfony/Component/Intl/Resources/emoji/README.md b/src/Symfony/Component/Intl/Resources/emoji/README.md index 96acc9e872db2..adf627cad4c6a 100644 --- a/src/Symfony/Component/Intl/Resources/emoji/README.md +++ b/src/Symfony/Component/Intl/Resources/emoji/README.md @@ -6,11 +6,17 @@ This folder contains the tool to build all transliterator rules. * composer * PHP +* curl +* make ## Update the rules To update the rules, you need to update the version of `unicode-org/cldr` in the -`composer.json` file, then run `make update`. +`composer.json` file, then run: + +```bash +make update +``` Finally, run the following command: diff --git a/src/Symfony/Component/Intl/Resources/emoji/build.php b/src/Symfony/Component/Intl/Resources/emoji/build.php index cf34ea11baf2c..34a23d8790211 100755 --- a/src/Symfony/Component/Intl/Resources/emoji/build.php +++ b/src/Symfony/Component/Intl/Resources/emoji/build.php @@ -28,7 +28,7 @@ final class Builder public static function getEmojisCodePoints(): array { - $lines = file(__DIR__.'/vendor/unicode-org/cldr/tools/cldr-code/src/main/resources/org/unicode/cldr/util/data/emoji/emoji-test.txt'); + $lines = file(__DIR__.'/vendor/emoji-test.txt'); $emojisCodePoints = []; foreach ($lines as $line) { diff --git a/src/Symfony/Component/Ldap/LICENSE b/src/Symfony/Component/Ldap/LICENSE index 88bf75bb4d6a2..008370457251e 100644 --- a/src/Symfony/Component/Ldap/LICENSE +++ b/src/Symfony/Component/Ldap/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2022 Fabien Potencier +Copyright (c) 2004-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Lock/LICENSE b/src/Symfony/Component/Lock/LICENSE index 7fa9539054928..f2345234aa9ea 100644 --- a/src/Symfony/Component/Lock/LICENSE +++ b/src/Symfony/Component/Lock/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2016-2022 Fabien Potencier +Copyright (c) 2016-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Mailer/Bridge/Amazon/LICENSE b/src/Symfony/Component/Mailer/Bridge/Amazon/LICENSE index 9c907a46a6218..5c7ba0551cb65 100644 --- a/src/Symfony/Component/Mailer/Bridge/Amazon/LICENSE +++ b/src/Symfony/Component/Mailer/Bridge/Amazon/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2019-2022 Fabien Potencier +Copyright (c) 2019-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Mailer/Bridge/Google/LICENSE b/src/Symfony/Component/Mailer/Bridge/Google/LICENSE index 9c907a46a6218..5c7ba0551cb65 100644 --- a/src/Symfony/Component/Mailer/Bridge/Google/LICENSE +++ b/src/Symfony/Component/Mailer/Bridge/Google/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2019-2022 Fabien Potencier +Copyright (c) 2019-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Mailer/Bridge/Infobip/LICENSE b/src/Symfony/Component/Mailer/Bridge/Infobip/LICENSE index 9c907a46a6218..5c7ba0551cb65 100644 --- a/src/Symfony/Component/Mailer/Bridge/Infobip/LICENSE +++ b/src/Symfony/Component/Mailer/Bridge/Infobip/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2019-2022 Fabien Potencier +Copyright (c) 2019-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Mailer/Bridge/MailPace/LICENSE b/src/Symfony/Component/Mailer/Bridge/MailPace/LICENSE index 0ece8964f767d..074eb2b39259e 100644 --- a/src/Symfony/Component/Mailer/Bridge/MailPace/LICENSE +++ b/src/Symfony/Component/Mailer/Bridge/MailPace/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2022 Fabien Potencier +Copyright (c) 2022-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Mailer/Bridge/Mailchimp/LICENSE b/src/Symfony/Component/Mailer/Bridge/Mailchimp/LICENSE index 9c907a46a6218..5c7ba0551cb65 100644 --- a/src/Symfony/Component/Mailer/Bridge/Mailchimp/LICENSE +++ b/src/Symfony/Component/Mailer/Bridge/Mailchimp/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2019-2022 Fabien Potencier +Copyright (c) 2019-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Mailer/Bridge/Mailgun/LICENSE b/src/Symfony/Component/Mailer/Bridge/Mailgun/LICENSE index 9c907a46a6218..5c7ba0551cb65 100644 --- a/src/Symfony/Component/Mailer/Bridge/Mailgun/LICENSE +++ b/src/Symfony/Component/Mailer/Bridge/Mailgun/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2019-2022 Fabien Potencier +Copyright (c) 2019-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Mailer/Bridge/Mailjet/LICENSE b/src/Symfony/Component/Mailer/Bridge/Mailjet/LICENSE index 9c907a46a6218..5c7ba0551cb65 100644 --- a/src/Symfony/Component/Mailer/Bridge/Mailjet/LICENSE +++ b/src/Symfony/Component/Mailer/Bridge/Mailjet/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2019-2022 Fabien Potencier +Copyright (c) 2019-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Mailer/Bridge/OhMySmtp/LICENSE b/src/Symfony/Component/Mailer/Bridge/OhMySmtp/LICENSE index 48d17c4fb34f1..d354b95ffee02 100644 --- a/src/Symfony/Component/Mailer/Bridge/OhMySmtp/LICENSE +++ b/src/Symfony/Component/Mailer/Bridge/OhMySmtp/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2021-2022 Fabien Potencier +Copyright (c) 2021-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Mailer/Bridge/Postmark/LICENSE b/src/Symfony/Component/Mailer/Bridge/Postmark/LICENSE index 9c907a46a6218..5c7ba0551cb65 100644 --- a/src/Symfony/Component/Mailer/Bridge/Postmark/LICENSE +++ b/src/Symfony/Component/Mailer/Bridge/Postmark/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2019-2022 Fabien Potencier +Copyright (c) 2019-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Mailer/Bridge/Sendgrid/LICENSE b/src/Symfony/Component/Mailer/Bridge/Sendgrid/LICENSE index 9c907a46a6218..5c7ba0551cb65 100644 --- a/src/Symfony/Component/Mailer/Bridge/Sendgrid/LICENSE +++ b/src/Symfony/Component/Mailer/Bridge/Sendgrid/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2019-2022 Fabien Potencier +Copyright (c) 2019-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Mailer/Bridge/Sendinblue/LICENSE b/src/Symfony/Component/Mailer/Bridge/Sendinblue/LICENSE index 9c907a46a6218..5c7ba0551cb65 100644 --- a/src/Symfony/Component/Mailer/Bridge/Sendinblue/LICENSE +++ b/src/Symfony/Component/Mailer/Bridge/Sendinblue/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2019-2022 Fabien Potencier +Copyright (c) 2019-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Mailer/LICENSE b/src/Symfony/Component/Mailer/LICENSE index 9c907a46a6218..5c7ba0551cb65 100644 --- a/src/Symfony/Component/Mailer/LICENSE +++ b/src/Symfony/Component/Mailer/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2019-2022 Fabien Potencier +Copyright (c) 2019-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Mailer/composer.json b/src/Symfony/Component/Mailer/composer.json index e83a4aade2ee9..ded4309c531c3 100644 --- a/src/Symfony/Component/Mailer/composer.json +++ b/src/Symfony/Component/Mailer/composer.json @@ -17,7 +17,7 @@ ], "require": { "php": ">=8.1", - "egulias/email-validator": "^2.1.10|^3", + "egulias/email-validator": "^2.1.10|^3|^4", "psr/event-dispatcher": "^1", "psr/log": "^1|^2|^3", "symfony/event-dispatcher": "^5.4|^6.0", diff --git a/src/Symfony/Component/Messenger/Bridge/AmazonSqs/LICENSE b/src/Symfony/Component/Messenger/Bridge/AmazonSqs/LICENSE index 406242ff28554..0f262c225767a 100644 --- a/src/Symfony/Component/Messenger/Bridge/AmazonSqs/LICENSE +++ b/src/Symfony/Component/Messenger/Bridge/AmazonSqs/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2020-2022 Fabien Potencier +Copyright (c) 2020-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Messenger/Bridge/Amqp/LICENSE b/src/Symfony/Component/Messenger/Bridge/Amqp/LICENSE index 74cdc2dbf6dbe..99757d517117d 100644 --- a/src/Symfony/Component/Messenger/Bridge/Amqp/LICENSE +++ b/src/Symfony/Component/Messenger/Bridge/Amqp/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2018-2022 Fabien Potencier +Copyright (c) 2018-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Messenger/Bridge/Beanstalkd/LICENSE b/src/Symfony/Component/Messenger/Bridge/Beanstalkd/LICENSE index 74cdc2dbf6dbe..99757d517117d 100644 --- a/src/Symfony/Component/Messenger/Bridge/Beanstalkd/LICENSE +++ b/src/Symfony/Component/Messenger/Bridge/Beanstalkd/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2018-2022 Fabien Potencier +Copyright (c) 2018-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Messenger/Bridge/Doctrine/LICENSE b/src/Symfony/Component/Messenger/Bridge/Doctrine/LICENSE index 74cdc2dbf6dbe..99757d517117d 100644 --- a/src/Symfony/Component/Messenger/Bridge/Doctrine/LICENSE +++ b/src/Symfony/Component/Messenger/Bridge/Doctrine/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2018-2022 Fabien Potencier +Copyright (c) 2018-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Messenger/Bridge/Redis/LICENSE b/src/Symfony/Component/Messenger/Bridge/Redis/LICENSE index 74cdc2dbf6dbe..99757d517117d 100644 --- a/src/Symfony/Component/Messenger/Bridge/Redis/LICENSE +++ b/src/Symfony/Component/Messenger/Bridge/Redis/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2018-2022 Fabien Potencier +Copyright (c) 2018-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Messenger/LICENSE b/src/Symfony/Component/Messenger/LICENSE index 74cdc2dbf6dbe..99757d517117d 100644 --- a/src/Symfony/Component/Messenger/LICENSE +++ b/src/Symfony/Component/Messenger/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2018-2022 Fabien Potencier +Copyright (c) 2018-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Messenger/README.md b/src/Symfony/Component/Messenger/README.md index 02fd6b5081e76..8cd03f157aec0 100644 --- a/src/Symfony/Component/Messenger/README.md +++ b/src/Symfony/Component/Messenger/README.md @@ -7,7 +7,7 @@ other applications or via message queues. Sponsor ------- -The Messenger component for Symfony 6.1 is [backed][1] by [SensioLabs][2]. +The Messenger component for Symfony 6.2 is [backed][1] by [SensioLabs][2]. As the creator of Symfony, SensioLabs supports companies using Symfony, with an offering encompassing consultancy, expertise, services, training, and technical diff --git a/src/Symfony/Component/Messenger/Tests/Middleware/HandleMessageMiddlewareTest.php b/src/Symfony/Component/Messenger/Tests/Middleware/HandleMessageMiddlewareTest.php index a732d1aecd7e1..b2af3a914a3b5 100644 --- a/src/Symfony/Component/Messenger/Tests/Middleware/HandleMessageMiddlewareTest.php +++ b/src/Symfony/Component/Messenger/Tests/Middleware/HandleMessageMiddlewareTest.php @@ -70,16 +70,28 @@ public function testItAddsHandledStamps(array $handlers, array $expectedStamps, public function itAddsHandledStampsProvider(): iterable { - $first = $this->createPartialMock(HandleMessageMiddlewareTestCallable::class, ['__invoke']); - $first->method('__invoke')->willReturn('first result'); + $first = new class() extends HandleMessageMiddlewareTestCallable { + public function __invoke() + { + return 'first result'; + } + }; $firstClass = $first::class; - $second = $this->createPartialMock(HandleMessageMiddlewareTestCallable::class, ['__invoke']); - $second->method('__invoke')->willReturn(null); + $second = new class() extends HandleMessageMiddlewareTestCallable { + public function __invoke() + { + return null; + } + }; $secondClass = $second::class; - $failing = $this->createPartialMock(HandleMessageMiddlewareTestCallable::class, ['__invoke']); - $failing->method('__invoke')->will($this->throwException(new \Exception('handler failed.'))); + $failing = new class() extends HandleMessageMiddlewareTestCallable { + public function __invoke() + { + throw new \Exception('handler failed.'); + } + }; yield 'A stamp is added' => [ [$first], diff --git a/src/Symfony/Component/Mime/LICENSE b/src/Symfony/Component/Mime/LICENSE index 298be14166c20..58b42bc8a98fe 100644 --- a/src/Symfony/Component/Mime/LICENSE +++ b/src/Symfony/Component/Mime/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2010-2022 Fabien Potencier +Copyright (c) 2010-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Mime/composer.json b/src/Symfony/Component/Mime/composer.json index 7c6719f38222d..adaf43852f9fa 100644 --- a/src/Symfony/Component/Mime/composer.json +++ b/src/Symfony/Component/Mime/composer.json @@ -21,7 +21,7 @@ "symfony/polyfill-mbstring": "^1.0" }, "require-dev": { - "egulias/email-validator": "^2.1.10|^3.1", + "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": "^5.4|^6.0", diff --git a/src/Symfony/Component/Notifier/Bridge/AllMySms/LICENSE b/src/Symfony/Component/Notifier/Bridge/AllMySms/LICENSE index 48d17c4fb34f1..d354b95ffee02 100644 --- a/src/Symfony/Component/Notifier/Bridge/AllMySms/LICENSE +++ b/src/Symfony/Component/Notifier/Bridge/AllMySms/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2021-2022 Fabien Potencier +Copyright (c) 2021-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Notifier/Bridge/AmazonSns/LICENSE b/src/Symfony/Component/Notifier/Bridge/AmazonSns/LICENSE index 48d17c4fb34f1..d354b95ffee02 100644 --- a/src/Symfony/Component/Notifier/Bridge/AmazonSns/LICENSE +++ b/src/Symfony/Component/Notifier/Bridge/AmazonSns/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2021-2022 Fabien Potencier +Copyright (c) 2021-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Notifier/Bridge/Chatwork/LICENSE b/src/Symfony/Component/Notifier/Bridge/Chatwork/LICENSE index 0ece8964f767d..074eb2b39259e 100644 --- a/src/Symfony/Component/Notifier/Bridge/Chatwork/LICENSE +++ b/src/Symfony/Component/Notifier/Bridge/Chatwork/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2022 Fabien Potencier +Copyright (c) 2022-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Notifier/Bridge/Clickatell/LICENSE b/src/Symfony/Component/Notifier/Bridge/Clickatell/LICENSE index 48d17c4fb34f1..d354b95ffee02 100644 --- a/src/Symfony/Component/Notifier/Bridge/Clickatell/LICENSE +++ b/src/Symfony/Component/Notifier/Bridge/Clickatell/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2021-2022 Fabien Potencier +Copyright (c) 2021-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Notifier/Bridge/ContactEveryone/LICENSE b/src/Symfony/Component/Notifier/Bridge/ContactEveryone/LICENSE index 0ece8964f767d..074eb2b39259e 100644 --- a/src/Symfony/Component/Notifier/Bridge/ContactEveryone/LICENSE +++ b/src/Symfony/Component/Notifier/Bridge/ContactEveryone/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2022 Fabien Potencier +Copyright (c) 2022-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Notifier/Bridge/Discord/LICENSE b/src/Symfony/Component/Notifier/Bridge/Discord/LICENSE index 9c907a46a6218..5c7ba0551cb65 100644 --- a/src/Symfony/Component/Notifier/Bridge/Discord/LICENSE +++ b/src/Symfony/Component/Notifier/Bridge/Discord/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2019-2022 Fabien Potencier +Copyright (c) 2019-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Notifier/Bridge/Engagespot/LICENSE b/src/Symfony/Component/Notifier/Bridge/Engagespot/LICENSE index 0ece8964f767d..074eb2b39259e 100644 --- a/src/Symfony/Component/Notifier/Bridge/Engagespot/LICENSE +++ b/src/Symfony/Component/Notifier/Bridge/Engagespot/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2022 Fabien Potencier +Copyright (c) 2022-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Notifier/Bridge/Esendex/LICENSE b/src/Symfony/Component/Notifier/Bridge/Esendex/LICENSE index 9c907a46a6218..5c7ba0551cb65 100644 --- a/src/Symfony/Component/Notifier/Bridge/Esendex/LICENSE +++ b/src/Symfony/Component/Notifier/Bridge/Esendex/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2019-2022 Fabien Potencier +Copyright (c) 2019-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Notifier/Bridge/Expo/LICENSE b/src/Symfony/Component/Notifier/Bridge/Expo/LICENSE index 48d17c4fb34f1..d354b95ffee02 100644 --- a/src/Symfony/Component/Notifier/Bridge/Expo/LICENSE +++ b/src/Symfony/Component/Notifier/Bridge/Expo/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2021-2022 Fabien Potencier +Copyright (c) 2021-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Notifier/Bridge/FakeChat/LICENSE b/src/Symfony/Component/Notifier/Bridge/FakeChat/LICENSE index 48d17c4fb34f1..d354b95ffee02 100644 --- a/src/Symfony/Component/Notifier/Bridge/FakeChat/LICENSE +++ b/src/Symfony/Component/Notifier/Bridge/FakeChat/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2021-2022 Fabien Potencier +Copyright (c) 2021-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Notifier/Bridge/FakeSms/LICENSE b/src/Symfony/Component/Notifier/Bridge/FakeSms/LICENSE index 48d17c4fb34f1..d354b95ffee02 100644 --- a/src/Symfony/Component/Notifier/Bridge/FakeSms/LICENSE +++ b/src/Symfony/Component/Notifier/Bridge/FakeSms/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2021-2022 Fabien Potencier +Copyright (c) 2021-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Notifier/Bridge/Firebase/LICENSE b/src/Symfony/Component/Notifier/Bridge/Firebase/LICENSE index 9c907a46a6218..5c7ba0551cb65 100644 --- a/src/Symfony/Component/Notifier/Bridge/Firebase/LICENSE +++ b/src/Symfony/Component/Notifier/Bridge/Firebase/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2019-2022 Fabien Potencier +Copyright (c) 2019-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Notifier/Bridge/FortySixElks/LICENSE b/src/Symfony/Component/Notifier/Bridge/FortySixElks/LICENSE index 0ece8964f767d..074eb2b39259e 100644 --- a/src/Symfony/Component/Notifier/Bridge/FortySixElks/LICENSE +++ b/src/Symfony/Component/Notifier/Bridge/FortySixElks/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2022 Fabien Potencier +Copyright (c) 2022-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Notifier/Bridge/FreeMobile/LICENSE b/src/Symfony/Component/Notifier/Bridge/FreeMobile/LICENSE index 406242ff28554..0f262c225767a 100644 --- a/src/Symfony/Component/Notifier/Bridge/FreeMobile/LICENSE +++ b/src/Symfony/Component/Notifier/Bridge/FreeMobile/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2020-2022 Fabien Potencier +Copyright (c) 2020-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Notifier/Bridge/GatewayApi/LICENSE b/src/Symfony/Component/Notifier/Bridge/GatewayApi/LICENSE index 48d17c4fb34f1..d354b95ffee02 100644 --- a/src/Symfony/Component/Notifier/Bridge/GatewayApi/LICENSE +++ b/src/Symfony/Component/Notifier/Bridge/GatewayApi/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2021-2022 Fabien Potencier +Copyright (c) 2021-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Notifier/Bridge/Gitter/LICENSE b/src/Symfony/Component/Notifier/Bridge/Gitter/LICENSE index 48d17c4fb34f1..d354b95ffee02 100644 --- a/src/Symfony/Component/Notifier/Bridge/Gitter/LICENSE +++ b/src/Symfony/Component/Notifier/Bridge/Gitter/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2021-2022 Fabien Potencier +Copyright (c) 2021-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Notifier/Bridge/GoogleChat/LICENSE b/src/Symfony/Component/Notifier/Bridge/GoogleChat/LICENSE index 9c907a46a6218..5c7ba0551cb65 100644 --- a/src/Symfony/Component/Notifier/Bridge/GoogleChat/LICENSE +++ b/src/Symfony/Component/Notifier/Bridge/GoogleChat/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2019-2022 Fabien Potencier +Copyright (c) 2019-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Notifier/Bridge/Infobip/LICENSE b/src/Symfony/Component/Notifier/Bridge/Infobip/LICENSE index 9c907a46a6218..5c7ba0551cb65 100644 --- a/src/Symfony/Component/Notifier/Bridge/Infobip/LICENSE +++ b/src/Symfony/Component/Notifier/Bridge/Infobip/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2019-2022 Fabien Potencier +Copyright (c) 2019-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Notifier/Bridge/Iqsms/LICENSE b/src/Symfony/Component/Notifier/Bridge/Iqsms/LICENSE index 406242ff28554..0f262c225767a 100644 --- a/src/Symfony/Component/Notifier/Bridge/Iqsms/LICENSE +++ b/src/Symfony/Component/Notifier/Bridge/Iqsms/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2020-2022 Fabien Potencier +Copyright (c) 2020-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Notifier/Bridge/KazInfoTeh/LICENSE b/src/Symfony/Component/Notifier/Bridge/KazInfoTeh/LICENSE index 48d17c4fb34f1..d354b95ffee02 100644 --- a/src/Symfony/Component/Notifier/Bridge/KazInfoTeh/LICENSE +++ b/src/Symfony/Component/Notifier/Bridge/KazInfoTeh/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2021-2022 Fabien Potencier +Copyright (c) 2021-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Notifier/Bridge/LightSms/LICENSE b/src/Symfony/Component/Notifier/Bridge/LightSms/LICENSE index 48d17c4fb34f1..d354b95ffee02 100644 --- a/src/Symfony/Component/Notifier/Bridge/LightSms/LICENSE +++ b/src/Symfony/Component/Notifier/Bridge/LightSms/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2021-2022 Fabien Potencier +Copyright (c) 2021-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Notifier/Bridge/LinkedIn/LICENSE b/src/Symfony/Component/Notifier/Bridge/LinkedIn/LICENSE index 406242ff28554..0f262c225767a 100644 --- a/src/Symfony/Component/Notifier/Bridge/LinkedIn/LICENSE +++ b/src/Symfony/Component/Notifier/Bridge/LinkedIn/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2020-2022 Fabien Potencier +Copyright (c) 2020-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Notifier/Bridge/Mailjet/LICENSE b/src/Symfony/Component/Notifier/Bridge/Mailjet/LICENSE index 48d17c4fb34f1..d354b95ffee02 100644 --- a/src/Symfony/Component/Notifier/Bridge/Mailjet/LICENSE +++ b/src/Symfony/Component/Notifier/Bridge/Mailjet/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2021-2022 Fabien Potencier +Copyright (c) 2021-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Notifier/Bridge/Mattermost/LICENSE b/src/Symfony/Component/Notifier/Bridge/Mattermost/LICENSE index 406242ff28554..0f262c225767a 100644 --- a/src/Symfony/Component/Notifier/Bridge/Mattermost/LICENSE +++ b/src/Symfony/Component/Notifier/Bridge/Mattermost/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2020-2022 Fabien Potencier +Copyright (c) 2020-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Notifier/Bridge/Mercure/LICENSE b/src/Symfony/Component/Notifier/Bridge/Mercure/LICENSE index 48d17c4fb34f1..d354b95ffee02 100644 --- a/src/Symfony/Component/Notifier/Bridge/Mercure/LICENSE +++ b/src/Symfony/Component/Notifier/Bridge/Mercure/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2021-2022 Fabien Potencier +Copyright (c) 2021-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Notifier/Bridge/Mercure/Tests/stella-maris-clock/ClockInterface.php b/src/Symfony/Component/Notifier/Bridge/Mercure/Tests/stella-maris-clock/ClockInterface.php deleted file mode 100644 index d8b2e86692260..0000000000000 --- a/src/Symfony/Component/Notifier/Bridge/Mercure/Tests/stella-maris-clock/ClockInterface.php +++ /dev/null @@ -1,23 +0,0 @@ - and ClockInterfaceContributors - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - */ - -namespace StellaMaris\Clock; - -use DateTimeImmutable; - -interface ClockInterface -{ - /** - * Return the current point in time as a DateTimeImmutable object - */ - public function now() : DateTimeImmutable; -} diff --git a/src/Symfony/Component/Notifier/Bridge/Mercure/Tests/stella-maris-clock/composer.json b/src/Symfony/Component/Notifier/Bridge/Mercure/Tests/stella-maris-clock/composer.json deleted file mode 100644 index fb838caed6e88..0000000000000 --- a/src/Symfony/Component/Notifier/Bridge/Mercure/Tests/stella-maris-clock/composer.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "name": "stella-maris/clock", - "description": "A local fork to workaround gitlab failing to serve the package reliably", - "homepage": "https://gitlab.com/stella-maris/clock", - "license": "MIT", - "authors": [ - { - "name": "Andreas Heigl", - "role": "Maintainer" - } - ], - "autoload": { - "psr-4": { - "StellaMaris\\Clock\\": "" - } - } -} diff --git a/src/Symfony/Component/Notifier/Bridge/Mercure/composer.json b/src/Symfony/Component/Notifier/Bridge/Mercure/composer.json index 05ae5933c1c5c..fffab4b7e16d6 100644 --- a/src/Symfony/Component/Notifier/Bridge/Mercure/composer.json +++ b/src/Symfony/Component/Notifier/Bridge/Mercure/composer.json @@ -28,16 +28,5 @@ "/Tests/" ] }, - "repositories": [ - { - "type": "path", - "url": "Tests/stella-maris-clock", - "options": { - "versions": { - "stella-maris/clock": "0.1.x-dev" - } - } - } - ], "minimum-stability": "dev" } diff --git a/src/Symfony/Component/Notifier/Bridge/MessageBird/LICENSE b/src/Symfony/Component/Notifier/Bridge/MessageBird/LICENSE index 48d17c4fb34f1..d354b95ffee02 100644 --- a/src/Symfony/Component/Notifier/Bridge/MessageBird/LICENSE +++ b/src/Symfony/Component/Notifier/Bridge/MessageBird/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2021-2022 Fabien Potencier +Copyright (c) 2021-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Notifier/Bridge/MessageMedia/LICENSE b/src/Symfony/Component/Notifier/Bridge/MessageMedia/LICENSE index 48d17c4fb34f1..d354b95ffee02 100644 --- a/src/Symfony/Component/Notifier/Bridge/MessageMedia/LICENSE +++ b/src/Symfony/Component/Notifier/Bridge/MessageMedia/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2021-2022 Fabien Potencier +Copyright (c) 2021-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Notifier/Bridge/MicrosoftTeams/LICENSE b/src/Symfony/Component/Notifier/Bridge/MicrosoftTeams/LICENSE index 48d17c4fb34f1..d354b95ffee02 100644 --- a/src/Symfony/Component/Notifier/Bridge/MicrosoftTeams/LICENSE +++ b/src/Symfony/Component/Notifier/Bridge/MicrosoftTeams/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2021-2022 Fabien Potencier +Copyright (c) 2021-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Notifier/Bridge/Mobyt/LICENSE b/src/Symfony/Component/Notifier/Bridge/Mobyt/LICENSE index 9c907a46a6218..5c7ba0551cb65 100644 --- a/src/Symfony/Component/Notifier/Bridge/Mobyt/LICENSE +++ b/src/Symfony/Component/Notifier/Bridge/Mobyt/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2019-2022 Fabien Potencier +Copyright (c) 2019-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Notifier/Bridge/Octopush/LICENSE b/src/Symfony/Component/Notifier/Bridge/Octopush/LICENSE index 48d17c4fb34f1..d354b95ffee02 100644 --- a/src/Symfony/Component/Notifier/Bridge/Octopush/LICENSE +++ b/src/Symfony/Component/Notifier/Bridge/Octopush/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2021-2022 Fabien Potencier +Copyright (c) 2021-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Notifier/Bridge/OneSignal/LICENSE b/src/Symfony/Component/Notifier/Bridge/OneSignal/LICENSE index 48d17c4fb34f1..d354b95ffee02 100644 --- a/src/Symfony/Component/Notifier/Bridge/OneSignal/LICENSE +++ b/src/Symfony/Component/Notifier/Bridge/OneSignal/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2021-2022 Fabien Potencier +Copyright (c) 2021-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Notifier/Bridge/OrangeSms/LICENSE b/src/Symfony/Component/Notifier/Bridge/OrangeSms/LICENSE index 0ece8964f767d..074eb2b39259e 100644 --- a/src/Symfony/Component/Notifier/Bridge/OrangeSms/LICENSE +++ b/src/Symfony/Component/Notifier/Bridge/OrangeSms/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2022 Fabien Potencier +Copyright (c) 2022-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Notifier/Bridge/OvhCloud/LICENSE b/src/Symfony/Component/Notifier/Bridge/OvhCloud/LICENSE index 9c907a46a6218..5c7ba0551cb65 100644 --- a/src/Symfony/Component/Notifier/Bridge/OvhCloud/LICENSE +++ b/src/Symfony/Component/Notifier/Bridge/OvhCloud/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2019-2022 Fabien Potencier +Copyright (c) 2019-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Notifier/Bridge/OvhCloud/OvhCloudTransport.php b/src/Symfony/Component/Notifier/Bridge/OvhCloud/OvhCloudTransport.php index 7bca67f9fe43c..8a97aaadde581 100644 --- a/src/Symfony/Component/Notifier/Bridge/OvhCloud/OvhCloudTransport.php +++ b/src/Symfony/Component/Notifier/Bridge/OvhCloud/OvhCloudTransport.php @@ -134,6 +134,10 @@ protected function doSend(MessageInterface $message): SentMessage $success = $response->toArray(false); + if (!isset($success['ids'][0])) { + throw new TransportException(sprintf('Attempt to send the SMS to invalid receivers: "%s".', implode(',', $success['invalidReceivers'])), $response); + } + $sentMessage = new SentMessage($message, (string) $this); $sentMessage->setMessageId($success['ids'][0]); diff --git a/src/Symfony/Component/Notifier/Bridge/OvhCloud/Tests/OvhCloudTransportTest.php b/src/Symfony/Component/Notifier/Bridge/OvhCloud/Tests/OvhCloudTransportTest.php index f0678323788fa..f5e1a01aab159 100644 --- a/src/Symfony/Component/Notifier/Bridge/OvhCloud/Tests/OvhCloudTransportTest.php +++ b/src/Symfony/Component/Notifier/Bridge/OvhCloud/Tests/OvhCloudTransportTest.php @@ -14,6 +14,7 @@ use Symfony\Component\HttpClient\MockHttpClient; use Symfony\Component\HttpClient\Response\MockResponse; use Symfony\Component\Notifier\Bridge\OvhCloud\OvhCloudTransport; +use Symfony\Component\Notifier\Exception\TransportException; use Symfony\Component\Notifier\Message\ChatMessage; use Symfony\Component\Notifier\Message\MessageInterface; use Symfony\Component\Notifier\Message\SmsMessage; @@ -89,4 +90,26 @@ public function testValidSignature(string $message) $toSign = 'applicationSecret+consumerKey+POST+'.$endpoint.'+'.$body.'+'.$time; $this->assertSame('$1$'.sha1($toSign), $signature); } + + public function testInvalidReceiver() + { + $smsMessage = new SmsMessage('invalid_receiver', 'lorem ipsum'); + + $data = json_encode([ + 'totalCreditsRemoved' => '1', + 'invalidReceivers' => ['invalid_receiver'], + 'ids' => [], + 'validReceivers' => [], + ]); + $responses = [ + new MockResponse((string) time()), + new MockResponse($data), + ]; + + $transport = $this->createTransport(new MockHttpClient($responses)); + + $this->expectException(TransportException::class); + $this->expectExceptionMessage('Attempt to send the SMS to invalid receivers: "invalid_receiver"'); + $transport->send($smsMessage); + } } diff --git a/src/Symfony/Component/Notifier/Bridge/RocketChat/LICENSE b/src/Symfony/Component/Notifier/Bridge/RocketChat/LICENSE index 9c907a46a6218..5c7ba0551cb65 100644 --- a/src/Symfony/Component/Notifier/Bridge/RocketChat/LICENSE +++ b/src/Symfony/Component/Notifier/Bridge/RocketChat/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2019-2022 Fabien Potencier +Copyright (c) 2019-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Notifier/Bridge/Sendberry/LICENSE b/src/Symfony/Component/Notifier/Bridge/Sendberry/LICENSE index 0ece8964f767d..074eb2b39259e 100644 --- a/src/Symfony/Component/Notifier/Bridge/Sendberry/LICENSE +++ b/src/Symfony/Component/Notifier/Bridge/Sendberry/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2022 Fabien Potencier +Copyright (c) 2022-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Notifier/Bridge/Sendinblue/LICENSE b/src/Symfony/Component/Notifier/Bridge/Sendinblue/LICENSE index 9c907a46a6218..5c7ba0551cb65 100644 --- a/src/Symfony/Component/Notifier/Bridge/Sendinblue/LICENSE +++ b/src/Symfony/Component/Notifier/Bridge/Sendinblue/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2019-2022 Fabien Potencier +Copyright (c) 2019-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Notifier/Bridge/Sinch/LICENSE b/src/Symfony/Component/Notifier/Bridge/Sinch/LICENSE index 9c907a46a6218..5c7ba0551cb65 100644 --- a/src/Symfony/Component/Notifier/Bridge/Sinch/LICENSE +++ b/src/Symfony/Component/Notifier/Bridge/Sinch/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2019-2022 Fabien Potencier +Copyright (c) 2019-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Notifier/Bridge/Slack/LICENSE b/src/Symfony/Component/Notifier/Bridge/Slack/LICENSE index 9c907a46a6218..5c7ba0551cb65 100644 --- a/src/Symfony/Component/Notifier/Bridge/Slack/LICENSE +++ b/src/Symfony/Component/Notifier/Bridge/Slack/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2019-2022 Fabien Potencier +Copyright (c) 2019-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Notifier/Bridge/Sms77/LICENSE b/src/Symfony/Component/Notifier/Bridge/Sms77/LICENSE index 48d17c4fb34f1..d354b95ffee02 100644 --- a/src/Symfony/Component/Notifier/Bridge/Sms77/LICENSE +++ b/src/Symfony/Component/Notifier/Bridge/Sms77/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2021-2022 Fabien Potencier +Copyright (c) 2021-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Notifier/Bridge/SmsBiuras/LICENSE b/src/Symfony/Component/Notifier/Bridge/SmsBiuras/LICENSE index 48d17c4fb34f1..d354b95ffee02 100644 --- a/src/Symfony/Component/Notifier/Bridge/SmsBiuras/LICENSE +++ b/src/Symfony/Component/Notifier/Bridge/SmsBiuras/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2021-2022 Fabien Potencier +Copyright (c) 2021-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Notifier/Bridge/SmsFactor/LICENSE b/src/Symfony/Component/Notifier/Bridge/SmsFactor/LICENSE index 0ece8964f767d..074eb2b39259e 100644 --- a/src/Symfony/Component/Notifier/Bridge/SmsFactor/LICENSE +++ b/src/Symfony/Component/Notifier/Bridge/SmsFactor/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2022 Fabien Potencier +Copyright (c) 2022-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Notifier/Bridge/Smsapi/LICENSE b/src/Symfony/Component/Notifier/Bridge/Smsapi/LICENSE index 406242ff28554..0f262c225767a 100644 --- a/src/Symfony/Component/Notifier/Bridge/Smsapi/LICENSE +++ b/src/Symfony/Component/Notifier/Bridge/Smsapi/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2020-2022 Fabien Potencier +Copyright (c) 2020-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Notifier/Bridge/Smsc/LICENSE b/src/Symfony/Component/Notifier/Bridge/Smsc/LICENSE index 48d17c4fb34f1..d354b95ffee02 100644 --- a/src/Symfony/Component/Notifier/Bridge/Smsc/LICENSE +++ b/src/Symfony/Component/Notifier/Bridge/Smsc/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2021-2022 Fabien Potencier +Copyright (c) 2021-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Notifier/Bridge/SpotHit/LICENSE b/src/Symfony/Component/Notifier/Bridge/SpotHit/LICENSE index 48d17c4fb34f1..d354b95ffee02 100644 --- a/src/Symfony/Component/Notifier/Bridge/SpotHit/LICENSE +++ b/src/Symfony/Component/Notifier/Bridge/SpotHit/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2021-2022 Fabien Potencier +Copyright (c) 2021-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Notifier/Bridge/Telegram/LICENSE b/src/Symfony/Component/Notifier/Bridge/Telegram/LICENSE index 9c907a46a6218..5c7ba0551cb65 100644 --- a/src/Symfony/Component/Notifier/Bridge/Telegram/LICENSE +++ b/src/Symfony/Component/Notifier/Bridge/Telegram/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2019-2022 Fabien Potencier +Copyright (c) 2019-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Notifier/Bridge/Telnyx/LICENSE b/src/Symfony/Component/Notifier/Bridge/Telnyx/LICENSE index 48d17c4fb34f1..d354b95ffee02 100644 --- a/src/Symfony/Component/Notifier/Bridge/Telnyx/LICENSE +++ b/src/Symfony/Component/Notifier/Bridge/Telnyx/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2021-2022 Fabien Potencier +Copyright (c) 2021-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Notifier/Bridge/TurboSms/LICENSE b/src/Symfony/Component/Notifier/Bridge/TurboSms/LICENSE index 48d17c4fb34f1..d354b95ffee02 100644 --- a/src/Symfony/Component/Notifier/Bridge/TurboSms/LICENSE +++ b/src/Symfony/Component/Notifier/Bridge/TurboSms/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2021-2022 Fabien Potencier +Copyright (c) 2021-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Notifier/Bridge/Twilio/LICENSE b/src/Symfony/Component/Notifier/Bridge/Twilio/LICENSE index 9c907a46a6218..5c7ba0551cb65 100644 --- a/src/Symfony/Component/Notifier/Bridge/Twilio/LICENSE +++ b/src/Symfony/Component/Notifier/Bridge/Twilio/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2019-2022 Fabien Potencier +Copyright (c) 2019-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Notifier/Bridge/Vonage/LICENSE b/src/Symfony/Component/Notifier/Bridge/Vonage/LICENSE index 48d17c4fb34f1..d354b95ffee02 100644 --- a/src/Symfony/Component/Notifier/Bridge/Vonage/LICENSE +++ b/src/Symfony/Component/Notifier/Bridge/Vonage/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2021-2022 Fabien Potencier +Copyright (c) 2021-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Notifier/Bridge/Yunpian/LICENSE b/src/Symfony/Component/Notifier/Bridge/Yunpian/LICENSE index 48d17c4fb34f1..d354b95ffee02 100644 --- a/src/Symfony/Component/Notifier/Bridge/Yunpian/LICENSE +++ b/src/Symfony/Component/Notifier/Bridge/Yunpian/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2021-2022 Fabien Potencier +Copyright (c) 2021-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Notifier/Bridge/Zendesk/LICENSE b/src/Symfony/Component/Notifier/Bridge/Zendesk/LICENSE index 0ece8964f767d..074eb2b39259e 100644 --- a/src/Symfony/Component/Notifier/Bridge/Zendesk/LICENSE +++ b/src/Symfony/Component/Notifier/Bridge/Zendesk/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2022 Fabien Potencier +Copyright (c) 2022-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Notifier/Bridge/Zulip/LICENSE b/src/Symfony/Component/Notifier/Bridge/Zulip/LICENSE index 406242ff28554..0f262c225767a 100644 --- a/src/Symfony/Component/Notifier/Bridge/Zulip/LICENSE +++ b/src/Symfony/Component/Notifier/Bridge/Zulip/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2020-2022 Fabien Potencier +Copyright (c) 2020-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Notifier/LICENSE b/src/Symfony/Component/Notifier/LICENSE index 9c907a46a6218..5c7ba0551cb65 100644 --- a/src/Symfony/Component/Notifier/LICENSE +++ b/src/Symfony/Component/Notifier/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2019-2022 Fabien Potencier +Copyright (c) 2019-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/OptionsResolver/LICENSE b/src/Symfony/Component/OptionsResolver/LICENSE index 88bf75bb4d6a2..008370457251e 100644 --- a/src/Symfony/Component/OptionsResolver/LICENSE +++ b/src/Symfony/Component/OptionsResolver/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2022 Fabien Potencier +Copyright (c) 2004-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/PasswordHasher/LICENSE b/src/Symfony/Component/PasswordHasher/LICENSE index 88bf75bb4d6a2..008370457251e 100644 --- a/src/Symfony/Component/PasswordHasher/LICENSE +++ b/src/Symfony/Component/PasswordHasher/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2022 Fabien Potencier +Copyright (c) 2004-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Process/LICENSE b/src/Symfony/Component/Process/LICENSE index 88bf75bb4d6a2..008370457251e 100644 --- a/src/Symfony/Component/Process/LICENSE +++ b/src/Symfony/Component/Process/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2022 Fabien Potencier +Copyright (c) 2004-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/PropertyAccess/LICENSE b/src/Symfony/Component/PropertyAccess/LICENSE index 88bf75bb4d6a2..008370457251e 100644 --- a/src/Symfony/Component/PropertyAccess/LICENSE +++ b/src/Symfony/Component/PropertyAccess/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2022 Fabien Potencier +Copyright (c) 2004-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/PropertyInfo/Extractor/PhpDocExtractor.php b/src/Symfony/Component/PropertyInfo/Extractor/PhpDocExtractor.php index 3f235ca127687..d04b82484b3eb 100644 --- a/src/Symfony/Component/PropertyInfo/Extractor/PhpDocExtractor.php +++ b/src/Symfony/Component/PropertyInfo/Extractor/PhpDocExtractor.php @@ -180,7 +180,7 @@ public function getTypesFromConstructor(string $class, string $property): ?array } } - if (!isset($types[0])) { + if (!isset($types[0]) || [] === $types[0]) { return null; } diff --git a/src/Symfony/Component/PropertyInfo/LICENSE b/src/Symfony/Component/PropertyInfo/LICENSE index 4e90b1b5ae4df..63af57a7115e5 100644 --- a/src/Symfony/Component/PropertyInfo/LICENSE +++ b/src/Symfony/Component/PropertyInfo/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2015-2022 Fabien Potencier +Copyright (c) 2015-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/PropertyInfo/Tests/Extractor/PhpDocExtractorTest.php b/src/Symfony/Component/PropertyInfo/Tests/Extractor/PhpDocExtractorTest.php index 65f8ced54e88e..016334dfc260a 100644 --- a/src/Symfony/Component/PropertyInfo/Tests/Extractor/PhpDocExtractorTest.php +++ b/src/Symfony/Component/PropertyInfo/Tests/Extractor/PhpDocExtractorTest.php @@ -406,6 +406,7 @@ public function constructorTypesProvider() ['dateObject', [new Type(Type::BUILTIN_TYPE_OBJECT, false, 'DateTimeInterface')]], ['dateTime', null], ['ddd', null], + ['mixed', null], ]; } diff --git a/src/Symfony/Component/PropertyInfo/Tests/Fixtures/ConstructorDummy.php b/src/Symfony/Component/PropertyInfo/Tests/Fixtures/ConstructorDummy.php index e64660d083a35..38fa02ec0d515 100644 --- a/src/Symfony/Component/PropertyInfo/Tests/Fixtures/ConstructorDummy.php +++ b/src/Symfony/Component/PropertyInfo/Tests/Fixtures/ConstructorDummy.php @@ -29,8 +29,9 @@ class ConstructorDummy * @param \DateTimeZone $timezone * @param int $date Timestamp * @param \DateTimeInterface $dateObject + * @param mixed $mixed */ - public function __construct(\DateTimeZone $timezone, $date, $dateObject, \DateTimeImmutable $dateTime) + public function __construct(\DateTimeZone $timezone, $date, $dateObject, \DateTimeImmutable $dateTime, $mixed) { $this->timezone = $timezone->getName(); $this->date = \DateTimeImmutable::createFromFormat('U', $date); diff --git a/src/Symfony/Component/RateLimiter/LICENSE b/src/Symfony/Component/RateLimiter/LICENSE index 7fa9539054928..f2345234aa9ea 100644 --- a/src/Symfony/Component/RateLimiter/LICENSE +++ b/src/Symfony/Component/RateLimiter/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2016-2022 Fabien Potencier +Copyright (c) 2016-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Routing/LICENSE b/src/Symfony/Component/Routing/LICENSE index 88bf75bb4d6a2..008370457251e 100644 --- a/src/Symfony/Component/Routing/LICENSE +++ b/src/Symfony/Component/Routing/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2022 Fabien Potencier +Copyright (c) 2004-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Runtime/Internal/ComposerPlugin.php b/src/Symfony/Component/Runtime/Internal/ComposerPlugin.php index 6e2220acfdb34..a48ff46ff00b1 100644 --- a/src/Symfony/Component/Runtime/Internal/ComposerPlugin.php +++ b/src/Symfony/Component/Runtime/Internal/ComposerPlugin.php @@ -58,7 +58,7 @@ public function uninstall(Composer $composer, IOInterface $io): void public function updateAutoloadFile(): void { - $vendorDir = $this->composer->getConfig()->get('vendor-dir'); + $vendorDir = realpath($this->composer->getConfig()->get('vendor-dir')); if (!is_file($autoloadFile = $vendorDir.'/autoload.php') || false === $extra = $this->composer->getPackage()->getExtra()['runtime'] ?? [] diff --git a/src/Symfony/Component/Runtime/LICENSE b/src/Symfony/Component/Runtime/LICENSE index 48d17c4fb34f1..d354b95ffee02 100644 --- a/src/Symfony/Component/Runtime/LICENSE +++ b/src/Symfony/Component/Runtime/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2021-2022 Fabien Potencier +Copyright (c) 2021-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Security/Core/LICENSE b/src/Symfony/Component/Security/Core/LICENSE index 88bf75bb4d6a2..008370457251e 100644 --- a/src/Symfony/Component/Security/Core/LICENSE +++ b/src/Symfony/Component/Security/Core/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2022 Fabien Potencier +Copyright (c) 2004-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Security/Core/README.md b/src/Symfony/Component/Security/Core/README.md index 00e74cbada342..f930a12bfe9cc 100644 --- a/src/Symfony/Component/Security/Core/README.md +++ b/src/Symfony/Component/Security/Core/README.md @@ -41,7 +41,7 @@ if (!$accessDecisionManager->decide($token, ['ROLE_ADMIN'])) { Sponsor ------- -The Security component for Symfony 6.1 is [backed][1] by [SymfonyCasts][2]. +The Security component for Symfony 6.2 is [backed][1] by [SymfonyCasts][2]. Learn Symfony faster by watching real projects being built and actively coding along with them. SymfonyCasts bridges that learning gap, bringing you video diff --git a/src/Symfony/Component/Security/Core/Signature/SignatureHasher.php b/src/Symfony/Component/Security/Core/Signature/SignatureHasher.php index 092a05eae864a..aede020e15e77 100644 --- a/src/Symfony/Component/Security/Core/Signature/SignatureHasher.php +++ b/src/Symfony/Component/Security/Core/Signature/SignatureHasher.php @@ -45,7 +45,9 @@ public function __construct(PropertyAccessorInterface $propertyAccessor, array $ } /** - * Verifies the hash using the provided user and expire time. + * Verifies the hash using the provided user identifier and expire time. + * + * This method must be called before the user object is loaded from a provider. * * @param int $expires The expiry time as a unix timestamp * @param string $hash The plaintext hash provided by the request @@ -53,16 +55,38 @@ public function __construct(PropertyAccessorInterface $propertyAccessor, array $ * @throws InvalidSignatureException If the signature does not match the provided parameters * @throws ExpiredSignatureException If the signature is no longer valid */ - public function verifySignatureHash(UserInterface $user, int $expires, string $hash): void + public function acceptSignatureHash(string $userIdentifier, int $expires, string $hash): void { - if (!hash_equals($hash, $this->computeSignatureHash($user, $expires))) { + if ($expires < time()) { + throw new ExpiredSignatureException('Signature has expired.'); + } + $hmac = substr($hash, 0, 44); + $payload = substr($hash, 44).':'.$expires.':'.$userIdentifier; + + if (!hash_equals($hmac, $this->generateHash($payload))) { throw new InvalidSignatureException('Invalid or expired signature.'); } + } + /** + * Verifies the hash using the provided user and expire time. + * + * @param int $expires The expiry time as a unix timestamp + * @param string $hash The plaintext hash provided by the request + * + * @throws InvalidSignatureException If the signature does not match the provided parameters + * @throws ExpiredSignatureException If the signature is no longer valid + */ + public function verifySignatureHash(UserInterface $user, int $expires, string $hash): void + { if ($expires < time()) { throw new ExpiredSignatureException('Signature has expired.'); } + if (!hash_equals($hash, $this->computeSignatureHash($user, $expires))) { + throw new InvalidSignatureException('Invalid or expired signature.'); + } + if ($this->expiredSignaturesStorage && $this->maxUses) { if ($this->expiredSignaturesStorage->countUsages($hash) >= $this->maxUses) { throw new ExpiredSignatureException(sprintf('Signature can only be used "%d" times.', $this->maxUses)); @@ -79,7 +103,8 @@ public function verifySignatureHash(UserInterface $user, int $expires, string $h */ public function computeSignatureHash(UserInterface $user, int $expires): string { - $signatureFields = [base64_encode($user->getUserIdentifier()), $expires]; + $userIdentifier = $user->getUserIdentifier(); + $fieldsHash = hash_init('sha256'); foreach ($this->signatureProperties as $property) { $value = $this->propertyAccessor->getValue($user, $property) ?? ''; @@ -90,9 +115,16 @@ public function computeSignatureHash(UserInterface $user, int $expires): string if (!\is_scalar($value) && !$value instanceof \Stringable) { throw new \InvalidArgumentException(sprintf('The property path "%s" on the user object "%s" must return a value that can be cast to a string, but "%s" was returned.', $property, $user::class, get_debug_type($value))); } - $signatureFields[] = base64_encode($value); + hash_update($fieldsHash, ':'.base64_encode($value)); } - return base64_encode(hash_hmac('sha256', implode(':', $signatureFields), $this->secret)); + $fieldsHash = strtr(base64_encode(hash_final($fieldsHash, true)), '+/=', '-_~'); + + return $this->generateHash($fieldsHash.':'.$expires.':'.$userIdentifier).$fieldsHash; + } + + private function generateHash(string $tokenValue): string + { + return strtr(base64_encode(hash_hmac('sha256', $tokenValue, $this->secret, true)), '+/=', '-_~'); } } diff --git a/src/Symfony/Component/Security/Csrf/LICENSE b/src/Symfony/Component/Security/Csrf/LICENSE index 88bf75bb4d6a2..008370457251e 100644 --- a/src/Symfony/Component/Security/Csrf/LICENSE +++ b/src/Symfony/Component/Security/Csrf/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2022 Fabien Potencier +Copyright (c) 2004-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Security/Csrf/README.md b/src/Symfony/Component/Security/Csrf/README.md index 65c63c19cb741..d4887824125f7 100644 --- a/src/Symfony/Component/Security/Csrf/README.md +++ b/src/Symfony/Component/Security/Csrf/README.md @@ -7,7 +7,7 @@ The Security CSRF (cross-site request forgery) component provides a class Sponsor ------- -The Security component for Symfony 6.1 is [backed][1] by [SymfonyCasts][2]. +The Security component for Symfony 6.2 is [backed][1] by [SymfonyCasts][2]. Learn Symfony faster by watching real projects being built and actively coding along with them. SymfonyCasts bridges that learning gap, bringing you video diff --git a/src/Symfony/Component/Security/Http/Controller/UserValueResolver.php b/src/Symfony/Component/Security/Http/Controller/UserValueResolver.php index a4c21f7719854..520515e465745 100644 --- a/src/Symfony/Component/Security/Http/Controller/UserValueResolver.php +++ b/src/Symfony/Component/Security/Http/Controller/UserValueResolver.php @@ -47,11 +47,6 @@ public function supports(Request $request, ArgumentMetadata $argument): bool return false; } - // if no user is present but a default value exists we delegate to DefaultValueResolver - if ($argument->hasDefaultValue() && null === $this->tokenStorage->getToken()?->getUser()) { - return false; - } - return true; } @@ -62,20 +57,21 @@ public function resolve(Request $request, ArgumentMetadata $argument): array if (UserInterface::class !== $argument->getType() && !$argument->getAttributesOfType(CurrentUser::class, ArgumentMetadata::IS_INSTANCEOF)) { return []; } - $user = $this->tokenStorage->getToken()?->getUser(); - // if no user is present but a default value exists we delegate to DefaultValueResolver - if ($argument->hasDefaultValue() && null === $user) { - return []; - } + if (null === $user = $this->tokenStorage->getToken()?->getUser()) { + // if no user is present but a default value exists we use it to prevent the EntityValueResolver or others + // from attempting resolution of the User as the current logged in user was requested here + if ($argument->hasDefaultValue()) { + return [$argument->getDefaultValue()]; + } - if (null === $user) { if (!$argument->isNullable()) { throw new AccessDeniedException(sprintf('There is no logged-in user to pass to $%s, make the argument nullable if you want to allow anonymous access to the action.', $argument->getName())); } return [null]; } + if (null === $argument->getType() || $user instanceof ($argument->getType())) { return [$user]; } diff --git a/src/Symfony/Component/Security/Http/LICENSE b/src/Symfony/Component/Security/Http/LICENSE index 88bf75bb4d6a2..008370457251e 100644 --- a/src/Symfony/Component/Security/Http/LICENSE +++ b/src/Symfony/Component/Security/Http/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2022 Fabien Potencier +Copyright (c) 2004-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Security/Http/LoginLink/LoginLinkHandler.php b/src/Symfony/Component/Security/Http/LoginLink/LoginLinkHandler.php index 711302cdc55da..4d0e49adfa94d 100644 --- a/src/Symfony/Component/Security/Http/LoginLink/LoginLinkHandler.php +++ b/src/Symfony/Component/Security/Http/LoginLink/LoginLinkHandler.php @@ -83,12 +83,6 @@ public function consumeLoginLink(Request $request): UserInterface { $userIdentifier = $request->get('user'); - try { - $user = $this->userProvider->loadUserByIdentifier($userIdentifier); - } catch (UserNotFoundException $exception) { - throw new InvalidLoginLinkException('User not found.', 0, $exception); - } - if (!$hash = $request->get('hash')) { throw new InvalidLoginLinkException('Missing "hash" parameter.'); } @@ -97,7 +91,13 @@ public function consumeLoginLink(Request $request): UserInterface } try { + $this->signatureHasher->acceptSignatureHash($userIdentifier, $expires, $hash); + + $user = $this->userProvider->loadUserByIdentifier($userIdentifier); + $this->signatureHasher->verifySignatureHash($user, $expires, $hash); + } catch (UserNotFoundException $e) { + throw new InvalidLoginLinkException('User not found.', 0, $e); } catch (ExpiredSignatureException $e) { throw new ExpiredLoginLinkException(ucfirst(str_ireplace('signature', 'login link', $e->getMessage())), 0, $e); } catch (InvalidSignatureException $e) { diff --git a/src/Symfony/Component/Security/Http/README.md b/src/Symfony/Component/Security/Http/README.md index be8982a946d46..658d4cd9985a1 100644 --- a/src/Symfony/Component/Security/Http/README.md +++ b/src/Symfony/Component/Security/Http/README.md @@ -15,7 +15,7 @@ $ composer require symfony/security-http Sponsor ------- -The Security component for Symfony 6.1 is [backed][1] by [SymfonyCasts][2]. +The Security component for Symfony 6.2 is [backed][1] by [SymfonyCasts][2]. Learn Symfony faster by watching real projects being built and actively coding along with them. SymfonyCasts bridges that learning gap, bringing you video diff --git a/src/Symfony/Component/Security/Http/RememberMe/PersistentRememberMeHandler.php b/src/Symfony/Component/Security/Http/RememberMe/PersistentRememberMeHandler.php index 39cac556304c7..1d68ef4d740de 100644 --- a/src/Symfony/Component/Security/Http/RememberMe/PersistentRememberMeHandler.php +++ b/src/Symfony/Component/Security/Http/RememberMe/PersistentRememberMeHandler.php @@ -50,15 +50,16 @@ public function __construct(TokenProviderInterface $tokenProvider, #[\SensitiveP public function createRememberMeCookie(UserInterface $user): void { - $series = base64_encode(random_bytes(64)); - $tokenValue = $this->generateHash(base64_encode(random_bytes(64))); + $series = random_bytes(66); + $tokenValue = strtr(base64_encode(substr($series, 33)), '+/=', '-_~'); + $series = strtr(base64_encode(substr($series, 0, 33)), '+/=', '-_~'); $token = new PersistentToken($user::class, $user->getUserIdentifier(), $series, $tokenValue, new \DateTime()); $this->tokenProvider->createNewToken($token); $this->createCookie(RememberMeDetails::fromPersistentToken($token, time() + $this->options['lifetime'])); } - public function processRememberMe(RememberMeDetails $rememberMeDetails, UserInterface $user): void + public function consumeRememberMeCookie(RememberMeDetails $rememberMeDetails): UserInterface { if (!str_contains($rememberMeDetails->getValue(), ':')) { throw new AuthenticationException('The cookie is incorrectly formatted.'); @@ -80,16 +81,26 @@ public function processRememberMe(RememberMeDetails $rememberMeDetails, UserInte throw new AuthenticationException('The cookie has expired.'); } + return parent::consumeRememberMeCookie($rememberMeDetails->withValue($persistentToken->getLastUsed()->getTimestamp().':'.$rememberMeDetails->getValue().':'.$persistentToken->getClass())); + } + + public function processRememberMe(RememberMeDetails $rememberMeDetails, UserInterface $user): void + { + [$lastUsed, $series, $tokenValue, $class] = explode(':', $rememberMeDetails->getValue(), 4); + $persistentToken = new PersistentToken($class, $rememberMeDetails->getUserIdentifier(), $series, $tokenValue, new \DateTime('@'.$lastUsed)); + // if a token was regenerated less than a minute ago, there is no need to regenerate it // if multiple concurrent requests reauthenticate a user we do not want to update the token several times - if ($persistentToken->getLastUsed()->getTimestamp() + 60 < time()) { - $tokenValue = $this->generateHash(base64_encode(random_bytes(64))); - $tokenLastUsed = new \DateTime(); - $this->tokenVerifier?->updateExistingToken($persistentToken, $tokenValue, $tokenLastUsed); - $this->tokenProvider->updateToken($series, $tokenValue, $tokenLastUsed); - - $this->createCookie($rememberMeDetails->withValue($series.':'.$tokenValue)); + if ($persistentToken->getLastUsed()->getTimestamp() + 60 >= time()) { + return; } + + $tokenValue = strtr(base64_encode(random_bytes(33)), '+/=', '-_~'); + $tokenLastUsed = new \DateTime(); + $this->tokenVerifier?->updateExistingToken($persistentToken, $tokenValue, $tokenLastUsed); + $this->tokenProvider->updateToken($series, $tokenValue, $tokenLastUsed); + + $this->createCookie($rememberMeDetails->withValue($series.':'.$tokenValue)); } public function clearRememberMeCookie(): void @@ -102,7 +113,7 @@ public function clearRememberMeCookie(): void } $rememberMeDetails = RememberMeDetails::fromRawCookie($cookie); - [$series, ] = explode(':', $rememberMeDetails->getValue()); + [$series] = explode(':', $rememberMeDetails->getValue()); $this->tokenProvider->deleteTokenBySeries($series); } @@ -113,9 +124,4 @@ public function getTokenProvider(): TokenProviderInterface { return $this->tokenProvider; } - - private function generateHash(string $tokenValue): string - { - return hash_hmac('sha256', $tokenValue, $this->secret); - } } diff --git a/src/Symfony/Component/Security/Http/RememberMe/RememberMeDetails.php b/src/Symfony/Component/Security/Http/RememberMe/RememberMeDetails.php index 7945b417fdf49..76dc07504a229 100644 --- a/src/Symfony/Component/Security/Http/RememberMe/RememberMeDetails.php +++ b/src/Symfony/Component/Security/Http/RememberMe/RememberMeDetails.php @@ -36,13 +36,14 @@ public function __construct(string $userFqcn, string $userIdentifier, int $expir public static function fromRawCookie(string $rawCookie): self { - $cookieParts = explode(self::COOKIE_DELIMITER, base64_decode($rawCookie), 4); + $cookieParts = explode(self::COOKIE_DELIMITER, $rawCookie, 4); if (4 !== \count($cookieParts)) { throw new AuthenticationException('The cookie contains invalid data.'); } - if (false === $cookieParts[1] = base64_decode($cookieParts[1], true)) { + if (false === $cookieParts[1] = base64_decode(strtr($cookieParts[1], '-_~', '+/='), true)) { throw new AuthenticationException('The user identifier contains a character from outside the base64 alphabet.'); } + $cookieParts[0] = strtr($cookieParts[0], '.', '\\'); return new static(...$cookieParts); } @@ -83,6 +84,6 @@ public function getValue(): string public function toString(): string { // $userIdentifier is encoded because it might contain COOKIE_DELIMITER, we assume other values don't - return base64_encode(implode(self::COOKIE_DELIMITER, [$this->userFqcn, base64_encode($this->userIdentifier), $this->expires, $this->value])); + return implode(self::COOKIE_DELIMITER, [strtr($this->userFqcn, '\\', '.'), strtr(base64_encode($this->userIdentifier), '+/=', '-_~'), $this->expires, $this->value]); } } diff --git a/src/Symfony/Component/Security/Http/RememberMe/SignatureRememberMeHandler.php b/src/Symfony/Component/Security/Http/RememberMe/SignatureRememberMeHandler.php index 8a12dbea9ab57..85686c5bba6c8 100644 --- a/src/Symfony/Component/Security/Http/RememberMe/SignatureRememberMeHandler.php +++ b/src/Symfony/Component/Security/Http/RememberMe/SignatureRememberMeHandler.php @@ -50,6 +50,19 @@ public function createRememberMeCookie(UserInterface $user): void $this->createCookie($details); } + public function consumeRememberMeCookie(RememberMeDetails $rememberMeDetails): UserInterface + { + try { + $this->signatureHasher->acceptSignatureHash($rememberMeDetails->getUserIdentifier(), $rememberMeDetails->getExpires(), $rememberMeDetails->getValue()); + } catch (InvalidSignatureException $e) { + throw new AuthenticationException('The cookie\'s hash is invalid.', 0, $e); + } catch (ExpiredSignatureException $e) { + throw new AuthenticationException('The cookie has expired.', 0, $e); + } + + return parent::consumeRememberMeCookie($rememberMeDetails); + } + public function processRememberMe(RememberMeDetails $rememberMeDetails, UserInterface $user): void { try { diff --git a/src/Symfony/Component/Security/Http/Tests/Controller/UserValueResolverTest.php b/src/Symfony/Component/Security/Http/Tests/Controller/UserValueResolverTest.php index 4ee4c0a07d46e..4e780879b6c2f 100644 --- a/src/Symfony/Component/Security/Http/Tests/Controller/UserValueResolverTest.php +++ b/src/Symfony/Component/Security/Http/Tests/Controller/UserValueResolverTest.php @@ -28,7 +28,7 @@ class UserValueResolverTest extends TestCase { /** - * In Symfony 7, keep this test case but remove the call to supports() + * In Symfony 7, keep this test case but remove the call to supports(). * * @group legacy */ @@ -43,7 +43,7 @@ public function testSupportsFailsWithNoType() } /** - * In Symfony 7, keep this test case but remove the call to supports() + * In Symfony 7, keep this test case but remove the call to supports(). * * @group legacy */ @@ -51,10 +51,10 @@ public function testSupportsFailsWhenDefaultValAndNoUser() { $tokenStorage = new TokenStorage(); $resolver = new UserValueResolver($tokenStorage); - $metadata = new ArgumentMetadata('foo', UserInterface::class, false, true, new InMemoryUser('username', 'password')); + $metadata = new ArgumentMetadata('foo', UserInterface::class, false, true, $default = new InMemoryUser('username', 'password')); - $this->assertSame([], $resolver->resolve(Request::create('/'), $metadata)); - $this->assertFalse($resolver->supports(Request::create('/'), $metadata)); + $this->assertSame([$default], $resolver->resolve(Request::create('/'), $metadata)); + $this->assertTrue($resolver->supports(Request::create('/'), $metadata)); } public function testResolveSucceedsWithUserInterface() diff --git a/src/Symfony/Component/Security/Http/Tests/LoginLink/LoginLinkHandlerTest.php b/src/Symfony/Component/Security/Http/Tests/LoginLink/LoginLinkHandlerTest.php index 52aaf89faea2e..8ff7047f80ecb 100644 --- a/src/Symfony/Component/Security/Http/Tests/LoginLink/LoginLinkHandlerTest.php +++ b/src/Symfony/Component/Security/Http/Tests/LoginLink/LoginLinkHandlerTest.php @@ -53,6 +53,7 @@ protected function setUp(): void /** * @group time-sensitive + * * @dataProvider provideCreateLoginLinkData */ public function testCreateLoginLink($user, array $extraProperties, Request $request = null) @@ -68,7 +69,7 @@ public function testCreateLoginLink($user, array $extraProperties, Request $requ // allow a small expiration offset to avoid time-sensitivity && abs(time() + 600 - $parameters['expires']) <= 1 // make sure hash is what we expect - && $parameters['hash'] === $this->createSignatureHash('weaverryan', $parameters['expires'], array_values($extraProperties)); + && $parameters['hash'] === $this->createSignatureHash('weaverryan', $parameters['expires'], $extraProperties); }), UrlGeneratorInterface::ABSOLUTE_URL ) @@ -127,7 +128,7 @@ public function testCreateLoginLinkWithLifetime() && abs(time() + 1000 - $parameters['expires']) <= 1 && isset($parameters['hash']) // make sure hash is what we expect - && $parameters['hash'] === $this->createSignatureHash('weaverryan', $parameters['expires'], array_values($extraProperties)); + && $parameters['hash'] === $this->createSignatureHash('weaverryan', $parameters['expires'], $extraProperties); }), UrlGeneratorInterface::ABSOLUTE_URL ) @@ -147,7 +148,7 @@ public function testCreateLoginLinkWithLifetime() public function testConsumeLoginLink() { $expires = time() + 500; - $signature = $this->createSignatureHash('weaverryan', $expires, ['ryan@symfonycasts.com', 'pwhash']); + $signature = $this->createSignatureHash('weaverryan', $expires); $request = Request::create(sprintf('/login/verify?user=weaverryan&hash=%s&expires=%d', $signature, $expires)); $user = new TestLoginLinkHandlerUser('weaverryan', 'ryan@symfonycasts.com', 'pwhash'); @@ -163,44 +164,37 @@ public function testConsumeLoginLink() public function testConsumeLoginLinkWithExpired() { - $this->expectException(ExpiredLoginLinkException::class); $expires = time() - 500; - $signature = $this->createSignatureHash('weaverryan', $expires, ['ryan@symfonycasts.com', 'pwhash']); + $signature = $this->createSignatureHash('weaverryan', $expires); $request = Request::create(sprintf('/login/verify?user=weaverryan&hash=%s&expires=%d', $signature, $expires)); - $user = new TestLoginLinkHandlerUser('weaverryan', 'ryan@symfonycasts.com', 'pwhash'); - $this->userProvider->createUser($user); - $linker = $this->createLinker(['max_uses' => 3]); + $this->expectException(ExpiredLoginLinkException::class); $linker->consumeLoginLink($request); } public function testConsumeLoginLinkWithUserNotFound() { - $this->expectException(InvalidLoginLinkException::class); - $request = Request::create('/login/verify?user=weaverryan&hash=thehash&expires=10000'); + $request = Request::create('/login/verify?user=weaverryan&hash=thehash&expires='.(time() + 500)); $linker = $this->createLinker(); + $this->expectException(InvalidLoginLinkException::class); $linker->consumeLoginLink($request); } public function testConsumeLoginLinkWithDifferentSignature() { - $this->expectException(InvalidLoginLinkException::class); $request = Request::create(sprintf('/login/verify?user=weaverryan&hash=fake_hash&expires=%d', time() + 500)); - $user = new TestLoginLinkHandlerUser('weaverryan', 'ryan@symfonycasts.com', 'pwhash'); - $this->userProvider->createUser($user); - $linker = $this->createLinker(); + $this->expectException(InvalidLoginLinkException::class); $linker->consumeLoginLink($request); } public function testConsumeLoginLinkExceedsMaxUsage() { - $this->expectException(ExpiredLoginLinkException::class); $expires = time() + 500; - $signature = $this->createSignatureHash('weaverryan', $expires, ['ryan@symfonycasts.com', 'pwhash']); + $signature = $this->createSignatureHash('weaverryan', $expires); $request = Request::create(sprintf('/login/verify?user=weaverryan&hash=%s&expires=%d', $signature, $expires)); $user = new TestLoginLinkHandlerUser('weaverryan', 'ryan@symfonycasts.com', 'pwhash'); @@ -211,6 +205,7 @@ public function testConsumeLoginLinkExceedsMaxUsage() $this->expiredLinkCache->save($item); $linker = $this->createLinker(['max_uses' => 3]); + $this->expectException(ExpiredLoginLinkException::class); $linker->consumeLoginLink($request); } @@ -238,15 +233,12 @@ public function testConsumeLoginLinkWithMissingExpiration() $linker->consumeLoginLink($request); } - private function createSignatureHash(string $username, int $expires, array $extraFields): string + private function createSignatureHash(string $username, int $expires, array $extraFields = ['emailProperty' => 'ryan@symfonycasts.com', 'passwordProperty' => 'pwhash']): string { - $fields = [base64_encode($username), $expires]; - foreach ($extraFields as $extraField) { - $fields[] = base64_encode($extraField); - } + $hasher = new SignatureHasher($this->propertyAccessor, array_keys($extraFields), 's3cret'); + $user = new TestLoginLinkHandlerUser($username, $extraFields['emailProperty'] ?? '', $extraFields['passwordProperty'] ?? '', $extraFields['lastAuthenticatedAt'] ?? null); - // matches hash logic in the class - return base64_encode(hash_hmac('sha256', implode(':', $fields), 's3cret')); + return $hasher->computeSignatureHash($user, $expires); } private function createLinker(array $options = [], array $extraProperties = ['emailProperty', 'passwordProperty']): LoginLinkHandler @@ -330,7 +322,7 @@ public function loadUserByIdentifier(string $userIdentifier): TestLoginLinkHandl public function refreshUser(UserInterface $user): TestLoginLinkHandlerUser { - return $this->users[$username]; + return $this->users[$user->getUserIdentifier()]; } public function supportsClass(string $class): bool diff --git a/src/Symfony/Component/Security/Http/Tests/RememberMe/PersistentRememberMeHandlerTest.php b/src/Symfony/Component/Security/Http/Tests/RememberMe/PersistentRememberMeHandlerTest.php index 4e2c0980ba0aa..da4f26eaaf6d4 100644 --- a/src/Symfony/Component/Security/Http/Tests/RememberMe/PersistentRememberMeHandlerTest.php +++ b/src/Symfony/Component/Security/Http/Tests/RememberMe/PersistentRememberMeHandlerTest.php @@ -93,8 +93,8 @@ public function testConsumeRememberMeCookieValid() /** @var Cookie $cookie */ $cookie = $this->request->attributes->get(ResponseListener::COOKIE_ATTR_NAME); - $rememberParts = explode(':', base64_decode($rememberMeDetails->toString()), 4); - $cookieParts = explode(':', base64_decode($cookie->getValue()), 4); + $rememberParts = explode(':', $rememberMeDetails->toString(), 4); + $cookieParts = explode(':', $cookie->getValue(), 4); $this->assertSame($rememberParts[0], $cookieParts[0]); // class $this->assertSame($rememberParts[1], $cookieParts[1]); // identifier diff --git a/src/Symfony/Component/Security/Http/Tests/RememberMe/SignatureRememberMeHandlerTest.php b/src/Symfony/Component/Security/Http/Tests/RememberMe/SignatureRememberMeHandlerTest.php index d7b7b85673cd7..8205009448a64 100644 --- a/src/Symfony/Component/Security/Http/Tests/RememberMe/SignatureRememberMeHandlerTest.php +++ b/src/Symfony/Component/Security/Http/Tests/RememberMe/SignatureRememberMeHandlerTest.php @@ -12,13 +12,11 @@ namespace Symfony\Component\Security\Http\Tests\RememberMe; use PHPUnit\Framework\TestCase; -use Symfony\Bridge\PhpUnit\ClockMock; use Symfony\Component\HttpFoundation\Cookie; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Component\PropertyAccess\PropertyAccess; use Symfony\Component\Security\Core\Exception\AuthenticationException; -use Symfony\Component\Security\Core\Signature\Exception\ExpiredSignatureException; -use Symfony\Component\Security\Core\Signature\Exception\InvalidSignatureException; use Symfony\Component\Security\Core\Signature\SignatureHasher; use Symfony\Component\Security\Core\User\InMemoryUser; use Symfony\Component\Security\Core\User\InMemoryUserProvider; @@ -36,10 +34,8 @@ class SignatureRememberMeHandlerTest extends TestCase protected function setUp(): void { - $this->signatureHasher = $this->createMock(SignatureHasher::class); + $this->signatureHasher = new SignatureHasher(PropertyAccess::createPropertyAccessor(), [], 's3cret'); $this->userProvider = new InMemoryUserProvider(); - $user = new InMemoryUser('wouter', null); - $this->userProvider->createUser($user); $this->requestStack = new RequestStack(); $this->request = Request::create('/login'); $this->requestStack->push($this->request); @@ -51,10 +47,9 @@ protected function setUp(): void */ public function testCreateRememberMeCookie() { - ClockMock::register(SignatureRememberMeHandler::class); - $user = new InMemoryUser('wouter', null); - $this->signatureHasher->expects($this->once())->method('computeSignatureHash')->with($user, $expire = time() + 31536000)->willReturn('abc'); + $signature = $this->signatureHasher->computeSignatureHash($user, $expire = time() + 31536000); + $this->userProvider->createUser(new InMemoryUser('wouter', null)); $this->handler->createRememberMeCookie($user); @@ -62,7 +57,7 @@ public function testCreateRememberMeCookie() /** @var Cookie $cookie */ $cookie = $this->request->attributes->get(ResponseListener::COOKIE_ATTR_NAME); - $this->assertEquals(base64_encode(InMemoryUser::class.':d291dGVy:'.$expire.':abc'), $cookie->getValue()); + $this->assertEquals(strtr(InMemoryUser::class, '\\', '.').':d291dGVy:'.$expire.':'.$signature, $cookie->getValue()); } public function testClearRememberMeCookie() @@ -76,50 +71,36 @@ public function testClearRememberMeCookie() $this->assertNull($cookie->getValue()); } - /** - * @group time-sensitive - */ public function testConsumeRememberMeCookieValid() { - $this->signatureHasher->expects($this->once())->method('verifySignatureHash')->with($user = new InMemoryUser('wouter', null), 360, 'signature'); - $this->signatureHasher->expects($this->any()) - ->method('computeSignatureHash') - ->with($user, $expire = time() + 31536000) - ->willReturn('newsignature'); + $user = new InMemoryUser('wouter', null); + $signature = $this->signatureHasher->computeSignatureHash($user, $expire = time() + 3600); + $this->userProvider->createUser(new InMemoryUser('wouter', null)); - $rememberMeDetails = new RememberMeDetails(InMemoryUser::class, 'wouter', 360, 'signature'); + $rememberMeDetails = new RememberMeDetails(InMemoryUser::class, 'wouter', $expire, $signature); $this->handler->consumeRememberMeCookie($rememberMeDetails); $this->assertTrue($this->request->attributes->has(ResponseListener::COOKIE_ATTR_NAME)); /** @var Cookie $cookie */ $cookie = $this->request->attributes->get(ResponseListener::COOKIE_ATTR_NAME); - $this->assertEquals((new RememberMeDetails(InMemoryUser::class, 'wouter', $expire, 'newsignature'))->toString(), $cookie->getValue()); + $this->assertNotEquals((new RememberMeDetails(InMemoryUser::class, 'wouter', $expire, $signature))->toString(), $cookie->getValue()); } public function testConsumeRememberMeCookieInvalidHash() { $this->expectException(AuthenticationException::class); $this->expectExceptionMessage('The cookie\'s hash is invalid.'); - - $this->signatureHasher->expects($this->any()) - ->method('verifySignatureHash') - ->with(new InMemoryUser('wouter', null), 360, 'badsignature') - ->will($this->throwException(new InvalidSignatureException())); - - $this->handler->consumeRememberMeCookie(new RememberMeDetails(InMemoryUser::class, 'wouter', 360, 'badsignature')); + $this->handler->consumeRememberMeCookie(new RememberMeDetails(InMemoryUser::class, 'wouter', time() + 600, 'badsignature')); } public function testConsumeRememberMeCookieExpired() { + $user = new InMemoryUser('wouter', null); + $signature = $this->signatureHasher->computeSignatureHash($user, 360); + $this->expectException(AuthenticationException::class); $this->expectExceptionMessage('The cookie has expired.'); - - $this->signatureHasher->expects($this->any()) - ->method('verifySignatureHash') - ->with(new InMemoryUser('wouter', null), 360, 'signature') - ->will($this->throwException(new ExpiredSignatureException())); - - $this->handler->consumeRememberMeCookie(new RememberMeDetails(InMemoryUser::class, 'wouter', 360, 'signature')); + $this->handler->consumeRememberMeCookie(new RememberMeDetails(InMemoryUser::class, 'wouter', 360, $signature)); } } diff --git a/src/Symfony/Component/Security/Http/composer.json b/src/Symfony/Component/Security/Http/composer.json index 66876a3e38e4b..6ab35dae07456 100644 --- a/src/Symfony/Component/Security/Http/composer.json +++ b/src/Symfony/Component/Security/Http/composer.json @@ -18,7 +18,7 @@ "require": { "php": ">=8.1", "symfony/deprecation-contracts": "^2.1|^3", - "symfony/security-core": "^6.0", + "symfony/security-core": "~6.0.19|~6.1.11|^6.2.5", "symfony/http-foundation": "^5.4|^6.0", "symfony/http-kernel": "^6.2", "symfony/polyfill-mbstring": "~1.0", diff --git a/src/Symfony/Component/Semaphore/LICENSE b/src/Symfony/Component/Semaphore/LICENSE index 7fa9539054928..f2345234aa9ea 100644 --- a/src/Symfony/Component/Semaphore/LICENSE +++ b/src/Symfony/Component/Semaphore/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2016-2022 Fabien Potencier +Copyright (c) 2016-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Serializer/LICENSE b/src/Symfony/Component/Serializer/LICENSE index 88bf75bb4d6a2..008370457251e 100644 --- a/src/Symfony/Component/Serializer/LICENSE +++ b/src/Symfony/Component/Serializer/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2022 Fabien Potencier +Copyright (c) 2004-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Serializer/Normalizer/GetSetMethodNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/GetSetMethodNormalizer.php index 71459dd945df7..54b2f7c9d48cb 100644 --- a/src/Symfony/Component/Serializer/Normalizer/GetSetMethodNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/GetSetMethodNormalizer.php @@ -140,14 +140,7 @@ protected function setAttributeValue(object $object, string $attribute, mixed $v $key = $object::class.':'.$setter; if (!isset(self::$setterAccessibleCache[$key])) { - try { - // We have to use is_callable() here since method_exists() - // does not "see" protected/private methods - self::$setterAccessibleCache[$key] = \is_callable([$object, $setter]) && !(new \ReflectionMethod($object, $setter))->isStatic(); - } catch (\ReflectionException $e) { - // Method does not exist in the class, probably a magic method - self::$setterAccessibleCache[$key] = false; - } + self::$setterAccessibleCache[$key] = method_exists($object, $setter) && \is_callable([$object, $setter]) && !(new \ReflectionMethod($object, $setter))->isStatic(); } if (self::$setterAccessibleCache[$key]) { diff --git a/src/Symfony/Component/Serializer/SerializerInterface.php b/src/Symfony/Component/Serializer/SerializerInterface.php index eb5ee000ea095..b883dbea5b975 100644 --- a/src/Symfony/Component/Serializer/SerializerInterface.php +++ b/src/Symfony/Component/Serializer/SerializerInterface.php @@ -33,6 +33,8 @@ public function serialize(mixed $data, string $format, array $context = []): str * @param array $context * * @psalm-return (TType is class-string ? TObject : mixed) + * + * @phpstan-return ($type is class-string ? TObject : mixed) */ public function deserialize(mixed $data, string $type, string $format, array $context = []): mixed; } diff --git a/src/Symfony/Component/Stopwatch/LICENSE b/src/Symfony/Component/Stopwatch/LICENSE index 88bf75bb4d6a2..008370457251e 100644 --- a/src/Symfony/Component/Stopwatch/LICENSE +++ b/src/Symfony/Component/Stopwatch/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2022 Fabien Potencier +Copyright (c) 2004-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/String/LICENSE b/src/Symfony/Component/String/LICENSE index 9c907a46a6218..5c7ba0551cb65 100644 --- a/src/Symfony/Component/String/LICENSE +++ b/src/Symfony/Component/String/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2019-2022 Fabien Potencier +Copyright (c) 2019-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Templating/LICENSE b/src/Symfony/Component/Templating/LICENSE index 88bf75bb4d6a2..008370457251e 100644 --- a/src/Symfony/Component/Templating/LICENSE +++ b/src/Symfony/Component/Templating/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2022 Fabien Potencier +Copyright (c) 2004-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Translation/Bridge/Crowdin/LICENSE b/src/Symfony/Component/Translation/Bridge/Crowdin/LICENSE index 48d17c4fb34f1..d354b95ffee02 100644 --- a/src/Symfony/Component/Translation/Bridge/Crowdin/LICENSE +++ b/src/Symfony/Component/Translation/Bridge/Crowdin/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2021-2022 Fabien Potencier +Copyright (c) 2021-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Translation/Bridge/Loco/LICENSE b/src/Symfony/Component/Translation/Bridge/Loco/LICENSE index 48d17c4fb34f1..d354b95ffee02 100644 --- a/src/Symfony/Component/Translation/Bridge/Loco/LICENSE +++ b/src/Symfony/Component/Translation/Bridge/Loco/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2021-2022 Fabien Potencier +Copyright (c) 2021-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Translation/Bridge/Lokalise/LICENSE b/src/Symfony/Component/Translation/Bridge/Lokalise/LICENSE index 48d17c4fb34f1..d354b95ffee02 100644 --- a/src/Symfony/Component/Translation/Bridge/Lokalise/LICENSE +++ b/src/Symfony/Component/Translation/Bridge/Lokalise/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2021-2022 Fabien Potencier +Copyright (c) 2021-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Translation/DependencyInjection/TranslatorPass.php b/src/Symfony/Component/Translation/DependencyInjection/TranslatorPass.php index 4b50958eb593b..b060171caebdd 100644 --- a/src/Symfony/Component/Translation/DependencyInjection/TranslatorPass.php +++ b/src/Symfony/Component/Translation/DependencyInjection/TranslatorPass.php @@ -55,8 +55,10 @@ public function process(ContainerBuilder $container) foreach ($container->findTaggedServiceIds('validator.constraint_validator', true) as $id => $attributes) { $serviceDefinition = $container->getDefinition($id); + // Resolve constraint validator FQCN even if defined as %foo.validator.class% parameter + $className = $container->getParameterBag()->resolveValue($serviceDefinition->getClass()); // Extraction of the constraint class name from the Constraint Validator FQCN - $constraintClassNames[] = str_replace('Validator', '', substr(strrchr($serviceDefinition->getClass(), '\\'), 1)); + $constraintClassNames[] = str_replace('Validator', '', substr(strrchr($className, '\\'), 1)); } $constraintVisitorDefinition->setArgument(0, $constraintClassNames); diff --git a/src/Symfony/Component/Translation/Extractor/Visitor/AbstractVisitor.php b/src/Symfony/Component/Translation/Extractor/Visitor/AbstractVisitor.php index 62e7a3da1622b..c341056409115 100644 --- a/src/Symfony/Component/Translation/Extractor/Visitor/AbstractVisitor.php +++ b/src/Symfony/Component/Translation/Extractor/Visitor/AbstractVisitor.php @@ -68,6 +68,19 @@ protected function hasNodeNamedArguments(Node\Expr\CallLike|Node\Attribute|Node\ return false; } + protected function nodeFirstNamedArgumentIndex(Node\Expr\CallLike|Node\Attribute|Node\Expr\New_ $node): int + { + $args = $node instanceof Node\Expr\CallLike ? $node->getRawArgs() : $node->args; + + foreach ($args as $i => $arg) { + if ($arg instanceof Node\Arg && null !== $arg->name) { + return $i; + } + } + + return \PHP_INT_MAX; + } + private function getStringNamedArguments(Node\Expr\CallLike|Node\Attribute $node, string $argumentName = null, bool $isArgumentNamePattern = false): array { $args = $node instanceof Node\Expr\CallLike ? $node->getArgs() : $node->args; diff --git a/src/Symfony/Component/Translation/Extractor/Visitor/TransMethodVisitor.php b/src/Symfony/Component/Translation/Extractor/Visitor/TransMethodVisitor.php index 2c61659427b14..0b537baa24c13 100644 --- a/src/Symfony/Component/Translation/Extractor/Visitor/TransMethodVisitor.php +++ b/src/Symfony/Component/Translation/Extractor/Visitor/TransMethodVisitor.php @@ -37,12 +37,13 @@ public function enterNode(Node $node): ?Node $name = (string) $node->name; if ('trans' === $name || 't' === $name) { - $nodeHasNamedArguments = $this->hasNodeNamedArguments($node); - if (!$messages = $this->getStringArguments($node, $nodeHasNamedArguments ? 'message' : 0)) { + $firstNamedArgumentIndex = $this->nodeFirstNamedArgumentIndex($node); + + if (!$messages = $this->getStringArguments($node, 0 < $firstNamedArgumentIndex ? 0 : 'message')) { return null; } - $domain = $this->getStringArguments($node, $nodeHasNamedArguments ? 'domain' : 2)[0] ?? null; + $domain = $this->getStringArguments($node, 2 < $firstNamedArgumentIndex ? 2 : 'domain')[0] ?? null; foreach ($messages as $message) { $this->addMessageToCatalogue($message, $domain, $node->getStartLine()); diff --git a/src/Symfony/Component/Translation/Extractor/Visitor/TranslatableMessageVisitor.php b/src/Symfony/Component/Translation/Extractor/Visitor/TranslatableMessageVisitor.php index 423982c82ce66..c1505a135437d 100644 --- a/src/Symfony/Component/Translation/Extractor/Visitor/TranslatableMessageVisitor.php +++ b/src/Symfony/Component/Translation/Extractor/Visitor/TranslatableMessageVisitor.php @@ -38,13 +38,13 @@ public function enterNode(Node $node): ?Node return null; } - $nodeHasNamedArguments = $this->hasNodeNamedArguments($node); + $firstNamedArgumentIndex = $this->nodeFirstNamedArgumentIndex($node); - if (!$messages = $this->getStringArguments($node, $nodeHasNamedArguments ? 'message' : 0)) { + if (!$messages = $this->getStringArguments($node, 0 < $firstNamedArgumentIndex ? 0 : 'message')) { return null; } - $domain = $this->getStringArguments($node, $nodeHasNamedArguments ? 'domain' : 2)[0] ?? null; + $domain = $this->getStringArguments($node, 2 < $firstNamedArgumentIndex ? 2 : 'domain')[0] ?? null; foreach ($messages as $message) { $this->addMessageToCatalogue($message, $domain, $node->getStartLine()); diff --git a/src/Symfony/Component/Translation/LICENSE b/src/Symfony/Component/Translation/LICENSE index 88bf75bb4d6a2..008370457251e 100644 --- a/src/Symfony/Component/Translation/LICENSE +++ b/src/Symfony/Component/Translation/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2022 Fabien Potencier +Copyright (c) 2004-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Translation/Tests/DependencyInjection/TranslatorPassTest.php b/src/Symfony/Component/Translation/Tests/DependencyInjection/TranslatorPassTest.php index 624ca3cda0323..1efd932be9014 100644 --- a/src/Symfony/Component/Translation/Tests/DependencyInjection/TranslatorPassTest.php +++ b/src/Symfony/Component/Translation/Tests/DependencyInjection/TranslatorPassTest.php @@ -18,12 +18,10 @@ use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\Translation\DependencyInjection\TranslatorPass; use Symfony\Component\Translation\Extractor\Visitor\ConstraintVisitor; -use Symfony\Component\Validator\Constraints\Isbn; use Symfony\Component\Validator\Constraints\IsbnValidator; use Symfony\Component\Validator\Constraints\LengthValidator; -use Symfony\Component\Validator\Constraints\NotBlank; use Symfony\Component\Validator\Constraints\NotBlankValidator; -use Symfony\Component\Validator\Validator\ValidatorInterface; +use Symfony\Component\Validator\Constraints\TimeValidator; class TranslatorPassTest extends TestCase { @@ -140,10 +138,13 @@ public function testValidPhpAstExtractorConstraintVisitorArguments() ->addTag('validator.constraint_validator'); $container->register('validator.length', LengthValidator::class) ->addTag('validator.constraint_validator'); + $container->register('validator.time', '%foo.time.validator.class%') + ->addTag('validator.constraint_validator'); + $container->setParameter('foo.time.validator.class', TimeValidator::class); $pass = new TranslatorPass(); $pass->process($container); - $this->assertSame(['NotBlank', 'Isbn', 'Length'], $constraintVisitor->getArgument(0)); + $this->assertSame(['NotBlank', 'Isbn', 'Length', 'Time'], $constraintVisitor->getArgument(0)); } } diff --git a/src/Symfony/Component/Translation/Tests/Extractor/PhpAstExtractorTest.php b/src/Symfony/Component/Translation/Tests/Extractor/PhpAstExtractorTest.php index 18f38f93fbec1..2c5b119eba0f9 100644 --- a/src/Symfony/Component/Translation/Tests/Extractor/PhpAstExtractorTest.php +++ b/src/Symfony/Component/Translation/Tests/Extractor/PhpAstExtractorTest.php @@ -91,6 +91,9 @@ public function testExtraction(iterable|string $resource) $expectedNowdoc => 'prefix'.$expectedNowdoc, 'concatenated message with heredoc and nowdoc' => 'prefixconcatenated message with heredoc and nowdoc', 'default domain' => 'prefixdefault domain', + 'mix-named-arguments' => 'prefixmix-named-arguments', + 'mix-named-arguments-locale' => 'prefixmix-named-arguments-locale', + 'mix-named-arguments-without-domain' => 'prefixmix-named-arguments-without-domain', ], 'not_messages' => [ 'translatable other-domain-test-no-params-short-array' => 'prefixtranslatable other-domain-test-no-params-short-array', @@ -119,6 +122,8 @@ public function testExtraction(iterable|string $resource) 'variable-assignation-inlined-in-trans-method-call2' => 'prefixvariable-assignation-inlined-in-trans-method-call2', 'variable-assignation-inlined-in-trans-method-call3' => 'prefixvariable-assignation-inlined-in-trans-method-call3', 'variable-assignation-inlined-with-named-arguments-in-trans-method' => 'prefixvariable-assignation-inlined-with-named-arguments-in-trans-method', + 'mix-named-arguments-without-parameters' => 'prefixmix-named-arguments-without-parameters', + 'mix-named-arguments-disordered' => 'prefixmix-named-arguments-disordered', ], 'validators' => [ 'message-in-constraint-attribute' => 'prefixmessage-in-constraint-attribute', diff --git a/src/Symfony/Component/Translation/Tests/fixtures/extractor-ast/translation.html.php b/src/Symfony/Component/Translation/Tests/fixtures/extractor-ast/translation.html.php index e34863f3dc660..db27b303c5945 100644 --- a/src/Symfony/Component/Translation/Tests/fixtures/extractor-ast/translation.html.php +++ b/src/Symfony/Component/Translation/Tests/fixtures/extractor-ast/translation.html.php @@ -55,4 +55,10 @@ trans(domain: $domain = 'not_messages', message: $key = 'variable-assignation-inlined-with-named-arguments-in-trans-method', parameters: $parameters = []); ?> +trans('mix-named-arguments', parameters: ['foo' => 'bar']); ?> +trans('mix-named-arguments-locale', parameters: ['foo' => 'bar'], locale: 'de'); ?> +trans('mix-named-arguments-without-domain', parameters: ['foo' => 'bar']); ?> +trans('mix-named-arguments-without-parameters', domain: 'not_messages'); ?> +trans('mix-named-arguments-disordered', domain: 'not_messages', parameters: []); ?> + trans(...); // should not fail ?> diff --git a/src/Symfony/Component/Uid/LICENSE b/src/Symfony/Component/Uid/LICENSE index 406242ff28554..0f262c225767a 100644 --- a/src/Symfony/Component/Uid/LICENSE +++ b/src/Symfony/Component/Uid/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2020-2022 Fabien Potencier +Copyright (c) 2020-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Uid/Tests/UuidTest.php b/src/Symfony/Component/Uid/Tests/UuidTest.php index 3ac925aa4194a..f65504a73cb93 100644 --- a/src/Symfony/Component/Uid/Tests/UuidTest.php +++ b/src/Symfony/Component/Uid/Tests/UuidTest.php @@ -204,6 +204,25 @@ public function testIsValid() $this->assertTrue(UuidV4::isValid(self::A_UUID_V4)); } + public function testIsValidWithNilUuid() + { + $this->assertTrue(Uuid::isValid('00000000-0000-0000-0000-000000000000')); + $this->assertTrue(NilUuid::isValid('00000000-0000-0000-0000-000000000000')); + + $this->assertFalse(UuidV1::isValid('00000000-0000-0000-0000-000000000000')); + $this->assertFalse(UuidV4::isValid('00000000-0000-0000-0000-000000000000')); + } + + public function testIsValidWithMaxUuid() + { + $this->assertTrue(Uuid::isValid('ffffffff-ffff-ffff-ffff-ffffffffffff')); + $this->assertTrue(Uuid::isValid('FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF')); + $this->assertTrue(Uuid::isValid('fFFFFFFF-ffff-FFFF-FFFF-FFFFffFFFFFF')); + + $this->assertFalse(UuidV5::isValid('ffffffff-ffff-ffff-ffff-ffffffffffff')); + $this->assertFalse(UuidV6::isValid('ffffffff-ffff-ffff-ffff-ffffffffffff')); + } + public function testEquals() { $uuid1 = new UuidV1(self::A_UUID_V1); diff --git a/src/Symfony/Component/Uid/Uuid.php b/src/Symfony/Component/Uid/Uuid.php index da0bfcb39957a..5b0564f88adc4 100644 --- a/src/Symfony/Component/Uid/Uuid.php +++ b/src/Symfony/Component/Uid/Uuid.php @@ -56,7 +56,7 @@ public static function fromString(string $uuid): static $uuid = substr_replace($uuid, '-', 18, 0); $uuid = substr_replace($uuid, '-', 23, 0); } elseif (26 === \strlen($uuid) && Ulid::isValid($uuid)) { - $ulid = new Ulid('00000000000000000000000000'); + $ulid = new NilUlid(); $ulid->uid = strtoupper($uuid); $uuid = $ulid->toRfc4122(); } @@ -132,6 +132,14 @@ final public static function v8(string $uuid): UuidV8 public static function isValid(string $uuid): bool { + if (self::NIL === $uuid && \in_array(static::class, [__CLASS__, NilUuid::class], true)) { + return true; + } + + if (self::MAX === strtr($uuid, 'F', 'f') && \in_array(static::class, [__CLASS__, MaxUuid::class], true)) { + return true; + } + if (!preg_match('{^[0-9a-f]{8}(?:-[0-9a-f]{4}){2}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$}Di', $uuid)) { return false; } diff --git a/src/Symfony/Component/Validator/Constraints/EmailValidator.php b/src/Symfony/Component/Validator/Constraints/EmailValidator.php index 6ff845bb6f712..df5cd14081d2f 100644 --- a/src/Symfony/Component/Validator/Constraints/EmailValidator.php +++ b/src/Symfony/Component/Validator/Constraints/EmailValidator.php @@ -16,6 +16,7 @@ use Egulias\EmailValidator\Validation\NoRFCWarningsValidation; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\ConstraintValidator; +use Symfony\Component\Validator\Exception\LogicException; use Symfony\Component\Validator\Exception\UnexpectedTypeException; use Symfony\Component\Validator\Exception\UnexpectedValueException; @@ -74,7 +75,7 @@ public function validate(mixed $value, Constraint $constraint) if (null === $constraint->mode) { if (Email::VALIDATION_MODE_STRICT === $this->defaultMode && !class_exists(EguliasEmailValidator::class)) { - throw new LogicException(sprintf('The "egulias/email-validator" component is required to make the "%s" constraint default to strict mode.', EguliasEmailValidator::class)); + throw new LogicException(sprintf('The "egulias/email-validator" component is required to make the "%s" constraint default to strict mode.', Email::class)); } $constraint->mode = $this->defaultMode; diff --git a/src/Symfony/Component/Validator/Constraints/FileValidator.php b/src/Symfony/Component/Validator/Constraints/FileValidator.php index e4e02c80fd18b..363183ec4d4bb 100644 --- a/src/Symfony/Component/Validator/Constraints/FileValidator.php +++ b/src/Symfony/Component/Validator/Constraints/FileValidator.php @@ -171,7 +171,7 @@ public function validate(mixed $value, Constraint $constraint) $mimeTypes = (array) $constraint->mimeTypes; if ($constraint->extensions) { - $fileExtension = pathinfo($basename, \PATHINFO_EXTENSION); + $fileExtension = strtolower(pathinfo($basename, \PATHINFO_EXTENSION)); $found = false; $normalizedExtensions = []; diff --git a/src/Symfony/Component/Validator/Constraints/UniqueValidator.php b/src/Symfony/Component/Validator/Constraints/UniqueValidator.php index de2edce631837..09278454f7400 100644 --- a/src/Symfony/Component/Validator/Constraints/UniqueValidator.php +++ b/src/Symfony/Component/Validator/Constraints/UniqueValidator.php @@ -76,7 +76,7 @@ private function reduceElementKeys(array $fields, array $element): array if (!\is_string($field)) { throw new UnexpectedTypeException($field, 'string'); } - if (isset($element[$field])) { + if (\array_key_exists($field, $element)) { $output[$field] = $element[$field]; } } diff --git a/src/Symfony/Component/Validator/LICENSE b/src/Symfony/Component/Validator/LICENSE index 88bf75bb4d6a2..008370457251e 100644 --- a/src/Symfony/Component/Validator/LICENSE +++ b/src/Symfony/Component/Validator/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2022 Fabien Potencier +Copyright (c) 2004-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/Validator/Tests/Constraints/FileValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/FileValidatorTest.php index bb2b19eef204e..3643adee821a3 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/FileValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/FileValidatorTest.php @@ -556,6 +556,7 @@ private function validExtensionProvider(): iterable yield ['test.gif']; yield ['test.png.gif']; yield ['ccc.txt']; + yield ['uppercased-extension.TXT']; } /** diff --git a/src/Symfony/Component/Validator/Tests/Constraints/Fixtures/uppercased-extension.TXT b/src/Symfony/Component/Validator/Tests/Constraints/Fixtures/uppercased-extension.TXT new file mode 100644 index 0000000000000..323fae03f4606 --- /dev/null +++ b/src/Symfony/Component/Validator/Tests/Constraints/Fixtures/uppercased-extension.TXT @@ -0,0 +1 @@ +foobar diff --git a/src/Symfony/Component/Validator/Tests/Constraints/UniqueValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/UniqueValidatorTest.php index eeaa6f8cdfa26..d4540dcc4585b 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/UniqueValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/UniqueValidatorTest.php @@ -281,6 +281,14 @@ public function getInvalidCollectionValues(): array ['id' => 1, 'email' => 'bar@email.com'], ['id' => 1, 'email' => 'foo@email.com'], ], ['id']], + 'unique null' => [ + [null, null], + [], + ], + 'unique field null' => [ + [['nullField' => null], ['nullField' => null]], + ['nullField'], + ], ]; } } diff --git a/src/Symfony/Component/Validator/composer.json b/src/Symfony/Component/Validator/composer.json index 2691295eada91..d6e690c02e8b5 100644 --- a/src/Symfony/Component/Validator/composer.json +++ b/src/Symfony/Component/Validator/composer.json @@ -39,7 +39,7 @@ "symfony/property-info": "^5.4|^6.0", "symfony/translation": "^5.4|^6.0", "doctrine/annotations": "^1.13|^2", - "egulias/email-validator": "^2.1.10|^3" + "egulias/email-validator": "^2.1.10|^3|^4" }, "conflict": { "doctrine/annotations": "<1.13", diff --git a/src/Symfony/Component/VarDumper/Dumper/HtmlDumper.php b/src/Symfony/Component/VarDumper/Dumper/HtmlDumper.php index 843df10da8ee8..c818c919c43b1 100644 --- a/src/Symfony/Component/VarDumper/Dumper/HtmlDumper.php +++ b/src/Symfony/Component/VarDumper/Dumper/HtmlDumper.php @@ -158,9 +158,9 @@ protected function getDumpHeader() }; refStyle.innerHTML = 'pre.sf-dump .sf-dump-compact, .sf-dump-str-collapse .sf-dump-str-collapse, .sf-dump-str-expand .sf-dump-str-expand { display: none; }'; -(doc.documentElement.firstElementChild || doc.documentElement.children[0]).appendChild(refStyle); +doc.head.appendChild(refStyle); refStyle = doc.createElement('style'); -(doc.documentElement.firstElementChild || doc.documentElement.children[0]).appendChild(refStyle); +doc.head.appendChild(refStyle); if (!doc.addEventListener) { addEventListener = function (element, eventName, callback) { diff --git a/src/Symfony/Component/VarDumper/LICENSE b/src/Symfony/Component/VarDumper/LICENSE index a843ec124ea70..72412a62b2029 100644 --- a/src/Symfony/Component/VarDumper/LICENSE +++ b/src/Symfony/Component/VarDumper/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2014-2022 Fabien Potencier +Copyright (c) 2014-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/VarExporter/Internal/Exporter.php b/src/Symfony/Component/VarExporter/Internal/Exporter.php index 24f587b7fcb90..ae12ec414a79f 100644 --- a/src/Symfony/Component/VarExporter/Internal/Exporter.php +++ b/src/Symfony/Component/VarExporter/Internal/Exporter.php @@ -200,7 +200,7 @@ public static function export($value, $indent = '') case true === $value: return 'true'; case null === $value: return 'null'; case '' === $value: return "''"; - case $value instanceof \UnitEnum: return ltrim(var_export($value, true), '\\'); + case $value instanceof \UnitEnum: return '\\'.ltrim(var_export($value, true), '\\'); } if ($value instanceof Reference) { diff --git a/src/Symfony/Component/VarExporter/LICENSE b/src/Symfony/Component/VarExporter/LICENSE index 74cdc2dbf6dbe..99757d517117d 100644 --- a/src/Symfony/Component/VarExporter/LICENSE +++ b/src/Symfony/Component/VarExporter/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2018-2022 Fabien Potencier +Copyright (c) 2018-2023 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Component/VarExporter/LazyGhostTrait.php b/src/Symfony/Component/VarExporter/LazyGhostTrait.php index a0dea4dd5b8ee..92c240b1f2f96 100644 --- a/src/Symfony/Component/VarExporter/LazyGhostTrait.php +++ b/src/Symfony/Component/VarExporter/LazyGhostTrait.php @@ -41,8 +41,9 @@ trait LazyGhostTrait * |array{"\0": \Closure(static, array):array}) $initializer * @param array|null $skippedProperties An array indexed by the properties to skip, aka the ones * that the initializer doesn't set when its a closure + * @param static|null $instance */ - public static function createLazyGhost(\Closure|array $initializer, array $skippedProperties = null, self $instance = null): static + public static function createLazyGhost(\Closure|array $initializer, array $skippedProperties = null, object $instance = null): static { $onlyProperties = null === $skippedProperties && \is_array($initializer) ? $initializer : null; diff --git a/src/Symfony/Component/VarExporter/LazyProxyTrait.php b/src/Symfony/Component/VarExporter/LazyProxyTrait.php index fa6a741fd6d40..71ecb5b2c7ac8 100644 --- a/src/Symfony/Component/VarExporter/LazyProxyTrait.php +++ b/src/Symfony/Component/VarExporter/LazyProxyTrait.php @@ -25,8 +25,9 @@ trait LazyProxyTrait * Creates a lazy-loading virtual proxy. * * @param \Closure():object $initializer Returns the proxied object + * @param static|null $instance */ - public static function createLazyProxy(\Closure $initializer, self $instance = null): static + public static function createLazyProxy(\Closure $initializer, object $instance = null): static { if (self::class !== $class = $instance ? $instance::class : static::class) { $skippedProperties = ["\0".self::class."\0lazyObjectState" => true]; diff --git a/src/Symfony/Component/VarExporter/Tests/Fixtures/unit-enum.php b/src/Symfony/Component/VarExporter/Tests/Fixtures/unit-enum.php index 48ab51dd0141b..98bc12272ed5d 100644 --- a/src/Symfony/Component/VarExporter/Tests/Fixtures/unit-enum.php +++ b/src/Symfony/Component/VarExporter/Tests/Fixtures/unit-enum.php @@ -1,5 +1,5 @@