diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/translation.php b/src/Symfony/Bundle/FrameworkBundle/Resources/config/translation.php
index 280e03f355cd4..dcfa2bc15716d 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/translation.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/translation.php
@@ -186,10 +186,11 @@
->set('translation.locale_switcher', LocaleSwitcher::class)
->args([
param('kernel.default_locale'),
- tagged_iterator('kernel.locale_aware'),
+ tagged_iterator('kernel.locale_aware', exclude: 'translation.locale_switcher'),
service('router.request_context')->ignoreOnInvalid(),
])
->tag('kernel.reset', ['method' => 'reset'])
+ ->tag('kernel.locale_aware')
->alias(LocaleAwareInterface::class, 'translation.locale_switcher')
->alias(LocaleSwitcher::class, 'translation.locale_switcher')
;
diff --git a/src/Symfony/Bundle/FrameworkBundle/Test/MailerAssertionsTrait.php b/src/Symfony/Bundle/FrameworkBundle/Test/MailerAssertionsTrait.php
index 1a629d6255fbe..d6b29d2b5a0c6 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Test/MailerAssertionsTrait.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Test/MailerAssertionsTrait.php
@@ -123,10 +123,6 @@ private static function getMessageMailerEvents(): MessageEvents
return $container->get('mailer.message_logger_listener')->getEvents();
}
- if ($container->has('mailer.logger_message_listener')) {
- return $container->get('mailer.logger_message_listener')->getEvents();
- }
-
static::fail('A client must have Mailer enabled to make email assertions. Did you forget to require symfony/mailer?');
}
}
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/exceptions.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/exceptions.xml
index 2e6048fa7c7aa..4a877c1eb3499 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/exceptions.xml
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/exceptions.xml
@@ -6,11 +6,25 @@
http://symfony.com/schema/dic/symfony https://symfony.com/schema/dic/symfony/symfony-1.0.xsd">
-
-
-
-
-
-
+
+
+
+
+
+
+
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/exceptions_legacy.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/exceptions_legacy.xml
new file mode 100644
index 0000000000000..2e6048fa7c7aa
--- /dev/null
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/exceptions_legacy.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php
index 640a152402bea..945cd330fa94e 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php
@@ -37,6 +37,7 @@
use Symfony\Component\DependencyInjection\ChildDefinition;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\Compiler\ResolveInstanceofConditionalsPass;
+use Symfony\Component\DependencyInjection\Compiler\ResolveTaggedIteratorArgumentPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\Definition;
@@ -579,24 +580,34 @@ public function testExceptionsConfig()
{
$container = $this->createContainerFromFile('exceptions');
+ $configuration = $container->getDefinition('exception_listener')->getArgument(3);
+
$this->assertSame([
- \Symfony\Component\HttpKernel\Exception\BadRequestHttpException::class => [
- 'log_level' => 'info',
- 'status_code' => 422,
- ],
- \Symfony\Component\HttpKernel\Exception\NotFoundHttpException::class => [
- 'log_level' => 'info',
- 'status_code' => null,
- ],
- \Symfony\Component\HttpKernel\Exception\ConflictHttpException::class => [
- 'log_level' => 'info',
- 'status_code' => null,
- ],
- \Symfony\Component\HttpKernel\Exception\ServiceUnavailableHttpException::class => [
- 'log_level' => null,
- 'status_code' => 500,
- ],
- ], $container->getDefinition('exception_listener')->getArgument(3));
+ \Symfony\Component\HttpKernel\Exception\BadRequestHttpException::class,
+ \Symfony\Component\HttpKernel\Exception\NotFoundHttpException::class,
+ \Symfony\Component\HttpKernel\Exception\ConflictHttpException::class,
+ \Symfony\Component\HttpKernel\Exception\ServiceUnavailableHttpException::class,
+ ], array_keys($configuration));
+
+ $this->assertEqualsCanonicalizing([
+ 'log_level' => 'info',
+ 'status_code' => 422,
+ ], $configuration[\Symfony\Component\HttpKernel\Exception\BadRequestHttpException::class]);
+
+ $this->assertEqualsCanonicalizing([
+ 'log_level' => 'info',
+ 'status_code' => null,
+ ], $configuration[\Symfony\Component\HttpKernel\Exception\NotFoundHttpException::class]);
+
+ $this->assertEqualsCanonicalizing([
+ 'log_level' => 'info',
+ 'status_code' => null,
+ ], $configuration[\Symfony\Component\HttpKernel\Exception\ConflictHttpException::class]);
+
+ $this->assertEqualsCanonicalizing([
+ 'log_level' => null,
+ 'status_code' => 500,
+ ], $configuration[\Symfony\Component\HttpKernel\Exception\ServiceUnavailableHttpException::class]);
}
public function testRouter()
@@ -2067,7 +2078,9 @@ public function testLocaleSwitcherServiceRegistered()
$this->markTestSkipped('LocaleSwitcher not available.');
}
- $container = $this->createContainerFromFile('full');
+ $container = $this->createContainerFromFile('full', compile: false);
+ $container->addCompilerPass(new ResolveTaggedIteratorArgumentPass());
+ $container->compile();
$this->assertTrue($container->has('translation.locale_switcher'));
@@ -2077,6 +2090,10 @@ public function testLocaleSwitcherServiceRegistered()
$this->assertInstanceOf(TaggedIteratorArgument::class, $switcherDef->getArgument(1));
$this->assertSame('kernel.locale_aware', $switcherDef->getArgument(1)->getTag());
$this->assertEquals(new Reference('router.request_context', ContainerBuilder::IGNORE_ON_INVALID_REFERENCE), $switcherDef->getArgument(2));
+
+ $localeAwareServices = array_map(fn (Reference $r) => (string) $r, $switcherDef->getArgument(1)->getValues());
+
+ $this->assertNotContains('translation.locale_switcher', $localeAwareServices);
}
public function testHtmlSanitizer()
@@ -2143,7 +2160,9 @@ public function testHtmlSanitizerDefaultNullAllowedLinkMediaHost()
$calls = $container->getDefinition('html_sanitizer.config.custom_default')->getMethodCalls();
$this->assertContains(['allowLinkHosts', [null], true], $calls);
+ $this->assertContains(['allowRelativeLinks', [false], true], $calls);
$this->assertContains(['allowMediaHosts', [null], true], $calls);
+ $this->assertContains(['allowRelativeMedias', [false], true], $calls);
}
public function testHtmlSanitizerDefaultConfig()
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/XmlFrameworkExtensionTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/XmlFrameworkExtensionTest.php
index ebc37d93bed84..131bb07f0c657 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/XmlFrameworkExtensionTest.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/XmlFrameworkExtensionTest.php
@@ -32,4 +32,38 @@ public function testMessengerMiddlewareFactoryErroneousFormat()
{
$this->markTestSkipped('XML configuration will not allow erroneous format.');
}
+
+ public function testLegacyExceptionsConfig()
+ {
+ $container = $this->createContainerFromFile('exceptions_legacy');
+
+ $configuration = $container->getDefinition('exception_listener')->getArgument(3);
+
+ $this->assertSame([
+ \Symfony\Component\HttpKernel\Exception\BadRequestHttpException::class,
+ \Symfony\Component\HttpKernel\Exception\NotFoundHttpException::class,
+ \Symfony\Component\HttpKernel\Exception\ConflictHttpException::class,
+ \Symfony\Component\HttpKernel\Exception\ServiceUnavailableHttpException::class,
+ ], array_keys($configuration));
+
+ $this->assertEqualsCanonicalizing([
+ 'log_level' => 'info',
+ 'status_code' => 422,
+ ], $configuration[\Symfony\Component\HttpKernel\Exception\BadRequestHttpException::class]);
+
+ $this->assertEqualsCanonicalizing([
+ 'log_level' => 'info',
+ 'status_code' => null,
+ ], $configuration[\Symfony\Component\HttpKernel\Exception\NotFoundHttpException::class]);
+
+ $this->assertEqualsCanonicalizing([
+ 'log_level' => 'info',
+ 'status_code' => null,
+ ], $configuration[\Symfony\Component\HttpKernel\Exception\ConflictHttpException::class]);
+
+ $this->assertEqualsCanonicalizing([
+ 'log_level' => null,
+ 'status_code' => 500,
+ ], $configuration[\Symfony\Component\HttpKernel\Exception\ServiceUnavailableHttpException::class]);
+ }
}
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/CacheAttributeListenerTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/CacheAttributeListenerTest.php
new file mode 100644
index 0000000000000..72b2c12266d87
--- /dev/null
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/CacheAttributeListenerTest.php
@@ -0,0 +1,98 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bundle\FrameworkBundle\Tests\Functional;
+
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Response;
+use Symfony\Component\HttpKernel\Attribute\Cache;
+use Symfony\Component\HttpKernel\Controller\ValueResolverInterface;
+use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata;
+use Symfony\Component\Security\Core\User\InMemoryUser;
+use Symfony\Component\Security\Http\Attribute\IsGranted;
+
+class CacheAttributeListenerTest extends AbstractWebTestCase
+{
+ public function testAnonimousUserWithEtag()
+ {
+ $client = self::createClient(['test_case' => 'CacheAttributeListener']);
+
+ $client->request('GET', '/', server: ['HTTP_IF_NONE_MATCH' => sprintf('"%s"', hash('sha256', '12345'))]);
+
+ self::assertTrue($client->getResponse()->isRedirect('http://localhost/login'));
+ }
+
+ public function testAnonimousUserWithoutEtag()
+ {
+ $client = self::createClient(['test_case' => 'CacheAttributeListener']);
+
+ $client->request('GET', '/');
+
+ self::assertTrue($client->getResponse()->isRedirect('http://localhost/login'));
+ }
+
+ public function testLoggedInUserWithEtag()
+ {
+ $client = self::createClient(['test_case' => 'CacheAttributeListener']);
+
+ $client->loginUser(new InMemoryUser('the-username', 'the-password', ['ROLE_USER']));
+ $client->request('GET', '/', server: ['HTTP_IF_NONE_MATCH' => sprintf('"%s"', hash('sha256', '12345'))]);
+
+ $response = $client->getResponse();
+
+ self::assertSame(304, $response->getStatusCode());
+ self::assertSame('', $response->getContent());
+ }
+
+ public function testLoggedInUserWithoutEtag()
+ {
+ $client = self::createClient(['test_case' => 'CacheAttributeListener']);
+
+ $client->loginUser(new InMemoryUser('the-username', 'the-password', ['ROLE_USER']));
+ $client->request('GET', '/');
+
+ $response = $client->getResponse();
+
+ self::assertSame(200, $response->getStatusCode());
+ self::assertSame('Hi there!', $response->getContent());
+ }
+}
+
+class TestEntityValueResolver implements ValueResolverInterface
+{
+ public function resolve(Request $request, ArgumentMetadata $argument): iterable
+ {
+ return Post::class === $argument->getType() ? [new Post()] : [];
+ }
+}
+
+class Post
+{
+ public function getId(): int
+ {
+ return 1;
+ }
+
+ public function getEtag(): string
+ {
+ return '12345';
+ }
+}
+
+class WithAttributesController
+{
+ #[IsGranted('ROLE_USER')]
+ #[Cache(etag: 'post.getEtag()')]
+ public function __invoke(Post $post): Response
+ {
+ return new Response('Hi there!');
+ }
+}
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/CacheAttributeListener/bundles.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/CacheAttributeListener/bundles.php
new file mode 100644
index 0000000000000..9a26fb163a77d
--- /dev/null
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/CacheAttributeListener/bundles.php
@@ -0,0 +1,18 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+use Symfony\Bundle\FrameworkBundle\FrameworkBundle;
+use Symfony\Bundle\SecurityBundle\SecurityBundle;
+
+return [
+ new FrameworkBundle(),
+ new SecurityBundle(),
+];
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/CacheAttributeListener/config.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/CacheAttributeListener/config.yml
new file mode 100644
index 0000000000000..21890451a1094
--- /dev/null
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/CacheAttributeListener/config.yml
@@ -0,0 +1,24 @@
+imports:
+ - { resource: ../config/default.yml }
+
+services:
+ Symfony\Bundle\FrameworkBundle\Tests\Functional\TestEntityValueResolver:
+ tags:
+ - { name: controller.argument_value_resolver, priority: 110 }
+
+ Symfony\Bundle\FrameworkBundle\Tests\Functional\WithAttributesController:
+ public: true
+
+security:
+ providers:
+ main:
+ memory:
+ users:
+ the-username: { password: the-password, roles: [ 'ROLE_USER' ] }
+
+ firewalls:
+ main:
+ pattern: ^/
+ form_login:
+ login_path: /login
+ provider: main
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/CacheAttributeListener/routing.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/CacheAttributeListener/routing.yml
new file mode 100644
index 0000000000000..50c37b823fcbe
--- /dev/null
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/CacheAttributeListener/routing.yml
@@ -0,0 +1,3 @@
+with_attributes_controller:
+ path: /
+ controller: Symfony\Bundle\FrameworkBundle\Tests\Functional\WithAttributesController
diff --git a/src/Symfony/Bundle/FrameworkBundle/composer.json b/src/Symfony/Bundle/FrameworkBundle/composer.json
index 4ab6b2e919e32..00986c7854f08 100644
--- a/src/Symfony/Bundle/FrameworkBundle/composer.json
+++ b/src/Symfony/Bundle/FrameworkBundle/composer.json
@@ -26,7 +26,7 @@
"symfony/error-handler": "^6.1",
"symfony/event-dispatcher": "^5.4|^6.0",
"symfony/http-foundation": "^6.2",
- "symfony/http-kernel": "^6.2",
+ "symfony/http-kernel": "^6.2.1",
"symfony/polyfill-mbstring": "~1.0",
"symfony/filesystem": "^5.4|^6.0",
"symfony/finder": "^5.4|^6.0",
diff --git a/src/Symfony/Bundle/FrameworkBundle/phpunit.xml.dist b/src/Symfony/Bundle/FrameworkBundle/phpunit.xml.dist
index d00ee0f1e214e..3ea6d9e6e218e 100644
--- a/src/Symfony/Bundle/FrameworkBundle/phpunit.xml.dist
+++ b/src/Symfony/Bundle/FrameworkBundle/phpunit.xml.dist
@@ -10,6 +10,7 @@
>
+
diff --git a/src/Symfony/Bundle/SecurityBundle/Command/DebugFirewallCommand.php b/src/Symfony/Bundle/SecurityBundle/Command/DebugFirewallCommand.php
index 757f61629f6f5..8a87422862287 100644
--- a/src/Symfony/Bundle/SecurityBundle/Command/DebugFirewallCommand.php
+++ b/src/Symfony/Bundle/SecurityBundle/Command/DebugFirewallCommand.php
@@ -245,7 +245,7 @@ private function formatCallable(mixed $callable): string
if (str_contains($r->name, '{closure}')) {
return 'Closure()';
}
- if ($class = $r->getClosureScopeClass()) {
+ if ($class = \PHP_VERSION_ID >= 80111 ? $r->getClosureCalledClass() : $r->getClosureScopeClass()) {
return sprintf('%s::%s()', $class->name, $r->name);
}
diff --git a/src/Symfony/Bundle/SecurityBundle/Security.php b/src/Symfony/Bundle/SecurityBundle/Security.php
index 05a5f7c9394eb..d78cfc0edd02f 100644
--- a/src/Symfony/Bundle/SecurityBundle/Security.php
+++ b/src/Symfony/Bundle/SecurityBundle/Security.php
@@ -24,6 +24,7 @@
use Symfony\Component\Security\Http\Authenticator\AuthenticatorInterface;
use Symfony\Component\Security\Http\Event\LogoutEvent;
use Symfony\Component\Security\Http\ParameterBagUtils;
+use Symfony\Component\Security\Http\SecurityRequestAttributes;
use Symfony\Contracts\Service\ServiceProviderInterface;
/**
@@ -37,6 +38,10 @@
*/
class Security extends LegacySecurity
{
+ public const ACCESS_DENIED_ERROR = SecurityRequestAttributes::ACCESS_DENIED_ERROR;
+ public const AUTHENTICATION_ERROR = SecurityRequestAttributes::AUTHENTICATION_ERROR;
+ public const LAST_USERNAME = SecurityRequestAttributes::LAST_USERNAME;
+
public function __construct(private readonly ContainerInterface $container, private readonly array $authenticators = [])
{
parent::__construct($container, false);
@@ -111,7 +116,7 @@ public function logout(bool $validateCsrfToken = true): ?Response
private function getAuthenticator(?string $authenticatorName, string $firewallName): AuthenticatorInterface
{
- if (!\array_key_exists($firewallName, $this->authenticators)) {
+ if (!isset($this->authenticators[$firewallName])) {
throw new LogicException(sprintf('No authenticators found for firewall "%s".', $firewallName));
}
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/SecurityTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/SecurityTest.php
index 7ee7a2465263d..b69467cb95cf3 100644
--- a/src/Symfony/Bundle/SecurityBundle/Tests/SecurityTest.php
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/SecurityTest.php
@@ -169,6 +169,38 @@ public function testLogin()
$security->login($user);
}
+ public function testLoginWithoutAuthenticatorThrows()
+ {
+ $request = new Request();
+ $authenticator = $this->createMock(AuthenticatorInterface::class);
+ $requestStack = $this->createMock(RequestStack::class);
+ $firewallMap = $this->createMock(FirewallMap::class);
+ $firewall = new FirewallConfig('main', 'main');
+ $user = $this->createMock(UserInterface::class);
+ $userChecker = $this->createMock(UserCheckerInterface::class);
+
+ $container = $this->createMock(ContainerInterface::class);
+ $container
+ ->expects($this->atLeastOnce())
+ ->method('get')
+ ->willReturnMap([
+ ['request_stack', $requestStack],
+ ['security.firewall.map', $firewallMap],
+ ['security.user_checker', $userChecker],
+ ])
+ ;
+
+ $requestStack->expects($this->once())->method('getCurrentRequest')->willReturn($request);
+ $firewallMap->expects($this->once())->method('getFirewallConfig')->willReturn($firewall);
+
+ $security = new Security($container, ['main' => null]);
+
+ $this->expectException(\LogicException::class);
+ $this->expectExceptionMessage('No authenticators found for firewall "main".');
+
+ $security->login($user);
+ }
+
public function testLogout()
{
$request = new Request();
diff --git a/src/Symfony/Bundle/TwigBundle/Resources/config/mailer.php b/src/Symfony/Bundle/TwigBundle/Resources/config/mailer.php
index 1444481f2c0ba..e43658a7da02c 100644
--- a/src/Symfony/Bundle/TwigBundle/Resources/config/mailer.php
+++ b/src/Symfony/Bundle/TwigBundle/Resources/config/mailer.php
@@ -13,6 +13,7 @@
use Symfony\Bridge\Twig\Mime\BodyRenderer;
use Symfony\Component\Mailer\EventListener\MessageListener;
+use Symfony\Component\Mime\BodyRendererInterface;
return static function (ContainerConfigurator $container) {
$container->services()
@@ -22,5 +23,6 @@
->set('twig.mime_body_renderer', BodyRenderer::class)
->args([service('twig')])
+ ->alias(BodyRendererInterface::class, 'twig.mime_body_renderer')
;
};
diff --git a/src/Symfony/Bundle/WebProfilerBundle/Tests/Csp/ContentSecurityPolicyHandlerTest.php b/src/Symfony/Bundle/WebProfilerBundle/Tests/Csp/ContentSecurityPolicyHandlerTest.php
index fbaf2f7965d05..c345b5fbb89c8 100644
--- a/src/Symfony/Bundle/WebProfilerBundle/Tests/Csp/ContentSecurityPolicyHandlerTest.php
+++ b/src/Symfony/Bundle/WebProfilerBundle/Tests/Csp/ContentSecurityPolicyHandlerTest.php
@@ -66,10 +66,10 @@ public function provideRequestAndResponses()
];
return [
- [$nonce, ['csp_script_nonce' => $nonce, 'csp_style_nonce' => $nonce], $this->createRequest(), $this->createResponse()],
- [$nonce, ['csp_script_nonce' => $requestScriptNonce, 'csp_style_nonce' => $requestStyleNonce], $this->createRequest($requestNonceHeaders), $this->createResponse($responseNonceHeaders)],
- [$nonce, ['csp_script_nonce' => $requestScriptNonce, 'csp_style_nonce' => $requestStyleNonce], $this->createRequest($requestNonceHeaders), $this->createResponse()],
- [$nonce, ['csp_script_nonce' => $responseScriptNonce, 'csp_style_nonce' => $responseStyleNonce], $this->createRequest(), $this->createResponse($responseNonceHeaders)],
+ [$nonce, ['csp_script_nonce' => $nonce, 'csp_style_nonce' => $nonce], self::createRequest(), self::createResponse()],
+ [$nonce, ['csp_script_nonce' => $requestScriptNonce, 'csp_style_nonce' => $requestStyleNonce], self::createRequest($requestNonceHeaders), self::createResponse($responseNonceHeaders)],
+ [$nonce, ['csp_script_nonce' => $requestScriptNonce, 'csp_style_nonce' => $requestStyleNonce], self::createRequest($requestNonceHeaders), self::createResponse()],
+ [$nonce, ['csp_script_nonce' => $responseScriptNonce, 'csp_style_nonce' => $responseStyleNonce], self::createRequest(), self::createResponse($responseNonceHeaders)],
];
}
@@ -96,104 +96,104 @@ public function provideRequestAndResponsesForOnKernelResponse()
[
$nonce,
['csp_script_nonce' => $nonce, 'csp_style_nonce' => $nonce],
- $this->createRequest(),
- $this->createResponse(),
+ self::createRequest(),
+ self::createResponse(),
['Content-Security-Policy' => null, 'Content-Security-Policy-Report-Only' => null, 'X-Content-Security-Policy' => null],
],
[
$nonce, ['csp_script_nonce' => $requestScriptNonce, 'csp_style_nonce' => $requestStyleNonce],
- $this->createRequest($requestNonceHeaders),
- $this->createResponse($responseNonceHeaders),
+ self::createRequest($requestNonceHeaders),
+ self::createResponse($responseNonceHeaders),
['Content-Security-Policy' => null, 'Content-Security-Policy-Report-Only' => null, 'X-Content-Security-Policy' => null],
],
[
$nonce,
['csp_script_nonce' => $requestScriptNonce, 'csp_style_nonce' => $requestStyleNonce],
- $this->createRequest($requestNonceHeaders),
- $this->createResponse(),
+ self::createRequest($requestNonceHeaders),
+ self::createResponse(),
['Content-Security-Policy' => null, 'Content-Security-Policy-Report-Only' => null, 'X-Content-Security-Policy' => null],
],
[
$nonce,
['csp_script_nonce' => $responseScriptNonce, 'csp_style_nonce' => $responseStyleNonce],
- $this->createRequest(),
- $this->createResponse($responseNonceHeaders),
+ self::createRequest(),
+ self::createResponse($responseNonceHeaders),
['Content-Security-Policy' => null, 'Content-Security-Policy-Report-Only' => null, 'X-Content-Security-Policy' => null],
],
[
$nonce,
['csp_script_nonce' => $nonce, 'csp_style_nonce' => $nonce],
- $this->createRequest(),
- $this->createResponse(['Content-Security-Policy' => 'frame-ancestors https: ; form-action: https:', 'Content-Security-Policy-Report-Only' => 'frame-ancestors http: ; form-action: http:']),
+ self::createRequest(),
+ self::createResponse(['Content-Security-Policy' => 'frame-ancestors https: ; form-action: https:', 'Content-Security-Policy-Report-Only' => 'frame-ancestors http: ; form-action: http:']),
['Content-Security-Policy' => 'frame-ancestors https: ; form-action: https:', 'Content-Security-Policy-Report-Only' => 'frame-ancestors http: ; form-action: http:', 'X-Content-Security-Policy' => null],
],
[
$nonce,
['csp_script_nonce' => $nonce, 'csp_style_nonce' => $nonce],
- $this->createRequest(),
- $this->createResponse(['Content-Security-Policy' => 'default-src \'self\' domain.com; script-src \'self\' \'unsafe-inline\'', 'Content-Security-Policy-Report-Only' => 'default-src \'self\' domain-report-only.com; script-src \'self\' \'unsafe-inline\'']),
+ self::createRequest(),
+ self::createResponse(['Content-Security-Policy' => 'default-src \'self\' domain.com; script-src \'self\' \'unsafe-inline\'', 'Content-Security-Policy-Report-Only' => 'default-src \'self\' domain-report-only.com; script-src \'self\' \'unsafe-inline\'']),
['Content-Security-Policy' => 'default-src \'self\' domain.com; script-src \'self\' \'unsafe-inline\'; style-src \'self\' domain.com \'unsafe-inline\' \'nonce-'.$nonce.'\'', 'Content-Security-Policy-Report-Only' => 'default-src \'self\' domain-report-only.com; script-src \'self\' \'unsafe-inline\'; style-src \'self\' domain-report-only.com \'unsafe-inline\' \'nonce-'.$nonce.'\'', 'X-Content-Security-Policy' => null],
],
[
$nonce,
['csp_script_nonce' => $nonce, 'csp_style_nonce' => $nonce],
- $this->createRequest(),
- $this->createResponse(['Content-Security-Policy' => 'default-src \'self\' domain.com; script-src \'self\' \'unsafe-inline\'; script-src-elem \'self\'; style-src \'self\' \'unsafe-inline\'; style-src-elem \'self\'', 'Content-Security-Policy-Report-Only' => 'default-src \'self\' domain-report-only.com; script-src \'self\' \'unsafe-inline\'; script-src-elem \'self\'; style-src \'self\' \'unsafe-inline\'; style-src-elem \'self\'']),
+ self::createRequest(),
+ self::createResponse(['Content-Security-Policy' => 'default-src \'self\' domain.com; script-src \'self\' \'unsafe-inline\'; script-src-elem \'self\'; style-src \'self\' \'unsafe-inline\'; style-src-elem \'self\'', 'Content-Security-Policy-Report-Only' => 'default-src \'self\' domain-report-only.com; script-src \'self\' \'unsafe-inline\'; script-src-elem \'self\'; style-src \'self\' \'unsafe-inline\'; style-src-elem \'self\'']),
['Content-Security-Policy' => 'default-src \'self\' domain.com; script-src \'self\' \'unsafe-inline\'; script-src-elem \'self\' \'unsafe-inline\' \'nonce-'.$nonce.'\'; style-src \'self\' \'unsafe-inline\'; style-src-elem \'self\' \'unsafe-inline\' \'nonce-'.$nonce.'\'', 'Content-Security-Policy-Report-Only' => 'default-src \'self\' domain-report-only.com; script-src \'self\' \'unsafe-inline\'; script-src-elem \'self\' \'unsafe-inline\' \'nonce-'.$nonce.'\'; style-src \'self\' \'unsafe-inline\'; style-src-elem \'self\' \'unsafe-inline\' \'nonce-'.$nonce.'\'', 'X-Content-Security-Policy' => null],
],
[
$nonce,
['csp_script_nonce' => $nonce, 'csp_style_nonce' => $nonce],
- $this->createRequest(),
- $this->createResponse(['Content-Security-Policy' => 'default-src \'none\'', 'Content-Security-Policy-Report-Only' => 'default-src \'none\'']),
+ self::createRequest(),
+ self::createResponse(['Content-Security-Policy' => 'default-src \'none\'', 'Content-Security-Policy-Report-Only' => 'default-src \'none\'']),
['Content-Security-Policy' => 'default-src \'none\'; script-src \'unsafe-inline\' \'nonce-'.$nonce.'\'; style-src \'unsafe-inline\' \'nonce-'.$nonce.'\'', 'Content-Security-Policy-Report-Only' => 'default-src \'none\'; script-src \'unsafe-inline\' \'nonce-'.$nonce.'\'; style-src \'unsafe-inline\' \'nonce-'.$nonce.'\'', 'X-Content-Security-Policy' => null],
],
[
$nonce,
['csp_script_nonce' => $nonce, 'csp_style_nonce' => $nonce],
- $this->createRequest(),
- $this->createResponse(['Content-Security-Policy' => 'script-src \'self\' \'unsafe-inline\'']),
+ self::createRequest(),
+ self::createResponse(['Content-Security-Policy' => 'script-src \'self\' \'unsafe-inline\'']),
['Content-Security-Policy' => 'script-src \'self\' \'unsafe-inline\'', 'X-Content-Security-Policy' => null],
],
[
$nonce,
['csp_script_nonce' => $nonce, 'csp_style_nonce' => $nonce],
- $this->createRequest(),
- $this->createResponse(['Content-Security-Policy' => 'script-src \'self\'; style-src \'self\'']),
+ self::createRequest(),
+ self::createResponse(['Content-Security-Policy' => 'script-src \'self\'; style-src \'self\'']),
['Content-Security-Policy' => 'script-src \'self\' \'unsafe-inline\' \'nonce-'.$nonce.'\'; style-src \'self\' \'unsafe-inline\' \'nonce-'.$nonce.'\'', 'X-Content-Security-Policy' => null],
],
[
$nonce,
['csp_script_nonce' => $nonce, 'csp_style_nonce' => $nonce],
- $this->createRequest(),
- $this->createResponse(['X-Content-Security-Policy' => 'script-src \'self\' \'unsafe-inline\'']),
+ self::createRequest(),
+ self::createResponse(['X-Content-Security-Policy' => 'script-src \'self\' \'unsafe-inline\'']),
['X-Content-Security-Policy' => 'script-src \'self\' \'unsafe-inline\'', 'Content-Security-Policy' => null],
],
[
$nonce,
['csp_script_nonce' => $nonce, 'csp_style_nonce' => $nonce],
- $this->createRequest(),
- $this->createResponse(['X-Content-Security-Policy' => 'script-src \'self\'']),
+ self::createRequest(),
+ self::createResponse(['X-Content-Security-Policy' => 'script-src \'self\'']),
['X-Content-Security-Policy' => 'script-src \'self\' \'unsafe-inline\' \'nonce-'.$nonce.'\'', 'Content-Security-Policy' => null],
],
[
$nonce,
['csp_script_nonce' => $nonce, 'csp_style_nonce' => $nonce],
- $this->createRequest(),
- $this->createResponse(['X-Content-Security-Policy' => 'script-src \'self\' \'unsafe-inline\' \'sha384-LALALALALAAL\'']),
+ self::createRequest(),
+ self::createResponse(['X-Content-Security-Policy' => 'script-src \'self\' \'unsafe-inline\' \'sha384-LALALALALAAL\'']),
['X-Content-Security-Policy' => 'script-src \'self\' \'unsafe-inline\' \'sha384-LALALALALAAL\' \'nonce-'.$nonce.'\'', 'Content-Security-Policy' => null],
],
[
$nonce,
['csp_script_nonce' => $nonce, 'csp_style_nonce' => $nonce],
- $this->createRequest(),
- $this->createResponse(['Content-Security-Policy' => 'script-src \'self\'; style-src \'self\'', 'X-Content-Security-Policy' => 'script-src \'self\' \'unsafe-inline\'; style-src \'self\'']),
+ self::createRequest(),
+ self::createResponse(['Content-Security-Policy' => 'script-src \'self\'; style-src \'self\'', 'X-Content-Security-Policy' => 'script-src \'self\' \'unsafe-inline\'; style-src \'self\'']),
['Content-Security-Policy' => 'script-src \'self\' \'unsafe-inline\' \'nonce-'.$nonce.'\'; style-src \'self\' \'unsafe-inline\' \'nonce-'.$nonce.'\'', 'X-Content-Security-Policy' => 'script-src \'self\' \'unsafe-inline\'; style-src \'self\' \'unsafe-inline\' \'nonce-'.$nonce.'\''],
],
];
}
- private function createRequest(array $headers = [])
+ private static function createRequest(array $headers = [])
{
$request = new Request();
$request->headers->add($headers);
@@ -201,7 +201,7 @@ private function createRequest(array $headers = [])
return $request;
}
- private function createResponse(array $headers = [])
+ private static function createResponse(array $headers = [])
{
$response = new Response();
$response->headers->add($headers);
diff --git a/src/Symfony/Bundle/WebProfilerBundle/Tests/DependencyInjection/WebProfilerExtensionTest.php b/src/Symfony/Bundle/WebProfilerBundle/Tests/DependencyInjection/WebProfilerExtensionTest.php
index 0038736820388..fbfd2ed586f87 100644
--- a/src/Symfony/Bundle/WebProfilerBundle/Tests/DependencyInjection/WebProfilerExtensionTest.php
+++ b/src/Symfony/Bundle/WebProfilerBundle/Tests/DependencyInjection/WebProfilerExtensionTest.php
@@ -33,8 +33,12 @@ class WebProfilerExtensionTest extends TestCase
public static function assertSaneContainer(Container $container)
{
+ $removedIds = $container->getRemovedIds();
$errors = [];
foreach ($container->getServiceIds() as $id) {
+ if (isset($removedIds[$id])) {
+ continue;
+ }
try {
$container->get($id);
} catch (\Exception $e) {
diff --git a/src/Symfony/Bundle/WebProfilerBundle/Tests/Twig/WebProfilerExtensionTest.php b/src/Symfony/Bundle/WebProfilerBundle/Tests/Twig/WebProfilerExtensionTest.php
new file mode 100644
index 0000000000000..6b026bcc53385
--- /dev/null
+++ b/src/Symfony/Bundle/WebProfilerBundle/Tests/Twig/WebProfilerExtensionTest.php
@@ -0,0 +1,61 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bundle\WebProfilerBundle\Tests\Twig;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Bundle\WebProfilerBundle\Twig\WebProfilerExtension;
+use Symfony\Component\VarDumper\Cloner\VarCloner;
+use Twig\Environment;
+use Twig\Extension\CoreExtension;
+use Twig\Extension\EscaperExtension;
+
+class WebProfilerExtensionTest extends TestCase
+{
+ /**
+ * @dataProvider provideMessages
+ */
+ public function testDumpHeaderIsDisplayed(string $message, array $context, bool $dump1HasHeader, bool $dump2HasHeader)
+ {
+ class_exists(CoreExtension::class); // Load twig_convert_encoding()
+ class_exists(EscaperExtension::class); // Load twig_escape_filter()
+
+ $twigEnvironment = $this->mockTwigEnvironment();
+ $varCloner = new VarCloner();
+
+ $webProfilerExtension = new WebProfilerExtension();
+
+ $needle = 'window.Sfdump';
+
+ $dump1 = $webProfilerExtension->dumpLog($twigEnvironment, $message, $varCloner->cloneVar($context));
+ self::assertSame($dump1HasHeader, str_contains($dump1, $needle));
+
+ $dump2 = $webProfilerExtension->dumpData($twigEnvironment, $varCloner->cloneVar([]));
+ self::assertSame($dump2HasHeader, str_contains($dump2, $needle));
+ }
+
+ public function provideMessages(): iterable
+ {
+ yield ['Some message', ['foo' => 'foo', 'bar' => 'bar'], false, true];
+ yield ['Some message {@see some text}', ['foo' => 'foo', 'bar' => 'bar'], false, true];
+ yield ['Some message {foo}', ['foo' => 'foo', 'bar' => 'bar'], true, false];
+ yield ['Some message {foo}', ['bar' => 'bar'], false, true];
+ }
+
+ private function mockTwigEnvironment()
+ {
+ $twigEnvironment = $this->createMock(Environment::class);
+
+ $twigEnvironment->expects($this->any())->method('getCharset')->willReturn('UTF-8');
+
+ return $twigEnvironment;
+ }
+}
diff --git a/src/Symfony/Bundle/WebProfilerBundle/Twig/WebProfilerExtension.php b/src/Symfony/Bundle/WebProfilerBundle/Twig/WebProfilerExtension.php
index bd7d57de02432..2f35c3bea36a1 100644
--- a/src/Symfony/Bundle/WebProfilerBundle/Twig/WebProfilerExtension.php
+++ b/src/Symfony/Bundle/WebProfilerBundle/Twig/WebProfilerExtension.php
@@ -87,13 +87,19 @@ public function dumpLog(Environment $env, string $message, Data $context = null)
$message = twig_escape_filter($env, $message);
$message = preg_replace('/"(.*?)"/', '"$1"', $message);
- if (null === $context || !str_contains($message, '{')) {
+ $replacements = [];
+ foreach ($context ?? [] as $k => $v) {
+ $k = '{'.twig_escape_filter($env, $k).'}';
+ if (str_contains($message, $k)) {
+ $replacements[$k] = $v;
+ }
+ }
+
+ if (!$replacements) {
return ''.$message.'';
}
- $replacements = [];
- foreach ($context as $k => $v) {
- $k = '{'.twig_escape_filter($env, $k).'}';
+ foreach ($replacements as $k => $v) {
$replacements['"'.$k.'"'] = $replacements['"'.$k.'"'] = $replacements[$k] = $this->dumpData($env, $v);
}
diff --git a/src/Symfony/Component/Cache/Adapter/RedisTagAwareAdapter.php b/src/Symfony/Component/Cache/Adapter/RedisTagAwareAdapter.php
index 01128eb150714..a004709ba504d 100644
--- a/src/Symfony/Component/Cache/Adapter/RedisTagAwareAdapter.php
+++ b/src/Symfony/Component/Cache/Adapter/RedisTagAwareAdapter.php
@@ -65,7 +65,7 @@ public function __construct(\Redis|\RedisArray|\RedisCluster|\Predis\ClientInter
throw new InvalidArgumentException(sprintf('Unsupported Predis cluster connection: only "%s" is, "%s" given.', PredisCluster::class, get_debug_type($redis->getConnection())));
}
- if (\defined('Redis::OPT_COMPRESSION') && ($redis instanceof \Redis || $redis instanceof \RedisArray || $redis instanceof \RedisCluster)) {
+ if (\defined('Redis::OPT_COMPRESSION') && \in_array($redis::class, [\Redis::class, \RedisArray::class, \RedisCluster::class], true)) {
$compression = $redis->getOption(\Redis::OPT_COMPRESSION);
foreach (\is_array($compression) ? $compression : [$compression] as $c) {
diff --git a/src/Symfony/Component/Cache/Tests/Traits/RedisProxiesTest.php b/src/Symfony/Component/Cache/Tests/Traits/RedisProxiesTest.php
index 67b212e520243..27fabf700af8a 100644
--- a/src/Symfony/Component/Cache/Tests/Traits/RedisProxiesTest.php
+++ b/src/Symfony/Component/Cache/Tests/Traits/RedisProxiesTest.php
@@ -60,6 +60,7 @@ public function testRedis6Proxy($class, $stub)
$stub = file_get_contents("https://raw.githubusercontent.com/phpredis/phpredis/develop/{$stub}.stub.php");
$stub = preg_replace('/^class /m', 'return; \0', $stub);
$stub = preg_replace('/^return; class ([a-zA-Z]++)/m', 'interface \1StubInterface', $stub, 1);
+ $stub = preg_replace('/^ public const .*/m', '', $stub);
eval(substr($stub, 5));
$proxy = file_get_contents(\dirname(__DIR__, 2)."/Traits/{$class}6Proxy.php");
diff --git a/src/Symfony/Component/Cache/Traits/Redis6Proxy.php b/src/Symfony/Component/Cache/Traits/Redis6Proxy.php
index f5373df72f3bf..8d9ac740fd52c 100644
--- a/src/Symfony/Component/Cache/Traits/Redis6Proxy.php
+++ b/src/Symfony/Component/Cache/Traits/Redis6Proxy.php
@@ -44,9 +44,9 @@ public function _compress($value): string
return $this->lazyObjectReal->_compress(...\func_get_args());
}
- public function _pack($value): string
+ public function _uncompress($value): string
{
- return $this->lazyObjectReal->_pack(...\func_get_args());
+ return $this->lazyObjectReal->_uncompress(...\func_get_args());
}
public function _prefix($key): string
@@ -59,19 +59,19 @@ public function _serialize($value): string
return $this->lazyObjectReal->_serialize(...\func_get_args());
}
- public function _uncompress($value): string
+ public function _unserialize($value): mixed
{
- return $this->lazyObjectReal->_uncompress(...\func_get_args());
+ return $this->lazyObjectReal->_unserialize(...\func_get_args());
}
- public function _unpack($value): mixed
+ public function _pack($value): string
{
- return $this->lazyObjectReal->_unpack(...\func_get_args());
+ return $this->lazyObjectReal->_pack(...\func_get_args());
}
- public function _unserialize($value): mixed
+ public function _unpack($value): mixed
{
- return $this->lazyObjectReal->_unserialize(...\func_get_args());
+ return $this->lazyObjectReal->_unpack(...\func_get_args());
}
public function acl($subcmd, ...$args): mixed
@@ -114,12 +114,12 @@ public function bitpos($key, $bit, $start = 0, $end = -1, $bybit = false): \Redi
return $this->lazyObjectReal->bitpos(...\func_get_args());
}
- public function blPop($key, $timeout_or_key, ...$extra_args): \Redis|array|false|null
+ public function blPop($key_or_keys, $timeout_or_key, ...$extra_args): \Redis|array|false|null
{
return $this->lazyObjectReal->blPop(...\func_get_args());
}
- public function brPop($key, $timeout_or_key, ...$extra_args): \Redis|array|false|null
+ public function brPop($key_or_keys, $timeout_or_key, ...$extra_args): \Redis|array|false|null
{
return $this->lazyObjectReal->brPop(...\func_get_args());
}
@@ -159,7 +159,7 @@ public function lmpop($keys, $from, $count = 1): \Redis|array|false|null
return $this->lazyObjectReal->lmpop(...\func_get_args());
}
- public function clearLastError(): \Redis|bool
+ public function clearLastError(): bool
{
return $this->lazyObjectReal->clearLastError(...\func_get_args());
}
@@ -174,7 +174,7 @@ public function close(): bool
return $this->lazyObjectReal->close(...\func_get_args());
}
- public function command($opt, $arg): mixed
+ public function command($opt = null, ...$args): mixed
{
return $this->lazyObjectReal->command(...\func_get_args());
}
@@ -194,7 +194,7 @@ public function copy($src, $dst, $options = null): \Redis|bool
return $this->lazyObjectReal->copy(...\func_get_args());
}
- public function dbSize(): \Redis|int
+ public function dbSize(): \Redis|false|int
{
return $this->lazyObjectReal->dbSize(...\func_get_args());
}
@@ -304,7 +304,7 @@ public function flushDB($sync = null): \Redis|bool
return $this->lazyObjectReal->flushDB(...\func_get_args());
}
- public function geoadd($key, $lng, $lat, $member, ...$other_triples): \Redis|false|int
+ public function geoadd($key, $lng, $lat, $member, ...$other_triples_and_options): \Redis|false|int
{
return $this->lazyObjectReal->geoadd(...\func_get_args());
}
@@ -424,7 +424,7 @@ public function lcs($key1, $key2, $options = null): \Redis|array|false|int|strin
return $this->lazyObjectReal->lcs(...\func_get_args());
}
- public function getReadTimeout(): int
+ public function getReadTimeout(): float
{
return $this->lazyObjectReal->getReadTimeout(...\func_get_args());
}
@@ -434,17 +434,27 @@ public function getset($key, $value): \Redis|false|string
return $this->lazyObjectReal->getset(...\func_get_args());
}
- public function getTimeout(): int
+ public function getTimeout(): false|float
{
return $this->lazyObjectReal->getTimeout(...\func_get_args());
}
- public function hDel($key, $member, ...$other_members): \Redis|false|int
+ public function getTransferredBytes(): array
+ {
+ return $this->lazyObjectReal->getTransferredBytes(...\func_get_args());
+ }
+
+ public function clearTransferredBytes(): void
+ {
+ $this->lazyObjectReal->clearTransferredBytes(...\func_get_args());
+ }
+
+ public function hDel($key, $field, ...$other_fields): \Redis|false|int
{
return $this->lazyObjectReal->hDel(...\func_get_args());
}
- public function hExists($key, $member): \Redis|bool
+ public function hExists($key, $field): \Redis|bool
{
return $this->lazyObjectReal->hExists(...\func_get_args());
}
@@ -459,12 +469,12 @@ public function hGetAll($key): \Redis|array|false
return $this->lazyObjectReal->hGetAll(...\func_get_args());
}
- public function hIncrBy($key, $member, $value): \Redis|false|int
+ public function hIncrBy($key, $field, $value): \Redis|false|int
{
return $this->lazyObjectReal->hIncrBy(...\func_get_args());
}
- public function hIncrByFloat($key, $member, $value): \Redis|false|float
+ public function hIncrByFloat($key, $field, $value): \Redis|false|float
{
return $this->lazyObjectReal->hIncrByFloat(...\func_get_args());
}
@@ -479,12 +489,12 @@ public function hLen($key): \Redis|false|int
return $this->lazyObjectReal->hLen(...\func_get_args());
}
- public function hMget($key, $keys): \Redis|array|false
+ public function hMget($key, $fields): \Redis|array|false
{
return $this->lazyObjectReal->hMget(...\func_get_args());
}
- public function hMset($key, $keyvals): \Redis|bool
+ public function hMset($key, $fieldvals): \Redis|bool
{
return $this->lazyObjectReal->hMset(...\func_get_args());
}
@@ -499,12 +509,12 @@ public function hSet($key, $member, $value): \Redis|false|int
return $this->lazyObjectReal->hSet(...\func_get_args());
}
- public function hSetNx($key, $member, $value): \Redis|bool
+ public function hSetNx($key, $field, $value): \Redis|bool
{
return $this->lazyObjectReal->hSetNx(...\func_get_args());
}
- public function hStrLen($key, $member): \Redis|false|int
+ public function hStrLen($key, $field): \Redis|false|int
{
return $this->lazyObjectReal->hStrLen(...\func_get_args());
}
@@ -519,17 +529,17 @@ public function hscan($key, &$iterator, $pattern = null, $count = 0): \Redis|arr
return $this->lazyObjectReal->hscan(...\func_get_args());
}
- public function incr($key, $by = 1)
+ public function incr($key, $by = 1): \Redis|false|int
{
return $this->lazyObjectReal->incr(...\func_get_args());
}
- public function incrBy($key, $value)
+ public function incrBy($key, $value): \Redis|false|int
{
return $this->lazyObjectReal->incrBy(...\func_get_args());
}
- public function incrByFloat($key, $value)
+ public function incrByFloat($key, $value): \Redis|false|float
{
return $this->lazyObjectReal->incrByFloat(...\func_get_args());
}
@@ -564,6 +574,11 @@ public function lMove($src, $dst, $wherefrom, $whereto): \Redis|false|string
return $this->lazyObjectReal->lMove(...\func_get_args());
}
+ public function blmove($src, $dst, $wherefrom, $whereto, $timeout): \Redis|false|string
+ {
+ return $this->lazyObjectReal->blmove(...\func_get_args());
+ }
+
public function lPop($key, $count = 0): \Redis|array|bool|string
{
return $this->lazyObjectReal->lPop(...\func_get_args());
@@ -574,22 +589,22 @@ public function lPos($key, $value, $options = null): \Redis|array|bool|int|null
return $this->lazyObjectReal->lPos(...\func_get_args());
}
- public function lPush($key, ...$elements)
+ public function lPush($key, ...$elements): \Redis|false|int
{
return $this->lazyObjectReal->lPush(...\func_get_args());
}
- public function rPush($key, ...$elements)
+ public function rPush($key, ...$elements): \Redis|false|int
{
return $this->lazyObjectReal->rPush(...\func_get_args());
}
- public function lPushx($key, $value)
+ public function lPushx($key, $value): \Redis|false|int
{
return $this->lazyObjectReal->lPushx(...\func_get_args());
}
- public function rPushx($key, $value)
+ public function rPushx($key, $value): \Redis|false|int
{
return $this->lazyObjectReal->rPushx(...\func_get_args());
}
@@ -614,7 +629,7 @@ public function lrange($key, $start, $end): \Redis|array|false
return $this->lazyObjectReal->lrange(...\func_get_args());
}
- public function lrem($key, $value, $count = 0)
+ public function lrem($key, $value, $count = 0): \Redis|false|int
{
return $this->lazyObjectReal->lrem(...\func_get_args());
}
@@ -624,7 +639,7 @@ public function ltrim($key, $start, $end): \Redis|bool
return $this->lazyObjectReal->ltrim(...\func_get_args());
}
- public function mget($keys)
+ public function mget($keys): \Redis|array
{
return $this->lazyObjectReal->mget(...\func_get_args());
}
@@ -634,7 +649,7 @@ public function migrate($host, $port, $key, $dstdb, $timeout, $copy = false, $re
return $this->lazyObjectReal->migrate(...\func_get_args());
}
- public function move($key, $index): bool
+ public function move($key, $index): \Redis|bool
{
return $this->lazyObjectReal->move(...\func_get_args());
}
@@ -669,7 +684,7 @@ public function pconnect($host, $port = 6379, $timeout = 0.0, $persistent_id = n
return $this->lazyObjectReal->pconnect(...\func_get_args());
}
- public function persist($key): bool
+ public function persist($key): \Redis|bool
{
return $this->lazyObjectReal->persist(...\func_get_args());
}
@@ -679,27 +694,27 @@ public function pexpire($key, $timeout, $mode = null): bool
return $this->lazyObjectReal->pexpire(...\func_get_args());
}
- public function pexpireAt($key, $timestamp, $mode = null): bool
+ public function pexpireAt($key, $timestamp, $mode = null): \Redis|bool
{
return $this->lazyObjectReal->pexpireAt(...\func_get_args());
}
- public function pfadd($key, $elements): int
+ public function pfadd($key, $elements): \Redis|int
{
return $this->lazyObjectReal->pfadd(...\func_get_args());
}
- public function pfcount($key): int
+ public function pfcount($key): \Redis|int
{
return $this->lazyObjectReal->pfcount(...\func_get_args());
}
- public function pfmerge($dst, $keys): bool
+ public function pfmerge($dst, $srckeys): \Redis|bool
{
return $this->lazyObjectReal->pfmerge(...\func_get_args());
}
- public function ping($key = null)
+ public function ping($message = null): \Redis|bool|string
{
return $this->lazyObjectReal->ping(...\func_get_args());
}
@@ -714,7 +729,7 @@ public function popen($host, $port = 6379, $timeout = 0.0, $persistent_id = null
return $this->lazyObjectReal->popen(...\func_get_args());
}
- public function psetex($key, $expire, $value)
+ public function psetex($key, $expire, $value): \Redis|bool
{
return $this->lazyObjectReal->psetex(...\func_get_args());
}
@@ -729,7 +744,7 @@ public function pttl($key): \Redis|false|int
return $this->lazyObjectReal->pttl(...\func_get_args());
}
- public function publish($channel, $message): mixed
+ public function publish($channel, $message): \Redis|false|int
{
return $this->lazyObjectReal->publish(...\func_get_args());
}
@@ -749,7 +764,7 @@ public function rPop($key, $count = 0): \Redis|array|bool|string
return $this->lazyObjectReal->rPop(...\func_get_args());
}
- public function randomKey()
+ public function randomKey(): \Redis|false|string
{
return $this->lazyObjectReal->randomKey(...\func_get_args());
}
@@ -759,17 +774,17 @@ public function rawcommand($command, ...$args): mixed
return $this->lazyObjectReal->rawcommand(...\func_get_args());
}
- public function rename($key_src, $key_dst)
+ public function rename($old_name, $new_name): \Redis|bool
{
return $this->lazyObjectReal->rename(...\func_get_args());
}
- public function renameNx($key_src, $key_dst)
+ public function renameNx($key_src, $key_dst): \Redis|bool
{
return $this->lazyObjectReal->renameNx(...\func_get_args());
}
- public function restore($key, $timeout, $value, $options = null): bool
+ public function restore($key, $ttl, $value, $options = null): \Redis|bool
{
return $this->lazyObjectReal->restore(...\func_get_args());
}
@@ -779,7 +794,7 @@ public function role(): mixed
return $this->lazyObjectReal->role(...\func_get_args());
}
- public function rpoplpush($src, $dst): \Redis|false|string
+ public function rpoplpush($srckey, $dstkey): \Redis|false|string
{
return $this->lazyObjectReal->rpoplpush(...\func_get_args());
}
@@ -824,7 +839,7 @@ public function sMembers($key): \Redis|array|false
return $this->lazyObjectReal->sMembers(...\func_get_args());
}
- public function sMisMember($key, $member, ...$other_members): array
+ public function sMisMember($key, $member, ...$other_members): \Redis|array|false
{
return $this->lazyObjectReal->sMisMember(...\func_get_args());
}
@@ -854,7 +869,7 @@ public function sUnionStore($dst, $key, ...$other_keys): \Redis|false|int
return $this->lazyObjectReal->sUnionStore(...\func_get_args());
}
- public function save(): bool
+ public function save(): \Redis|bool
{
return $this->lazyObjectReal->save(...\func_get_args());
}
@@ -879,17 +894,17 @@ public function select($db): \Redis|bool
return $this->lazyObjectReal->select(...\func_get_args());
}
- public function set($key, $value, $opt = null): \Redis|bool|string
+ public function set($key, $value, $options = null): \Redis|bool|string
{
return $this->lazyObjectReal->set(...\func_get_args());
}
- public function setBit($key, $idx, $value)
+ public function setBit($key, $idx, $value): \Redis|false|int
{
return $this->lazyObjectReal->setBit(...\func_get_args());
}
- public function setRange($key, $start, $value)
+ public function setRange($key, $index, $value): \Redis|false|int
{
return $this->lazyObjectReal->setRange(...\func_get_args());
}
@@ -904,7 +919,7 @@ public function setex($key, $expire, $value)
return $this->lazyObjectReal->setex(...\func_get_args());
}
- public function setnx($key, $value)
+ public function setnx($key, $value): \Redis|bool
{
return $this->lazyObjectReal->setnx(...\func_get_args());
}
@@ -914,11 +929,21 @@ public function sismember($key, $value): \Redis|bool
return $this->lazyObjectReal->sismember(...\func_get_args());
}
- public function slaveof($host = null, $port = 6379): bool
+ public function slaveof($host = null, $port = 6379): \Redis|bool
{
return $this->lazyObjectReal->slaveof(...\func_get_args());
}
+ public function replicaof($host = null, $port = 6379): \Redis|bool
+ {
+ return $this->lazyObjectReal->replicaof(...\func_get_args());
+ }
+
+ public function touch($key_or_array, ...$more_keys): \Redis|false|int
+ {
+ return $this->lazyObjectReal->touch(...\func_get_args());
+ }
+
public function slowlog($operation, $length = 0): mixed
{
return $this->lazyObjectReal->slowlog(...\func_get_args());
@@ -929,6 +954,11 @@ public function sort($key, $options = null): mixed
return $this->lazyObjectReal->sort(...\func_get_args());
}
+ public function sort_ro($key, $options = null): mixed
+ {
+ return $this->lazyObjectReal->sort_ro(...\func_get_args());
+ }
+
public function sortAsc($key, $pattern = null, $get = null, $offset = -1, $count = -1, $store = null): array
{
return $this->lazyObjectReal->sortAsc(...\func_get_args());
@@ -959,7 +989,12 @@ public function sscan($key, &$iterator, $pattern = null, $count = 0): array|fals
return $this->lazyObjectReal->sscan(...\func_get_args());
}
- public function strlen($key)
+ public function ssubscribe($channels, $cb): bool
+ {
+ return $this->lazyObjectReal->ssubscribe(...\func_get_args());
+ }
+
+ public function strlen($key): \Redis|false|int
{
return $this->lazyObjectReal->strlen(...\func_get_args());
}
@@ -969,12 +1004,17 @@ public function subscribe($channels, $cb): bool
return $this->lazyObjectReal->subscribe(...\func_get_args());
}
- public function swapdb($src, $dst): bool
+ public function sunsubscribe($channels): \Redis|array|bool
+ {
+ return $this->lazyObjectReal->sunsubscribe(...\func_get_args());
+ }
+
+ public function swapdb($src, $dst): \Redis|bool
{
return $this->lazyObjectReal->swapdb(...\func_get_args());
}
- public function time(): array
+ public function time(): \Redis|array
{
return $this->lazyObjectReal->time(...\func_get_args());
}
@@ -984,12 +1024,12 @@ public function ttl($key): \Redis|false|int
return $this->lazyObjectReal->ttl(...\func_get_args());
}
- public function type($key)
+ public function type($key): \Redis|false|int
{
return $this->lazyObjectReal->type(...\func_get_args());
}
- public function unlink($key, ...$other_keys)
+ public function unlink($key, ...$other_keys): \Redis|false|int
{
return $this->lazyObjectReal->unlink(...\func_get_args());
}
@@ -999,17 +1039,17 @@ public function unsubscribe($channels): \Redis|array|bool
return $this->lazyObjectReal->unsubscribe(...\func_get_args());
}
- public function unwatch()
+ public function unwatch(): \Redis|bool
{
return $this->lazyObjectReal->unwatch(...\func_get_args());
}
- public function watch($key, ...$other_keys)
+ public function watch($key, ...$other_keys): \Redis|bool
{
return $this->lazyObjectReal->watch(...\func_get_args());
}
- public function wait($count, $timeout): false|int
+ public function wait($numreplicas, $timeout): false|int
{
return $this->lazyObjectReal->wait(...\func_get_args());
}
@@ -1039,7 +1079,7 @@ public function xdel($key, $ids): \Redis|false|int
return $this->lazyObjectReal->xdel(...\func_get_args());
}
- public function xgroup($operation, $key = null, $arg1 = null, $arg2 = null, $arg3 = false): mixed
+ public function xgroup($operation, $key = null, $group = null, $id_or_consumer = null, $mkstream = false, $entries_read = -2): mixed
{
return $this->lazyObjectReal->xgroup(...\func_get_args());
}
@@ -1074,12 +1114,12 @@ public function xreadgroup($group, $consumer, $streams, $count = 1, $block = 1):
return $this->lazyObjectReal->xreadgroup(...\func_get_args());
}
- public function xrevrange($key, $start, $end, $count = -1): \Redis|array|bool
+ public function xrevrange($key, $end, $start, $count = -1): \Redis|array|bool
{
return $this->lazyObjectReal->xrevrange(...\func_get_args());
}
- public function xtrim($key, $maxlen, $approx = false, $minid = false, $limit = -1): \Redis|false|int
+ public function xtrim($key, $threshold, $approx = false, $minid = false, $limit = -1): \Redis|false|int
{
return $this->lazyObjectReal->xtrim(...\func_get_args());
}
@@ -1114,12 +1154,12 @@ public function zMscore($key, $member, ...$other_members): \Redis|array|false
return $this->lazyObjectReal->zMscore(...\func_get_args());
}
- public function zPopMax($key, $value = null): \Redis|array|false
+ public function zPopMax($key, $count = null): \Redis|array|false
{
return $this->lazyObjectReal->zPopMax(...\func_get_args());
}
- public function zPopMin($key, $value = null): \Redis|array|false
+ public function zPopMin($key, $count = null): \Redis|array|false
{
return $this->lazyObjectReal->zPopMin(...\func_get_args());
}
@@ -1179,12 +1219,12 @@ public function zRevRange($key, $start, $end, $scores = null): \Redis|array|fals
return $this->lazyObjectReal->zRevRange(...\func_get_args());
}
- public function zRevRangeByLex($key, $min, $max, $offset = -1, $count = -1): \Redis|array|false
+ public function zRevRangeByLex($key, $max, $min, $offset = -1, $count = -1): \Redis|array|false
{
return $this->lazyObjectReal->zRevRangeByLex(...\func_get_args());
}
- public function zRevRangeByScore($key, $start, $end, $options = []): \Redis|array|false
+ public function zRevRangeByScore($key, $max, $min, $options = []): \Redis|array|false
{
return $this->lazyObjectReal->zRevRangeByScore(...\func_get_args());
}
@@ -1204,7 +1244,7 @@ public function zdiff($keys, $options = null): \Redis|array|false
return $this->lazyObjectReal->zdiff(...\func_get_args());
}
- public function zdiffstore($dst, $keys, $options = null): \Redis|false|int
+ public function zdiffstore($dst, $keys): \Redis|false|int
{
return $this->lazyObjectReal->zdiffstore(...\func_get_args());
}
@@ -1224,7 +1264,7 @@ public function zinterstore($dst, $keys, $weights = null, $aggregate = null): \R
return $this->lazyObjectReal->zinterstore(...\func_get_args());
}
- public function zscan($key, &$iterator, $pattern = null, $count = 0): \Redis|array|bool
+ public function zscan($key, &$iterator, $pattern = null, $count = 0): \Redis|array|false
{
return $this->lazyObjectReal->zscan(...\func_get_args());
}
diff --git a/src/Symfony/Component/Cache/Traits/RedisCluster6Proxy.php b/src/Symfony/Component/Cache/Traits/RedisCluster6Proxy.php
index 9cc6abf7fad94..e94fa2b36711e 100644
--- a/src/Symfony/Component/Cache/Traits/RedisCluster6Proxy.php
+++ b/src/Symfony/Component/Cache/Traits/RedisCluster6Proxy.php
@@ -44,44 +44,44 @@ public function _compress($value): string
return $this->lazyObjectReal->_compress(...\func_get_args());
}
- public function _masters(): array
+ public function _uncompress($value): string
{
- return $this->lazyObjectReal->_masters(...\func_get_args());
+ return $this->lazyObjectReal->_uncompress(...\func_get_args());
}
- public function _pack($value): string
+ public function _serialize($value): bool|string
{
- return $this->lazyObjectReal->_pack(...\func_get_args());
+ return $this->lazyObjectReal->_serialize(...\func_get_args());
}
- public function _prefix($key): bool|string
+ public function _unserialize($value): mixed
{
- return $this->lazyObjectReal->_prefix(...\func_get_args());
+ return $this->lazyObjectReal->_unserialize(...\func_get_args());
}
- public function _redir(): ?string
+ public function _pack($value): string
{
- return $this->lazyObjectReal->_redir(...\func_get_args());
+ return $this->lazyObjectReal->_pack(...\func_get_args());
}
- public function _serialize($value): bool|string
+ public function _unpack($value): mixed
{
- return $this->lazyObjectReal->_serialize(...\func_get_args());
+ return $this->lazyObjectReal->_unpack(...\func_get_args());
}
- public function _uncompress($value): string
+ public function _prefix($key): bool|string
{
- return $this->lazyObjectReal->_uncompress(...\func_get_args());
+ return $this->lazyObjectReal->_prefix(...\func_get_args());
}
- public function _unpack($value): mixed
+ public function _masters(): array
{
- return $this->lazyObjectReal->_unpack(...\func_get_args());
+ return $this->lazyObjectReal->_masters(...\func_get_args());
}
- public function _unserialize($value): mixed
+ public function _redir(): ?string
{
- return $this->lazyObjectReal->_unserialize(...\func_get_args());
+ return $this->lazyObjectReal->_redir(...\func_get_args());
}
public function acl($key_or_address, $subcmd, ...$args): mixed
@@ -134,6 +134,16 @@ public function brpoplpush($srckey, $deskey, $timeout): mixed
return $this->lazyObjectReal->brpoplpush(...\func_get_args());
}
+ public function lmove($src, $dst, $wherefrom, $whereto): \Redis|false|string
+ {
+ return $this->lazyObjectReal->lmove(...\func_get_args());
+ }
+
+ public function blmove($src, $dst, $wherefrom, $whereto, $timeout): \Redis|false|string
+ {
+ return $this->lazyObjectReal->blmove(...\func_get_args());
+ }
+
public function bzpopmax($key, $timeout_or_key, ...$extra_args): array
{
return $this->lazyObjectReal->bzpopmax(...\func_get_args());
@@ -199,6 +209,11 @@ public function dbsize($key_or_address): \RedisCluster|int
return $this->lazyObjectReal->dbsize(...\func_get_args());
}
+ public function copy($src, $dst, $options = null): \RedisCluster|bool
+ {
+ return $this->lazyObjectReal->copy(...\func_get_args());
+ }
+
public function decr($key, $by = 1): \RedisCluster|false|int
{
return $this->lazyObjectReal->decr(...\func_get_args());
@@ -264,6 +279,11 @@ public function exists($key, ...$other_keys): \RedisCluster|bool|int
return $this->lazyObjectReal->exists(...\func_get_args());
}
+ public function touch($key, ...$other_keys): \RedisCluster|bool|int
+ {
+ return $this->lazyObjectReal->touch(...\func_get_args());
+ }
+
public function expire($key, $timeout, $mode = null): \RedisCluster|bool
{
return $this->lazyObjectReal->expire(...\func_get_args());
@@ -294,7 +314,7 @@ public function flushdb($key_or_address, $async = false): \RedisCluster|bool
return $this->lazyObjectReal->flushdb(...\func_get_args());
}
- public function geoadd($key, $lng, $lat, $member, ...$other_triples): \RedisCluster|int
+ public function geoadd($key, $lng, $lat, $member, ...$other_triples_and_options): \RedisCluster|false|int
{
return $this->lazyObjectReal->geoadd(...\func_get_args());
}
@@ -334,6 +354,16 @@ public function georadiusbymember_ro($key, $member, $radius, $unit, $options = [
return $this->lazyObjectReal->georadiusbymember_ro(...\func_get_args());
}
+ public function geosearch($key, $position, $shape, $unit, $options = []): \RedisCluster|array
+ {
+ return $this->lazyObjectReal->geosearch(...\func_get_args());
+ }
+
+ public function geosearchstore($dst, $src, $position, $shape, $unit, $options = []): \RedisCluster|array|false|int
+ {
+ return $this->lazyObjectReal->geosearchstore(...\func_get_args());
+ }
+
public function get($key): mixed
{
return $this->lazyObjectReal->get(...\func_get_args());
@@ -374,6 +404,16 @@ public function getset($key, $value): \RedisCluster|bool|string
return $this->lazyObjectReal->getset(...\func_get_args());
}
+ public function gettransferredbytes(): array|false
+ {
+ return $this->lazyObjectReal->gettransferredbytes(...\func_get_args());
+ }
+
+ public function cleartransferredbytes(): void
+ {
+ $this->lazyObjectReal->cleartransferredbytes(...\func_get_args());
+ }
+
public function hdel($key, $member, ...$other_members): \RedisCluster|false|int
{
return $this->lazyObjectReal->hdel(...\func_get_args());
@@ -429,6 +469,11 @@ public function hscan($key, &$iterator, $pattern = null, $count = 0): array|bool
return $this->lazyObjectReal->hscan(...\func_get_args());
}
+ public function hrandfield($key, $options = null): \RedisCluster|array|string
+ {
+ return $this->lazyObjectReal->hrandfield(...\func_get_args());
+ }
+
public function hset($key, $member, $value): \RedisCluster|false|int
{
return $this->lazyObjectReal->hset(...\func_get_args());
@@ -504,6 +549,11 @@ public function lpop($key, $count = 0): \RedisCluster|array|bool|string
return $this->lazyObjectReal->lpop(...\func_get_args());
}
+ public function lpos($key, $value, $options = null): \Redis|array|bool|int|null
+ {
+ return $this->lazyObjectReal->lpos(...\func_get_args());
+ }
+
public function lpush($key, $value, ...$other_values): \RedisCluster|bool|int
{
return $this->lazyObjectReal->lpush(...\func_get_args());
@@ -764,6 +814,11 @@ public function sismember($key, $value): \RedisCluster|bool
return $this->lazyObjectReal->sismember(...\func_get_args());
}
+ public function smismember($key, $member, ...$other_members): \RedisCluster|array|false
+ {
+ return $this->lazyObjectReal->smismember(...\func_get_args());
+ }
+
public function slowlog($key_or_address, ...$args): mixed
{
return $this->lazyObjectReal->slowlog(...\func_get_args());
@@ -784,6 +839,11 @@ public function sort($key, $options = null): \RedisCluster|array|bool|int|string
return $this->lazyObjectReal->sort(...\func_get_args());
}
+ public function sort_ro($key, $options = null): \RedisCluster|array|bool|int|string
+ {
+ return $this->lazyObjectReal->sort_ro(...\func_get_args());
+ }
+
public function spop($key, $count = 0): \RedisCluster|array|false|string
{
return $this->lazyObjectReal->spop(...\func_get_args());
@@ -879,11 +939,16 @@ public function xdel($key, $ids): \RedisCluster|false|int
return $this->lazyObjectReal->xdel(...\func_get_args());
}
- public function xgroup($operation, $key = null, $arg1 = null, $arg2 = null, $arg3 = false): mixed
+ public function xgroup($operation, $key = null, $group = null, $id_or_consumer = null, $mkstream = false, $entries_read = -2): mixed
{
return $this->lazyObjectReal->xgroup(...\func_get_args());
}
+ public function xautoclaim($key, $group, $consumer, $min_idle, $start, $count = -1, $justid = false): \RedisCluster|array|bool
+ {
+ return $this->lazyObjectReal->xautoclaim(...\func_get_args());
+ }
+
public function xinfo($operation, $arg1 = null, $arg2 = null, $count = -1): mixed
{
return $this->lazyObjectReal->xinfo(...\func_get_args());
@@ -979,6 +1044,11 @@ public function zrangestore($dstkey, $srckey, $start, $end, $options = null): \R
return $this->lazyObjectReal->zrangestore(...\func_get_args());
}
+ public function zrandmember($key, $options = null): \RedisCluster|array|string
+ {
+ return $this->lazyObjectReal->zrandmember(...\func_get_args());
+ }
+
public function zrangebylex($key, $min, $max, $offset = -1, $count = -1): \RedisCluster|array|false
{
return $this->lazyObjectReal->zrangebylex(...\func_get_args());
@@ -1044,8 +1114,33 @@ public function zscore($key, $member): \RedisCluster|false|float
return $this->lazyObjectReal->zscore(...\func_get_args());
}
+ public function zmscore($key, $member, ...$other_members): \Redis|array|false
+ {
+ return $this->lazyObjectReal->zmscore(...\func_get_args());
+ }
+
public function zunionstore($dst, $keys, $weights = null, $aggregate = null): \RedisCluster|false|int
{
return $this->lazyObjectReal->zunionstore(...\func_get_args());
}
+
+ public function zinter($keys, $weights = null, $options = null): \RedisCluster|array|false
+ {
+ return $this->lazyObjectReal->zinter(...\func_get_args());
+ }
+
+ public function zdiffstore($dst, $keys): \RedisCluster|false|int
+ {
+ return $this->lazyObjectReal->zdiffstore(...\func_get_args());
+ }
+
+ public function zunion($keys, $weights = null, $options = null): \RedisCluster|array|false
+ {
+ return $this->lazyObjectReal->zunion(...\func_get_args());
+ }
+
+ public function zdiff($keys, $options = null): \RedisCluster|array|false
+ {
+ return $this->lazyObjectReal->zdiff(...\func_get_args());
+ }
}
diff --git a/src/Symfony/Component/Cache/Traits/RedisTrait.php b/src/Symfony/Component/Cache/Traits/RedisTrait.php
index ccbcb0f9a5cdf..f791e82343f3a 100644
--- a/src/Symfony/Component/Cache/Traits/RedisTrait.php
+++ b/src/Symfony/Component/Cache/Traits/RedisTrait.php
@@ -550,7 +550,11 @@ private function pipeline(\Closure $generator, object $redis = null): \Generator
if (!$redis instanceof \Predis\ClientInterface && 'eval' === $command && $redis->getLastError()) {
$e = new \RedisException($redis->getLastError());
- $results = array_map(function ($v) use ($e) { return false === $v ? $e : $v; }, $results);
+ $results = array_map(function ($v) use ($e) { return false === $v ? $e : $v; }, (array) $results);
+ }
+
+ if (\is_bool($results)) {
+ return;
}
foreach ($ids as $k => $id) {
diff --git a/src/Symfony/Component/Clock/MonotonicClock.php b/src/Symfony/Component/Clock/MonotonicClock.php
index ef4bfd50cb697..badb99f1daba0 100644
--- a/src/Symfony/Component/Clock/MonotonicClock.php
+++ b/src/Symfony/Component/Clock/MonotonicClock.php
@@ -67,7 +67,7 @@ public function sleep(float|int $seconds): void
}
if (0 < $us = $seconds - $s) {
- usleep($us * 1E6);
+ usleep((int) ($us * 1E6));
}
}
diff --git a/src/Symfony/Component/Clock/NativeClock.php b/src/Symfony/Component/Clock/NativeClock.php
index 0fcf7879a2d9d..21ade183fb435 100644
--- a/src/Symfony/Component/Clock/NativeClock.php
+++ b/src/Symfony/Component/Clock/NativeClock.php
@@ -41,7 +41,7 @@ public function sleep(float|int $seconds): void
}
if (0 < $us = $seconds - $s) {
- usleep($us * 1E6);
+ usleep((int) ($us * 1E6));
}
}
diff --git a/src/Symfony/Component/Clock/Tests/MonotonicClockTest.php b/src/Symfony/Component/Clock/Tests/MonotonicClockTest.php
index 3230c75371b2e..54cfe78c375cc 100644
--- a/src/Symfony/Component/Clock/Tests/MonotonicClockTest.php
+++ b/src/Symfony/Component/Clock/Tests/MonotonicClockTest.php
@@ -56,7 +56,7 @@ public function testSleep()
usleep(10);
$after = microtime(true);
- $this->assertGreaterThanOrEqual($before + 1.5, $now);
+ $this->assertGreaterThanOrEqual($before + 1.499999, $now);
$this->assertLessThan($after, $now);
$this->assertLessThan(1.9, $now - $before);
$this->assertSame($tz, $clock->now()->getTimezone()->getName());
diff --git a/src/Symfony/Component/Console/Application.php b/src/Symfony/Component/Console/Application.php
index 99548faf858b8..d4ec1be090927 100644
--- a/src/Symfony/Component/Console/Application.php
+++ b/src/Symfony/Component/Console/Application.php
@@ -298,6 +298,8 @@ public function doRun(InputInterface $input, OutputInterface $output)
return isset($event) ? $event->getExitCode() : 1;
}
+
+ throw $e;
} catch (NamespaceNotFoundException) {
throw $e;
}
diff --git a/src/Symfony/Component/Console/Tests/ApplicationTest.php b/src/Symfony/Component/Console/Tests/ApplicationTest.php
index c178151688928..c82c3eb188418 100644
--- a/src/Symfony/Component/Console/Tests/ApplicationTest.php
+++ b/src/Symfony/Component/Console/Tests/ApplicationTest.php
@@ -18,6 +18,7 @@
use Symfony\Component\Console\Command\HelpCommand;
use Symfony\Component\Console\Command\LazyCommand;
use Symfony\Component\Console\Command\SignalableCommandInterface;
+use Symfony\Component\Console\CommandLoader\CommandLoaderInterface;
use Symfony\Component\Console\CommandLoader\FactoryCommandLoader;
use Symfony\Component\Console\DependencyInjection\AddConsoleCommandPass;
use Symfony\Component\Console\Event\ConsoleCommandEvent;
@@ -1466,6 +1467,25 @@ public function testRunWithError()
}
}
+ public function testRunWithFindError()
+ {
+ $this->expectException(\Error::class);
+ $this->expectExceptionMessage('Find exception');
+
+ $application = new Application();
+ $application->setAutoExit(false);
+ $application->setCatchExceptions(false);
+
+ // Throws an exception when find fails
+ $commandLoader = $this->createMock(CommandLoaderInterface::class);
+ $commandLoader->method('getNames')->willThrowException(new \Error('Find exception'));
+ $application->setCommandLoader($commandLoader);
+
+ // The exception should not be ignored
+ $tester = new ApplicationTester($application);
+ $tester->run(['command' => 'foo']);
+ }
+
public function testRunAllowsErrorListenersToSilenceTheException()
{
$dispatcher = $this->getDispatcher();
diff --git a/src/Symfony/Component/DependencyInjection/Compiler/ResolveInstanceofConditionalsPass.php b/src/Symfony/Component/DependencyInjection/Compiler/ResolveInstanceofConditionalsPass.php
index c629798bfca93..c8217154741c9 100644
--- a/src/Symfony/Component/DependencyInjection/Compiler/ResolveInstanceofConditionalsPass.php
+++ b/src/Symfony/Component/DependencyInjection/Compiler/ResolveInstanceofConditionalsPass.php
@@ -126,7 +126,7 @@ private function processDefinition(ContainerBuilder $container, string $id, Defi
foreach ($instanceofTags[$i] as $k => $v) {
if (null === $definition->getDecoratedService() || \in_array($k, $tagsToKeep, true)) {
foreach ($v as $v) {
- if ($definition->hasTag($k) && (!$v || \in_array($v, $definition->getTag($k)))) {
+ if ($definition->hasTag($k) && \in_array($v, $definition->getTag($k))) {
continue;
}
$definition->addTag($k, $v);
diff --git a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php
index 049b611ea73b3..ae7ca22ee5035 100644
--- a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php
+++ b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php
@@ -506,8 +506,8 @@ public function has(string $id): bool
*/
public function get(string $id, int $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE): ?object
{
- if ($this->isCompiled() && isset($this->removedIds[$id]) && ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE >= $invalidBehavior) {
- return parent::get($id);
+ if ($this->isCompiled() && isset($this->removedIds[$id])) {
+ return ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE >= $invalidBehavior ? parent::get($id) : null;
}
return $this->doGet($id, $invalidBehavior);
@@ -524,9 +524,9 @@ private function doGet(string $id, int $invalidBehavior = ContainerInterface::EX
}
try {
if (ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE === $invalidBehavior) {
- return parent::get($id, $invalidBehavior);
+ return $this->privates[$id] ?? parent::get($id, $invalidBehavior);
}
- if ($service = parent::get($id, ContainerInterface::NULL_ON_INVALID_REFERENCE)) {
+ if (null !== $service = $this->privates[$id] ?? parent::get($id, ContainerInterface::NULL_ON_INVALID_REFERENCE)) {
return $service;
}
} catch (ServiceCircularReferenceException $e) {
@@ -1029,8 +1029,8 @@ private function createService(Definition $definition, array &$inlineServices, b
$arguments = $this->doResolveServices($parameterBag->unescapeValue($parameterBag->resolveValue($arguments)), $inlineServices, $isConstructorArgument);
- if (null !== $id && $definition->isShared() && isset($this->services[$id]) && (true === $tryProxy || !$definition->isLazy())) {
- return $this->services[$id];
+ if (null !== $id && $definition->isShared() && (isset($this->services[$id]) || isset($this->privates[$id])) && (true === $tryProxy || !$definition->isLazy())) {
+ return $this->services[$id] ?? $this->privates[$id];
}
if (null !== $factory) {
@@ -1587,7 +1587,11 @@ private function shareService(Definition $definition, mixed $service, ?string $i
$inlineServices[$id ?? spl_object_hash($definition)] = $service;
if (null !== $id && $definition->isShared()) {
- $this->services[$id] = $service;
+ if ($definition->isPrivate() && $this->isCompiled()) {
+ $this->privates[$id] = $service;
+ } else {
+ $this->services[$id] = $service;
+ }
unset($this->loading[$id]);
}
}
diff --git a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php
index 4d827d53e28b7..e930724810ae9 100644
--- a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php
+++ b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php
@@ -545,10 +545,10 @@ private function generateProxyClasses(): array
if (!$definition = $this->isProxyCandidate($definition, $asGhostObject, $id)) {
continue;
}
- if (isset($alreadyGenerated[$class = $definition->getClass()])) {
+ if (isset($alreadyGenerated[$asGhostObject][$class = $definition->getClass()])) {
continue;
}
- $alreadyGenerated[$class] = true;
+ $alreadyGenerated[$asGhostObject][$class] = true;
// register class' reflector for resource tracking
$this->container->getReflectionClass($class);
if ("\n" === $proxyCode = "\n".$proxyDumper->getProxyCode($definition, $id)) {
diff --git a/src/Symfony/Component/DependencyInjection/LazyProxy/Instantiator/LazyServiceInstantiator.php b/src/Symfony/Component/DependencyInjection/LazyProxy/Instantiator/LazyServiceInstantiator.php
index cb846445de994..3de3d0361d775 100644
--- a/src/Symfony/Component/DependencyInjection/LazyProxy/Instantiator/LazyServiceInstantiator.php
+++ b/src/Symfony/Component/DependencyInjection/LazyProxy/Instantiator/LazyServiceInstantiator.php
@@ -13,8 +13,8 @@
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\Definition;
+use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
use Symfony\Component\DependencyInjection\LazyProxy\PhpDumper\LazyServiceDumper;
-use Symfony\Component\VarExporter\LazyGhostTrait;
/**
* @author Nicolas Grekas
@@ -25,10 +25,14 @@ public function instantiateProxy(ContainerInterface $container, Definition $defi
{
$dumper = new LazyServiceDumper();
- if (!class_exists($proxyClass = $dumper->getProxyClass($definition, $class), false)) {
+ if (!$dumper->isProxyCandidate($definition, $asGhostObject, $id)) {
+ throw new InvalidArgumentException(sprintf('Cannot instantiate lazy proxy for service "%s".', $id));
+ }
+
+ if (!class_exists($proxyClass = $dumper->getProxyClass($definition, $asGhostObject, $class), false)) {
eval($dumper->getProxyCode($definition, $id));
}
- return isset(class_uses($proxyClass)[LazyGhostTrait::class]) ? $proxyClass::createLazyGhost($realInstantiator) : $proxyClass::createLazyProxy($realInstantiator);
+ return $asGhostObject ? $proxyClass::createLazyGhost($realInstantiator) : $proxyClass::createLazyProxy($realInstantiator);
}
}
diff --git a/src/Symfony/Component/DependencyInjection/LazyProxy/PhpDumper/LazyServiceDumper.php b/src/Symfony/Component/DependencyInjection/LazyProxy/PhpDumper/LazyServiceDumper.php
index 8f69da33929e9..eeef902b0f889 100644
--- a/src/Symfony/Component/DependencyInjection/LazyProxy/PhpDumper/LazyServiceDumper.php
+++ b/src/Symfony/Component/DependencyInjection/LazyProxy/PhpDumper/LazyServiceDumper.php
@@ -72,9 +72,10 @@ public function getProxyFactoryCode(Definition $definition, string $id, string $
$instantiation .= sprintf(' $this->%s[%s] =', $definition->isPublic() && !$definition->isPrivate() ? 'services' : 'privates', var_export($id, true));
}
- $proxyClass = $this->getProxyClass($definition);
+ $asGhostObject = str_contains($factoryCode, '$proxy');
+ $proxyClass = $this->getProxyClass($definition, $asGhostObject);
- if (!str_contains($factoryCode, '$proxy')) {
+ if (!$asGhostObject) {
return <<createProxy('$proxyClass', fn () => \\$proxyClass::createLazyProxy(fn () => $factoryCode));
@@ -104,7 +105,7 @@ public function getProxyCode(Definition $definition, string $id = null): string
if (!$this->isProxyCandidate($definition, $asGhostObject, $id)) {
throw new InvalidArgumentException(sprintf('Cannot instantiate lazy proxy for service "%s".', $id ?? $definition->getClass()));
}
- $proxyClass = $this->getProxyClass($definition, $class);
+ $proxyClass = $this->getProxyClass($definition, $asGhostObject, $class);
if ($asGhostObject) {
try {
@@ -142,10 +143,12 @@ public function getProxyCode(Definition $definition, string $id = null): string
}
}
- public function getProxyClass(Definition $definition, \ReflectionClass &$class = null): string
+ public function getProxyClass(Definition $definition, bool $asGhostObject, \ReflectionClass &$class = null): string
{
$class = new \ReflectionClass($definition->getClass());
- return preg_replace('/^.*\\\\/', '', $class->name).'_'.substr(hash('sha256', $this->salt.'+'.$class->name), -7);
+ return preg_replace('/^.*\\\\/', '', $class->name)
+ .($asGhostObject ? 'Ghost' : 'Proxy')
+ .ucfirst(substr(hash('sha256', $this->salt.'+'.$class->name), -7));
}
}
diff --git a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php
index 76e2d1b8555a3..ab76eb9e0df08 100644
--- a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php
+++ b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php
@@ -336,7 +336,7 @@ private function parseDefinition(\DOMElement $service, string $file, Definition
$tags = $this->getChildren($service, 'tag');
foreach ($tags as $tag) {
- if ('' === $tagName = $tag->hasChildNodes() || '' === $tag->nodeValue ? $tag->getAttribute('name') : $tag->nodeValue) {
+ if ('' === $tagName = $tag->childElementCount || '' === $tag->nodeValue ? $tag->getAttribute('name') : $tag->nodeValue) {
throw new InvalidArgumentException(sprintf('The tag name for service "%s" in "%s" must be a non-empty string.', (string) $service->getAttribute('id'), $file));
}
diff --git a/src/Symfony/Component/DependencyInjection/ReverseContainer.php b/src/Symfony/Component/DependencyInjection/ReverseContainer.php
index 9635c5bceefe5..9b1d331d85e63 100644
--- a/src/Symfony/Component/DependencyInjection/ReverseContainer.php
+++ b/src/Symfony/Component/DependencyInjection/ReverseContainer.php
@@ -63,10 +63,6 @@ public function getId(object $service): ?string
*/
public function getService(string $id): object
{
- if ($this->serviceContainer->has($id)) {
- return $this->serviceContainer->get($id);
- }
-
if ($this->reversibleLocator->has($id)) {
return $this->reversibleLocator->get($id);
}
@@ -75,7 +71,6 @@ public function getService(string $id): object
throw new ServiceNotFoundException($id, null, null, [], sprintf('The "%s" service is private and cannot be accessed by reference. You should either make it public, or tag it as "%s".', $id, $this->tagName));
}
- // will throw a ServiceNotFoundException
- $this->serviceContainer->get($id);
+ return $this->serviceContainer->get($id);
}
}
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/IntegrationTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/IntegrationTest.php
index 72f875db6903d..e7fcac4623f84 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/IntegrationTest.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/IntegrationTest.php
@@ -849,7 +849,6 @@ static function (ChildDefinition $definition, CustomAutoconfiguration $attribute
$definition->addTag('app.custom_tag', get_object_vars($attribute) + ['class' => $reflector->getName()]);
}
);
- $container->registerForAutoconfiguration(TaggedService1::class)->addTag('app.custom_tag');
$container->register('one', TaggedService1::class)
->setPublic(true)
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ServiceLocatorTagPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ServiceLocatorTagPassTest.php
index 4d248306a0b7d..6ddbc88d8ee8c 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ServiceLocatorTagPassTest.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ServiceLocatorTagPassTest.php
@@ -184,19 +184,19 @@ public function testIndexedByServiceIdWithDecoration()
$container->setDefinition(Service::class, $service);
- $decorated = new Definition(Decorated::class);
+ $decorated = new Definition(DecoratedService::class);
$decorated->setPublic(true);
$decorated->setDecoratedService(Service::class);
- $container->setDefinition(Decorated::class, $decorated);
+ $container->setDefinition(DecoratedService::class, $decorated);
$container->compile();
/** @var ServiceLocator $locator */
$locator = $container->get(Locator::class)->locator;
static::assertTrue($locator->has(Service::class));
- static::assertFalse($locator->has(Decorated::class));
- static::assertInstanceOf(Decorated::class, $locator->get(Service::class));
+ static::assertFalse($locator->has(DecoratedService::class));
+ static::assertInstanceOf(DecoratedService::class, $locator->get(Service::class));
}
public function testDefinitionOrderIsTheSame()
diff --git a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php
index bbf618b981e85..9ec5d0631fe61 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php
@@ -1127,10 +1127,19 @@ public function testPrivateServiceUser()
$container->addDefinitions([
'bar' => $fooDefinition,
'bar_user' => $fooUserDefinition->setPublic(true),
+ 'bar_user2' => $fooUserDefinition->setPublic(true),
]);
$container->compile();
+ $this->assertNull($container->get('bar', $container::NULL_ON_INVALID_REFERENCE));
$this->assertInstanceOf(\BarClass::class, $container->get('bar_user')->bar);
+
+ // Ensure that accessing a public service with a shared private service
+ // does not make the private service available.
+ $this->assertNull($container->get('bar', $container::NULL_ON_INVALID_REFERENCE));
+
+ // Ensure the private service is still shared.
+ $this->assertSame($container->get('bar_user')->bar, $container->get('bar_user2')->bar);
}
public function testThrowsExceptionWhenSetServiceOnACompiledContainer()
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php
index 12d5b80f8baf3..870d448fc0c76 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php
@@ -772,24 +772,18 @@ public function testCircularReferenceAllowanceForLazyServices()
$dumper->dump();
}
- /**
- * @testWith [false]
- * [true]
- */
- public function testDedupLazyProxy(bool $asGhostObject)
+ public function testDedupLazyProxy()
{
$container = new ContainerBuilder();
$container->register('foo', 'stdClass')->setLazy(true)->setPublic(true);
$container->register('bar', 'stdClass')->setLazy(true)->setPublic(true);
+ $container->register('baz', 'stdClass')->setLazy(true)->setPublic(true)->setFactory('foo_bar');
+ $container->register('buz', 'stdClass')->setLazy(true)->setPublic(true)->setFactory('foo_bar');
$container->compile();
$dumper = new PhpDumper($container);
- if (!$asGhostObject) {
- $dumper->setProxyDumper(new \DummyProxyDumper());
- }
-
- $this->assertStringEqualsFile(self::$fixturesPath.'/php/services_dedup_lazy'.($asGhostObject ? '_ghost' : '_proxy').'.php', $dumper->dump());
+ $this->assertStringEqualsFile(self::$fixturesPath.'/php/services_dedup_lazy.php', $dumper->dump());
}
public function testLazyArgumentProvideGenerator()
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_lazy_inlined_factories.txt b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_lazy_inlined_factories.txt
index 7af59984549ae..b606736e93f02 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_lazy_inlined_factories.txt
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services9_lazy_inlined_factories.txt
@@ -6,11 +6,11 @@ namespace Container%s;
include_once $this->targetDir.''.'/Fixtures/includes/foo.php';
-class FooClass_2b16075 extends \Bar\FooClass implements \Symfony\Component\VarExporter\LazyObjectInterface
+class FooClassGhost2b16075 extends \Bar\FooClass implements \Symfony\Component\VarExporter\LazyObjectInterface
%A
-if (!\class_exists('FooClass_%s', false)) {
- \class_alias(__NAMESPACE__.'\\FooClass_%s', 'FooClass_%s', false);
+if (!\class_exists('FooClassGhost2b16075', false)) {
+ \class_alias(__NAMESPACE__.'\\FooClassGhost2b16075', 'FooClassGhost2b16075', false);
}
[Container%s/ProjectServiceContainer.php] => services['lazy_foo'] = $this->createProxy('FooClass_2b16075', fn () => \FooClass_2b16075::createLazyGhost($this->getLazyFooService(...)));
+ return $this->services['lazy_foo'] = $this->createProxy('FooClassGhost2b16075', fn () => \FooClassGhost2b16075::createLazyGhost($this->getLazyFooService(...)));
}
include_once $this->targetDir.''.'/Fixtures/includes/foo_lazy.php';
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.php
similarity index 55%
rename from src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_dedup_lazy_ghost.php
rename to src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_dedup_lazy.php
index a7972921fadba..867cda7e7a289 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.php
@@ -21,6 +21,8 @@ public function __construct()
$this->services = $this->privates = [];
$this->methodMap = [
'bar' => 'getBarService',
+ 'baz' => 'getBazService',
+ 'buz' => 'getBuzService',
'foo' => 'getFooService',
];
@@ -50,12 +52,40 @@ protected function createProxy($class, \Closure $factory)
protected function getBarService($lazyLoad = true)
{
if (true === $lazyLoad) {
- return $this->services['bar'] = $this->createProxy('stdClass_5a8a5eb', fn () => \stdClass_5a8a5eb::createLazyGhost($this->getBarService(...)));
+ return $this->services['bar'] = $this->createProxy('stdClassGhost5a8a5eb', fn () => \stdClassGhost5a8a5eb::createLazyGhost($this->getBarService(...)));
}
return $lazyLoad;
}
+ /**
+ * Gets the public 'baz' shared service.
+ *
+ * @return \stdClass
+ */
+ protected function getBazService($lazyLoad = true)
+ {
+ if (true === $lazyLoad) {
+ return $this->services['baz'] = $this->createProxy('stdClassProxy5a8a5eb', fn () => \stdClassProxy5a8a5eb::createLazyProxy(fn () => $this->getBazService(false)));
+ }
+
+ return \foo_bar();
+ }
+
+ /**
+ * Gets the public 'buz' shared service.
+ *
+ * @return \stdClass
+ */
+ protected function getBuzService($lazyLoad = true)
+ {
+ if (true === $lazyLoad) {
+ return $this->services['buz'] = $this->createProxy('stdClassProxy5a8a5eb', fn () => \stdClassProxy5a8a5eb::createLazyProxy(fn () => $this->getBuzService(false)));
+ }
+
+ return \foo_bar();
+ }
+
/**
* Gets the public 'foo' shared service.
*
@@ -64,14 +94,14 @@ protected function getBarService($lazyLoad = true)
protected function getFooService($lazyLoad = true)
{
if (true === $lazyLoad) {
- return $this->services['foo'] = $this->createProxy('stdClass_5a8a5eb', fn () => \stdClass_5a8a5eb::createLazyGhost($this->getFooService(...)));
+ return $this->services['foo'] = $this->createProxy('stdClassGhost5a8a5eb', fn () => \stdClassGhost5a8a5eb::createLazyGhost($this->getFooService(...)));
}
return $lazyLoad;
}
}
-class stdClass_5a8a5eb extends \stdClass implements \Symfony\Component\VarExporter\LazyObjectInterface
+class stdClassGhost5a8a5eb extends \stdClass implements \Symfony\Component\VarExporter\LazyObjectInterface
{
use \Symfony\Component\VarExporter\LazyGhostTrait;
@@ -82,3 +112,18 @@ class stdClass_5a8a5eb extends \stdClass implements \Symfony\Component\VarExport
class_exists(\Symfony\Component\VarExporter\Internal\Hydrator::class);
class_exists(\Symfony\Component\VarExporter\Internal\LazyObjectRegistry::class);
class_exists(\Symfony\Component\VarExporter\Internal\LazyObjectState::class);
+
+class stdClassProxy5a8a5eb extends \stdClass implements \Symfony\Component\VarExporter\LazyObjectInterface
+{
+ use \Symfony\Component\VarExporter\LazyProxyTrait;
+
+ private const LAZY_OBJECT_PROPERTY_SCOPES = [
+ 'lazyObjectReal' => [self::class, 'lazyObjectReal', null],
+ "\0".self::class."\0lazyObjectReal" => [self::class, 'lazyObjectReal', null],
+ ];
+}
+
+// Help opcache.preload discover always-needed symbols
+class_exists(\Symfony\Component\VarExporter\Internal\Hydrator::class);
+class_exists(\Symfony\Component\VarExporter\Internal\LazyObjectRegistry::class);
+class_exists(\Symfony\Component\VarExporter\Internal\LazyObjectState::class);
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_dedup_lazy_proxy.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_dedup_lazy_proxy.php
deleted file mode 100644
index 841c892216f68..0000000000000
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_dedup_lazy_proxy.php
+++ /dev/null
@@ -1,70 +0,0 @@
-services = $this->privates = [];
- $this->methodMap = [
- 'bar' => 'getBarService',
- 'foo' => 'getFooService',
- ];
-
- $this->aliases = [];
- }
-
- public function compile(): void
- {
- throw new LogicException('You cannot compile a dumped container that was already compiled.');
- }
-
- public function isCompiled(): bool
- {
- return true;
- }
-
- protected function createProxy($class, \Closure $factory)
- {
- return $factory();
- }
-
- /**
- * Gets the public 'bar' shared service.
- *
- * @return \stdClass
- */
- protected function getBarService($lazyLoad = true)
- {
- // lazy factory for stdClass
-
- return new \stdClass();
- }
-
- /**
- * Gets the public 'foo' shared service.
- *
- * @return \stdClass
- */
- protected function getFooService($lazyLoad = true)
- {
- // lazy factory for stdClass
-
- return new \stdClass();
- }
-}
-
-// proxy code for stdClass
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 a52a32a27dadd..a4e8807c71039 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
@@ -22,7 +22,7 @@ class getNonSharedFooService extends ProjectServiceContainer
$container->factories['non_shared_foo'] ??= fn () => self::do($container);
if (true === $lazyLoad) {
- return $container->createProxy('FooLazyClass_f814e3a', fn () => \FooLazyClass_f814e3a::createLazyGhost(fn ($proxy) => self::do($container, $proxy)));
+ return $container->createProxy('FooLazyClassGhostF814e3a', fn () => \FooLazyClassGhostF814e3a::createLazyGhost(fn ($proxy) => self::do($container, $proxy)));
}
static $include = true;
@@ -37,11 +37,11 @@ class getNonSharedFooService extends ProjectServiceContainer
}
}
- [Container%s/FooLazyClass_f814e3a.php] => set(\Container%s\ProjectServiceContainer::class, null);
-require __DIR__.'/Container%s/FooLazyClass_f814e3a.php';
+require __DIR__.'/Container%s/FooLazyClassGhostF814e3a.php';
require __DIR__.'/Container%s/getNonSharedFooService.php';
$classes = [];
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 c304c9461fd1c..7d584eef41063 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
@@ -68,14 +68,14 @@ protected function getFooService($lazyLoad = true)
$this->factories['service_container']['foo'] ??= $this->getFooService(...);
if (true === $lazyLoad) {
- return $this->createProxy('stdClass_5a8a5eb', fn () => \stdClass_5a8a5eb::createLazyGhost($this->getFooService(...)));
+ return $this->createProxy('stdClassGhost5a8a5eb', fn () => \stdClassGhost5a8a5eb::createLazyGhost($this->getFooService(...)));
}
return $lazyLoad;
}
}
-class stdClass_5a8a5eb extends \stdClass implements \Symfony\Component\VarExporter\LazyObjectInterface
+class stdClassGhost5a8a5eb extends \stdClass implements \Symfony\Component\VarExporter\LazyObjectInterface
{
use \Symfony\Component\VarExporter\LazyGhostTrait;
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 5de8772d6746e..b0bbc7b640be8 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
@@ -56,7 +56,7 @@ protected function createProxy($class, \Closure $factory)
protected function getWitherService($lazyLoad = true)
{
if (true === $lazyLoad) {
- return $this->services['wither'] = $this->createProxy('Wither_94fa281', fn () => \Wither_94fa281::createLazyProxy(fn () => $this->getWitherService(false)));
+ return $this->services['wither'] = $this->createProxy('WitherProxy94fa281', fn () => \WitherProxy94fa281::createLazyProxy(fn () => $this->getWitherService(false)));
}
$instance = new \Symfony\Component\DependencyInjection\Tests\Compiler\Wither();
@@ -71,7 +71,7 @@ protected function getWitherService($lazyLoad = true)
}
}
-class Wither_94fa281 extends \Symfony\Component\DependencyInjection\Tests\Compiler\Wither implements \Symfony\Component\VarExporter\LazyObjectInterface
+class WitherProxy94fa281 extends \Symfony\Component\DependencyInjection\Tests\Compiler\Wither implements \Symfony\Component\VarExporter\LazyObjectInterface
{
use \Symfony\Component\VarExporter\LazyProxyTrait;
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services10.xml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services10.xml
index 921070e1b44a0..89b96413913d2 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services10.xml
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services10.xml
@@ -11,6 +11,10 @@
other-option="lorem"
an_other-option="ipsum"
/>
+ bar_tag
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php b/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php
index b3de370ba370f..9207742bc25f1 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php
@@ -369,13 +369,17 @@ public function testParsesIteratorArgument()
$this->assertEquals([new IteratorArgument(['k1' => new Reference('foo.baz'), 'k2' => new Reference('service_container')]), new IteratorArgument([])], $lazyDefinition->getArguments(), '->load() parses lazy arguments');
}
- public function testParsesTags()
+ /**
+ * @testWith ["foo_tag"]
+ * ["bar_tag"]
+ */
+ public function testParsesTags(string $tag)
{
$container = new ContainerBuilder();
$loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml'));
$loader->load('services10.xml');
- $services = $container->findTaggedServiceIds('foo_tag');
+ $services = $container->findTaggedServiceIds($tag);
$this->assertCount(1, $services);
foreach ($services as $id => $tagAttributes) {
diff --git a/src/Symfony/Component/ErrorHandler/DebugClassLoader.php b/src/Symfony/Component/ErrorHandler/DebugClassLoader.php
index 5befc7fc8cbe9..69c28c114c51c 100644
--- a/src/Symfony/Component/ErrorHandler/DebugClassLoader.php
+++ b/src/Symfony/Component/ErrorHandler/DebugClassLoader.php
@@ -57,7 +57,7 @@ class DebugClassLoader
'null' => 'null',
'resource' => 'resource',
'boolean' => 'bool',
- 'true' => 'bool',
+ 'true' => 'true',
'false' => 'false',
'integer' => 'int',
'array' => 'array',
@@ -75,6 +75,7 @@ class DebugClassLoader
'$this' => 'static',
'list' => 'array',
'class-string' => 'string',
+ 'never' => 'never',
];
private const BUILTIN_RETURN_TYPES = [
@@ -92,6 +93,9 @@ class DebugClassLoader
'parent' => true,
'mixed' => true,
'static' => true,
+ 'null' => true,
+ 'true' => true,
+ 'never' => true,
];
private const MAGIC_METHODS = [
@@ -796,6 +800,12 @@ private function setReturnType(string $types, string $class, string $method, str
return;
}
+ if ('null' === $types) {
+ self::$returnTypes[$class][$method] = ['null', 'null', $class, $filename];
+
+ return;
+ }
+
if ($nullable = str_starts_with($types, 'null|')) {
$types = substr($types, 5);
} elseif ($nullable = str_ends_with($types, '|null')) {
diff --git a/src/Symfony/Component/ErrorHandler/ErrorRenderer/HtmlErrorRenderer.php b/src/Symfony/Component/ErrorHandler/ErrorRenderer/HtmlErrorRenderer.php
index a4d67708850a3..57f34d8fcad4a 100644
--- a/src/Symfony/Component/ErrorHandler/ErrorRenderer/HtmlErrorRenderer.php
+++ b/src/Symfony/Component/ErrorHandler/ErrorRenderer/HtmlErrorRenderer.php
@@ -50,7 +50,7 @@ public function __construct(bool|callable $debug = false, string $charset = null
{
$this->debug = \is_bool($debug) ? $debug : $debug(...);
$this->charset = $charset ?: (\ini_get('default_charset') ?: 'UTF-8');
- $fileLinkFormat ??= $_SERVER['SYMFONY_IDE'] ?? null;
+ $fileLinkFormat ??= $_ENV['SYMFONY_IDE'] ?? $_SERVER['SYMFONY_IDE'] ?? null;
$this->fileLinkFormat = \is_string($fileLinkFormat)
? (ErrorRendererInterface::IDE_LINK_FORMATS[$fileLinkFormat] ?? $fileLinkFormat ?: false)
: ($fileLinkFormat ?: \ini_get('xdebug.file_link_format') ?: get_cfg_var('xdebug.file_link_format') ?: false);
diff --git a/src/Symfony/Component/ErrorHandler/Internal/TentativeTypes.php b/src/Symfony/Component/ErrorHandler/Internal/TentativeTypes.php
index 2168a1c075816..1e8afe39b9a4e 100644
--- a/src/Symfony/Component/ErrorHandler/Internal/TentativeTypes.php
+++ b/src/Symfony/Component/ErrorHandler/Internal/TentativeTypes.php
@@ -753,6 +753,7 @@ class TentativeTypes
'isVariadic' => 'bool',
'isStatic' => 'bool',
'getClosureThis' => '?object',
+ 'getClosureCalledClass' => '?ReflectionClass',
'getClosureScopeClass' => '?ReflectionClass',
'getDocComment' => 'string|false',
'getEndLine' => 'int|false',
diff --git a/src/Symfony/Component/ErrorHandler/Tests/DebugClassLoaderTest.php b/src/Symfony/Component/ErrorHandler/Tests/DebugClassLoaderTest.php
index fbfcdf90bd8f1..ab2ea41a08c6c 100644
--- a/src/Symfony/Component/ErrorHandler/Tests/DebugClassLoaderTest.php
+++ b/src/Symfony/Component/ErrorHandler/Tests/DebugClassLoaderTest.php
@@ -397,6 +397,10 @@ class_exists('Test\\'.ReturnType::class, true);
'Method "Symfony\Component\ErrorHandler\Tests\Fixtures\ReturnTypeParent::mixed()" might add "mixed" as a native return type declaration in the future. Do the same in child class "Test\Symfony\Component\ErrorHandler\Tests\ReturnType" now to avoid errors or add an explicit @return annotation to suppress this message.',
'Method "Symfony\Component\ErrorHandler\Tests\Fixtures\ReturnTypeParent::nullableMixed()" might add "mixed" as a native return type declaration in the future. Do the same in child class "Test\Symfony\Component\ErrorHandler\Tests\ReturnType" now to avoid errors or add an explicit @return annotation to suppress this message.',
'Method "Symfony\Component\ErrorHandler\Tests\Fixtures\ReturnTypeParent::static()" might add "static" as a native return type declaration in the future. Do the same in child class "Test\Symfony\Component\ErrorHandler\Tests\ReturnType" now to avoid errors or add an explicit @return annotation to suppress this message.',
+ 'Method "Symfony\Component\ErrorHandler\Tests\Fixtures\ReturnTypeParent::false()" might add "false" as a native return type declaration in the future. Do the same in child class "Test\Symfony\Component\ErrorHandler\Tests\ReturnType" now to avoid errors or add an explicit @return annotation to suppress this message.',
+ 'Method "Symfony\Component\ErrorHandler\Tests\Fixtures\ReturnTypeParent::true()" might add "true" as a native return type declaration in the future. Do the same in child class "Test\Symfony\Component\ErrorHandler\Tests\ReturnType" now to avoid errors or add an explicit @return annotation to suppress this message.',
+ 'Method "Symfony\Component\ErrorHandler\Tests\Fixtures\ReturnTypeParent::never()" might add "never" as a native return type declaration in the future. Do the same in child class "Test\Symfony\Component\ErrorHandler\Tests\ReturnType" now to avoid errors or add an explicit @return annotation to suppress this message.',
+ 'Method "Symfony\Component\ErrorHandler\Tests\Fixtures\ReturnTypeParent::null()" might add "null" as a native return type declaration in the future. Do the same in child class "Test\Symfony\Component\ErrorHandler\Tests\ReturnType" now to avoid errors or add an explicit @return annotation to suppress this message.',
], $deprecations);
}
diff --git a/src/Symfony/Component/ErrorHandler/Tests/Fixtures/ReturnType.php b/src/Symfony/Component/ErrorHandler/Tests/Fixtures/ReturnType.php
index 21c4d2012f663..1b8138001de78 100644
--- a/src/Symfony/Component/ErrorHandler/Tests/Fixtures/ReturnType.php
+++ b/src/Symfony/Component/ErrorHandler/Tests/Fixtures/ReturnType.php
@@ -46,5 +46,9 @@ public function this() { }
public function mixed() { }
public function nullableMixed() { }
public function static() { }
+ public function false() { }
+ public function true() { }
+ public function never() { }
+ public function null() { }
public function outsideMethod() { }
}
diff --git a/src/Symfony/Component/ErrorHandler/Tests/Fixtures/ReturnTypeParent.php b/src/Symfony/Component/ErrorHandler/Tests/Fixtures/ReturnTypeParent.php
index c7b0b6f0fca0c..d42c7c8c02167 100644
--- a/src/Symfony/Component/ErrorHandler/Tests/Fixtures/ReturnTypeParent.php
+++ b/src/Symfony/Component/ErrorHandler/Tests/Fixtures/ReturnTypeParent.php
@@ -220,6 +220,34 @@ public function static()
{
}
+ /**
+ * @return false
+ */
+ public function false()
+ {
+ }
+
+ /**
+ * @return true
+ */
+ public function true()
+ {
+ }
+
+ /**
+ * @return never
+ */
+ public function never()
+ {
+ }
+
+ /**
+ * @return null
+ */
+ public function null()
+ {
+ }
+
/**
* @return int
*/
diff --git a/src/Symfony/Component/ErrorHandler/phpunit.xml.dist b/src/Symfony/Component/ErrorHandler/phpunit.xml.dist
index b23ccab51b8a7..b5b52f5a5208f 100644
--- a/src/Symfony/Component/ErrorHandler/phpunit.xml.dist
+++ b/src/Symfony/Component/ErrorHandler/phpunit.xml.dist
@@ -10,6 +10,7 @@
>
+
diff --git a/src/Symfony/Component/EventDispatcher/Debug/WrappedListener.php b/src/Symfony/Component/EventDispatcher/Debug/WrappedListener.php
index 91f8a3dc836bc..af257fff48393 100644
--- a/src/Symfony/Component/EventDispatcher/Debug/WrappedListener.php
+++ b/src/Symfony/Component/EventDispatcher/Debug/WrappedListener.php
@@ -50,7 +50,7 @@ public function __construct(callable|array $listener, ?string $name, Stopwatch $
$r = new \ReflectionFunction($listener);
if (str_contains($r->name, '{closure}')) {
$this->pretty = $this->name = 'closure';
- } elseif ($class = $r->getClosureScopeClass()) {
+ } elseif ($class = \PHP_VERSION_ID >= 80111 ? $r->getClosureCalledClass() : $r->getClosureScopeClass()) {
$this->name = $class->name;
$this->pretty = $this->name.'::'.$r->name;
} else {
diff --git a/src/Symfony/Component/ExpressionLanguage/Node/GetAttrNode.php b/src/Symfony/Component/ExpressionLanguage/Node/GetAttrNode.php
index 1bad653328965..e9b10c3295d75 100644
--- a/src/Symfony/Component/ExpressionLanguage/Node/GetAttrNode.php
+++ b/src/Symfony/Component/ExpressionLanguage/Node/GetAttrNode.php
@@ -149,4 +149,15 @@ public function toArray()
return [$this->nodes['node'], '[', $this->nodes['attribute'], ']'];
}
}
+
+ /**
+ * Provides BC with instances serialized before v6.2
+ */
+ public function __unserialize(array $data): void
+ {
+ $this->nodes = $data['nodes'];
+ $this->attributes = $data['attributes'];
+ $this->attributes['is_null_coalesce'] ??= false;
+ $this->attributes['is_short_circuited'] ??= $data["\x00Symfony\Component\ExpressionLanguage\Node\GetAttrNode\x00isShortCircuited"] ?? false;
+ }
}
diff --git a/src/Symfony/Component/HtmlSanitizer/Tests/TextSanitizer/UrlSanitizerTest.php b/src/Symfony/Component/HtmlSanitizer/Tests/TextSanitizer/UrlSanitizerTest.php
index 3216244e9ed10..18fec32dee43d 100644
--- a/src/Symfony/Component/HtmlSanitizer/Tests/TextSanitizer/UrlSanitizerTest.php
+++ b/src/Symfony/Component/HtmlSanitizer/Tests/TextSanitizer/UrlSanitizerTest.php
@@ -45,6 +45,33 @@ public function provideSanitize()
'output' => null,
];
+ yield [
+ 'input' => 'http://trusted.com/link.php',
+ 'allowedSchemes' => null,
+ 'allowedHosts' => null,
+ 'forceHttps' => false,
+ 'allowRelative' => false,
+ 'output' => 'http://trusted.com/link.php',
+ ];
+
+ yield [
+ 'input' => 'https://trusted.com/link.php',
+ 'allowedSchemes' => null,
+ 'allowedHosts' => null,
+ 'forceHttps' => false,
+ 'allowRelative' => false,
+ 'output' => 'https://trusted.com/link.php',
+ ];
+
+ yield [
+ 'input' => 'data:text/plain;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7',
+ 'allowedSchemes' => null,
+ 'allowedHosts' => null,
+ 'forceHttps' => false,
+ 'allowRelative' => false,
+ 'output' => 'data:text/plain;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7',
+ ];
+
yield [
'input' => 'https://trusted.com/link.php',
'allowedSchemes' => ['https'],
diff --git a/src/Symfony/Component/HttpClient/DependencyInjection/HttpClientPass.php b/src/Symfony/Component/HttpClient/DependencyInjection/HttpClientPass.php
index a5be24dcca09b..c42d873fc4f5c 100644
--- a/src/Symfony/Component/HttpClient/DependencyInjection/HttpClientPass.php
+++ b/src/Symfony/Component/HttpClient/DependencyInjection/HttpClientPass.php
@@ -29,7 +29,7 @@ public function process(ContainerBuilder $container)
$container->register('.debug.'.$id, TraceableHttpClient::class)
->setArguments([new Reference('.debug.'.$id.'.inner'), new Reference('debug.stopwatch', ContainerInterface::IGNORE_ON_INVALID_REFERENCE)])
->addTag('kernel.reset', ['method' => 'reset'])
- ->setDecoratedService($id);
+ ->setDecoratedService($id, null, 5);
$container->getDefinition('data_collector.http_client')
->addMethodCall('registerClient', [$id, new Reference('.debug.'.$id)]);
}
diff --git a/src/Symfony/Component/HttpClient/Tests/DependencyInjection/HttpClientPassTest.php b/src/Symfony/Component/HttpClient/Tests/DependencyInjection/HttpClientPassTest.php
index eb04f88226d1f..c6dcf4fcf7902 100755
--- a/src/Symfony/Component/HttpClient/Tests/DependencyInjection/HttpClientPassTest.php
+++ b/src/Symfony/Component/HttpClient/Tests/DependencyInjection/HttpClientPassTest.php
@@ -38,7 +38,7 @@ public function testItDecoratesHttpClientWithTraceableHttpClient()
$sut->process($container);
$this->assertTrue($container->hasDefinition('.debug.foo'));
$this->assertSame(TraceableHttpClient::class, $container->getDefinition('.debug.foo')->getClass());
- $this->assertSame(['foo', null, 0], $container->getDefinition('.debug.foo')->getDecoratedService());
+ $this->assertSame(['foo', null, 5], $container->getDefinition('.debug.foo')->getDecoratedService());
}
public function testItRegistersDebugHttpClientToCollector()
diff --git a/src/Symfony/Component/HttpFoundation/IpUtils.php b/src/Symfony/Component/HttpFoundation/IpUtils.php
index 49f433b76cc30..746720ea78ea2 100644
--- a/src/Symfony/Component/HttpFoundation/IpUtils.php
+++ b/src/Symfony/Component/HttpFoundation/IpUtils.php
@@ -114,10 +114,6 @@ public static function checkIp6(string $requestIp, string $ip): bool
}
// Check to see if we were given a IP4 $requestIp or $ip by mistake
- if (str_contains($requestIp, '.') || str_contains($ip, '.')) {
- return self::$checkedIps[$cacheKey] = false;
- }
-
if (!filter_var($requestIp, \FILTER_VALIDATE_IP, \FILTER_FLAG_IPV6)) {
return self::$checkedIps[$cacheKey] = false;
}
@@ -125,6 +121,10 @@ public static function checkIp6(string $requestIp, string $ip): bool
if (str_contains($ip, '/')) {
[$address, $netmask] = explode('/', $ip, 2);
+ if (!filter_var($address, \FILTER_VALIDATE_IP, \FILTER_FLAG_IPV6)) {
+ return self::$checkedIps[$cacheKey] = false;
+ }
+
if ('0' === $netmask) {
return (bool) unpack('n*', @inet_pton($address));
}
@@ -133,6 +133,10 @@ public static function checkIp6(string $requestIp, string $ip): bool
return self::$checkedIps[$cacheKey] = false;
}
} else {
+ if (!filter_var($ip, \FILTER_VALIDATE_IP, \FILTER_FLAG_IPV6)) {
+ return self::$checkedIps[$cacheKey] = false;
+ }
+
$address = $ip;
$netmask = 128;
}
diff --git a/src/Symfony/Component/HttpFoundation/Request.php b/src/Symfony/Component/HttpFoundation/Request.php
index a2b3409b54162..3cf8a9954007d 100644
--- a/src/Symfony/Component/HttpFoundation/Request.php
+++ b/src/Symfony/Component/HttpFoundation/Request.php
@@ -502,7 +502,7 @@ public function __toString(): string
$cookies = [];
foreach ($this->cookies as $k => $v) {
- $cookies[] = $k.'='.$v;
+ $cookies[] = \is_array($v) ? http_build_query([$k => $v], '', '; ', \PHP_QUERY_RFC3986) : "$k=$v";
}
if ($cookies) {
diff --git a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/MemcachedSessionHandler.php b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/MemcachedSessionHandler.php
index 948033ea98a8e..2bc29b459cacd 100644
--- a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/MemcachedSessionHandler.php
+++ b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/MemcachedSessionHandler.php
@@ -66,17 +66,27 @@ protected function doRead(string $sessionId): string
public function updateTimestamp(string $sessionId, string $data): bool
{
- $ttl = ($this->ttl instanceof \Closure ? ($this->ttl)() : $this->ttl) ?? \ini_get('session.gc_maxlifetime');
- $this->memcached->touch($this->prefix.$sessionId, time() + (int) $ttl);
+ $this->memcached->touch($this->prefix.$sessionId, $this->getCompatibleTtl());
return true;
}
protected function doWrite(string $sessionId, string $data): bool
+ {
+ return $this->memcached->set($this->prefix.$sessionId, $data, $this->getCompatibleTtl());
+ }
+
+ private function getCompatibleTtl(): int
{
$ttl = ($this->ttl instanceof \Closure ? ($this->ttl)() : $this->ttl) ?? \ini_get('session.gc_maxlifetime');
- return $this->memcached->set($this->prefix.$sessionId, $data, time() + (int) $ttl);
+ // If the relative TTL that is used exceeds 30 days, memcached will treat the value as Unix time.
+ // We have to convert it to an absolute Unix time at this point, to make sure the TTL is correct.
+ if ($ttl > 60 * 60 * 24 * 30) {
+ $ttl += time();
+ }
+
+ return $ttl;
}
protected function doDestroy(string $sessionId): bool
diff --git a/src/Symfony/Component/HttpFoundation/Tests/IpUtilsTest.php b/src/Symfony/Component/HttpFoundation/Tests/IpUtilsTest.php
index 9db54719a65b5..6085331b28bf3 100644
--- a/src/Symfony/Component/HttpFoundation/Tests/IpUtilsTest.php
+++ b/src/Symfony/Component/HttpFoundation/Tests/IpUtilsTest.php
@@ -78,6 +78,7 @@ public function getIpv6Data()
[false, '0.0.0.0/8', '::1'],
[false, '::1', '127.0.0.1'],
[false, '::1', '0.0.0.0/8'],
+ [true, '::ffff:10.126.42.2', '::ffff:10.0.0.0/0'],
];
}
diff --git a/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php b/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php
index 9a0892c12e3d1..23acd2e05f7fe 100644
--- a/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php
+++ b/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php
@@ -1721,6 +1721,12 @@ public function testToString()
$asString = (string) $request;
$this->assertStringContainsString('Cookie: Foo=Bar; Another=Cookie', $asString);
+
+ $request->cookies->set('foo.bar', [1, 2]);
+
+ $asString = (string) $request;
+
+ $this->assertStringContainsString('foo.bar%5B0%5D=1; foo.bar%5B1%5D=2', $asString);
}
public function testIsMethod()
diff --git a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/MemcachedSessionHandlerTest.php b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/MemcachedSessionHandlerTest.php
index eac380fd9094a..3e0e7844104b4 100644
--- a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/MemcachedSessionHandlerTest.php
+++ b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/MemcachedSessionHandlerTest.php
@@ -16,6 +16,7 @@
/**
* @requires extension memcached
+ *
* @group time-sensitive
*/
class MemcachedSessionHandlerTest extends TestCase
@@ -92,13 +93,30 @@ public function testWriteSession()
$this->memcached
->expects($this->once())
->method('set')
- ->with(self::PREFIX.'id', 'data', $this->equalTo(time() + self::TTL, 2))
+ ->with(self::PREFIX.'id', 'data', $this->equalTo(self::TTL, 2))
->willReturn(true)
;
$this->assertTrue($this->storage->write('id', 'data'));
}
+ public function testWriteSessionWithLargeTTL()
+ {
+ $this->memcached
+ ->expects($this->once())
+ ->method('set')
+ ->with(self::PREFIX.'id', 'data', $this->equalTo(time() + self::TTL + 60 * 60 * 24 * 30, 2))
+ ->willReturn(true)
+ ;
+
+ $storage = new MemcachedSessionHandler(
+ $this->memcached,
+ ['prefix' => self::PREFIX, 'expiretime' => self::TTL + 60 * 60 * 24 * 30]
+ );
+
+ $this->assertTrue($storage->write('id', 'data'));
+ }
+
public function testDestroySession()
{
$this->memcached
diff --git a/src/Symfony/Component/HttpKernel/DataCollector/RequestDataCollector.php b/src/Symfony/Component/HttpKernel/DataCollector/RequestDataCollector.php
index b7b7d981c140c..aa505783bbd46 100644
--- a/src/Symfony/Component/HttpKernel/DataCollector/RequestDataCollector.php
+++ b/src/Symfony/Component/HttpKernel/DataCollector/RequestDataCollector.php
@@ -471,7 +471,7 @@ private function parseController(array|object|string|null $controller): array|st
}
$controller['method'] = $r->name;
- if ($class = $r->getClosureScopeClass()) {
+ if ($class = \PHP_VERSION_ID >= 80111 ? $r->getClosureCalledClass() : $r->getClosureScopeClass()) {
$controller['class'] = $class->name;
} else {
return $r->name;
diff --git a/src/Symfony/Component/HttpKernel/Debug/FileLinkFormatter.php b/src/Symfony/Component/HttpKernel/Debug/FileLinkFormatter.php
index 094465274181b..4eaeeb1513713 100644
--- a/src/Symfony/Component/HttpKernel/Debug/FileLinkFormatter.php
+++ b/src/Symfony/Component/HttpKernel/Debug/FileLinkFormatter.php
@@ -35,7 +35,8 @@ class FileLinkFormatter
*/
public function __construct(string|array $fileLinkFormat = null, RequestStack $requestStack = null, string $baseDir = null, string|\Closure $urlFormat = null)
{
- $fileLinkFormat ??= $_SERVER['SYMFONY_IDE'] ?? '';
+ $fileLinkFormat ??= $_ENV['SYMFONY_IDE'] ?? $_SERVER['SYMFONY_IDE'] ?? '';
+
if (!\is_array($fileLinkFormat) && $fileLinkFormat = (ErrorRendererInterface::IDE_LINK_FORMATS[$fileLinkFormat] ?? $fileLinkFormat) ?: \ini_get('xdebug.file_link_format') ?: get_cfg_var('xdebug.file_link_format') ?: false) {
$i = strpos($f = $fileLinkFormat, '&', max(strrpos($f, '%f'), strrpos($f, '%l'))) ?: \strlen($f);
$fileLinkFormat = [substr($f, 0, $i)] + preg_split('/&([^>]++)>/', substr($f, $i), -1, \PREG_SPLIT_DELIM_CAPTURE);
diff --git a/src/Symfony/Component/HttpKernel/Event/ControllerEvent.php b/src/Symfony/Component/HttpKernel/Event/ControllerEvent.php
index efbdb944800ff..b012263b93a61 100644
--- a/src/Symfony/Component/HttpKernel/Event/ControllerEvent.php
+++ b/src/Symfony/Component/HttpKernel/Event/ControllerEvent.php
@@ -92,7 +92,7 @@ public function getAttributes(): array
} elseif (\is_string($this->controller) && false !== $i = strpos($this->controller, '::')) {
$class = new \ReflectionClass(substr($this->controller, 0, $i));
} else {
- $class = str_contains($this->controllerReflector->name, '{closure}') ? null : $this->controllerReflector->getClosureScopeClass();
+ $class = str_contains($this->controllerReflector->name, '{closure}') ? null : (\PHP_VERSION_ID >= 80111 ? $this->controllerReflector->getClosureCalledClass() : $this->controllerReflector->getClosureScopeClass());
}
$this->attributes = [];
diff --git a/src/Symfony/Component/HttpKernel/EventListener/AbstractSessionListener.php b/src/Symfony/Component/HttpKernel/EventListener/AbstractSessionListener.php
index 5e6b70d10839e..c41d6fbef37ab 100644
--- a/src/Symfony/Component/HttpKernel/EventListener/AbstractSessionListener.php
+++ b/src/Symfony/Component/HttpKernel/EventListener/AbstractSessionListener.php
@@ -195,10 +195,11 @@ public function onKernelResponse(ResponseEvent $event)
}
if ($autoCacheControl) {
+ $maxAge = $response->headers->hasCacheControlDirective('public') ? 0 : (int) $response->getMaxAge();
$response
- ->setExpires(new \DateTimeImmutable())
+ ->setExpires(new \DateTimeImmutable('+'.$maxAge.' seconds'))
->setPrivate()
- ->setMaxAge(0)
+ ->setMaxAge($maxAge)
->headers->addCacheControlDirective('must-revalidate');
}
diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php
index 0e99228e0937f..358dab3aed06a 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.1';
- public const VERSION_ID = 60201;
+ public const VERSION = '6.2.2';
+ public const VERSION_ID = 60202;
public const MAJOR_VERSION = 6;
public const MINOR_VERSION = 2;
- public const RELEASE_VERSION = 1;
+ public const RELEASE_VERSION = 2;
public const EXTRA_VERSION = '';
public const END_OF_MAINTENANCE = '07/2023';
diff --git a/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/RegisterControllerArgumentLocatorsPassTest.php b/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/RegisterControllerArgumentLocatorsPassTest.php
index 302f9e35dd70d..d6be9d3bd023c 100644
--- a/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/RegisterControllerArgumentLocatorsPassTest.php
+++ b/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/RegisterControllerArgumentLocatorsPassTest.php
@@ -467,7 +467,7 @@ public function testAutowireAttribute()
$container = new ContainerBuilder();
$resolver = $container->register('argument_resolver.service', 'stdClass')->addArgument([]);
- $container->register('some.id', \stdClass::class);
+ $container->register('some.id', \stdClass::class)->setPublic(true);
$container->setParameter('some.parameter', 'foo');
$container->register('foo', WithAutowireAttribute::class)
diff --git a/src/Symfony/Component/HttpKernel/Tests/EventListener/SessionListenerTest.php b/src/Symfony/Component/HttpKernel/Tests/EventListener/SessionListenerTest.php
index ca28b40eec2a4..7dea0f85c94a7 100644
--- a/src/Symfony/Component/HttpKernel/Tests/EventListener/SessionListenerTest.php
+++ b/src/Symfony/Component/HttpKernel/Tests/EventListener/SessionListenerTest.php
@@ -38,6 +38,7 @@ class SessionListenerTest extends TestCase
{
/**
* @dataProvider provideSessionOptions
+ *
* @runInSeparateProcess
*/
public function testSessionCookieOptions(array $phpSessionOptions, array $sessionOptions, array $expectedSessionOptions)
@@ -554,6 +555,69 @@ public function testUninitializedSessionWithoutInitializedSession()
$this->assertSame('60', $response->headers->getCacheControlDirective('s-maxage'));
}
+ public function testResponseHeadersMaxAgeAndExpiresNotBeOverridenIfSessionStarted()
+ {
+ $session = $this->createMock(Session::class);
+ $session->expects($this->once())->method('getUsageIndex')->willReturn(1);
+ $session->expects($this->once())->method('getName')->willReturn('foo');
+ $sessionFactory = $this->createMock(SessionFactory::class);
+ $sessionFactory->expects($this->once())->method('createSession')->willReturn($session);
+
+ $container = new Container();
+ $container->set('session_factory', $sessionFactory);
+
+ $listener = new SessionListener($container);
+ $kernel = $this->createMock(HttpKernelInterface::class);
+
+ $request = new Request();
+ $listener->onKernelRequest(new RequestEvent($kernel, $request, HttpKernelInterface::MAIN_REQUEST));
+
+ $request->getSession();
+
+ $response = new Response();
+ $response->setPrivate();
+ $expiresHeader = gmdate('D, d M Y H:i:s', time() + 600).' GMT';
+ $response->setMaxAge(600);
+ $listener->onKernelResponse(new ResponseEvent($kernel, $request, HttpKernelInterface::MAIN_REQUEST, $response));
+
+ $this->assertTrue($response->headers->has('expires'));
+ $this->assertSame($expiresHeader, $response->headers->get('expires'));
+ $this->assertFalse($response->headers->has('max-age'));
+ $this->assertSame('600', $response->headers->getCacheControlDirective('max-age'));
+ $this->assertFalse($response->headers->hasCacheControlDirective('public'));
+ $this->assertTrue($response->headers->hasCacheControlDirective('private'));
+ $this->assertTrue($response->headers->hasCacheControlDirective('must-revalidate'));
+ $this->assertFalse($response->headers->has(AbstractSessionListener::NO_AUTO_CACHE_CONTROL_HEADER));
+ }
+
+ public function testResponseHeadersMaxAgeAndExpiresDefaultValuesIfSessionStarted()
+ {
+ $session = $this->createMock(Session::class);
+ $session->expects($this->once())->method('getUsageIndex')->willReturn(1);
+
+ $container = new Container();
+
+ $listener = new SessionListener($container);
+ $kernel = $this->createMock(HttpKernelInterface::class);
+
+ $request = new Request();
+ $request->setSession($session);
+ $listener->onKernelRequest(new RequestEvent($kernel, $request, HttpKernelInterface::MAIN_REQUEST));
+
+ $response = new Response();
+ $expiresHeader = gmdate('D, d M Y H:i:s', time()).' GMT';
+ $listener->onKernelResponse(new ResponseEvent($kernel, $request, HttpKernelInterface::MAIN_REQUEST, $response));
+
+ $this->assertTrue($response->headers->has('expires'));
+ $this->assertSame($expiresHeader, $response->headers->get('expires'));
+ $this->assertFalse($response->headers->has('max-age'));
+ $this->assertSame('0', $response->headers->getCacheControlDirective('max-age'));
+ $this->assertFalse($response->headers->hasCacheControlDirective('public'));
+ $this->assertTrue($response->headers->hasCacheControlDirective('private'));
+ $this->assertTrue($response->headers->hasCacheControlDirective('must-revalidate'));
+ $this->assertFalse($response->headers->has(AbstractSessionListener::NO_AUTO_CACHE_CONTROL_HEADER));
+ }
+
public function testSurrogateMainRequestIsPublic()
{
$session = $this->createMock(Session::class);
diff --git a/src/Symfony/Component/HttpKernel/phpunit.xml.dist b/src/Symfony/Component/HttpKernel/phpunit.xml.dist
index 7e2c738f869f1..a72dcbcca2979 100644
--- a/src/Symfony/Component/HttpKernel/phpunit.xml.dist
+++ b/src/Symfony/Component/HttpKernel/phpunit.xml.dist
@@ -10,6 +10,7 @@
>
+
diff --git a/src/Symfony/Component/Mailer/Tests/Transport/RoundRobinTransportTest.php b/src/Symfony/Component/Mailer/Tests/Transport/RoundRobinTransportTest.php
index b6828bbaac76d..78d53b994a0c8 100644
--- a/src/Symfony/Component/Mailer/Tests/Transport/RoundRobinTransportTest.php
+++ b/src/Symfony/Component/Mailer/Tests/Transport/RoundRobinTransportTest.php
@@ -13,6 +13,7 @@
use PHPUnit\Framework\TestCase;
use Symfony\Component\Mailer\Exception\TransportException;
+use Symfony\Component\Mailer\Exception\TransportExceptionInterface;
use Symfony\Component\Mailer\Transport\RoundRobinTransport;
use Symfony\Component\Mailer\Transport\TransportInterface;
use Symfony\Component\Mime\RawMessage;
@@ -60,10 +61,21 @@ public function testSendAllDead()
$t2 = $this->createMock(TransportInterface::class);
$t2->expects($this->once())->method('send')->will($this->throwException(new TransportException()));
$t = new RoundRobinTransport([$t1, $t2]);
- $this->expectException(TransportException::class);
- $this->expectExceptionMessage('All transports failed.');
- $t->send(new RawMessage(''));
- $this->assertTransports($t, 1, [$t1, $t2]);
+ $p = new \ReflectionProperty($t, 'cursor');
+ $p->setAccessible(true);
+ $p->setValue($t, 0);
+
+ try {
+ $t->send(new RawMessage(''));
+ } catch (\Exception $e) {
+ $this->assertInstanceOf(TransportException::class, $e);
+ $this->assertStringContainsString('All transports failed.', $e->getMessage());
+ $this->assertTransports($t, 0, [$t1, $t2]);
+
+ return;
+ }
+
+ $this->fail('The expected exception was not thrown.');
}
public function testSendOneDead()
@@ -124,6 +136,34 @@ public function testSendOneDeadAndRecoveryWithinRetryPeriod()
$this->assertTransports($t, 1, []);
}
+ public function testFailureDebugInformation()
+ {
+ $t1 = $this->createMock(TransportInterface::class);
+ $e1 = new TransportException();
+ $e1->appendDebug('Debug message 1');
+ $t1->expects($this->once())->method('send')->will($this->throwException($e1));
+ $t1->expects($this->once())->method('__toString')->willReturn('t1');
+
+ $t2 = $this->createMock(TransportInterface::class);
+ $e2 = new TransportException();
+ $e2->appendDebug('Debug message 2');
+ $t2->expects($this->once())->method('send')->will($this->throwException($e2));
+ $t2->expects($this->once())->method('__toString')->willReturn('t2');
+
+ $t = new RoundRobinTransport([$t1, $t2]);
+
+ try {
+ $t->send(new RawMessage(''));
+ } catch (TransportExceptionInterface $e) {
+ $this->assertStringContainsString('Transport "t1": Debug message 1', $e->getDebug());
+ $this->assertStringContainsString('Transport "t2": Debug message 2', $e->getDebug());
+
+ return;
+ }
+
+ $this->fail('Expected exception was not thrown!');
+ }
+
private function assertTransports(RoundRobinTransport $transport, int $cursor, array $deadTransports)
{
$p = new \ReflectionProperty($transport, 'cursor');
diff --git a/src/Symfony/Component/Mailer/Transport/RoundRobinTransport.php b/src/Symfony/Component/Mailer/Transport/RoundRobinTransport.php
index ab0e40458b658..c5587bb2a3585 100644
--- a/src/Symfony/Component/Mailer/Transport/RoundRobinTransport.php
+++ b/src/Symfony/Component/Mailer/Transport/RoundRobinTransport.php
@@ -48,15 +48,19 @@ public function __construct(array $transports, int $retryPeriod = 60)
public function send(RawMessage $message, Envelope $envelope = null): ?SentMessage
{
+ $exception = null;
+
while ($transport = $this->getNextTransport()) {
try {
return $transport->send($message, $envelope);
- } catch (TransportExceptionInterface) {
+ } catch (TransportExceptionInterface $e) {
+ $exception ??= new TransportException('All transports failed.');
+ $exception->appendDebug(sprintf("Transport \"%s\": %s\n", $transport, $e->getDebug()));
$this->deadTransports[$transport] = microtime(true);
}
}
- throw new TransportException('All transports failed.');
+ throw $exception ?? new TransportException('No transports found.');
}
public function __toString(): string
diff --git a/src/Symfony/Component/Messenger/Bridge/Amqp/Transport/Connection.php b/src/Symfony/Component/Messenger/Bridge/Amqp/Transport/Connection.php
index ec71f60f1394a..353d56a2ddb6c 100644
--- a/src/Symfony/Component/Messenger/Bridge/Amqp/Transport/Connection.php
+++ b/src/Symfony/Component/Messenger/Bridge/Amqp/Transport/Connection.php
@@ -32,6 +32,9 @@ class Connection
'x-message-ttl',
];
+ /**
+ * @see https://github.com/php-amqp/php-amqp/blob/master/amqp_connection_resource.h
+ */
private const AVAILABLE_OPTIONS = [
'host',
'port',
@@ -52,6 +55,7 @@ class Connection
'write_timeout',
'confirm_timeout',
'connect_timeout',
+ 'rpc_timeout',
'cacert',
'cert',
'key',
diff --git a/src/Symfony/Component/Messenger/Handler/HandlerDescriptor.php b/src/Symfony/Component/Messenger/Handler/HandlerDescriptor.php
index 260a4a7a6ca38..821754b37bb81 100644
--- a/src/Symfony/Component/Messenger/Handler/HandlerDescriptor.php
+++ b/src/Symfony/Component/Messenger/Handler/HandlerDescriptor.php
@@ -35,7 +35,7 @@ public function __construct(callable $handler, array $options = [])
if (str_contains($r->name, '{closure}')) {
$this->name = 'Closure';
} elseif (!$handler = $r->getClosureThis()) {
- $class = $r->getClosureScopeClass();
+ $class = \PHP_VERSION_ID >= 80111 ? $r->getClosureCalledClass() : $r->getClosureScopeClass();
$this->name = ($class ? $class->name.'::' : '').$r->name;
} else {
diff --git a/src/Symfony/Component/Mime/Part/MessagePart.php b/src/Symfony/Component/Mime/Part/MessagePart.php
index 1b5c23e2bc411..270d57aa343ac 100644
--- a/src/Symfony/Component/Mime/Part/MessagePart.php
+++ b/src/Symfony/Component/Mime/Part/MessagePart.php
@@ -59,4 +59,14 @@ public function bodyToIterable(): iterable
{
return $this->message->toIterable();
}
+
+ public function __sleep(): array
+ {
+ return ['message'];
+ }
+
+ public function __wakeup()
+ {
+ $this->__construct($this->message);
+ }
}
diff --git a/src/Symfony/Component/Mime/Tests/Part/MessagePartTest.php b/src/Symfony/Component/Mime/Tests/Part/MessagePartTest.php
index 2713d5bc079c7..c01958a4b94b8 100644
--- a/src/Symfony/Component/Mime/Tests/Part/MessagePartTest.php
+++ b/src/Symfony/Component/Mime/Tests/Part/MessagePartTest.php
@@ -39,4 +39,14 @@ public function testHeaders()
new ParameterizedHeader('Content-Disposition', 'attachment', ['name' => 'Subject.eml', 'filename' => 'Subject.eml'])
), $p->getPreparedHeaders());
}
+
+ public function testSerialize()
+ {
+ $email = (new Email())->from('fabien@symfony.com')->to('you@example.com')->text('content');
+ $email->getHeaders()->addIdHeader('Message-ID', $email->generateMessageId());
+
+ $p = new MessagePart($email);
+ $expected = clone $p;
+ $this->assertEquals($expected->toString(), unserialize(serialize($p))->toString());
+ }
}
diff --git a/src/Symfony/Component/Notifier/Bridge/Clickatell/composer.json b/src/Symfony/Component/Notifier/Bridge/Clickatell/composer.json
index e3f3227a9c278..a70b66590e6a8 100644
--- a/src/Symfony/Component/Notifier/Bridge/Clickatell/composer.json
+++ b/src/Symfony/Component/Notifier/Bridge/Clickatell/composer.json
@@ -24,9 +24,6 @@
"symfony/http-client": "^5.4|^6.0",
"symfony/notifier": "^6.2"
},
- "require-dev": {
- "symfony/event-dispatcher": "^5.4|^6.0"
- },
"autoload": {
"psr-4": { "Symfony\\Component\\Notifier\\Bridge\\Clickatell\\": "" },
"exclude-from-classmap": [
diff --git a/src/Symfony/Component/Notifier/Bridge/Discord/composer.json b/src/Symfony/Component/Notifier/Bridge/Discord/composer.json
index 62ee37ffa0f8a..2d857b661a696 100644
--- a/src/Symfony/Component/Notifier/Bridge/Discord/composer.json
+++ b/src/Symfony/Component/Notifier/Bridge/Discord/composer.json
@@ -21,9 +21,6 @@
"symfony/notifier": "^6.2",
"symfony/polyfill-mbstring": "^1.0"
},
- "require-dev": {
- "symfony/event-dispatcher": "^5.4|^6.0"
- },
"autoload": {
"psr-4": { "Symfony\\Component\\Notifier\\Bridge\\Discord\\": "" },
"exclude-from-classmap": [
diff --git a/src/Symfony/Component/Notifier/Bridge/Engagespot/composer.json b/src/Symfony/Component/Notifier/Bridge/Engagespot/composer.json
index b477f5865b43e..d3718f2891e1a 100644
--- a/src/Symfony/Component/Notifier/Bridge/Engagespot/composer.json
+++ b/src/Symfony/Component/Notifier/Bridge/Engagespot/composer.json
@@ -16,8 +16,8 @@
}
],
"require": {
- "php": ">=7.2.5",
- "symfony/http-client": "^4.3|^5.0|^6.0",
+ "php": ">=8.1",
+ "symfony/http-client": "^5.4|^6.0",
"symfony/notifier": "^6.2"
},
"autoload": {
diff --git a/src/Symfony/Component/Notifier/Bridge/FakeChat/composer.json b/src/Symfony/Component/Notifier/Bridge/FakeChat/composer.json
index 6518b750a70fd..aa4baaefe1d72 100644
--- a/src/Symfony/Component/Notifier/Bridge/FakeChat/composer.json
+++ b/src/Symfony/Component/Notifier/Bridge/FakeChat/composer.json
@@ -24,7 +24,6 @@
"php": ">=8.1",
"symfony/http-client": "^5.4|^6.0",
"symfony/notifier": "^6.2",
- "symfony/event-dispatcher-contracts": "^2|^3",
"symfony/mailer": "^5.4|^6.0"
},
"autoload": {
diff --git a/src/Symfony/Component/Notifier/Bridge/FakeSms/composer.json b/src/Symfony/Component/Notifier/Bridge/FakeSms/composer.json
index 5ec84dd6dd401..0df04f9ac04af 100644
--- a/src/Symfony/Component/Notifier/Bridge/FakeSms/composer.json
+++ b/src/Symfony/Component/Notifier/Bridge/FakeSms/composer.json
@@ -24,7 +24,6 @@
"php": ">=8.1",
"symfony/http-client": "^5.4|^6.0",
"symfony/notifier": "^6.2",
- "symfony/event-dispatcher-contracts": "^2|^3",
"symfony/mailer": "^5.4|^6.0"
},
"autoload": {
diff --git a/src/Symfony/Component/Notifier/Bridge/KazInfoTeh/composer.json b/src/Symfony/Component/Notifier/Bridge/KazInfoTeh/composer.json
index 181d46226a626..aee8470e119fb 100644
--- a/src/Symfony/Component/Notifier/Bridge/KazInfoTeh/composer.json
+++ b/src/Symfony/Component/Notifier/Bridge/KazInfoTeh/composer.json
@@ -1,32 +1,32 @@
{
- "name": "symfony/kaz-info-teh-notifier",
- "type": "symfony-bridge",
- "description": "Symfony KazInfoTeh Notifier Bridge",
- "keywords": ["KazInfoTeh", "notifier", "symfony", "sms"],
- "homepage": "https://symfony.com",
- "license": "MIT",
- "authors": [
- {
- "name": "Egor Taranov",
- "email": "dev@taranovegor.com",
- "homepage": "https://taranovegor.com/contribution"
+ "name": "symfony/kaz-info-teh-notifier",
+ "type": "symfony-bridge",
+ "description": "Symfony KazInfoTeh Notifier Bridge",
+ "keywords": ["KazInfoTeh", "notifier", "symfony", "sms"],
+ "homepage": "https://symfony.com",
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Egor Taranov",
+ "email": "dev@taranovegor.com",
+ "homepage": "https://taranovegor.com/contribution"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "require": {
+ "php": ">=8.1",
+ "ext-simplexml": "*",
+ "symfony/http-client": "^5.4|^6.0",
+ "symfony/notifier": "^6.2"
},
- {
- "name": "Symfony Community",
- "homepage": "https://symfony.com/contributors"
- }
- ],
- "require": {
- "php": ">=8.1",
- "ext-simplexml": "*",
- "symfony/http-client": "^5.4|^6.0",
- "symfony/notifier": "^6.2"
- },
- "autoload": {
- "psr-4": { "Symfony\\Component\\Notifier\\Bridge\\KazInfoTeh\\": "" },
- "exclude-from-classmap": [
- "/Tests/"
- ]
- },
- "minimum-stability": "dev"
+ "autoload": {
+ "psr-4": { "Symfony\\Component\\Notifier\\Bridge\\KazInfoTeh\\": "" },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "minimum-stability": "dev"
}
diff --git a/src/Symfony/Component/Notifier/Bridge/MessageMedia/composer.json b/src/Symfony/Component/Notifier/Bridge/MessageMedia/composer.json
index 8905c9ca932bd..2d76e1d99dd39 100644
--- a/src/Symfony/Component/Notifier/Bridge/MessageMedia/composer.json
+++ b/src/Symfony/Component/Notifier/Bridge/MessageMedia/composer.json
@@ -1,30 +1,30 @@
{
- "name": "symfony/message-media-notifier",
- "type": "symfony-notifier-bridge",
- "description": "Symfony MessageMedia Notifier Bridge",
- "keywords": ["sms", "messagemedia", "notifier"],
- "homepage": "https://symfony.com",
- "license": "MIT",
- "authors": [
- {
- "name": "Adrian Nguyen",
- "email": "vuphuong87@gmail.com"
+ "name": "symfony/message-media-notifier",
+ "type": "symfony-notifier-bridge",
+ "description": "Symfony MessageMedia Notifier Bridge",
+ "keywords": ["sms", "messagemedia", "notifier"],
+ "homepage": "https://symfony.com",
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Adrian Nguyen",
+ "email": "vuphuong87@gmail.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "require": {
+ "php": ">=8.1",
+ "symfony/http-client": "^5.4|^6.0",
+ "symfony/notifier": "^6.2"
},
- {
- "name": "Symfony Community",
- "homepage": "https://symfony.com/contributors"
- }
- ],
- "require": {
- "php": ">=8.1",
- "symfony/http-client": "^5.4|^6.0",
- "symfony/notifier": "^6.2"
- },
- "autoload": {
- "psr-4": { "Symfony\\Component\\Notifier\\Bridge\\MessageMedia\\": "" },
- "exclude-from-classmap": [
- "/Tests/"
- ]
- },
- "minimum-stability": "dev"
+ "autoload": {
+ "psr-4": { "Symfony\\Component\\Notifier\\Bridge\\MessageMedia\\": "" },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "minimum-stability": "dev"
}
diff --git a/src/Symfony/Component/Notifier/Bridge/OneSignal/composer.json b/src/Symfony/Component/Notifier/Bridge/OneSignal/composer.json
index 9175b3c3f7301..bee9e12596252 100644
--- a/src/Symfony/Component/Notifier/Bridge/OneSignal/composer.json
+++ b/src/Symfony/Component/Notifier/Bridge/OneSignal/composer.json
@@ -20,9 +20,6 @@
"symfony/http-client": "^5.4|^6.0",
"symfony/notifier": "^6.2"
},
- "require-dev": {
- "symfony/event-dispatcher": "^5.4|^6.0"
- },
"autoload": {
"psr-4": { "Symfony\\Component\\Notifier\\Bridge\\OneSignal\\": "" },
"exclude-from-classmap": [
diff --git a/src/Symfony/Component/Notifier/Bridge/Sendberry/composer.json b/src/Symfony/Component/Notifier/Bridge/Sendberry/composer.json
index 6edfc02be3a62..4f74162ef634a 100644
--- a/src/Symfony/Component/Notifier/Bridge/Sendberry/composer.json
+++ b/src/Symfony/Component/Notifier/Bridge/Sendberry/composer.json
@@ -16,7 +16,7 @@
}
],
"require": {
- "php": ">=8.0.2",
+ "php": ">=8.1",
"symfony/http-client": "^5.4|^6.0",
"symfony/notifier": "^6.2"
},
diff --git a/src/Symfony/Component/Notifier/Bridge/Slack/composer.json b/src/Symfony/Component/Notifier/Bridge/Slack/composer.json
index eb2e71e9ccfb0..ca14593031acf 100644
--- a/src/Symfony/Component/Notifier/Bridge/Slack/composer.json
+++ b/src/Symfony/Component/Notifier/Bridge/Slack/composer.json
@@ -20,9 +20,6 @@
"symfony/http-client": "^5.4|^6.0",
"symfony/notifier": "^6.2"
},
- "require-dev": {
- "symfony/event-dispatcher": "^5.4|^6.0"
- },
"autoload": {
"psr-4": { "Symfony\\Component\\Notifier\\Bridge\\Slack\\": "" },
"exclude-from-classmap": [
diff --git a/src/Symfony/Component/Notifier/Bridge/Telegram/composer.json b/src/Symfony/Component/Notifier/Bridge/Telegram/composer.json
index 7a1204c82390b..65818cb9f360a 100644
--- a/src/Symfony/Component/Notifier/Bridge/Telegram/composer.json
+++ b/src/Symfony/Component/Notifier/Bridge/Telegram/composer.json
@@ -20,9 +20,6 @@
"symfony/http-client": "^5.4|^6.0",
"symfony/notifier": "^6.2"
},
- "require-dev": {
- "symfony/event-dispatcher": "^5.4|^6.0"
- },
"autoload": {
"psr-4": { "Symfony\\Component\\Notifier\\Bridge\\Telegram\\": "" },
"exclude-from-classmap": [
diff --git a/src/Symfony/Component/Notifier/Bridge/Vonage/composer.json b/src/Symfony/Component/Notifier/Bridge/Vonage/composer.json
index 9adbbc558c5c3..013c59be0fc82 100644
--- a/src/Symfony/Component/Notifier/Bridge/Vonage/composer.json
+++ b/src/Symfony/Component/Notifier/Bridge/Vonage/composer.json
@@ -16,8 +16,8 @@
}
],
"require": {
- "php": ">=7.2.5",
- "symfony/http-client": "^4.3|^5.0|^6.0",
+ "php": ">=8.1",
+ "symfony/http-client": "^5.4|^6.0",
"symfony/notifier": "^6.2"
},
"autoload": {
diff --git a/src/Symfony/Component/RateLimiter/Reservation.php b/src/Symfony/Component/RateLimiter/Reservation.php
index 9e6029aa01251..f73b74bc263f7 100644
--- a/src/Symfony/Component/RateLimiter/Reservation.php
+++ b/src/Symfony/Component/RateLimiter/Reservation.php
@@ -45,6 +45,6 @@ public function getRateLimit(): RateLimit
public function wait(): void
{
- usleep($this->getWaitDuration() * 1e6);
+ usleep((int) ($this->getWaitDuration() * 1e6));
}
}
diff --git a/src/Symfony/Component/Security/Core/Security.php b/src/Symfony/Component/Security/Core/Security.php
index f1ebf822f5bac..97f1c8ce1f568 100644
--- a/src/Symfony/Component/Security/Core/Security.php
+++ b/src/Symfony/Component/Security/Core/Security.php
@@ -26,22 +26,16 @@ class Security implements AuthorizationCheckerInterface
{
/**
* @deprecated since Symfony 6.2, use \Symfony\Bundle\SecurityBundle\Security::ACCESS_DENIED_ERROR instead
- *
- * In 7.0, move this constant to the NewSecurityHelper class and make it reference SecurityRequestAttributes::ACCESS_DENIED_ERROR.
*/
public const ACCESS_DENIED_ERROR = '_security.403_error';
/**
* @deprecated since Symfony 6.2, use \Symfony\Bundle\SecurityBundle\Security::AUTHENTICATION_ERROR instead
- *
- * In 7.0, move this constant to the NewSecurityHelper class and make it reference SecurityRequestAttributes::AUTHENTICATION_ERROR.
*/
public const AUTHENTICATION_ERROR = '_security.last_error';
/**
* @deprecated since Symfony 6.2, use \Symfony\Bundle\SecurityBundle\Security::LAST_USERNAME instead
- *
- * In 7.0, move this constant to the NewSecurityHelper class and make it reference SecurityRequestAttributes::LAST_USERNAME.
*/
public const LAST_USERNAME = '_security.last_username';
diff --git a/src/Symfony/Component/Security/Core/User/UserInterface.php b/src/Symfony/Component/Security/Core/User/UserInterface.php
index c9b0930b19ffc..cace8f6aed6cf 100644
--- a/src/Symfony/Component/Security/Core/User/UserInterface.php
+++ b/src/Symfony/Component/Security/Core/User/UserInterface.php
@@ -55,7 +55,7 @@ public function getRoles(): array;
public function eraseCredentials();
/**
- * Returns the identifier for this user (e.g. its username or email address).
+ * Returns the identifier for this user (e.g. username or email address).
*/
public function getUserIdentifier(): string;
}
diff --git a/src/Symfony/Component/Security/Http/EventListener/IsGrantedAttributeListener.php b/src/Symfony/Component/Security/Http/EventListener/IsGrantedAttributeListener.php
index ce8fbacb8c1cc..518578b511c9b 100644
--- a/src/Symfony/Component/Security/Http/EventListener/IsGrantedAttributeListener.php
+++ b/src/Symfony/Component/Security/Http/EventListener/IsGrantedAttributeListener.php
@@ -77,7 +77,7 @@ public function onKernelControllerArguments(ControllerArgumentsEvent $event)
public static function getSubscribedEvents(): array
{
- return [KernelEvents::CONTROLLER_ARGUMENTS => ['onKernelControllerArguments', 10]];
+ return [KernelEvents::CONTROLLER_ARGUMENTS => ['onKernelControllerArguments', 20]];
}
private function getIsGrantedSubject(string|Expression $subjectRef, Request $request, array $arguments): mixed
diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php
index 12c778cb803af..829e178407bd2 100644
--- a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php
+++ b/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php
@@ -331,8 +331,8 @@ protected function instantiateObject(array &$data, string $class, array &$contex
$params = [];
foreach ($constructorParameters as $constructorParameter) {
$paramName = $constructorParameter->name;
+ $attributeContext = $this->getAttributeDenormalizationContext($class, $paramName, $context);
$key = $this->nameConverter ? $this->nameConverter->normalize($paramName, $class, $format, $context) : $paramName;
- $attributeContext = $this->getAttributeDenormalizationContext($class, $key, $context);
$allowed = false === $allowedAttributes || \in_array($paramName, $allowedAttributes);
$ignored = !$this->isAllowedAttribute($class, $paramName, $format, $context);
diff --git a/src/Symfony/Component/Serializer/Normalizer/GetSetMethodNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/GetSetMethodNormalizer.php
index ba39b41ff8d4d..71459dd945df7 100644
--- a/src/Symfony/Component/Serializer/Normalizer/GetSetMethodNormalizer.php
+++ b/src/Symfony/Component/Serializer/Normalizer/GetSetMethodNormalizer.php
@@ -117,17 +117,17 @@ protected function getAttributeValue(object $object, string $attribute, string $
$ucfirsted = ucfirst($attribute);
$getter = 'get'.$ucfirsted;
- if (\is_callable([$object, $getter])) {
+ if (method_exists($object, $getter) && \is_callable([$object, $getter])) {
return $object->$getter();
}
$isser = 'is'.$ucfirsted;
- if (\is_callable([$object, $isser])) {
+ if (method_exists($object, $isser) && \is_callable([$object, $isser])) {
return $object->$isser();
}
$haser = 'has'.$ucfirsted;
- if (\is_callable([$object, $haser])) {
+ if (method_exists($object, $haser) && \is_callable([$object, $haser])) {
return $object->$haser();
}
@@ -140,7 +140,14 @@ protected function setAttributeValue(object $object, string $attribute, mixed $v
$key = $object::class.':'.$setter;
if (!isset(self::$setterAccessibleCache[$key])) {
- self::$setterAccessibleCache[$key] = \is_callable([$object, $setter]) && !(new \ReflectionMethod($object, $setter))->isStatic();
+ 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;
+ }
}
if (self::$setterAccessibleCache[$key]) {
diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php
index 9c8ba3ffcf4e2..4cc6686586e89 100644
--- a/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php
+++ b/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php
@@ -15,6 +15,7 @@
use PHPUnit\Framework\TestCase;
use Symfony\Component\PropertyInfo\Extractor\PhpDocExtractor;
use Symfony\Component\PropertyInfo\Extractor\ReflectionExtractor;
+use Symfony\Component\PropertyInfo\PropertyInfoExtractor;
use Symfony\Component\PropertyInfo\Type;
use Symfony\Component\Serializer\Annotation\SerializedName;
use Symfony\Component\Serializer\Annotation\SerializedPath;
@@ -32,6 +33,7 @@
use Symfony\Component\Serializer\Mapping\Loader\AnnotationLoader;
use Symfony\Component\Serializer\NameConverter\MetadataAwareNameConverter;
use Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer;
+use Symfony\Component\Serializer\Normalizer\DateTimeNormalizer;
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
use Symfony\Component\Serializer\Serializer;
@@ -41,6 +43,7 @@
use Symfony\Component\Serializer\Tests\Fixtures\Annotations\AbstractDummyFirstChild;
use Symfony\Component\Serializer\Tests\Fixtures\Annotations\AbstractDummySecondChild;
use Symfony\Component\Serializer\Tests\Fixtures\DummySecondChildQuux;
+use Symfony\Component\Serializer\Tests\Normalizer\Features\ObjectDummyWithContextAttribute;
class AbstractObjectNormalizerTest extends TestCase
{
@@ -525,6 +528,20 @@ public function testDenormalizeRecursiveWithObjectAttributeWithStringValue()
$this->assertInstanceOf(ObjectInner::class, $obj->getInner());
}
+
+ public function testDenormalizeUsesContextAttributeForPropertiesInConstructorWithSeralizedName()
+ {
+ $classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()));
+
+ $extractor = new PropertyInfoExtractor([], [new PhpDocExtractor(), new ReflectionExtractor()]);
+ $normalizer = new ObjectNormalizer($classMetadataFactory, new MetadataAwareNameConverter($classMetadataFactory), null, $extractor);
+ $serializer = new Serializer([new DateTimeNormalizer([DateTimeNormalizer::FORMAT_KEY => 'd-m-Y']), $normalizer]);
+
+ /** @var ObjectDummyWithContextAttribute $obj */
+ $obj = $serializer->denormalize(['property_with_serialized_name' => '01-02-2022', 'propertyWithoutSerializedName' => '01-02-2022'], ObjectDummyWithContextAttribute::class);
+
+ $this->assertSame($obj->propertyWithSerializedName->format('Y-m-d'), $obj->propertyWithoutSerializedName->format('Y-m-d'));
+ }
}
class AbstractObjectNormalizerDummy extends AbstractObjectNormalizer
diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/Features/ObjectDummyWithContextAttribute.php b/src/Symfony/Component/Serializer/Tests/Normalizer/Features/ObjectDummyWithContextAttribute.php
new file mode 100644
index 0000000000000..b846f042fe620
--- /dev/null
+++ b/src/Symfony/Component/Serializer/Tests/Normalizer/Features/ObjectDummyWithContextAttribute.php
@@ -0,0 +1,29 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Serializer\Tests\Normalizer\Features;
+
+use Symfony\Component\Serializer\Annotation\Context;
+use Symfony\Component\Serializer\Annotation\SerializedName;
+use Symfony\Component\Serializer\Normalizer\DateTimeNormalizer;
+
+final class ObjectDummyWithContextAttribute
+{
+ public function __construct(
+ #[Context([DateTimeNormalizer::FORMAT_KEY => 'm-d-Y'])]
+ #[SerializedName('property_with_serialized_name')]
+ public \DateTimeImmutable $propertyWithSerializedName,
+
+ #[Context([DateTimeNormalizer::FORMAT_KEY => 'm-d-Y'])]
+ public \DateTimeImmutable $propertyWithoutSerializedName,
+ ) {
+ }
+}
diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/GetSetMethodNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/GetSetMethodNormalizerTest.php
index 6fd430bb47a43..e6f8396fe9d15 100644
--- a/src/Symfony/Component/Serializer/Tests/Normalizer/GetSetMethodNormalizerTest.php
+++ b/src/Symfony/Component/Serializer/Tests/Normalizer/GetSetMethodNormalizerTest.php
@@ -453,6 +453,22 @@ public function testHasGetterNormalize()
);
}
+ public function testCallMagicMethodDenormalize()
+ {
+ $obj = $this->normalizer->denormalize(['active' => true], ObjectWithMagicMethod::class);
+ $this->assertTrue($obj->isActive());
+ }
+
+ public function testCallMagicMethodNormalize()
+ {
+ $obj = new ObjectWithMagicMethod();
+
+ $this->assertSame(
+ ['active' => true],
+ $this->normalizer->normalize($obj, 'any')
+ );
+ }
+
protected function getObjectCollectionWithExpectedArray(): array
{
return [[
@@ -722,3 +738,18 @@ public function hasFoo()
return $this->foo;
}
}
+
+class ObjectWithMagicMethod
+{
+ private $active = true;
+
+ public function isActive()
+ {
+ return $this->active;
+ }
+
+ public function __call($key, $value)
+ {
+ throw new \RuntimeException('__call should not be called. Called with: '.$key);
+ }
+}
diff --git a/src/Symfony/Component/String/LazyString.php b/src/Symfony/Component/String/LazyString.php
index 77d50fbbb3ba1..9523b8cdb2248 100644
--- a/src/Symfony/Component/String/LazyString.php
+++ b/src/Symfony/Component/String/LazyString.php
@@ -127,7 +127,7 @@ private static function getPrettyName(callable $callback): string
} elseif ($callback instanceof \Closure) {
$r = new \ReflectionFunction($callback);
- if (str_contains($r->name, '{closure}') || !$class = $r->getClosureScopeClass()) {
+ if (str_contains($r->name, '{closure}') || !$class = \PHP_VERSION_ID >= 80111 ? $r->getClosureCalledClass() : $r->getClosureScopeClass()) {
return $r->name;
}
diff --git a/src/Symfony/Component/Translation/Extractor/Visitor/AbstractVisitor.php b/src/Symfony/Component/Translation/Extractor/Visitor/AbstractVisitor.php
index 8d5bd8e64eb4b..62e7a3da1622b 100644
--- a/src/Symfony/Component/Translation/Extractor/Visitor/AbstractVisitor.php
+++ b/src/Symfony/Component/Translation/Extractor/Visitor/AbstractVisitor.php
@@ -42,32 +42,25 @@ protected function addMessageToCatalogue(string $message, ?string $domain, int $
protected function getStringArguments(Node\Expr\CallLike|Node\Attribute|Node\Expr\New_ $node, int|string $index, bool $indexIsRegex = false): array
{
- $args = $node instanceof Node\Expr\CallLike ? $node->getArgs() : $node->args;
-
if (\is_string($index)) {
return $this->getStringNamedArguments($node, $index, $indexIsRegex);
}
- if (\count($args) < $index) {
- return [];
- }
+ $args = $node instanceof Node\Expr\CallLike ? $node->getRawArgs() : $node->args;
- /** @var Node\Arg $arg */
- $arg = $args[$index];
- if (!$result = $this->getStringValue($arg->value)) {
+ if (!($arg = $args[$index] ?? null) instanceof Node\Arg) {
return [];
}
- return [$result];
+ return (array) $this->getStringValue($arg->value);
}
protected function hasNodeNamedArguments(Node\Expr\CallLike|Node\Attribute|Node\Expr\New_ $node): bool
{
- $args = $node instanceof Node\Expr\CallLike ? $node->getArgs() : $node->args;
+ $args = $node instanceof Node\Expr\CallLike ? $node->getRawArgs() : $node->args;
- /** @var Node\Arg $arg */
foreach ($args as $arg) {
- if (null !== $arg->name) {
+ if ($arg instanceof Node\Arg && null !== $arg->name) {
return true;
}
}
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 781da00b64f9e..e34863f3dc660 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
@@ -54,3 +54,5 @@
trans('variable-assignation-inlined-in-trans-method-call3', [], $domain = 'not_messages'); ?>
trans(domain: $domain = 'not_messages', message: $key = 'variable-assignation-inlined-with-named-arguments-in-trans-method', parameters: $parameters = []); ?>
+
+trans(...); // should not fail ?>
diff --git a/src/Symfony/Component/Validator/ConstraintValidatorFactory.php b/src/Symfony/Component/Validator/ConstraintValidatorFactory.php
index 8152ad0dece11..778e202a84bf8 100644
--- a/src/Symfony/Component/Validator/ConstraintValidatorFactory.php
+++ b/src/Symfony/Component/Validator/ConstraintValidatorFactory.php
@@ -26,20 +26,17 @@ class ConstraintValidatorFactory implements ConstraintValidatorFactoryInterface
{
protected $validators = [];
- public function __construct()
+ public function __construct(array $validators = [])
{
+ $this->validators = $validators;
}
public function getInstance(Constraint $constraint): ConstraintValidatorInterface
{
- $className = $constraint->validatedBy();
-
- if (!isset($this->validators[$className])) {
- $this->validators[$className] = 'validator.expression' === $className
- ? new ExpressionValidator()
- : new $className();
+ if ('validator.expression' === $name = $class = $constraint->validatedBy()) {
+ $class = ExpressionValidator::class;
}
- return $this->validators[$className];
+ return $this->validators[$name] ??= new $class();
}
}
diff --git a/src/Symfony/Component/Validator/Tests/ConstraintValidatorFactoryTest.php b/src/Symfony/Component/Validator/Tests/ConstraintValidatorFactoryTest.php
new file mode 100644
index 0000000000000..d7d9b302d1bb7
--- /dev/null
+++ b/src/Symfony/Component/Validator/Tests/ConstraintValidatorFactoryTest.php
@@ -0,0 +1,33 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Validator\Tests;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Validator\ConstraintValidatorFactory;
+use Symfony\Component\Validator\Tests\Fixtures\DummyConstraint;
+use Symfony\Component\Validator\Tests\Fixtures\DummyConstraintValidator;
+
+class ConstraintValidatorFactoryTest extends TestCase
+{
+ public function testGetInstance()
+ {
+ $factory = new ConstraintValidatorFactory();
+ $this->assertInstanceOf(DummyConstraintValidator::class, $factory->getInstance(new DummyConstraint()));
+ }
+
+ public function testPredefinedGetInstance()
+ {
+ $validator = new DummyConstraintValidator();
+ $factory = new ConstraintValidatorFactory([DummyConstraintValidator::class => $validator]);
+ $this->assertSame($validator, $factory->getInstance(new DummyConstraint()));
+ }
+}
diff --git a/src/Symfony/Component/Validator/Tests/ContainerConstraintValidatorFactoryTest.php b/src/Symfony/Component/Validator/Tests/ContainerConstraintValidatorFactoryTest.php
index 3999b86b4f196..63b7f6f96ae01 100644
--- a/src/Symfony/Component/Validator/Tests/ContainerConstraintValidatorFactoryTest.php
+++ b/src/Symfony/Component/Validator/Tests/ContainerConstraintValidatorFactoryTest.php
@@ -15,9 +15,10 @@
use Symfony\Component\DependencyInjection\Container;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\Constraints\Blank as BlankConstraint;
-use Symfony\Component\Validator\ConstraintValidator;
use Symfony\Component\Validator\ContainerConstraintValidatorFactory;
use Symfony\Component\Validator\Exception\ValidatorException;
+use Symfony\Component\Validator\Tests\Fixtures\DummyConstraint;
+use Symfony\Component\Validator\Tests\Fixtures\DummyConstraintValidator;
class ContainerConstraintValidatorFactoryTest extends TestCase
{
@@ -59,18 +60,3 @@ public function testGetInstanceInvalidValidatorClass()
$factory->getInstance($constraint);
}
}
-
-class DummyConstraint extends Constraint
-{
- public function validatedBy(): string
- {
- return DummyConstraintValidator::class;
- }
-}
-
-class DummyConstraintValidator extends ConstraintValidator
-{
- public function validate($value, Constraint $constraint)
- {
- }
-}
diff --git a/src/Symfony/Component/Validator/Tests/Fixtures/DummyConstraint.php b/src/Symfony/Component/Validator/Tests/Fixtures/DummyConstraint.php
new file mode 100644
index 0000000000000..c2209f135f5ce
--- /dev/null
+++ b/src/Symfony/Component/Validator/Tests/Fixtures/DummyConstraint.php
@@ -0,0 +1,22 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Validator\Tests\Fixtures;
+
+use Symfony\Component\Validator\Constraint;
+
+class DummyConstraint extends Constraint
+{
+ public function validatedBy(): string
+ {
+ return DummyConstraintValidator::class;
+ }
+}
diff --git a/src/Symfony/Component/Validator/Tests/Fixtures/DummyConstraintValidator.php b/src/Symfony/Component/Validator/Tests/Fixtures/DummyConstraintValidator.php
new file mode 100644
index 0000000000000..3481a5e2d8c1f
--- /dev/null
+++ b/src/Symfony/Component/Validator/Tests/Fixtures/DummyConstraintValidator.php
@@ -0,0 +1,22 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Validator\Tests\Fixtures;
+
+use Symfony\Component\Validator\Constraint;
+use Symfony\Component\Validator\ConstraintValidator;
+
+class DummyConstraintValidator extends ConstraintValidator
+{
+ public function validate($value, Constraint $constraint)
+ {
+ }
+}
diff --git a/src/Symfony/Component/VarDumper/Caster/Caster.php b/src/Symfony/Component/VarDumper/Caster/Caster.php
index c2bbbd66ee14e..cd9b35f77b2ea 100644
--- a/src/Symfony/Component/VarDumper/Caster/Caster.php
+++ b/src/Symfony/Component/VarDumper/Caster/Caster.php
@@ -47,7 +47,7 @@ public static function castObject(object $obj, string $class, bool $hasDebugInfo
if ($hasDebugInfo) {
try {
$debugInfo = $obj->__debugInfo();
- } catch (\Exception) {
+ } catch (\Throwable) {
// ignore failing __debugInfo()
$hasDebugInfo = false;
}
diff --git a/src/Symfony/Component/VarDumper/Caster/ReflectionCaster.php b/src/Symfony/Component/VarDumper/Caster/ReflectionCaster.php
index e12b78cea1ba2..f6c8c7410aed1 100644
--- a/src/Symfony/Component/VarDumper/Caster/ReflectionCaster.php
+++ b/src/Symfony/Component/VarDumper/Caster/ReflectionCaster.php
@@ -197,7 +197,7 @@ public static function castFunctionAbstract(\ReflectionFunctionAbstract $c, arra
self::addMap($a, $c, [
'returnsReference' => 'returnsReference',
'returnType' => 'getReturnType',
- 'class' => 'getClosureScopeClass',
+ 'class' => \PHP_VERSION_ID >= 80111 ? 'getClosureCalledClass' : 'getClosureScopeClass',
'this' => 'getClosureThis',
]);
diff --git a/src/Symfony/Component/VarDumper/Tests/Caster/CasterTest.php b/src/Symfony/Component/VarDumper/Tests/Caster/CasterTest.php
index c39e82cf6adb0..f4be025c351fe 100644
--- a/src/Symfony/Component/VarDumper/Tests/Caster/CasterTest.php
+++ b/src/Symfony/Component/VarDumper/Tests/Caster/CasterTest.php
@@ -175,4 +175,14 @@ public function testAnonymousClass()
, $c
);
}
+
+ public function testTypeErrorInDebugInfo()
+ {
+ $this->assertDumpMatchesFormat('class@anonymous {}', new class() {
+ public function __debugInfo(): array
+ {
+ return ['class' => \get_class(null)];
+ }
+ });
+ }
}
diff --git a/src/Symfony/Component/VarExporter/LazyGhostTrait.php b/src/Symfony/Component/VarExporter/LazyGhostTrait.php
index 21bef1cb5b495..a0dea4dd5b8ee 100644
--- a/src/Symfony/Component/VarExporter/LazyGhostTrait.php
+++ b/src/Symfony/Component/VarExporter/LazyGhostTrait.php
@@ -193,17 +193,37 @@ public function &__get($name): mixed
get_in_scope:
- if (null === $scope) {
- if (null === $readonlyScope) {
- return $this->$name;
+ try {
+ if (null === $scope) {
+ if (null === $readonlyScope) {
+ return $this->$name;
+ }
+ $value = $this->$name;
+
+ return $value;
}
- $value = $this->$name;
+ $accessor = Registry::$classAccessors[$scope] ??= Registry::getClassAccessors($scope);
- return $value;
- }
- $accessor = Registry::$classAccessors[$scope] ??= Registry::getClassAccessors($scope);
+ return $accessor['get']($this, $name, null !== $readonlyScope);
+ } catch (\Error $e) {
+ if (\Error::class !== $e::class || !str_starts_with($e->getMessage(), 'Cannot access uninitialized non-nullable property')) {
+ throw $e;
+ }
+
+ try {
+ if (null === $scope) {
+ $this->$name = [];
+
+ return $this->$name;
+ }
- return $accessor['get']($this, $name, null !== $readonlyScope);
+ $accessor['set']($this, $name, []);
+
+ return $accessor['get']($this, $name, null !== $readonlyScope);
+ } catch (\Error) {
+ throw $e;
+ }
+ }
}
public function __set($name, $value): void
diff --git a/src/Symfony/Component/VarExporter/LazyProxyTrait.php b/src/Symfony/Component/VarExporter/LazyProxyTrait.php
index 196da7d608e07..fa6a741fd6d40 100644
--- a/src/Symfony/Component/VarExporter/LazyProxyTrait.php
+++ b/src/Symfony/Component/VarExporter/LazyProxyTrait.php
@@ -130,17 +130,37 @@ public function &__get($name): mixed
get_in_scope:
- if (null === $scope) {
- if (null === $readonlyScope && 1 !== $parent) {
- return $instance->$name;
+ try {
+ if (null === $scope) {
+ if (null === $readonlyScope && 1 !== $parent) {
+ return $instance->$name;
+ }
+ $value = $instance->$name;
+
+ return $value;
}
- $value = $instance->$name;
+ $accessor = Registry::$classAccessors[$scope] ??= Registry::getClassAccessors($scope);
- return $value;
- }
- $accessor = Registry::$classAccessors[$scope] ??= Registry::getClassAccessors($scope);
+ return $accessor['get']($instance, $name, null !== $readonlyScope || 1 === $parent);
+ } catch (\Error $e) {
+ if (\Error::class !== $e::class || !str_starts_with($e->getMessage(), 'Cannot access uninitialized non-nullable property')) {
+ throw $e;
+ }
+
+ try {
+ if (null === $scope) {
+ $instance->$name = [];
+
+ return $instance->$name;
+ }
- return $accessor['get']($instance, $name, null !== $readonlyScope || 1 === $parent);
+ $accessor['set']($instance, $name, []);
+
+ return $accessor['get']($instance, $name, null !== $readonlyScope || 1 === $parent);
+ } catch (\Error) {
+ throw $e;
+ }
+ }
}
public function __set($name, $value): void
@@ -337,7 +357,7 @@ public function __unserialize(array $data): void
PublicHydrator::hydrate($this, $data);
if (Registry::$parentMethods[$class]['wakeup']) {
- parent:__wakeup();
+ parent::__wakeup();
}
}
}
diff --git a/src/Symfony/Component/VarExporter/Tests/LazyGhostTraitTest.php b/src/Symfony/Component/VarExporter/Tests/LazyGhostTraitTest.php
index 25673fb073ca8..3ef2a8c95c1c2 100644
--- a/src/Symfony/Component/VarExporter/Tests/LazyGhostTraitTest.php
+++ b/src/Symfony/Component/VarExporter/Tests/LazyGhostTraitTest.php
@@ -13,6 +13,7 @@
use PHPUnit\Framework\TestCase;
use Symfony\Component\VarExporter\Internal\LazyObjectState;
+use Symfony\Component\VarExporter\ProxyHelper;
use Symfony\Component\VarExporter\Tests\Fixtures\LazyGhost\ChildMagicClass;
use Symfony\Component\VarExporter\Tests\Fixtures\LazyGhost\ChildStdClass;
use Symfony\Component\VarExporter\Tests\Fixtures\LazyGhost\ChildTestClass;
@@ -393,4 +394,41 @@ public function testFullInitializationAfterPartialInitialization()
$this->assertSame(3, ((array) $instance)["\0".TestClass::class."\0private"]);
$this->assertSame(1001, $counter);
}
+
+ public function testIndirectModification()
+ {
+ $obj = new class() {
+ public array $foo;
+ };
+ $proxy = $this->createLazyGhost($obj::class, fn () => null);
+
+ $proxy->foo[] = 123;
+
+ $this->assertSame([123], $proxy->foo);
+ }
+
+ /**
+ * @template T
+ *
+ * @param class-string $class
+ *
+ * @return T
+ */
+ private function createLazyGhost(string $class, \Closure|array $initializer, array $skippedProperties = null): object
+ {
+ $r = new \ReflectionClass($class);
+
+ if (str_contains($class, "\0")) {
+ $class = __CLASS__.'\\'.debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS, 2)[1]['function'].'_L'.$r->getStartLine();
+ class_alias($r->name, $class);
+ }
+ $proxy = str_replace($r->name, $class, ProxyHelper::generateLazyGhost($r));
+ $class = str_replace('\\', '_', $class).'_'.md5($proxy);
+
+ if (!class_exists($class, false)) {
+ eval((\PHP_VERSION_ID >= 80200 && $r->isReadOnly() ? 'readonly ' : '').'class '.$class.' '.$proxy);
+ }
+
+ return $class::createLazyGhost($initializer, $skippedProperties);
+ }
}
diff --git a/src/Symfony/Component/VarExporter/Tests/LazyProxyTraitTest.php b/src/Symfony/Component/VarExporter/Tests/LazyProxyTraitTest.php
index 04497f02b34ae..074934ae17991 100644
--- a/src/Symfony/Component/VarExporter/Tests/LazyProxyTraitTest.php
+++ b/src/Symfony/Component/VarExporter/Tests/LazyProxyTraitTest.php
@@ -233,6 +233,18 @@ public function setFoo($foo): static
$this->assertSame(234, $proxy->foo);
}
+ public function testIndirectModification()
+ {
+ $obj = new class() {
+ public array $foo;
+ };
+ $proxy = $this->createLazyProxy($obj::class, fn () => $obj);
+
+ $proxy->foo[] = 123;
+
+ $this->assertSame([123], $proxy->foo);
+ }
+
/**
* @requires PHP 8.2
*/
diff --git a/src/Symfony/Component/Yaml/Dumper.php b/src/Symfony/Component/Yaml/Dumper.php
index c5024ff4e32bf..205da6fd2c575 100644
--- a/src/Symfony/Component/Yaml/Dumper.php
+++ b/src/Symfony/Component/Yaml/Dumper.php
@@ -56,6 +56,8 @@ public function dump(mixed $input, int $inline = 0, int $indent = 0, int $flags
if ($inline <= 0 || (!\is_array($input) && !$input instanceof TaggedValue && $dumpObjectAsInlineMap) || empty($input)) {
$output .= $prefix.Inline::dump($input, $flags);
+ } elseif ($input instanceof TaggedValue) {
+ $output .= $this->dumpTaggedValue($input, $inline, $indent, $flags, $prefix);
} else {
$dumpAsMap = Inline::isHash($input);
@@ -135,4 +137,28 @@ public function dump(mixed $input, int $inline = 0, int $indent = 0, int $flags
return $output;
}
+
+ private function dumpTaggedValue(TaggedValue $value, int $inline, int $indent, int $flags, string $prefix): string
+ {
+ $output = sprintf('%s!%s', $prefix ? $prefix.' ' : '', $value->getTag());
+
+ if (Yaml::DUMP_MULTI_LINE_LITERAL_BLOCK & $flags && \is_string($value->getValue()) && false !== strpos($value->getValue(), "\n") && false === strpos($value->getValue(), "\r\n")) {
+ // If the first line starts with a space character, the spec requires a blockIndicationIndicator
+ // http://www.yaml.org/spec/1.2/spec.html#id2793979
+ $blockIndentationIndicator = (' ' === substr($value->getValue(), 0, 1)) ? (string) $this->indentation : '';
+ $output .= sprintf(' |%s', $blockIndentationIndicator);
+
+ foreach (explode("\n", $value->getValue()) as $row) {
+ $output .= sprintf("\n%s%s%s", $prefix, str_repeat(' ', $this->indentation), $row);
+ }
+
+ return $output;
+ }
+
+ if ($inline - 1 <= 0 || null === $value->getValue() || \is_scalar($value->getValue())) {
+ return $output.' '.$this->dump($value->getValue(), $inline - 1, 0, $flags)."\n";
+ }
+
+ return $output."\n".$this->dump($value->getValue(), $inline - 1, $indent, $flags);
+ }
}
diff --git a/src/Symfony/Component/Yaml/Tests/DumperTest.php b/src/Symfony/Component/Yaml/Tests/DumperTest.php
index eedd3367d4e77..20308592f1c9b 100644
--- a/src/Symfony/Component/Yaml/Tests/DumperTest.php
+++ b/src/Symfony/Component/Yaml/Tests/DumperTest.php
@@ -440,6 +440,26 @@ public function testDumpingTaggedValueTopLevelAssocInline()
$this->assertSameData($data, $this->parser->parse($yaml, Yaml::PARSE_CUSTOM_TAGS));
}
+ public function testDumpingTaggedValueTopLevelAssoc()
+ {
+ $data = new TaggedValue('user', ['name' => 'jane']);
+
+ $expected = <<<'YAML'
+!user
+name: jane
+
+YAML;
+ $yaml = $this->dumper->dump($data, 2);
+ $this->assertSame($expected, $yaml);
+ }
+
+ public function testDumpingTaggedValueTopLevelMultiLine()
+ {
+ $data = new TaggedValue('text', "a\nb\n");
+
+ $this->assertSame("!text |\n a\n b\n ", $this->dumper->dump($data, 2, 0, Yaml::DUMP_MULTI_LINE_LITERAL_BLOCK));
+ }
+
public function testDumpingTaggedValueSpecialCharsInTag()
{
// @todo Validate the tag name in the TaggedValue constructor.
diff --git a/src/Symfony/Contracts/Translation/Test/TranslatorTest.php b/src/Symfony/Contracts/Translation/Test/TranslatorTest.php
index fbfa161929a4a..c5c37b355a5cb 100644
--- a/src/Symfony/Contracts/Translation/Test/TranslatorTest.php
+++ b/src/Symfony/Contracts/Translation/Test/TranslatorTest.php
@@ -137,7 +137,7 @@ public function getTransChoiceTests()
}
/**
- * @dataProvider getInternal
+ * @dataProvider getInterval
*/
public function testInterval($expected, $number, $interval)
{
@@ -146,7 +146,7 @@ public function testInterval($expected, $number, $interval)
$this->assertEquals($expected, $translator->trans($interval.' foo|[1,Inf[ bar', ['%count%' => $number]));
}
- public function getInternal()
+ public function getInterval()
{
return [
['foo', 3, '{1,2, 3 ,4}'],