From 81d085897a1b8598cd4d5f1b0cec633fbe648997 Mon Sep 17 00:00:00 2001 From: MatTheCat Date: Wed, 16 Nov 2022 15:28:59 +0100 Subject: [PATCH 01/23] [DependencyInjection] Process bindings in ServiceLocatorTagPass --- .../Compiler/ServiceLocatorTagPass.php | 4 ++++ .../Tests/Compiler/ServiceLocatorTagPassTest.php | 13 +++++++++++++ 2 files changed, 17 insertions(+) diff --git a/src/Symfony/Component/DependencyInjection/Compiler/ServiceLocatorTagPass.php b/src/Symfony/Component/DependencyInjection/Compiler/ServiceLocatorTagPass.php index 5fdbe5686dbfa..72b093043bf15 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/ServiceLocatorTagPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/ServiceLocatorTagPass.php @@ -39,6 +39,10 @@ protected function processValue($value, $isRoot = false) return self::register($this->container, $value->getValues()); } + if ($value instanceof Definition) { + $value->setBindings(parent::processValue($value->getBindings())); + } + if (!$value instanceof Definition || !$value->hasTag('container.service_locator')) { return parent::processValue($value, $isRoot); } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ServiceLocatorTagPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ServiceLocatorTagPassTest.php index 25063d35ff3b5..bf6428ee2de47 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ServiceLocatorTagPassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ServiceLocatorTagPassTest.php @@ -13,6 +13,7 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\DependencyInjection\Argument\BoundArgument; +use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument; use Symfony\Component\DependencyInjection\Compiler\ServiceLocatorTagPass; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; @@ -143,4 +144,16 @@ public function testBindingsAreCopied() $this->assertSame(['foo'], array_keys($locator->getBindings())); $this->assertInstanceOf(BoundArgument::class, $locator->getBindings()['foo']); } + + public function testBindingsAreProcessed() + { + $container = new ContainerBuilder(); + + $definition = $container->register('foo') + ->setBindings(['foo' => new ServiceLocatorArgument()]); + + (new ServiceLocatorTagPass())->process($container); + + $this->assertInstanceOf(Reference::class, $definition->getBindings()['foo']->getValues()[0]); + } } From 17e096dcaf423b4ad9e68bdc2882d95d884feab4 Mon Sep 17 00:00:00 2001 From: Thomas Calvet Date: Mon, 14 Nov 2022 17:09:36 +0100 Subject: [PATCH 02/23] Fix some native constant and function invocations --- src/Symfony/Bridge/PhpUnit/ClockMock.php | 2 +- src/Symfony/Component/Console/Helper/QuestionHelper.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Bridge/PhpUnit/ClockMock.php b/src/Symfony/Bridge/PhpUnit/ClockMock.php index 4f826f1c84b44..7ec22ccd85cb3 100644 --- a/src/Symfony/Bridge/PhpUnit/ClockMock.php +++ b/src/Symfony/Bridge/PhpUnit/ClockMock.php @@ -97,7 +97,7 @@ public static function hrtime($asNumber = false) if ($asNumber) { $number = sprintf('%d%d', (int) self::$now, $ns); - return PHP_INT_SIZE === 8 ? (int) $number : (float) $number; + return \PHP_INT_SIZE === 8 ? (int) $number : (float) $number; } return [(int) self::$now, (int) $ns]; diff --git a/src/Symfony/Component/Console/Helper/QuestionHelper.php b/src/Symfony/Component/Console/Helper/QuestionHelper.php index 76cf77df5e195..c345b4af7747f 100644 --- a/src/Symfony/Component/Console/Helper/QuestionHelper.php +++ b/src/Symfony/Component/Console/Helper/QuestionHelper.php @@ -430,7 +430,7 @@ private function getHiddenResponse(OutputInterface $output, $inputStream, bool $ $value = fgets($inputStream, 4096); - if (4095 === strlen($value)) { + if (4095 === \strlen($value)) { $errOutput = $output instanceof ConsoleOutputInterface ? $output->getErrorOutput() : $output; $errOutput->warning('The value was possibly truncated by your shell or terminal emulator'); } From 13212789fdd454f5415fe8a096dc8b01bfa3e706 Mon Sep 17 00:00:00 2001 From: Chi-teck Date: Wed, 9 Nov 2022 22:12:42 +0500 Subject: [PATCH 03/23] Support completion for bash functions --- src/Symfony/Component/Console/Resources/completion.bash | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Symfony/Component/Console/Resources/completion.bash b/src/Symfony/Component/Console/Resources/completion.bash index bf3edf511b2cb..64b87ccf7c7d5 100644 --- a/src/Symfony/Component/Console/Resources/completion.bash +++ b/src/Symfony/Component/Console/Resources/completion.bash @@ -11,13 +11,14 @@ _sf_{{ COMMAND_NAME }}() { local sf_cmd="${COMP_WORDS[0]}" # for an alias, get the real script behind it - if [[ $(type -t $sf_cmd) == "alias" ]]; then + sf_cmd_type=$(type -t $sf_cmd) + if [[ $sf_cmd_type == "alias" ]]; then sf_cmd=$(alias $sf_cmd | sed -E "s/alias $sf_cmd='(.*)'/\1/") - else + elif [[ $sf_cmd_type == "file" ]]; then sf_cmd=$(type -p $sf_cmd) fi - if [ ! -x "$sf_cmd" ]; then + if [[ $sf_cmd_type != "function" && ! -x $sf_cmd ]]; then return 1 fi From debae84b1999be830276cd9f98a11ae946283da4 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Sat, 19 Nov 2022 17:15:51 +0100 Subject: [PATCH 04/23] Bump Symfony version to 6.2.0 --- src/Symfony/Component/HttpKernel/Kernel.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index 613356dd5af51..3661c216a2c4b 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -75,12 +75,12 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl */ private static array $freshCache = []; - public const VERSION = '6.2.0-BETA3'; + public const VERSION = '6.2.0-DEV'; public const VERSION_ID = 60200; public const MAJOR_VERSION = 6; public const MINOR_VERSION = 2; public const RELEASE_VERSION = 0; - public const EXTRA_VERSION = 'BETA3'; + public const EXTRA_VERSION = 'DEV'; public const END_OF_MAINTENANCE = '07/2023'; public const END_OF_LIFE = '07/2023'; From be5b76abcd5ee351528e3317c4357250f97e7607 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Sat, 19 Nov 2022 18:35:30 +0100 Subject: [PATCH 05/23] ignore const expressions read by phpdocumentor With the upcoming release, phpdocumentator will use the PhpStan docblock parser to extract type information. This change ensure that constant expressions are ignored when extracting types (as we did before when phpdocumentor failed to extract the type) as we do not evaluate them inside the PhpDocExtractor. --- .../Component/PropertyInfo/Util/PhpDocTypeHelper.php | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/Symfony/Component/PropertyInfo/Util/PhpDocTypeHelper.php b/src/Symfony/Component/PropertyInfo/Util/PhpDocTypeHelper.php index cafbaaf231b52..c4a2edb174900 100644 --- a/src/Symfony/Component/PropertyInfo/Util/PhpDocTypeHelper.php +++ b/src/Symfony/Component/PropertyInfo/Util/PhpDocTypeHelper.php @@ -11,6 +11,7 @@ namespace Symfony\Component\PropertyInfo\Util; +use phpDocumentor\Reflection\PseudoTypes\ConstExpression; use phpDocumentor\Reflection\PseudoTypes\List_; use phpDocumentor\Reflection\Type as DocType; use phpDocumentor\Reflection\Types\Array_; @@ -39,6 +40,11 @@ final class PhpDocTypeHelper */ public function getTypes(DocType $varType): array { + if ($varType instanceof ConstExpression) { + // It's safer to fall back to other extractors here, as resolving const types correctly is not easy at the moment + return []; + } + $types = []; $nullable = false; @@ -64,6 +70,11 @@ public function getTypes(DocType $varType): array for ($typeIndex = 0; $varType->has($typeIndex); ++$typeIndex) { $type = $varType->get($typeIndex); + if ($type instanceof ConstExpression) { + // It's safer to fall back to other extractors here, as resolving const types correctly is not easy at the moment + return []; + } + // If null is present, all types are nullable if ($type instanceof Null_) { $nullable = true; From ec3e9450aaed5ad0af81d8db7cb8c6afe880ce46 Mon Sep 17 00:00:00 2001 From: prudhomme victor Date: Sun, 20 Nov 2022 14:01:27 +0100 Subject: [PATCH 06/23] [FileSystem] Use Yoda condition --- src/Symfony/Component/Filesystem/Tests/FilesystemTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/Filesystem/Tests/FilesystemTest.php b/src/Symfony/Component/Filesystem/Tests/FilesystemTest.php index 00a757d637645..c7ab562f2ef75 100644 --- a/src/Symfony/Component/Filesystem/Tests/FilesystemTest.php +++ b/src/Symfony/Component/Filesystem/Tests/FilesystemTest.php @@ -1768,7 +1768,7 @@ public function testDumpToProtectedDirectory() $this->markTestSkipped('This test is specific to Windows.'); } - if (($userProfilePath = getenv('USERPROFILE')) === false || !is_dir($userProfilePath)) { + if (false === ($userProfilePath = getenv('USERPROFILE')) || !is_dir($userProfilePath)) { throw new \RuntimeException('Failed to retrieve user profile path.'); } From 99fca7b9b1811cb56a4b72340b00b6261a3c118c Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Mon, 21 Nov 2022 15:08:01 +0100 Subject: [PATCH 07/23] [HttpKernel] Remove duplicate+unclear deprecation --- .../Controller/ArgumentResolver/TraceableValueResolver.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/TraceableValueResolver.php b/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/TraceableValueResolver.php index eb4ac7b06a217..edc30e1806c13 100644 --- a/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/TraceableValueResolver.php +++ b/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/TraceableValueResolver.php @@ -42,8 +42,6 @@ public function supports(Request $request, ArgumentMetadata $argument): bool return true; } - @trigger_deprecation('symfony/http-kernel', '6.2', 'The "%s()" method is deprecated, use "resolve()" instead.', __METHOD__); - $method = \get_class($this->inner).'::'.__FUNCTION__; $this->stopwatch->start($method, 'controller.argument_value_resolver'); From 39bf5602664281802dfcd66b8c241088769e4de7 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Mon, 21 Nov 2022 16:19:58 +0100 Subject: [PATCH 08/23] [HttpFoundation] Rename session expiry index --- src/Symfony/Component/HttpFoundation/CHANGELOG.md | 2 +- .../Session/Storage/Handler/PdoSessionHandler.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/HttpFoundation/CHANGELOG.md b/src/Symfony/Component/HttpFoundation/CHANGELOG.md index 790767d728190..fdea3d67e1b19 100644 --- a/src/Symfony/Component/HttpFoundation/CHANGELOG.md +++ b/src/Symfony/Component/HttpFoundation/CHANGELOG.md @@ -104,7 +104,7 @@ CHANGELOG make sure to run `ALTER TABLE sessions MODIFY sess_lifetime INTEGER UNSIGNED NOT NULL` to update your database. * `PdoSessionHandler` now precalculates the expiry timestamp in the lifetime column, - make sure to run `CREATE INDEX EXPIRY ON sessions (sess_lifetime)` to update your database + make sure to run `CREATE INDEX expiry ON sessions (sess_lifetime)` to update your database to speed up garbage collection of expired sessions. * added `SessionHandlerFactory` to create session handlers with a DSN * added `IpUtils::anonymize()` to help with GDPR compliance. diff --git a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php index 9e26ac56e1249..302372627ddb5 100644 --- a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php +++ b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php @@ -207,7 +207,7 @@ public function createTable() try { $this->pdo->exec($sql); - $this->pdo->exec("CREATE INDEX EXPIRY ON $this->table ($this->lifetimeCol)"); + $this->pdo->exec("CREATE INDEX expiry ON $this->table ($this->lifetimeCol)"); } catch (\PDOException $e) { $this->rollback(); From a1498da386fd59d56c85e4e8a35e226311f09787 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Mon, 21 Nov 2022 17:07:56 +0100 Subject: [PATCH 09/23] [WebProfilerBundle] Fix form panel when there are no view vars --- .../WebProfilerBundle/Resources/views/Collector/form.html.twig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/form.html.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/form.html.twig index 2f2f21a9f45c1..1dad6d8a8361e 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/form.html.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/form.html.twig @@ -714,7 +714,7 @@ - {% for variable, value in data.view_vars %} + {% for variable, value in data.view_vars ?? [] %} {{ variable }} {{ profiler_dump(value) }} From a379c354ac145a44f926dfedf9bbeec861ef8e94 Mon Sep 17 00:00:00 2001 From: Thomas Calvet Date: Mon, 21 Nov 2022 18:37:52 +0100 Subject: [PATCH 10/23] [HttpKernel] Fix message for unresovable arguments of invokable controllers --- .../NotTaggedControllerValueResolver.php | 5 +++-- .../NotTaggedControllerValueResolverTest.php | 11 +++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/NotTaggedControllerValueResolver.php b/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/NotTaggedControllerValueResolver.php index d4971cc1a5074..48ea6e742d519 100644 --- a/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/NotTaggedControllerValueResolver.php +++ b/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/NotTaggedControllerValueResolver.php @@ -69,8 +69,9 @@ public function resolve(Request $request, ArgumentMetadata $argument): iterable } if (!$this->container->has($controller)) { - $i = strrpos($controller, ':'); - $controller = substr($controller, 0, $i).strtolower(substr($controller, $i)); + $controller = (false !== $i = strrpos($controller, ':')) + ? substr($controller, 0, $i).strtolower(substr($controller, $i)) + : $controller.'::__invoke'; } $what = sprintf('argument $%s of "%s()"', $argument->getName(), $controller); diff --git a/src/Symfony/Component/HttpKernel/Tests/Controller/ArgumentResolver/NotTaggedControllerValueResolverTest.php b/src/Symfony/Component/HttpKernel/Tests/Controller/ArgumentResolver/NotTaggedControllerValueResolverTest.php index 3cf2f0f18562e..4577b5a6d2384 100644 --- a/src/Symfony/Component/HttpKernel/Tests/Controller/ArgumentResolver/NotTaggedControllerValueResolverTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/Controller/ArgumentResolver/NotTaggedControllerValueResolverTest.php @@ -98,6 +98,17 @@ public function testControllerNameIsAnArray() $resolver->resolve($request, $argument); } + public function testInvokableController() + { + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage('Could not resolve argument $dummy of "App\Controller\Mine::__invoke()", maybe you forgot to register the controller as a service or missed tagging it with the "controller.service_arguments"?'); + $resolver = new NotTaggedControllerValueResolver(new ServiceLocator([])); + $argument = new ArgumentMetadata('dummy', \stdClass::class, false, false, null); + $request = $this->requestWithAttributes(['_controller' => 'App\Controller\Mine']); + $this->assertTrue($resolver->supports($request, $argument)); + $resolver->resolve($request, $argument); + } + private function requestWithAttributes(array $attributes) { $request = Request::create('/'); From 8ad964261361ce3cb2f06e5507d71cc68dc65132 Mon Sep 17 00:00:00 2001 From: Thomas Calvet Date: Mon, 21 Nov 2022 19:36:40 +0100 Subject: [PATCH 11/23] Add more #[\SensitiveParameter] --- src/Symfony/Bundle/FrameworkBundle/Secrets/SodiumVault.php | 2 +- src/Symfony/Component/Security/Csrf/CsrfTokenManager.php | 2 +- .../Security/Csrf/TokenStorage/NativeSessionTokenStorage.php | 2 +- .../Security/Csrf/TokenStorage/SessionTokenStorage.php | 2 +- .../Security/Csrf/TokenStorage/TokenStorageInterface.php | 2 +- .../Security/Http/AccessToken/AccessTokenHandlerInterface.php | 2 +- .../Http/Authenticator/Passport/Badge/CsrfTokenBadge.php | 2 +- .../Http/Authenticator/Passport/Badge/PasswordUpgradeBadge.php | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/Secrets/SodiumVault.php b/src/Symfony/Bundle/FrameworkBundle/Secrets/SodiumVault.php index e8ce7d8f95eec..b6bb058b3f170 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Secrets/SodiumVault.php +++ b/src/Symfony/Bundle/FrameworkBundle/Secrets/SodiumVault.php @@ -30,7 +30,7 @@ class SodiumVault extends AbstractVault implements EnvVarLoaderInterface * @param $decryptionKey A string or a stringable object that defines the private key to use to decrypt the vault * or null to store generated keys in the provided $secretsDir */ - public function __construct(string $secretsDir, string|\Stringable $decryptionKey = null) + public function __construct(string $secretsDir, #[\SensitiveParameter] string|\Stringable $decryptionKey = null) { $this->pathPrefix = rtrim(strtr($secretsDir, '/', \DIRECTORY_SEPARATOR), \DIRECTORY_SEPARATOR).\DIRECTORY_SEPARATOR.basename($secretsDir).'.'; $this->decryptionKey = $decryptionKey; diff --git a/src/Symfony/Component/Security/Csrf/CsrfTokenManager.php b/src/Symfony/Component/Security/Csrf/CsrfTokenManager.php index 7d6dbda5489f4..7cefe8b0a0572 100644 --- a/src/Symfony/Component/Security/Csrf/CsrfTokenManager.php +++ b/src/Symfony/Component/Security/Csrf/CsrfTokenManager.php @@ -79,7 +79,7 @@ public function getToken(string $tokenId): CsrfToken return new CsrfToken($tokenId, $this->randomize($value)); } - public function refreshToken(#[\SensitiveParameter] string $tokenId): CsrfToken + public function refreshToken(string $tokenId): CsrfToken { $namespacedId = $this->getNamespace().$tokenId; $value = $this->generator->generateToken(); diff --git a/src/Symfony/Component/Security/Csrf/TokenStorage/NativeSessionTokenStorage.php b/src/Symfony/Component/Security/Csrf/TokenStorage/NativeSessionTokenStorage.php index 15bdaaf82046e..cf0ce03e9311d 100644 --- a/src/Symfony/Component/Security/Csrf/TokenStorage/NativeSessionTokenStorage.php +++ b/src/Symfony/Component/Security/Csrf/TokenStorage/NativeSessionTokenStorage.php @@ -51,7 +51,7 @@ public function getToken(string $tokenId): string return (string) $_SESSION[$this->namespace][$tokenId]; } - public function setToken(string $tokenId, string $token) + public function setToken(string $tokenId, #[\SensitiveParameter] string $token) { if (!$this->sessionStarted) { $this->startSession(); diff --git a/src/Symfony/Component/Security/Csrf/TokenStorage/SessionTokenStorage.php b/src/Symfony/Component/Security/Csrf/TokenStorage/SessionTokenStorage.php index bed96fa4c2960..fdbaf135d7654 100644 --- a/src/Symfony/Component/Security/Csrf/TokenStorage/SessionTokenStorage.php +++ b/src/Symfony/Component/Security/Csrf/TokenStorage/SessionTokenStorage.php @@ -56,7 +56,7 @@ public function getToken(string $tokenId): string return (string) $session->get($this->namespace.'/'.$tokenId); } - public function setToken(string $tokenId, string $token) + public function setToken(string $tokenId, #[\SensitiveParameter] string $token) { $session = $this->getSession(); if (!$session->isStarted()) { diff --git a/src/Symfony/Component/Security/Csrf/TokenStorage/TokenStorageInterface.php b/src/Symfony/Component/Security/Csrf/TokenStorage/TokenStorageInterface.php index a26439366e2ab..d119d6e977bcb 100644 --- a/src/Symfony/Component/Security/Csrf/TokenStorage/TokenStorageInterface.php +++ b/src/Symfony/Component/Security/Csrf/TokenStorage/TokenStorageInterface.php @@ -28,7 +28,7 @@ public function getToken(string $tokenId): string; /** * Stores a CSRF token. */ - public function setToken(string $tokenId, string $token); + public function setToken(string $tokenId, #[\SensitiveParameter] string $token); /** * Removes a CSRF token. diff --git a/src/Symfony/Component/Security/Http/AccessToken/AccessTokenHandlerInterface.php b/src/Symfony/Component/Security/Http/AccessToken/AccessTokenHandlerInterface.php index 33a0690d15cc5..8044a371293aa 100644 --- a/src/Symfony/Component/Security/Http/AccessToken/AccessTokenHandlerInterface.php +++ b/src/Symfony/Component/Security/Http/AccessToken/AccessTokenHandlerInterface.php @@ -24,5 +24,5 @@ interface AccessTokenHandlerInterface /** * @throws AuthenticationException */ - public function getUserIdentifierFrom(string $accessToken): string; + public function getUserIdentifierFrom(#[\SensitiveParameter] string $accessToken): string; } diff --git a/src/Symfony/Component/Security/Http/Authenticator/Passport/Badge/CsrfTokenBadge.php b/src/Symfony/Component/Security/Http/Authenticator/Passport/Badge/CsrfTokenBadge.php index b24e2c75f548c..52aeafe858eb1 100644 --- a/src/Symfony/Component/Security/Http/Authenticator/Passport/Badge/CsrfTokenBadge.php +++ b/src/Symfony/Component/Security/Http/Authenticator/Passport/Badge/CsrfTokenBadge.php @@ -33,7 +33,7 @@ class CsrfTokenBadge implements BadgeInterface * Using a different string for each authenticator improves its security. * @param string|null $csrfToken The CSRF token presented in the request, if any */ - public function __construct(string $csrfTokenId, ?string $csrfToken) + public function __construct(string $csrfTokenId, #[\SensitiveParameter] ?string $csrfToken) { $this->csrfTokenId = $csrfTokenId; $this->csrfToken = $csrfToken; diff --git a/src/Symfony/Component/Security/Http/Authenticator/Passport/Badge/PasswordUpgradeBadge.php b/src/Symfony/Component/Security/Http/Authenticator/Passport/Badge/PasswordUpgradeBadge.php index 992b50d0811c1..9cbf6d6e69343 100644 --- a/src/Symfony/Component/Security/Http/Authenticator/Passport/Badge/PasswordUpgradeBadge.php +++ b/src/Symfony/Component/Security/Http/Authenticator/Passport/Badge/PasswordUpgradeBadge.php @@ -32,7 +32,7 @@ class PasswordUpgradeBadge implements BadgeInterface * @param string $plaintextPassword The presented password, used in the rehash * @param PasswordUpgraderInterface|null $passwordUpgrader The password upgrader, defaults to the UserProvider if null */ - public function __construct(string $plaintextPassword, PasswordUpgraderInterface $passwordUpgrader = null) + public function __construct(#[\SensitiveParameter] string $plaintextPassword, PasswordUpgraderInterface $passwordUpgrader = null) { $this->plaintextPassword = $plaintextPassword; $this->passwordUpgrader = $passwordUpgrader; From cd7e4d64b78ac0ac26cd346434277a124dfb951d Mon Sep 17 00:00:00 2001 From: Vasilij Dusko Date: Sun, 20 Nov 2022 21:43:58 +0200 Subject: [PATCH 12/23] [Notifier] [SMSBiuras] `true`/`false` mismatch for `test_mode` option --- .../Bridge/SmsBiuras/SmsBiurasTransport.php | 2 +- .../Tests/SmsBiurasTransportTest.php | 78 +++++++++++++++++++ 2 files changed, 79 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/Notifier/Bridge/SmsBiuras/SmsBiurasTransport.php b/src/Symfony/Component/Notifier/Bridge/SmsBiuras/SmsBiurasTransport.php index b47c5c216eba0..79b3268016f36 100644 --- a/src/Symfony/Component/Notifier/Bridge/SmsBiuras/SmsBiurasTransport.php +++ b/src/Symfony/Component/Notifier/Bridge/SmsBiuras/SmsBiurasTransport.php @@ -85,7 +85,7 @@ protected function doSend(MessageInterface $message): SentMessage 'apikey' => $this->apiKey, 'message' => $message->getSubject(), 'from' => $this->from, - 'test' => $this->testMode ? 0 : 1, + 'test' => $this->testMode ? 1 : 0, 'to' => $message->getPhone(), ], ]); diff --git a/src/Symfony/Component/Notifier/Bridge/SmsBiuras/Tests/SmsBiurasTransportTest.php b/src/Symfony/Component/Notifier/Bridge/SmsBiuras/Tests/SmsBiurasTransportTest.php index 494e66c3bca23..6e0461b9828e6 100644 --- a/src/Symfony/Component/Notifier/Bridge/SmsBiuras/Tests/SmsBiurasTransportTest.php +++ b/src/Symfony/Component/Notifier/Bridge/SmsBiuras/Tests/SmsBiurasTransportTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Notifier\Bridge\SmsBiuras\Tests; +use Symfony\Component\HttpClient\MockHttpClient; use Symfony\Component\Notifier\Bridge\SmsBiuras\SmsBiurasTransport; use Symfony\Component\Notifier\Message\ChatMessage; use Symfony\Component\Notifier\Message\MessageInterface; @@ -18,6 +19,7 @@ use Symfony\Component\Notifier\Test\TransportTestCase; use Symfony\Component\Notifier\Transport\TransportInterface; use Symfony\Contracts\HttpClient\HttpClientInterface; +use Symfony\Contracts\HttpClient\ResponseInterface; final class SmsBiurasTransportTest extends TransportTestCase { @@ -44,4 +46,80 @@ public function unsupportedMessagesProvider(): iterable yield [new ChatMessage('Hello!')]; yield [$this->createMock(MessageInterface::class)]; } + + /** + * @dataProvider provideTestMode() + */ + public function testTestMode(array $expected, array $provided) + { + $message = new SmsMessage($provided['phone'], $provided['message']); + + $response = $this->createMock(ResponseInterface::class); + $response->expects($this->atLeast(1)) + ->method('getStatusCode') + ->willReturn(200); + $response->expects($this->atLeast(1)) + ->method('getContent') + ->willReturn('OK: 519545'); + + $client = new MockHttpClient(function (string $method, string $url, array $options = []) use ($response, $expected): ResponseInterface { + $this->assertSame('GET', $method); + $this->assertSame(sprintf( + 'https://savitarna.smsbiuras.lt/api?uid=uid&apikey=api_key&message=%s&from=from&test=%s&to=%s', + rawurlencode($expected['message']), + $expected['transport']['test_mode'], + rawurlencode($expected['phone']), + ), $url); + $this->assertSame($expected['transport']['test_mode'], $options['query']['test']); + + $this->assertSame(200, $response->getStatusCode()); + $this->assertSame('OK: 519545', $response->getContent()); + + return $response; + }); + + $transport = new SmsBiurasTransport('uid', 'api_key', 'from', $provided['transport']['test_mode'], $client); + + $sentMessage = $transport->send($message); + + $this->assertSame('519545', $sentMessage->getMessageId()); + } + + public static function provideTestMode(): array + { + return [ + [ + [ + 'phone' => '+37012345678', + 'message' => 'Hello world!', + 'transport' => [ + 'test_mode' => 0, + ], + ], + [ + 'phone' => '+37012345678', + 'message' => 'Hello world!', + 'transport' => [ + 'test_mode' => 0, + ], + ], + ], + [ + [ + 'phone' => '+37012345678', + 'message' => 'Hello world!', + 'transport' => [ + 'test_mode' => 1, + ], + ], + [ + 'phone' => '+37012345678', + 'message' => 'Hello world!', + 'transport' => [ + 'test_mode' => 1, + ], + ], + ], + ]; + } } From 9221451b6c1156c240dcbb937bc034a63f38f300 Mon Sep 17 00:00:00 2001 From: Oskar Stark Date: Tue, 22 Nov 2022 08:28:33 +0100 Subject: [PATCH 13/23] [Notifier][SmsBiuras] Simplify test and data provider Follows * #48262 --- .../Tests/SmsBiurasTransportTest.php | 54 ++++--------------- 1 file changed, 11 insertions(+), 43 deletions(-) diff --git a/src/Symfony/Component/Notifier/Bridge/SmsBiuras/Tests/SmsBiurasTransportTest.php b/src/Symfony/Component/Notifier/Bridge/SmsBiuras/Tests/SmsBiurasTransportTest.php index 6e0461b9828e6..7a232ca856cc1 100644 --- a/src/Symfony/Component/Notifier/Bridge/SmsBiuras/Tests/SmsBiurasTransportTest.php +++ b/src/Symfony/Component/Notifier/Bridge/SmsBiuras/Tests/SmsBiurasTransportTest.php @@ -50,9 +50,9 @@ public function unsupportedMessagesProvider(): iterable /** * @dataProvider provideTestMode() */ - public function testTestMode(array $expected, array $provided) + public function testTestMode(int $expected, bool $testMode) { - $message = new SmsMessage($provided['phone'], $provided['message']); + $message = new SmsMessage('+37012345678', 'Hello World!'); $response = $this->createMock(ResponseInterface::class); $response->expects($this->atLeast(1)) @@ -62,15 +62,15 @@ public function testTestMode(array $expected, array $provided) ->method('getContent') ->willReturn('OK: 519545'); - $client = new MockHttpClient(function (string $method, string $url, array $options = []) use ($response, $expected): ResponseInterface { + $client = new MockHttpClient(function (string $method, string $url, array $options = []) use ($response, $message, $testMode, $expected): ResponseInterface { $this->assertSame('GET', $method); $this->assertSame(sprintf( 'https://savitarna.smsbiuras.lt/api?uid=uid&apikey=api_key&message=%s&from=from&test=%s&to=%s', - rawurlencode($expected['message']), - $expected['transport']['test_mode'], - rawurlencode($expected['phone']), + rawurlencode($message->getSubject()), + $expected, + rawurlencode($message->getPhone()), ), $url); - $this->assertSame($expected['transport']['test_mode'], $options['query']['test']); + $this->assertSame($expected, $options['query']['test']); $this->assertSame(200, $response->getStatusCode()); $this->assertSame('OK: 519545', $response->getContent()); @@ -78,48 +78,16 @@ public function testTestMode(array $expected, array $provided) return $response; }); - $transport = new SmsBiurasTransport('uid', 'api_key', 'from', $provided['transport']['test_mode'], $client); + $transport = new SmsBiurasTransport('uid', 'api_key', 'from', $testMode, $client); $sentMessage = $transport->send($message); $this->assertSame('519545', $sentMessage->getMessageId()); } - public static function provideTestMode(): array + public static function provideTestMode(): iterable { - return [ - [ - [ - 'phone' => '+37012345678', - 'message' => 'Hello world!', - 'transport' => [ - 'test_mode' => 0, - ], - ], - [ - 'phone' => '+37012345678', - 'message' => 'Hello world!', - 'transport' => [ - 'test_mode' => 0, - ], - ], - ], - [ - [ - 'phone' => '+37012345678', - 'message' => 'Hello world!', - 'transport' => [ - 'test_mode' => 1, - ], - ], - [ - 'phone' => '+37012345678', - 'message' => 'Hello world!', - 'transport' => [ - 'test_mode' => 1, - ], - ], - ], - ]; + yield [1, true]; + yield [0, false]; } } From b79d6fd13768a941387eb5fafcdba7eaf0249549 Mon Sep 17 00:00:00 2001 From: MatTheCat Date: Wed, 23 Nov 2022 10:25:03 +0100 Subject: [PATCH 14/23] Fix SmsBiurasTransportTest::testTestMode --- .../Notifier/Bridge/SmsBiuras/Tests/SmsBiurasTransportTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/Notifier/Bridge/SmsBiuras/Tests/SmsBiurasTransportTest.php b/src/Symfony/Component/Notifier/Bridge/SmsBiuras/Tests/SmsBiurasTransportTest.php index 7a232ca856cc1..4a9d084129d52 100644 --- a/src/Symfony/Component/Notifier/Bridge/SmsBiuras/Tests/SmsBiurasTransportTest.php +++ b/src/Symfony/Component/Notifier/Bridge/SmsBiuras/Tests/SmsBiurasTransportTest.php @@ -68,7 +68,7 @@ public function testTestMode(int $expected, bool $testMode) 'https://savitarna.smsbiuras.lt/api?uid=uid&apikey=api_key&message=%s&from=from&test=%s&to=%s', rawurlencode($message->getSubject()), $expected, - rawurlencode($message->getPhone()), + rawurlencode($message->getPhone()) ), $url); $this->assertSame($expected, $options['query']['test']); From 712f057b0f894b722781ac92ad162dcb62b2f04e Mon Sep 17 00:00:00 2001 From: Oskar Stark Date: Wed, 23 Nov 2022 12:53:39 +0100 Subject: [PATCH 15/23] [Notifier][SMSBiuras] Fix CS --- .../Bridge/SmsBiuras/Tests/SmsBiurasTransportTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/Notifier/Bridge/SmsBiuras/Tests/SmsBiurasTransportTest.php b/src/Symfony/Component/Notifier/Bridge/SmsBiuras/Tests/SmsBiurasTransportTest.php index 4a9d084129d52..51c15c56d4a08 100644 --- a/src/Symfony/Component/Notifier/Bridge/SmsBiuras/Tests/SmsBiurasTransportTest.php +++ b/src/Symfony/Component/Notifier/Bridge/SmsBiuras/Tests/SmsBiurasTransportTest.php @@ -52,7 +52,7 @@ public function unsupportedMessagesProvider(): iterable */ public function testTestMode(int $expected, bool $testMode) { - $message = new SmsMessage('+37012345678', 'Hello World!'); + $message = new SmsMessage('+37012345678', 'Hello World!'); $response = $this->createMock(ResponseInterface::class); $response->expects($this->atLeast(1)) @@ -62,7 +62,7 @@ public function testTestMode(int $expected, bool $testMode) ->method('getContent') ->willReturn('OK: 519545'); - $client = new MockHttpClient(function (string $method, string $url, array $options = []) use ($response, $message, $testMode, $expected): ResponseInterface { + $client = new MockHttpClient(function (string $method, string $url, array $options = []) use ($response, $message, $expected): ResponseInterface { $this->assertSame('GET', $method); $this->assertSame(sprintf( 'https://savitarna.smsbiuras.lt/api?uid=uid&apikey=api_key&message=%s&from=from&test=%s&to=%s', From 1e1f3a59fb195cf439e70b5fd45e84ea01f1e334 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 24 Nov 2022 12:30:23 +0100 Subject: [PATCH 16/23] [VarExporter] List required properties in lazy traits --- src/Symfony/Component/Cache/Traits/Redis5Proxy.php | 3 --- src/Symfony/Component/Cache/Traits/Redis6Proxy.php | 3 --- .../Component/Cache/Traits/RedisCluster5Proxy.php | 3 --- .../Component/Cache/Traits/RedisCluster6Proxy.php | 3 --- .../Fixtures/php/services_dedup_lazy_ghost.php | 2 -- .../php/services_non_shared_lazy_as_files.txt | 2 -- .../Fixtures/php/services_non_shared_lazy_ghost.php | 2 -- .../Tests/Fixtures/php/services_wither_lazy.php | 3 --- .../Component/VarExporter/LazyGhostTrait.php | 8 +++++--- .../Component/VarExporter/LazyProxyTrait.php | 8 +++----- src/Symfony/Component/VarExporter/ProxyHelper.php | 13 ++++++------- src/Symfony/Component/VarExporter/README.md | 2 -- .../Tests/Fixtures/LazyGhost/ChildMagicClass.php | 1 - .../Tests/Fixtures/LazyGhost/ChildStdClass.php | 2 -- .../Tests/Fixtures/LazyGhost/LazyClass.php | 1 - .../Tests/Fixtures/LazyGhost/TestClass.php | 1 - .../VarExporter/Tests/LazyGhostTraitTest.php | 2 +- .../VarExporter/Tests/LazyProxyTraitTest.php | 3 --- .../Component/VarExporter/Tests/ProxyHelperTest.php | 6 ------ 19 files changed, 15 insertions(+), 53 deletions(-) diff --git a/src/Symfony/Component/Cache/Traits/Redis5Proxy.php b/src/Symfony/Component/Cache/Traits/Redis5Proxy.php index f9235b04df63f..bcfe8bc8d3288 100644 --- a/src/Symfony/Component/Cache/Traits/Redis5Proxy.php +++ b/src/Symfony/Component/Cache/Traits/Redis5Proxy.php @@ -29,9 +29,6 @@ class Redis5Proxy extends \Redis implements ResetInterface, LazyObjectInterface resetLazyObject as reset; } - private int $lazyObjectId; - private \Redis $lazyObjectReal; - private const LAZY_OBJECT_PROPERTY_SCOPES = [ 'lazyObjectReal' => [self::class, 'lazyObjectReal', null], "\0".self::class."\0lazyObjectReal" => [self::class, 'lazyObjectReal', null], diff --git a/src/Symfony/Component/Cache/Traits/Redis6Proxy.php b/src/Symfony/Component/Cache/Traits/Redis6Proxy.php index 2304bd878684f..f5373df72f3bf 100644 --- a/src/Symfony/Component/Cache/Traits/Redis6Proxy.php +++ b/src/Symfony/Component/Cache/Traits/Redis6Proxy.php @@ -29,9 +29,6 @@ class Redis6Proxy extends \Redis implements ResetInterface, LazyObjectInterface resetLazyObject as reset; } - private int $lazyObjectId; - private \Redis $lazyObjectReal; - private const LAZY_OBJECT_PROPERTY_SCOPES = [ 'lazyObjectReal' => [self::class, 'lazyObjectReal', null], "\0".self::class."\0lazyObjectReal" => [self::class, 'lazyObjectReal', null], diff --git a/src/Symfony/Component/Cache/Traits/RedisCluster5Proxy.php b/src/Symfony/Component/Cache/Traits/RedisCluster5Proxy.php index 58e5ad5cbff9f..d95aced028137 100644 --- a/src/Symfony/Component/Cache/Traits/RedisCluster5Proxy.php +++ b/src/Symfony/Component/Cache/Traits/RedisCluster5Proxy.php @@ -29,9 +29,6 @@ class RedisCluster5Proxy extends \RedisCluster implements ResetInterface, LazyOb resetLazyObject as reset; } - private int $lazyObjectId; - private \RedisCluster $lazyObjectReal; - private const LAZY_OBJECT_PROPERTY_SCOPES = [ 'lazyObjectReal' => [self::class, 'lazyObjectReal', null], "\0".self::class."\0lazyObjectReal" => [self::class, 'lazyObjectReal', null], diff --git a/src/Symfony/Component/Cache/Traits/RedisCluster6Proxy.php b/src/Symfony/Component/Cache/Traits/RedisCluster6Proxy.php index bf61a4c2c25f5..9cc6abf7fad94 100644 --- a/src/Symfony/Component/Cache/Traits/RedisCluster6Proxy.php +++ b/src/Symfony/Component/Cache/Traits/RedisCluster6Proxy.php @@ -29,9 +29,6 @@ class RedisCluster6Proxy extends \RedisCluster implements ResetInterface, LazyOb resetLazyObject as reset; } - private int $lazyObjectId; - private \RedisCluster $lazyObjectReal; - private const LAZY_OBJECT_PROPERTY_SCOPES = [ 'lazyObjectReal' => [self::class, 'lazyObjectReal', null], "\0".self::class."\0lazyObjectReal" => [self::class, 'lazyObjectReal', null], diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_dedup_lazy_ghost.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_dedup_lazy_ghost.php index 32e5364ef4e35..a7972921fadba 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_dedup_lazy_ghost.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_dedup_lazy_ghost.php @@ -75,8 +75,6 @@ class stdClass_5a8a5eb extends \stdClass implements \Symfony\Component\VarExport { use \Symfony\Component\VarExporter\LazyGhostTrait; - private int $lazyObjectId; - private const LAZY_OBJECT_PROPERTY_SCOPES = []; } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_non_shared_lazy_as_files.txt b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_non_shared_lazy_as_files.txt index d04e886a810b2..a52a32a27dadd 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_non_shared_lazy_as_files.txt +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_non_shared_lazy_as_files.txt @@ -45,8 +45,6 @@ class FooLazyClass_f814e3a extends \Bar\FooLazyClass implements \Symfony\Compone { use \Symfony\Component\VarExporter\LazyGhostTrait; - private int $lazyObjectId; - private const LAZY_OBJECT_PROPERTY_SCOPES = []; } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_non_shared_lazy_ghost.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_non_shared_lazy_ghost.php index f0143cb590f3f..c304c9461fd1c 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_non_shared_lazy_ghost.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_non_shared_lazy_ghost.php @@ -79,8 +79,6 @@ class stdClass_5a8a5eb extends \stdClass implements \Symfony\Component\VarExport { use \Symfony\Component\VarExporter\LazyGhostTrait; - private int $lazyObjectId; - private const LAZY_OBJECT_PROPERTY_SCOPES = []; } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_wither_lazy.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_wither_lazy.php index ec39e663e4ec7..5de8772d6746e 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_wither_lazy.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_wither_lazy.php @@ -75,9 +75,6 @@ class Wither_94fa281 extends \Symfony\Component\DependencyInjection\Tests\Compil { use \Symfony\Component\VarExporter\LazyProxyTrait; - private int $lazyObjectId; - private parent $lazyObjectReal; - private const LAZY_OBJECT_PROPERTY_SCOPES = [ 'lazyObjectReal' => [self::class, 'lazyObjectReal', null], "\0".self::class."\0lazyObjectReal" => [self::class, 'lazyObjectReal', null], diff --git a/src/Symfony/Component/VarExporter/LazyGhostTrait.php b/src/Symfony/Component/VarExporter/LazyGhostTrait.php index 16e40f7234567..46346b6a7b0d7 100644 --- a/src/Symfony/Component/VarExporter/LazyGhostTrait.php +++ b/src/Symfony/Component/VarExporter/LazyGhostTrait.php @@ -15,11 +15,10 @@ use Symfony\Component\VarExporter\Internal\LazyObjectRegistry as Registry; use Symfony\Component\VarExporter\Internal\LazyObjectState; -/** - * @property int $lazyObjectId This property must be declared as private in classes using this trait - */ trait LazyGhostTrait { + private int $lazyObjectId; + /** * Creates a lazy-loading ghost instance. * @@ -31,6 +30,9 @@ trait LazyGhostTrait * initialize, the property to initialize, its write-scope, and its default * value. Each closure should return the value of the corresponding property. * + * Properties should be indexed by their array-cast name, see + * https://php.net/manual/language.types.array#language.types.array.casting + * * @param \Closure(static):void|array $initializer * @param array $skippedProperties An array indexed by the properties to skip, aka the ones * that the initializer doesn't set when its a closure diff --git a/src/Symfony/Component/VarExporter/LazyProxyTrait.php b/src/Symfony/Component/VarExporter/LazyProxyTrait.php index 4b58b7a388160..d79806c086d5a 100644 --- a/src/Symfony/Component/VarExporter/LazyProxyTrait.php +++ b/src/Symfony/Component/VarExporter/LazyProxyTrait.php @@ -16,13 +16,11 @@ use Symfony\Component\VarExporter\Internal\LazyObjectRegistry as Registry; use Symfony\Component\VarExporter\Internal\LazyObjectState; -/** - * @property int $lazyObjectId This property must be declared as private in classes using this trait - * @property parent $lazyObjectReal This property must be declared as private in classes using this trait; - * its type should match the type of the proxied object - */ trait LazyProxyTrait { + private int $lazyObjectId; + private object $lazyObjectReal; + /** * Creates a lazy-loading virtual proxy. * diff --git a/src/Symfony/Component/VarExporter/ProxyHelper.php b/src/Symfony/Component/VarExporter/ProxyHelper.php index 3df1dd14a3a4a..ba24c4afb08a0 100644 --- a/src/Symfony/Component/VarExporter/ProxyHelper.php +++ b/src/Symfony/Component/VarExporter/ProxyHelper.php @@ -27,6 +27,9 @@ final class ProxyHelper */ public static function generateLazyGhost(\ReflectionClass $class): string { + if (\PHP_VERSION_ID >= 80200 && $class->isReadOnly()) { + throw new LogicException(sprintf('Cannot generate lazy ghost: class "%s" is read-only.', $class->name)); + } if ($class->isFinal()) { throw new LogicException(sprintf('Cannot generate lazy ghost: class "%s" is final.', $class->name)); } @@ -56,15 +59,12 @@ public static function generateLazyGhost(\ReflectionClass $class): string } } $propertyScopes = self::exportPropertyScopes($class->name); - $readonly = \PHP_VERSION_ID >= 80200 && $class->isReadOnly() ? 'readonly ' : ''; return <<name} implements \Symfony\Component\VarExporter\LazyObjectInterface { use \Symfony\Component\VarExporter\LazyGhostTrait; - private {$readonly}int \$lazyObjectId; - private const LAZY_OBJECT_PROPERTY_SCOPES = {$propertyScopes}; } @@ -91,6 +91,9 @@ public static function generateLazyProxy(?\ReflectionClass $class, array $interf if ($class?->isFinal()) { throw new LogicException(sprintf('Cannot generate lazy proxy: class "%s" is final.', $class->name)); } + if (\PHP_VERSION_ID >= 80200 && $class?->isReadOnly()) { + throw new LogicException(sprintf('Cannot generate lazy proxy: class "%s" is read-only.', $class->name)); + } $methodReflectors = [$class?->getMethods(\ReflectionMethod::IS_PUBLIC | \ReflectionMethod::IS_PROTECTED) ?? []]; foreach ($interfaces as $interface) { @@ -176,7 +179,6 @@ public static function generateLazyProxy(?\ReflectionClass $class, array $interf $methods[$lcName] = " {$signature}\n {\n{$body}\n }"; } - $readonly = \PHP_VERSION_ID >= 80200 && $class?->isReadOnly() ? 'readonly ' : ''; $types = $interfaces = array_unique(array_column($interfaces, 'name')); $interfaces[] = LazyObjectInterface::class; $interfaces = implode(', \\', $interfaces); @@ -198,9 +200,6 @@ public static function generateLazyProxy(?\ReflectionClass $class, array $interf { use \Symfony\Component\VarExporter\LazyProxyTrait; - private {$readonly}int \$lazyObjectId; - private {$readonly}{$type} \$lazyObjectReal; - private const LAZY_OBJECT_PROPERTY_SCOPES = [ 'lazyObjectReal' => [self::class, 'lazyObjectReal', null], "\\0".self::class."\\0lazyObjectReal" => [self::class, 'lazyObjectReal', null],{$propertyScopes} diff --git a/src/Symfony/Component/VarExporter/README.md b/src/Symfony/Component/VarExporter/README.md index 0107f99cae249..7c7a58e298357 100644 --- a/src/Symfony/Component/VarExporter/README.md +++ b/src/Symfony/Component/VarExporter/README.md @@ -92,8 +92,6 @@ when accessing a property. class FooLazyGhost extends Foo { use LazyGhostTrait; - - private int $lazyObjectId; } $foo = FooLazyGhost::createLazyGhost(initializer: function (Foo $instance): void { diff --git a/src/Symfony/Component/VarExporter/Tests/Fixtures/LazyGhost/ChildMagicClass.php b/src/Symfony/Component/VarExporter/Tests/Fixtures/LazyGhost/ChildMagicClass.php index a7dc5d3d73fb4..6cac9ffc03d01 100644 --- a/src/Symfony/Component/VarExporter/Tests/Fixtures/LazyGhost/ChildMagicClass.php +++ b/src/Symfony/Component/VarExporter/Tests/Fixtures/LazyGhost/ChildMagicClass.php @@ -18,6 +18,5 @@ class ChildMagicClass extends MagicClass implements LazyObjectInterface { use LazyGhostTrait; - private int $lazyObjectId; private int $data = 123; } diff --git a/src/Symfony/Component/VarExporter/Tests/Fixtures/LazyGhost/ChildStdClass.php b/src/Symfony/Component/VarExporter/Tests/Fixtures/LazyGhost/ChildStdClass.php index 0a131d9bfb5c1..265fb1d318c38 100644 --- a/src/Symfony/Component/VarExporter/Tests/Fixtures/LazyGhost/ChildStdClass.php +++ b/src/Symfony/Component/VarExporter/Tests/Fixtures/LazyGhost/ChildStdClass.php @@ -17,6 +17,4 @@ class ChildStdClass extends \stdClass implements LazyObjectInterface { use LazyGhostTrait; - - private int $lazyObjectId; } diff --git a/src/Symfony/Component/VarExporter/Tests/Fixtures/LazyGhost/LazyClass.php b/src/Symfony/Component/VarExporter/Tests/Fixtures/LazyGhost/LazyClass.php index 710b0571b1755..a3ce88d14e942 100644 --- a/src/Symfony/Component/VarExporter/Tests/Fixtures/LazyGhost/LazyClass.php +++ b/src/Symfony/Component/VarExporter/Tests/Fixtures/LazyGhost/LazyClass.php @@ -19,7 +19,6 @@ class LazyClass createLazyGhost as private; } - private int $lazyObjectId; public int $public; public function __construct(\Closure $initializer) diff --git a/src/Symfony/Component/VarExporter/Tests/Fixtures/LazyGhost/TestClass.php b/src/Symfony/Component/VarExporter/Tests/Fixtures/LazyGhost/TestClass.php index 1c1127d546399..f755235831b1e 100644 --- a/src/Symfony/Component/VarExporter/Tests/Fixtures/LazyGhost/TestClass.php +++ b/src/Symfony/Component/VarExporter/Tests/Fixtures/LazyGhost/TestClass.php @@ -17,7 +17,6 @@ class TestClass extends NoMagicClass { use LazyGhostTrait; - private int $lazyObjectId; public int $public = 1; protected int $protected = 2; protected readonly int $protectedReadonly; diff --git a/src/Symfony/Component/VarExporter/Tests/LazyGhostTraitTest.php b/src/Symfony/Component/VarExporter/Tests/LazyGhostTraitTest.php index a64747490bd0c..44ef1f17eede9 100644 --- a/src/Symfony/Component/VarExporter/Tests/LazyGhostTraitTest.php +++ b/src/Symfony/Component/VarExporter/Tests/LazyGhostTraitTest.php @@ -248,7 +248,7 @@ public function testPartialInitialization() $this->assertFalse($instance->isLazyObjectInitialized()); $this->assertSame(123, $instance->public); $this->assertFalse($instance->isLazyObjectInitialized()); - $this->assertSame(["\0".TestClass::class."\0lazyObjectId", 'public'], array_keys((array) $instance)); + $this->assertSame(['public', "\0".TestClass::class."\0lazyObjectId"], array_keys((array) $instance)); $this->assertSame(1, $counter); $instance->initializeLazyObject(); diff --git a/src/Symfony/Component/VarExporter/Tests/LazyProxyTraitTest.php b/src/Symfony/Component/VarExporter/Tests/LazyProxyTraitTest.php index c44c3b9cac6be..848e1d1c19e36 100644 --- a/src/Symfony/Component/VarExporter/Tests/LazyProxyTraitTest.php +++ b/src/Symfony/Component/VarExporter/Tests/LazyProxyTraitTest.php @@ -249,9 +249,6 @@ public function testLazyDecoratorClass() createLazyProxy as private; } - private int $lazyObjectId; - private parent $lazyObjectReal; - public function __construct() { self::createLazyProxy(fn () => new TestClass((object) ['foo' => 123]), $this); diff --git a/src/Symfony/Component/VarExporter/Tests/ProxyHelperTest.php b/src/Symfony/Component/VarExporter/Tests/ProxyHelperTest.php index 455a7b974ec55..260980d94f870 100644 --- a/src/Symfony/Component/VarExporter/Tests/ProxyHelperTest.php +++ b/src/Symfony/Component/VarExporter/Tests/ProxyHelperTest.php @@ -66,9 +66,6 @@ public function testGenerateLazyProxy() { use \Symfony\Component\VarExporter\LazyProxyTrait; - private int $lazyObjectId; - private parent $lazyObjectReal; - private const LAZY_OBJECT_PROPERTY_SCOPES = [ 'lazyObjectReal' => [self::class, 'lazyObjectReal', null], "\0".self::class."\0lazyObjectReal" => [self::class, 'lazyObjectReal', null], @@ -119,9 +116,6 @@ public function testGenerateLazyProxyForInterfaces() { use \Symfony\Component\VarExporter\LazyProxyTrait; - private int $lazyObjectId; - private \Symfony\Component\VarExporter\Tests\TestForProxyHelperInterface1&\Symfony\Component\VarExporter\Tests\TestForProxyHelperInterface2 $lazyObjectReal; - private const LAZY_OBJECT_PROPERTY_SCOPES = [ 'lazyObjectReal' => [self::class, 'lazyObjectReal', null], "\0".self::class."\0lazyObjectReal" => [self::class, 'lazyObjectReal', null], From 980900104c14b6c72e57adba9044d8484130ea5b Mon Sep 17 00:00:00 2001 From: Jeroeny Date: Tue, 22 Nov 2022 15:12:23 +0100 Subject: [PATCH 17/23] [Security] Support loading UserBadge directly from accessToken --- .../Security/Factory/AccessTokenFactory.php | 6 +++--- .../security_authenticator_access_token.php | 2 +- .../Security/Handler/AccessTokenHandler.php | 5 +++-- .../AccessTokenHandlerInterface.php | 3 ++- .../AccessTokenAuthenticator.php | 13 ++++++------ .../ChainedAccessTokenExtractorsTest.php | 13 ++++++------ ...ncodedBodyAccessTokenAuthenticatorTest.php | 19 +++++++++-------- .../HeaderAccessTokenAuthenticatorTest.php | 21 ++++++++++--------- .../QueryAccessTokenAuthenticatorTest.php | 19 +++++++++-------- .../InMemoryAccessTokenHandler.php | 7 ++++--- 10 files changed, 58 insertions(+), 50 deletions(-) diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/AccessTokenFactory.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/AccessTokenFactory.php index 1e38c58643638..b59f7974f1e87 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/AccessTokenFactory.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/AccessTokenFactory.php @@ -79,9 +79,9 @@ public function createAuthenticator(ContainerBuilder $container, string $firewal $container ->setDefinition($authenticatorId, new ChildDefinition('security.authenticator.access_token')) - ->replaceArgument(0, $userProvider) - ->replaceArgument(1, new Reference($config['token_handler'])) - ->replaceArgument(2, new Reference($extractorId)) + ->replaceArgument(0, new Reference($config['token_handler'])) + ->replaceArgument(1, new Reference($extractorId)) + ->replaceArgument(2, $userProvider) ->replaceArgument(3, $successHandler) ->replaceArgument(4, $failureHandler) ->replaceArgument(5, $config['realm']) diff --git a/src/Symfony/Bundle/SecurityBundle/Resources/config/security_authenticator_access_token.php b/src/Symfony/Bundle/SecurityBundle/Resources/config/security_authenticator_access_token.php index 17bc916c8d314..f1aea7cb2c3d1 100644 --- a/src/Symfony/Bundle/SecurityBundle/Resources/config/security_authenticator_access_token.php +++ b/src/Symfony/Bundle/SecurityBundle/Resources/config/security_authenticator_access_token.php @@ -26,12 +26,12 @@ ->set('security.authenticator.access_token', AccessTokenAuthenticator::class) ->abstract() ->args([ - abstract_arg('user provider'), abstract_arg('access token handler'), abstract_arg('access token extractor'), null, null, null, + null, ]) ->call('setTranslator', [service('translator')->ignoreOnInvalid()]) diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/AccessTokenBundle/Security/Handler/AccessTokenHandler.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/AccessTokenBundle/Security/Handler/AccessTokenHandler.php index 0d1e9e0c0e7fc..4f94cc6936a05 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/AccessTokenBundle/Security/Handler/AccessTokenHandler.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/AccessTokenBundle/Security/Handler/AccessTokenHandler.php @@ -13,6 +13,7 @@ use Symfony\Component\Security\Core\Exception\BadCredentialsException; use Symfony\Component\Security\Http\AccessToken\AccessTokenHandlerInterface; +use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge; class AccessTokenHandler implements AccessTokenHandlerInterface { @@ -20,10 +21,10 @@ public function __construct() { } - public function getUserIdentifierFrom(string $accessToken): string + public function getUserBadgeFrom(string $accessToken): UserBadge { return match ($accessToken) { - 'VALID_ACCESS_TOKEN' => 'dunglas', + 'VALID_ACCESS_TOKEN' => new UserBadge('dunglas'), default => throw new BadCredentialsException('Invalid credentials.'), }; } diff --git a/src/Symfony/Component/Security/Http/AccessToken/AccessTokenHandlerInterface.php b/src/Symfony/Component/Security/Http/AccessToken/AccessTokenHandlerInterface.php index 8044a371293aa..5cbc857a1c6f0 100644 --- a/src/Symfony/Component/Security/Http/AccessToken/AccessTokenHandlerInterface.php +++ b/src/Symfony/Component/Security/Http/AccessToken/AccessTokenHandlerInterface.php @@ -12,6 +12,7 @@ namespace Symfony\Component\Security\Http\AccessToken; use Symfony\Component\Security\Core\Exception\AuthenticationException; +use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge; /** * The token handler retrieves the user identifier from the token. @@ -24,5 +25,5 @@ interface AccessTokenHandlerInterface /** * @throws AuthenticationException */ - public function getUserIdentifierFrom(#[\SensitiveParameter] string $accessToken): string; + public function getUserBadgeFrom(#[\SensitiveParameter] string $accessToken): UserBadge; } diff --git a/src/Symfony/Component/Security/Http/Authenticator/AccessTokenAuthenticator.php b/src/Symfony/Component/Security/Http/Authenticator/AccessTokenAuthenticator.php index ae8f0a4ea3da2..d3ade9b0be73b 100644 --- a/src/Symfony/Component/Security/Http/Authenticator/AccessTokenAuthenticator.php +++ b/src/Symfony/Component/Security/Http/Authenticator/AccessTokenAuthenticator.php @@ -21,7 +21,6 @@ use Symfony\Component\Security\Http\AccessToken\AccessTokenHandlerInterface; use Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface; use Symfony\Component\Security\Http\Authentication\AuthenticationSuccessHandlerInterface; -use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge; use Symfony\Component\Security\Http\Authenticator\Passport\Passport; use Symfony\Component\Security\Http\Authenticator\Passport\SelfValidatingPassport; use Symfony\Component\Security\Http\Authenticator\Token\PostAuthenticationToken; @@ -38,9 +37,9 @@ class AccessTokenAuthenticator implements AuthenticatorInterface private ?TranslatorInterface $translator = null; public function __construct( - private readonly UserProviderInterface $userProvider, private readonly AccessTokenHandlerInterface $accessTokenHandler, private readonly AccessTokenExtractorInterface $accessTokenExtractor, + private readonly ?UserProviderInterface $userProvider = null, private readonly ?AuthenticationSuccessHandlerInterface $successHandler = null, private readonly ?AuthenticationFailureHandlerInterface $failureHandler = null, private readonly ?string $realm = null, @@ -58,11 +57,13 @@ public function authenticate(Request $request): Passport if (!$accessToken) { throw new BadCredentialsException('Invalid credentials.'); } - $userIdentifier = $this->accessTokenHandler->getUserIdentifierFrom($accessToken); - return new SelfValidatingPassport( - new UserBadge($userIdentifier, $this->userProvider->loadUserByIdentifier(...)) - ); + $userBadge = $this->accessTokenHandler->getUserBadgeFrom($accessToken); + if (null === $userBadge->getUserLoader() && $this->userProvider) { + $userBadge->setUserLoader($this->userProvider->loadUserByIdentifier(...)); + } + + return new SelfValidatingPassport($userBadge); } public function createToken(Passport $passport, string $firewallName): TokenInterface diff --git a/src/Symfony/Component/Security/Http/Tests/Authenticator/AccessToken/ChainedAccessTokenExtractorsTest.php b/src/Symfony/Component/Security/Http/Tests/Authenticator/AccessToken/ChainedAccessTokenExtractorsTest.php index e59ce918b0651..8dd3188eb9587 100644 --- a/src/Symfony/Component/Security/Http/Tests/Authenticator/AccessToken/ChainedAccessTokenExtractorsTest.php +++ b/src/Symfony/Component/Security/Http/Tests/Authenticator/AccessToken/ChainedAccessTokenExtractorsTest.php @@ -22,6 +22,7 @@ use Symfony\Component\Security\Http\AccessToken\HeaderAccessTokenExtractor; use Symfony\Component\Security\Http\AccessToken\QueryAccessTokenExtractor; use Symfony\Component\Security\Http\Authenticator\AccessTokenAuthenticator; +use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge; use Symfony\Component\Security\Http\Authenticator\Passport\SelfValidatingPassport; use Symfony\Component\Security\Http\Tests\Authenticator\InMemoryAccessTokenHandler; @@ -40,7 +41,7 @@ protected function setUp(): void /** * @dataProvider provideSupportData */ - public function testSupport($request): void + public function testSupport($request) { $this->setUpAuthenticator(); @@ -53,9 +54,9 @@ public function provideSupportData(): iterable yield [new Request([], [], [], [], [], ['HTTP_AUTHORIZATION' => 'Bearer INVALID_ACCESS_TOKEN'])]; } - public function testAuthenticate(): void + public function testAuthenticate() { - $this->accessTokenHandler->add('VALID_ACCESS_TOKEN', 'foo'); + $this->accessTokenHandler->add('VALID_ACCESS_TOKEN', new UserBadge('foo')); $this->setUpAuthenticator(); $request = new Request([], [], [], [], [], ['HTTP_AUTHORIZATION' => 'Bearer VALID_ACCESS_TOKEN']); @@ -66,7 +67,7 @@ public function testAuthenticate(): void /** * @dataProvider provideInvalidAuthenticateData */ - public function testAuthenticateInvalid($request, $errorMessage, $exceptionType = BadRequestHttpException::class): void + public function testAuthenticateInvalid($request, $errorMessage, $exceptionType = BadRequestHttpException::class) { $this->expectException($exceptionType); $this->expectExceptionMessage($errorMessage); @@ -100,13 +101,13 @@ public function provideInvalidAuthenticateData(): iterable private function setUpAuthenticator(): void { $this->authenticator = new AccessTokenAuthenticator( - $this->userProvider, $this->accessTokenHandler, new ChainAccessTokenExtractor([ new FormEncodedBodyExtractor(), new QueryAccessTokenExtractor(), new HeaderAccessTokenExtractor(), - ]) + ]), + $this->userProvider ); } } diff --git a/src/Symfony/Component/Security/Http/Tests/Authenticator/AccessToken/FormEncodedBodyAccessTokenAuthenticatorTest.php b/src/Symfony/Component/Security/Http/Tests/Authenticator/AccessToken/FormEncodedBodyAccessTokenAuthenticatorTest.php index 5f251bb71f197..b915a5d10631c 100644 --- a/src/Symfony/Component/Security/Http/Tests/Authenticator/AccessToken/FormEncodedBodyAccessTokenAuthenticatorTest.php +++ b/src/Symfony/Component/Security/Http/Tests/Authenticator/AccessToken/FormEncodedBodyAccessTokenAuthenticatorTest.php @@ -19,6 +19,7 @@ use Symfony\Component\Security\Http\AccessToken\AccessTokenHandlerInterface; use Symfony\Component\Security\Http\AccessToken\FormEncodedBodyExtractor; use Symfony\Component\Security\Http\Authenticator\AccessTokenAuthenticator; +use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge; use Symfony\Component\Security\Http\Authenticator\Passport\SelfValidatingPassport; use Symfony\Component\Security\Http\Tests\Authenticator\InMemoryAccessTokenHandler; @@ -34,7 +35,7 @@ protected function setUp(): void $this->accessTokenHandler = new InMemoryAccessTokenHandler(); } - public function testSupport(): void + public function testSupport() { $this->setUpAuthenticator(); $request = new Request([], [], [], [], [], ['CONTENT_TYPE' => 'application/x-www-form-urlencoded']); @@ -44,7 +45,7 @@ public function testSupport(): void $this->assertNull($this->authenticator->supports($request)); } - public function testSupportsWithCustomParameter(): void + public function testSupportsWithCustomParameter() { $this->setUpAuthenticator('protection-token'); $request = new Request([], [], [], [], [], ['CONTENT_TYPE' => 'application/x-www-form-urlencoded']); @@ -54,9 +55,9 @@ public function testSupportsWithCustomParameter(): void $this->assertNull($this->authenticator->supports($request)); } - public function testAuthenticate(): void + public function testAuthenticate() { - $this->accessTokenHandler->add('VALID_ACCESS_TOKEN', 'foo'); + $this->accessTokenHandler->add('VALID_ACCESS_TOKEN', new UserBadge('foo')); $this->setUpAuthenticator(); $request = new Request([], [], [], [], [], ['CONTENT_TYPE' => 'application/x-www-form-urlencoded'], 'access_token=VALID_ACCESS_TOKEN'); $request->request->set('access_token', 'VALID_ACCESS_TOKEN'); @@ -66,9 +67,9 @@ public function testAuthenticate(): void $this->assertInstanceOf(SelfValidatingPassport::class, $passport); } - public function testAuthenticateWithCustomParameter(): void + public function testAuthenticateWithCustomParameter() { - $this->accessTokenHandler->add('VALID_ACCESS_TOKEN', 'foo'); + $this->accessTokenHandler->add('VALID_ACCESS_TOKEN', new UserBadge('foo')); $this->setUpAuthenticator('protection-token'); $request = new Request([], [], [], [], [], ['CONTENT_TYPE' => 'application/x-www-form-urlencoded']); $request->request->set('protection-token', 'VALID_ACCESS_TOKEN'); @@ -81,7 +82,7 @@ public function testAuthenticateWithCustomParameter(): void /** * @dataProvider provideInvalidAuthenticateData */ - public function testAuthenticateInvalid($request, $errorMessage, $exceptionType = BadRequestHttpException::class): void + public function testAuthenticateInvalid($request, $errorMessage, $exceptionType = BadRequestHttpException::class) { $this->expectException($exceptionType); $this->expectExceptionMessage($errorMessage); @@ -119,9 +120,9 @@ public function provideInvalidAuthenticateData(): iterable private function setUpAuthenticator(string $parameter = 'access_token'): void { $this->authenticator = new AccessTokenAuthenticator( - $this->userProvider, $this->accessTokenHandler, - new FormEncodedBodyExtractor($parameter) + new FormEncodedBodyExtractor($parameter), + $this->userProvider ); } } diff --git a/src/Symfony/Component/Security/Http/Tests/Authenticator/AccessToken/HeaderAccessTokenAuthenticatorTest.php b/src/Symfony/Component/Security/Http/Tests/Authenticator/AccessToken/HeaderAccessTokenAuthenticatorTest.php index 89e91e34feecf..a4e7758bffed5 100644 --- a/src/Symfony/Component/Security/Http/Tests/Authenticator/AccessToken/HeaderAccessTokenAuthenticatorTest.php +++ b/src/Symfony/Component/Security/Http/Tests/Authenticator/AccessToken/HeaderAccessTokenAuthenticatorTest.php @@ -19,6 +19,7 @@ use Symfony\Component\Security\Http\AccessToken\AccessTokenHandlerInterface; use Symfony\Component\Security\Http\AccessToken\HeaderAccessTokenExtractor; use Symfony\Component\Security\Http\Authenticator\AccessTokenAuthenticator; +use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge; use Symfony\Component\Security\Http\Authenticator\Passport\SelfValidatingPassport; use Symfony\Component\Security\Http\Tests\Authenticator\InMemoryAccessTokenHandler; @@ -37,7 +38,7 @@ protected function setUp(): void /** * @dataProvider provideSupportData */ - public function testSupport($request): void + public function testSupport($request) { $this->setUpAuthenticator(); @@ -53,7 +54,7 @@ public function provideSupportData(): iterable /** * @dataProvider provideSupportsWithCustomTokenTypeData */ - public function testSupportsWithCustomTokenType($request, $result): void + public function testSupportsWithCustomTokenType($request, $result) { $this->setUpAuthenticator('Authorization', 'JWT'); @@ -71,7 +72,7 @@ public function provideSupportsWithCustomTokenTypeData(): iterable /** * @dataProvider provideSupportsWithCustomHeaderParameter */ - public function testSupportsWithCustomHeaderParameter($request, $result): void + public function testSupportsWithCustomHeaderParameter($request, $result) { $this->setUpAuthenticator('X-FOO'); @@ -86,9 +87,9 @@ public function provideSupportsWithCustomHeaderParameter(): iterable yield [new Request([], [], [], [], [], ['HTTP_AUTHORIZATION' => 'Bearer INVALID_ACCESS_TOKEN']), false]; } - public function testAuthenticate(): void + public function testAuthenticate() { - $this->accessTokenHandler->add('VALID_ACCESS_TOKEN', 'foo'); + $this->accessTokenHandler->add('VALID_ACCESS_TOKEN', new UserBadge('foo')); $this->setUpAuthenticator(); $request = new Request([], [], [], [], [], ['HTTP_AUTHORIZATION' => 'Bearer VALID_ACCESS_TOKEN']); @@ -96,9 +97,9 @@ public function testAuthenticate(): void $this->assertInstanceOf(SelfValidatingPassport::class, $passport); } - public function testAuthenticateWithCustomTokenType(): void + public function testAuthenticateWithCustomTokenType() { - $this->accessTokenHandler->add('VALID_ACCESS_TOKEN', 'foo'); + $this->accessTokenHandler->add('VALID_ACCESS_TOKEN', new UserBadge('foo')); $this->setUpAuthenticator('Authorization', 'JWT'); $request = new Request([], [], [], [], [], ['HTTP_AUTHORIZATION' => 'JWT VALID_ACCESS_TOKEN']); @@ -109,7 +110,7 @@ public function testAuthenticateWithCustomTokenType(): void /** * @dataProvider provideInvalidAuthenticateData */ - public function testAuthenticateInvalid($request, $errorMessage, $exceptionType = BadRequestHttpException::class): void + public function testAuthenticateInvalid($request, $errorMessage, $exceptionType = BadRequestHttpException::class) { $this->expectException($exceptionType); $this->expectExceptionMessage($errorMessage); @@ -143,9 +144,9 @@ public function provideInvalidAuthenticateData(): iterable private function setUpAuthenticator(string $headerParameter = 'Authorization', string $tokenType = 'Bearer'): void { $this->authenticator = new AccessTokenAuthenticator( - $this->userProvider, $this->accessTokenHandler, - new HeaderAccessTokenExtractor($headerParameter, $tokenType) + new HeaderAccessTokenExtractor($headerParameter, $tokenType), + $this->userProvider ); } } diff --git a/src/Symfony/Component/Security/Http/Tests/Authenticator/AccessToken/QueryAccessTokenAuthenticatorTest.php b/src/Symfony/Component/Security/Http/Tests/Authenticator/AccessToken/QueryAccessTokenAuthenticatorTest.php index c1a8206115452..73f8392a9fa94 100644 --- a/src/Symfony/Component/Security/Http/Tests/Authenticator/AccessToken/QueryAccessTokenAuthenticatorTest.php +++ b/src/Symfony/Component/Security/Http/Tests/Authenticator/AccessToken/QueryAccessTokenAuthenticatorTest.php @@ -19,6 +19,7 @@ use Symfony\Component\Security\Http\AccessToken\AccessTokenHandlerInterface; use Symfony\Component\Security\Http\AccessToken\QueryAccessTokenExtractor; use Symfony\Component\Security\Http\Authenticator\AccessTokenAuthenticator; +use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge; use Symfony\Component\Security\Http\Authenticator\Passport\SelfValidatingPassport; use Symfony\Component\Security\Http\Tests\Authenticator\InMemoryAccessTokenHandler; @@ -34,7 +35,7 @@ protected function setUp(): void $this->accessTokenHandler = new InMemoryAccessTokenHandler(); } - public function testSupport(): void + public function testSupport() { $this->setUpAuthenticator(); $request = new Request(); @@ -43,7 +44,7 @@ public function testSupport(): void $this->assertNull($this->authenticator->supports($request)); } - public function testSupportsWithCustomParameter(): void + public function testSupportsWithCustomParameter() { $this->setUpAuthenticator('protection-token'); $request = new Request(); @@ -52,9 +53,9 @@ public function testSupportsWithCustomParameter(): void $this->assertNull($this->authenticator->supports($request)); } - public function testAuthenticate(): void + public function testAuthenticate() { - $this->accessTokenHandler->add('VALID_ACCESS_TOKEN', 'foo'); + $this->accessTokenHandler->add('VALID_ACCESS_TOKEN', new UserBadge('foo')); $this->setUpAuthenticator(); $request = new Request(); $request->query->set('access_token', 'VALID_ACCESS_TOKEN'); @@ -63,9 +64,9 @@ public function testAuthenticate(): void $this->assertInstanceOf(SelfValidatingPassport::class, $passport); } - public function testAuthenticateWithCustomParameter(): void + public function testAuthenticateWithCustomParameter() { - $this->accessTokenHandler->add('VALID_ACCESS_TOKEN', 'foo'); + $this->accessTokenHandler->add('VALID_ACCESS_TOKEN', new UserBadge('foo')); $this->setUpAuthenticator('protection-token'); $request = new Request(); $request->query->set('protection-token', 'VALID_ACCESS_TOKEN'); @@ -77,7 +78,7 @@ public function testAuthenticateWithCustomParameter(): void /** * @dataProvider provideInvalidAuthenticateData */ - public function testAuthenticateInvalid($request, $errorMessage, $exceptionType = BadRequestHttpException::class): void + public function testAuthenticateInvalid($request, $errorMessage, $exceptionType = BadRequestHttpException::class) { $this->expectException($exceptionType); $this->expectExceptionMessage($errorMessage); @@ -111,9 +112,9 @@ public function provideInvalidAuthenticateData(): iterable private function setUpAuthenticator(string $parameter = 'access_token'): void { $this->authenticator = new AccessTokenAuthenticator( - $this->userProvider, $this->accessTokenHandler, - new QueryAccessTokenExtractor($parameter) + new QueryAccessTokenExtractor($parameter), + $this->userProvider ); } } diff --git a/src/Symfony/Component/Security/Http/Tests/Authenticator/InMemoryAccessTokenHandler.php b/src/Symfony/Component/Security/Http/Tests/Authenticator/InMemoryAccessTokenHandler.php index 9ace3ba8324a7..3fe4c850736ce 100644 --- a/src/Symfony/Component/Security/Http/Tests/Authenticator/InMemoryAccessTokenHandler.php +++ b/src/Symfony/Component/Security/Http/Tests/Authenticator/InMemoryAccessTokenHandler.php @@ -13,15 +13,16 @@ use Symfony\Component\Security\Core\Exception\BadCredentialsException; use Symfony\Component\Security\Http\AccessToken\AccessTokenHandlerInterface; +use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge; class InMemoryAccessTokenHandler implements AccessTokenHandlerInterface { /** - * @var array + * @var array */ private $accessTokens = []; - public function getUserIdentifierFrom(string $accessToken): string + public function getUserBadgeFrom(string $accessToken): UserBadge { if (!\array_key_exists($accessToken, $this->accessTokens)) { throw new BadCredentialsException('Invalid access token or invalid user.'); @@ -37,7 +38,7 @@ public function remove(string $accessToken): self return $this; } - public function add(string $accessToken, string $user): self + public function add(string $accessToken, UserBadge $user): self { $this->accessTokens[$accessToken] = $user; From cd5bf0cedbcfa5c5a91b106346c8bf68d2916ddf Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 24 Nov 2022 18:21:16 +0100 Subject: [PATCH 18/23] [VarExporter] Improve partial-initialization API for ghost objects --- .../VarExporter/Internal/LazyObjectState.php | 32 +++++-- .../Component/VarExporter/LazyGhostTrait.php | 49 +++++++++-- .../VarExporter/LazyObjectInterface.php | 4 +- .../Component/VarExporter/LazyProxyTrait.php | 4 +- .../VarExporter/Tests/LazyGhostTraitTest.php | 86 +++++++++++++++++++ 5 files changed, 157 insertions(+), 18 deletions(-) diff --git a/src/Symfony/Component/VarExporter/Internal/LazyObjectState.php b/src/Symfony/Component/VarExporter/Internal/LazyObjectState.php index 605f1fdd52831..99f721df2605c 100644 --- a/src/Symfony/Component/VarExporter/Internal/LazyObjectState.php +++ b/src/Symfony/Component/VarExporter/Internal/LazyObjectState.php @@ -55,16 +55,34 @@ public function initialize($instance, $propertyName, $propertyScope) $propertyScopes = Hydrator::$propertyScopes[$class]; $propertyScopes[$k = "\0$propertyScope\0$propertyName"] ?? $propertyScopes[$k = "\0*\0$propertyName"] ?? $k = $propertyName; - if (!$initializer = $this->initializer[$k] ?? null) { - return self::STATUS_UNINITIALIZED_PARTIAL; - } + if ($initializer = $this->initializer[$k] ?? null) { + $value = $initializer(...[$instance, $propertyName, $propertyScope, LazyObjectRegistry::$defaultProperties[$class][$k] ?? null]); + $accessor = LazyObjectRegistry::$classAccessors[$propertyScope] ??= LazyObjectRegistry::getClassAccessors($propertyScope); + $accessor['set']($instance, $propertyName, $value); - $value = $initializer(...[$instance, $propertyName, $propertyScope, LazyObjectRegistry::$defaultProperties[$class][$k] ?? null]); + return $this->status = self::STATUS_INITIALIZED_PARTIAL; + } - $accessor = LazyObjectRegistry::$classAccessors[$propertyScope] ??= LazyObjectRegistry::getClassAccessors($propertyScope); - $accessor['set']($instance, $propertyName, $value); + $status = self::STATUS_UNINITIALIZED_PARTIAL; + + if ($initializer = $this->initializer["\0"] ?? null) { + if (!\is_array($values = $initializer($instance, LazyObjectRegistry::$defaultProperties[$class]))) { + throw new \TypeError(sprintf('The lazy-initializer defined for instance of "%s" must return an array, got "%s".', $class, get_debug_type($values))); + } + $properties = (array) $instance; + foreach ($values as $key => $value) { + if ($k === $key) { + $status = self::STATUS_INITIALIZED_PARTIAL; + } + if (!\array_key_exists($key, $properties) && [$scope, $name, $readonlyScope] = $propertyScopes[$key] ?? null) { + $scope = $readonlyScope ?? ('*' !== $scope ? $scope : $class); + $accessor = LazyObjectRegistry::$classAccessors[$scope] ??= LazyObjectRegistry::getClassAccessors($scope); + $accessor['set']($instance, $name, $value); + } + } + } - return $this->status = self::STATUS_INITIALIZED_PARTIAL; + return $status; } $this->status = self::STATUS_INITIALIZED_FULL; diff --git a/src/Symfony/Component/VarExporter/LazyGhostTrait.php b/src/Symfony/Component/VarExporter/LazyGhostTrait.php index 46346b6a7b0d7..df40c267fd027 100644 --- a/src/Symfony/Component/VarExporter/LazyGhostTrait.php +++ b/src/Symfony/Component/VarExporter/LazyGhostTrait.php @@ -29,16 +29,23 @@ trait LazyGhostTrait * properties and closures should accept 4 arguments: the instance to * initialize, the property to initialize, its write-scope, and its default * value. Each closure should return the value of the corresponding property. + * The special "\0" key can be used to define a closure that returns all + * properties at once when full-initialization is needed; it takes the + * instance and its default properties as arguments. * * Properties should be indexed by their array-cast name, see * https://php.net/manual/language.types.array#language.types.array.casting * - * @param \Closure(static):void|array $initializer - * @param array $skippedProperties An array indexed by the properties to skip, aka the ones - * that the initializer doesn't set when its a closure + * @param (\Closure(static):void + * |array + * |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 */ - public static function createLazyGhost(\Closure|array $initializer, array $skippedProperties = [], self $instance = null): static + public static function createLazyGhost(\Closure|array $initializer, array $skippedProperties = null, self $instance = null): static { + $onlyProperties = null === $skippedProperties && \is_array($initializer) ? $initializer : null; + if (self::class !== $class = $instance ? $instance::class : static::class) { $skippedProperties["\0".self::class."\0lazyObjectId"] = true; } elseif (\defined($class.'::LAZY_OBJECT_PROPERTY_SCOPES')) { @@ -48,8 +55,7 @@ public static function createLazyGhost(\Closure|array $initializer, array $skipp $instance ??= (Registry::$classReflectors[$class] ??= new \ReflectionClass($class))->newInstanceWithoutConstructor(); Registry::$defaultProperties[$class] ??= (array) $instance; $instance->lazyObjectId = $id = spl_object_id($instance); - Registry::$states[$id] = new LazyObjectState($initializer, $skippedProperties); - $onlyProperties = \is_array($initializer) ? $initializer : null; + Registry::$states[$id] = new LazyObjectState($initializer, $skippedProperties ??= []); foreach (Registry::$classResetters[$class] ??= Registry::getClassResetters($class) as $reset) { $reset($instance, $skippedProperties, $onlyProperties); @@ -60,8 +66,10 @@ public static function createLazyGhost(\Closure|array $initializer, array $skipp /** * Returns whether the object is initialized. + * + * @param $partial Whether partially initialized objects should be considered as initialized */ - public function isLazyObjectInitialized(): bool + public function isLazyObjectInitialized(bool $partial = false): bool { if (!$state = Registry::$states[$this->lazyObjectId ?? ''] ?? null) { return true; @@ -73,6 +81,11 @@ public function isLazyObjectInitialized(): bool $class = $this::class; $properties = (array) $this; + + if ($partial) { + return (bool) array_intersect_key($state->initializer, $properties); + } + $propertyScopes = Hydrator::$propertyScopes[$class] ??= Hydrator::getPropertyScopes($class); foreach ($state->initializer as $key => $initializer) { if (!\array_key_exists($key, $properties) && isset($propertyScopes[$key])) { @@ -100,6 +113,8 @@ public function initializeLazyObject(): static return $this; } + $values = isset($state->initializer["\0"]) ? null : []; + $class = $this::class; $properties = (array) $this; $propertyScopes = Hydrator::$propertyScopes[$class] ??= Hydrator::getPropertyScopes($class); @@ -107,9 +122,25 @@ public function initializeLazyObject(): static if (\array_key_exists($key, $properties) || ![$scope, $name, $readonlyScope] = $propertyScopes[$key] ?? null) { continue; } + $scope = $readonlyScope ?? ('*' !== $scope ? $scope : $class); - $state->initialize($this, $name, $readonlyScope ?? ('*' !== $scope ? $scope : null)); - $properties = (array) $this; + if (null === $values) { + if (!\is_array($values = ($state->initializer["\0"])($this, Registry::$defaultProperties[$class]))) { + throw new \TypeError(sprintf('The lazy-initializer defined for instance of "%s" must return an array, got "%s".', $class, get_debug_type($values))); + } + + if (\array_key_exists($key, $properties = (array) $this)) { + continue; + } + } + + if (\array_key_exists($key, $values)) { + $accessor = Registry::$classAccessors[$scope] ??= Registry::getClassAccessors($scope); + $accessor['set']($this, $name, $properties[$key] = $values[$key]); + } else { + $state->initialize($this, $name, $scope); + $properties = (array) $this; + } } return $this; diff --git a/src/Symfony/Component/VarExporter/LazyObjectInterface.php b/src/Symfony/Component/VarExporter/LazyObjectInterface.php index 314ba85e37039..36708845912ca 100644 --- a/src/Symfony/Component/VarExporter/LazyObjectInterface.php +++ b/src/Symfony/Component/VarExporter/LazyObjectInterface.php @@ -15,8 +15,10 @@ interface LazyObjectInterface { /** * Returns whether the object is initialized. + * + * @param $partial Whether partially initialized objects should be considered as initialized */ - public function isLazyObjectInitialized(): bool; + public function isLazyObjectInitialized(bool $partial = false): bool; /** * Forces initialization of a lazy object and returns it. diff --git a/src/Symfony/Component/VarExporter/LazyProxyTrait.php b/src/Symfony/Component/VarExporter/LazyProxyTrait.php index d79806c086d5a..97509eb35321b 100644 --- a/src/Symfony/Component/VarExporter/LazyProxyTrait.php +++ b/src/Symfony/Component/VarExporter/LazyProxyTrait.php @@ -47,8 +47,10 @@ public static function createLazyProxy(\Closure $initializer, self $instance = n /** * Returns whether the object is initialized. + * + * @param $partial Whether partially initialized objects should be considered as initialized */ - public function isLazyObjectInitialized(): bool + public function isLazyObjectInitialized(bool $partial = false): bool { if (0 >= ($this->lazyObjectId ?? 0)) { return true; diff --git a/src/Symfony/Component/VarExporter/Tests/LazyGhostTraitTest.php b/src/Symfony/Component/VarExporter/Tests/LazyGhostTraitTest.php index 44ef1f17eede9..3663217435268 100644 --- a/src/Symfony/Component/VarExporter/Tests/LazyGhostTraitTest.php +++ b/src/Symfony/Component/VarExporter/Tests/LazyGhostTraitTest.php @@ -248,6 +248,7 @@ public function testPartialInitialization() $this->assertFalse($instance->isLazyObjectInitialized()); $this->assertSame(123, $instance->public); $this->assertFalse($instance->isLazyObjectInitialized()); + $this->assertTrue($instance->isLazyObjectInitialized(true)); $this->assertSame(['public', "\0".TestClass::class."\0lazyObjectId"], array_keys((array) $instance)); $this->assertSame(1, $counter); @@ -330,4 +331,89 @@ public function testReflectionPropertyGetValue() $this->assertSame(-3, $r->getValue($obj)); } + + public function testFullPartialInitialization() + { + $counter = 0; + $initializer = static function (ChildTestClass $instance, string $property, ?string $scope, mixed $default) use (&$counter) { + return 234; + }; + $instance = ChildTestClass::createLazyGhost([ + 'public' => $initializer, + 'publicReadonly' => $initializer, + "\0*\0protected" => $initializer, + "\0" => function ($obj, $defaults) use (&$instance, &$counter) { + $counter += 1000; + $this->assertSame($instance, $obj); + + return [ + 'public' => 345, + 'publicReadonly' => 456, + "\0*\0protected" => 567, + ] + $defaults; + }, + ]); + + $this->assertSame($instance, $instance->initializeLazyObject()); + $this->assertSame(345, $instance->public); + $this->assertSame(456, $instance->publicReadonly); + $this->assertSame(6, ((array) $instance)["\0".ChildTestClass::class."\0private"]); + $this->assertSame(3, ((array) $instance)["\0".TestClass::class."\0private"]); + $this->assertSame(1000, $counter); + } + + public function testPartialInitializationFallback() + { + $counter = 0; + $instance = ChildTestClass::createLazyGhost([ + "\0" => function ($obj) use (&$instance, &$counter) { + $counter += 1000; + $this->assertSame($instance, $obj); + + return [ + 'public' => 345, + 'publicReadonly' => 456, + "\0*\0protected" => 567, + ]; + }, + ], []); + + $this->assertSame(345, $instance->public); + $this->assertSame(456, $instance->publicReadonly); + $this->assertSame(567, ((array) $instance)["\0*\0protected"]); + $this->assertSame(1000, $counter); + } + + public function testFullInitializationAfterPartialInitialization() + { + $counter = 0; + $initializer = static function (ChildTestClass $instance, string $property, ?string $scope, mixed $default) use (&$counter) { + ++$counter; + + return 234; + }; + $instance = ChildTestClass::createLazyGhost([ + 'public' => $initializer, + 'publicReadonly' => $initializer, + "\0*\0protected" => $initializer, + "\0" => function ($obj, $defaults) use (&$instance, &$counter) { + $counter += 1000; + $this->assertSame($instance, $obj); + + return [ + 'public' => 345, + 'publicReadonly' => 456, + "\0*\0protected" => 567, + ] + $defaults; + }, + ]); + + $this->assertSame(234, $instance->public); + $this->assertSame($instance, $instance->initializeLazyObject()); + $this->assertSame(234, $instance->public); + $this->assertSame(456, $instance->publicReadonly); + $this->assertSame(6, ((array) $instance)["\0".ChildTestClass::class."\0private"]); + $this->assertSame(3, ((array) $instance)["\0".TestClass::class."\0private"]); + $this->assertSame(1001, $counter); + } } From fb0aa607ea849a4649562728803f0b92580a51a2 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Fri, 25 Nov 2022 09:30:17 +0100 Subject: [PATCH 19/23] [HttpKernel] fix merge --- .../NotTaggedControllerValueResolverTest.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Symfony/Component/HttpKernel/Tests/Controller/ArgumentResolver/NotTaggedControllerValueResolverTest.php b/src/Symfony/Component/HttpKernel/Tests/Controller/ArgumentResolver/NotTaggedControllerValueResolverTest.php index 8b86d47a37b13..71b5543b04092 100644 --- a/src/Symfony/Component/HttpKernel/Tests/Controller/ArgumentResolver/NotTaggedControllerValueResolverTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/Controller/ArgumentResolver/NotTaggedControllerValueResolverTest.php @@ -108,6 +108,11 @@ public function testControllerNameIsAnArray() $resolver->resolve($request, $argument); } + /** + * In Symfony 7, keep this test case but remove the call to supports(). + * + * @group legacy + */ public function testInvokableController() { $this->expectException(RuntimeException::class); From df735a233a143aba3a402e30f3a7eb041b6b2f8d Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Fri, 25 Nov 2022 09:33:31 +0100 Subject: [PATCH 20/23] [VarExporter] fix test --- .../Component/VarExporter/Tests/LazyProxyTraitTest.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Symfony/Component/VarExporter/Tests/LazyProxyTraitTest.php b/src/Symfony/Component/VarExporter/Tests/LazyProxyTraitTest.php index 848e1d1c19e36..04497f02b34ae 100644 --- a/src/Symfony/Component/VarExporter/Tests/LazyProxyTraitTest.php +++ b/src/Symfony/Component/VarExporter/Tests/LazyProxyTraitTest.php @@ -12,6 +12,7 @@ namespace Symfony\Component\VarExporter\Tests; use PHPUnit\Framework\TestCase; +use Symfony\Component\VarExporter\Exception\LogicException; use Symfony\Component\VarExporter\LazyProxyTrait; use Symfony\Component\VarExporter\ProxyHelper; use Symfony\Component\VarExporter\Tests\Fixtures\LazyProxy\FinalPublicClass; @@ -237,9 +238,9 @@ public function setFoo($foo): static */ public function testReadOnlyClass() { - $proxy = $this->createLazyProxy(ReadOnlyClass::class, fn () => new ReadOnlyClass(123)); - - $this->assertSame(123, $proxy->foo); + $this->expectException(LogicException::class); + $this->expectExceptionMessage('Cannot generate lazy proxy: class "Symfony\Component\VarExporter\Tests\Fixtures\LazyProxy\ReadOnlyClass" is read-only.'); + $this->createLazyProxy(ReadOnlyClass::class, fn () => new ReadOnlyClass(123)); } public function testLazyDecoratorClass() From bc5fea4e2190f00a207871115a83dd7df03d7637 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Fri, 25 Nov 2022 09:42:40 +0100 Subject: [PATCH 21/23] [validator] Fix deprecation --- .../Constraints/ExpressionLanguageSyntaxValidatorTest.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/Validator/Tests/Constraints/ExpressionLanguageSyntaxValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/ExpressionLanguageSyntaxValidatorTest.php index bfd4336de6e33..f44e606a49f12 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/ExpressionLanguageSyntaxValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/ExpressionLanguageSyntaxValidatorTest.php @@ -14,6 +14,7 @@ use Symfony\Component\ExpressionLanguage\ExpressionLanguage; use Symfony\Component\Validator\Constraints\ExpressionLanguageSyntax; use Symfony\Component\Validator\Constraints\ExpressionLanguageSyntaxValidator; +use Symfony\Component\Validator\ConstraintValidatorInterface; use Symfony\Component\Validator\Test\ConstraintValidatorTestCase; /** @@ -21,7 +22,7 @@ */ class ExpressionLanguageSyntaxValidatorTest extends ConstraintValidatorTestCase { - protected function createValidator(): ExpressionLanguageSyntaxValidator + protected function createValidator(): ConstraintValidatorInterface { return new ExpressionLanguageSyntaxValidator(new ExpressionLanguage()); } From 71dca4e392e80d05d3bbfcdc24dc37f04e44d63d Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Fri, 25 Nov 2022 11:37:24 +0100 Subject: [PATCH 22/23] Update CHANGELOG for 6.2.0-RC1 --- CHANGELOG-6.2.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/CHANGELOG-6.2.md b/CHANGELOG-6.2.md index 94acdbdcfc990..0870e46a6ab8f 100644 --- a/CHANGELOG-6.2.md +++ b/CHANGELOG-6.2.md @@ -7,6 +7,18 @@ 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.0-RC1 (2022-11-25) + + * bug #48312 [VarExporter] Improve partial-initialization API for ghost objects (nicolas-grekas) + * bug #48285 [Security] Support loading UserBadge directly from accessToken (Jeroeny) + * bug #48262 [Notifier] [SMSBiuras] `true`/`false` mismatch for `test_mode` option (StaffNowa) + * bug #48273 [HttpKernel] Fix message for unresovable arguments of invokable controllers (fancyweb) + * bug #48251 [PropertyInfo] ignore const expressions read by phpdocumentor (xabbuh) + * bug #48224 [DependencyInjection] Process bindings in `ServiceLocatorTagPass` (MatTheCat) + * bug #48271 [WebProfilerBundle] Fix form panel when there are no view vars (nicolas-grekas) + * bug #48274 Add more #[\SensitiveParameter] (fancyweb) + * bug #48179 [Console] Support completion for bash functions (Chi-teck) + * 6.2.0-BETA3 (2022-11-19) * bug #48217 [Console] Improve error message when shell is not detected in completion command (GromNaN) From 5f03eb8a91259a8dc08ac1e0bb1d2889b57f8bc1 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Fri, 25 Nov 2022 11:37:29 +0100 Subject: [PATCH 23/23] Update VERSION for 6.2.0-RC1 --- src/Symfony/Component/HttpKernel/Kernel.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index 3661c216a2c4b..52a61b868d73c 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -75,12 +75,12 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl */ private static array $freshCache = []; - public const VERSION = '6.2.0-DEV'; + public const VERSION = '6.2.0-RC1'; public const VERSION_ID = 60200; public const MAJOR_VERSION = 6; public const MINOR_VERSION = 2; public const RELEASE_VERSION = 0; - public const EXTRA_VERSION = 'DEV'; + public const EXTRA_VERSION = 'RC1'; public const END_OF_MAINTENANCE = '07/2023'; public const END_OF_LIFE = '07/2023';