diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/CustomPathBundle/src/CustomPathBundle.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/CustomPathBundle/src/CustomPathBundle.php
index ad7194de97b0e..f1f2bdc746e9e 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/CustomPathBundle/src/CustomPathBundle.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/CustomPathBundle/src/CustomPathBundle.php
@@ -9,7 +9,7 @@
* file that was distributed with this source code.
*/
-namespace Symfony\Bundle\FrameworkBundle\Tests;
+namespace Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Fixtures\CustomPathBundle;
use Symfony\Component\HttpKernel\Bundle\Bundle;
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/TestBundle/TestBundle.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/TestBundle/TestBundle.php
index 2f090b2de8d53..c58b25066bf4a 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/TestBundle/TestBundle.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/TestBundle/TestBundle.php
@@ -9,7 +9,7 @@
* file that was distributed with this source code.
*/
-namespace Symfony\Bundle\FrameworkBundle\Tests;
+namespace Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Fixtures\TestBundle;
use Symfony\Component\HttpKernel\Bundle\Bundle;
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/http_client_scoped_without_query_option.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/http_client_scoped_without_query_option.php
new file mode 100644
index 0000000000000..0d3dc88472f84
--- /dev/null
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/http_client_scoped_without_query_option.php
@@ -0,0 +1,11 @@
+loadFromExtension('framework', [
+ 'http_client' => [
+ 'scoped_clients' => [
+ 'foo' => [
+ 'scope' => '.*',
+ ],
+ ],
+ ],
+]);
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/http_client_scoped_without_query_option.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/http_client_scoped_without_query_option.xml
new file mode 100644
index 0000000000000..43043aeda2a01
--- /dev/null
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/http_client_scoped_without_query_option.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/http_client_scoped_without_query_option.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/http_client_scoped_without_query_option.yml
new file mode 100644
index 0000000000000..ecfc9d41fd4c3
--- /dev/null
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/http_client_scoped_without_query_option.yml
@@ -0,0 +1,5 @@
+framework:
+ http_client:
+ scoped_clients:
+ foo:
+ scope: '.*'
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php
index edcdb7aae487f..9a8fc0776f0af 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php
@@ -1547,6 +1547,14 @@ public function testHttpClientDefaultOptions()
$this->assertSame(ScopingHttpClient::class, $container->getDefinition('foo')->getClass());
}
+ public function testScopedHttpClientWithoutQueryOption()
+ {
+ $container = $this->createContainerFromFile('http_client_scoped_without_query_option');
+
+ $this->assertTrue($container->hasDefinition('foo'), 'should have the "foo" service.');
+ $this->assertSame(ScopingHttpClient::class, $container->getDefinition('foo')->getClass());
+ }
+
public function testHttpClientOverrideDefaultOptions()
{
$container = $this->createContainerFromFile('http_client_override_default_options');
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/ClassAliasExampleClass.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/ClassAliasExampleClass.php
new file mode 100644
index 0000000000000..c1298658597b1
--- /dev/null
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/ClassAliasExampleClass.php
@@ -0,0 +1,14 @@
+run(['command' => 'debug:autowiring', 'search' => 'redirect', '--all' => true]);
$this->assertStringContainsString('Pro-tip: use interfaces in your type-hints instead of classes to benefit from the dependency inversion principle.', $tester->getDisplay());
}
+
+ public function testNotConfusedByClassAliases()
+ {
+ static::bootKernel(['test_case' => 'ContainerDebug', 'root_config' => 'config.yml']);
+
+ $application = new Application(static::$kernel);
+ $application->setAutoExit(false);
+
+ $tester = new ApplicationTester($application);
+ $tester->run(['command' => 'debug:autowiring', 'search' => 'ClassAlias']);
+ $this->assertStringContainsString('Symfony\Bundle\FrameworkBundle\Tests\Fixtures\ClassAliasExampleClass (public)', $tester->getDisplay());
+ }
}
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/ContainerDebug/config.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/ContainerDebug/config.yml
index 0cc73dbc81422..25c1c784298b5 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/ContainerDebug/config.yml
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/ContainerDebug/config.yml
@@ -13,6 +13,7 @@ services:
public: false
Symfony\Bundle\FrameworkBundle\Tests\Fixtures\BackslashClass:
class: Symfony\Bundle\FrameworkBundle\Tests\Fixtures\BackslashClass
+ Symfony\Bundle\FrameworkBundle\Tests\Fixtures\ClassAliasExampleClass: '@public'
env:
class: stdClass
arguments:
diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php
index b9258a93f8d1b..42114072d27fc 100644
--- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php
+++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php
@@ -558,7 +558,7 @@ private function createEncoder(array $config)
$config['algorithm'] = 'native';
$config['native_algorithm'] = PASSWORD_ARGON2I;
} else {
- throw new InvalidConfigurationException(sprintf('Algorithm "argon2i" is not available. Either use %s"auto" or upgrade to PHP 7.2+ instead.', \defined('SODIUM_CRYPTO_PWHASH_ALG_ARGON2ID13') ? '"argon2id", ' : ''));
+ throw new InvalidConfigurationException(sprintf('Algorithm "argon2i" is not available. Either use "%s" or upgrade to PHP 7.2+ instead.', \defined('SODIUM_CRYPTO_PWHASH_ALG_ARGON2ID13') ? 'argon2id", "auto' : 'auto'));
}
return $this->createEncoder($config);
@@ -571,7 +571,7 @@ private function createEncoder(array $config)
$config['algorithm'] = 'native';
$config['native_algorithm'] = PASSWORD_ARGON2ID;
} else {
- throw new InvalidConfigurationException(sprintf('Algorithm "argon2id" is not available. Either use %s"auto", upgrade to PHP 7.3+ or use libsodium 1.0.15+ instead.', \defined('PASSWORD_ARGON2I') || $hasSodium ? '"argon2i", ' : ''));
+ throw new InvalidConfigurationException(sprintf('Algorithm "argon2id" is not available. Either use "%s", upgrade to PHP 7.3+ or use libsodium 1.0.15+ instead.', \defined('PASSWORD_ARGON2I') || $hasSodium ? 'argon2i", "auto' : 'auto'));
}
return $this->createEncoder($config);
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/GuardedBundle/AppCustomAuthenticator.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/GuardedBundle/AppCustomAuthenticator.php
index fef2732759fa1..22d378835e4c0 100644
--- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/GuardedBundle/AppCustomAuthenticator.php
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/GuardedBundle/AppCustomAuthenticator.php
@@ -23,7 +23,7 @@ class AppCustomAuthenticator extends AbstractGuardAuthenticator
{
public function supports(Request $request)
{
- return true;
+ return '/manual_login' !== $request->getPathInfo() && '/profile' !== $request->getPathInfo();
}
public function getCredentials(Request $request)
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/GuardedBundle/AuthenticationController.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/GuardedBundle/AuthenticationController.php
new file mode 100644
index 0000000000000..9833d05513833
--- /dev/null
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/GuardedBundle/AuthenticationController.php
@@ -0,0 +1,38 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\GuardedBundle;
+
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Response;
+use Symfony\Component\Security\Core\User\User;
+use Symfony\Component\Security\Core\User\UserInterface;
+use Symfony\Component\Security\Guard\GuardAuthenticatorHandler;
+use Symfony\Component\Security\Guard\Token\PostAuthenticationGuardToken;
+
+class AuthenticationController
+{
+ public function manualLoginAction(GuardAuthenticatorHandler $guardAuthenticatorHandler, Request $request)
+ {
+ $guardAuthenticatorHandler->authenticateWithToken(new PostAuthenticationGuardToken(new User('Jane', 'test', ['ROLE_USER']), 'secure', ['ROLE_USER']), $request, 'secure');
+
+ return new Response('Logged in.');
+ }
+
+ public function profileAction(UserInterface $user = null)
+ {
+ if (null === $user) {
+ return new Response('Not logged in.');
+ }
+
+ return new Response('Username: '.$user->getUsername());
+ }
+}
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/GuardedTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/GuardedTest.php
index bb0969c36a2fd..83cd4118d76e4 100644
--- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/GuardedTest.php
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/GuardedTest.php
@@ -21,4 +21,14 @@ public function testGuarded()
$this->assertSame(418, $client->getResponse()->getStatusCode());
}
+
+ public function testManualLogin()
+ {
+ $client = $this->createClient(['debug' => true, 'test_case' => 'Guarded', 'root_config' => 'config.yml']);
+
+ $client->request('GET', '/manual_login');
+ $client->request('GET', '/profile');
+
+ $this->assertSame('Username: Jane', $client->getResponse()->getContent());
+ }
}
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Guarded/config.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Guarded/config.yml
index 2d1f779a530ec..7f87c307d28b5 100644
--- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Guarded/config.yml
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Guarded/config.yml
@@ -10,8 +10,19 @@ framework:
services:
logger: { class: Psr\Log\NullLogger }
Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\GuardedBundle\AppCustomAuthenticator: ~
+ Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\GuardedBundle\AuthenticationController:
+ tags: [controller.service_arguments]
security:
+ encoders:
+ Symfony\Component\Security\Core\User\User: plaintext
+
+ providers:
+ in_memory:
+ memory:
+ users:
+ Jane: { password: test, roles: [ROLE_USER] }
+
firewalls:
secure:
pattern: ^/
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Guarded/routing.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Guarded/routing.yml
index 4d11154375219..146aa811a143d 100644
--- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Guarded/routing.yml
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Guarded/routing.yml
@@ -3,3 +3,12 @@ main:
defaults:
_controller: Symfony\Bundle\FrameworkBundle\Controller\RedirectController::urlRedirectAction
path: /app
+profile:
+ path: /profile
+ defaults:
+ _controller: Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\GuardedBundle\AuthenticationController::profileAction
+
+manual_login:
+ path: /manual_login
+ defaults:
+ _controller: Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\GuardedBundle\AuthenticationController::manualLoginAction
diff --git a/src/Symfony/Bundle/TwigBundle/Tests/Loader/FilesystemLoaderTest.php b/src/Symfony/Bundle/TwigBundle/Tests/Loader/FilesystemLoaderTest.php
index 2d678fa8f3502..b760cca92627e 100644
--- a/src/Symfony/Bundle/TwigBundle/Tests/Loader/FilesystemLoaderTest.php
+++ b/src/Symfony/Bundle/TwigBundle/Tests/Loader/FilesystemLoaderTest.php
@@ -101,7 +101,7 @@ public function testTwigErrorIfLocatorReturnsFalse()
public function testTwigErrorIfTemplateDoesNotExist()
{
$this->expectException('Twig\Error\LoaderError');
- $this->expectExceptionMessageRegExp('/Unable to find template "name\.format\.engine" \(looked into: .*Tests.Loader.\.\..DependencyInjection.Fixtures.templates\)/');
+ $this->expectExceptionMessageMatches('/Unable to find template "name\.format\.engine" \(looked into: .*Tests.Loader.\.\..DependencyInjection.Fixtures.templates\)/');
$parser = $this->getMockBuilder('Symfony\Component\Templating\TemplateNameParserInterface')->getMock();
$locator = $this->getMockBuilder('Symfony\Component\Config\FileLocatorInterface')->getMock();
diff --git a/src/Symfony/Bundle/WebProfilerBundle/Csp/ContentSecurityPolicyHandler.php b/src/Symfony/Bundle/WebProfilerBundle/Csp/ContentSecurityPolicyHandler.php
index 0679d2bf6d04a..7c111a8ba3560 100644
--- a/src/Symfony/Bundle/WebProfilerBundle/Csp/ContentSecurityPolicyHandler.php
+++ b/src/Symfony/Bundle/WebProfilerBundle/Csp/ContentSecurityPolicyHandler.php
@@ -124,7 +124,7 @@ private function updateCspHeaders(Response $response, array $nonces = []): array
$headers = $this->getCspHeaders($response);
foreach ($headers as $header => $directives) {
- foreach (['script-src' => 'csp_script_nonce', 'style-src' => 'csp_style_nonce'] as $type => $tokenName) {
+ foreach (['script-src' => 'csp_script_nonce', 'script-src-elem' => 'csp_script_nonce', 'style-src' => 'csp_style_nonce', 'style-src-elem' => 'csp_style_nonce'] as $type => $tokenName) {
if ($this->authorizesInline($directives, $type)) {
continue;
}
diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/memory.html.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/memory.html.twig
index 268f8fdc7e3f6..49878a72d5d65 100644
--- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/memory.html.twig
+++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/memory.html.twig
@@ -5,18 +5,18 @@
{% set status_color = (collector.memory / 1024 / 1024) > 50 ? 'yellow' : '' %}
{{ include('@WebProfiler/Icon/memory.svg') }}
{{ '%.1f'|format(collector.memory / 1024 / 1024) }}
- MB
+ MiB
{% endset %}
{% set text %}
Peak memory usage
- {{ '%.1f'|format(collector.memory / 1024 / 1024) }} MB
+ {{ '%.1f'|format(collector.memory / 1024 / 1024) }} MiB
PHP memory limit
- {{ collector.memoryLimit == -1 ? 'Unlimited' : '%.0f MB'|format(collector.memoryLimit / 1024 / 1024) }}
+ {{ collector.memoryLimit == -1 ? 'Unlimited' : '%.0f MiB'|format(collector.memoryLimit / 1024 / 1024) }}
{% endset %}
diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/time.html.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/time.html.twig
index 62187dbab4f31..9284a0a708d36 100644
--- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/time.html.twig
+++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/time.html.twig
@@ -52,7 +52,7 @@
{% if profile.collectors.memory %}
- {{ '%.2f'|format(profile.collectors.memory.memory / 1024 / 1024) }} MB
+ {{ '%.2f'|format(profile.collectors.memory.memory / 1024 / 1024) }} MiB
Peak memory usage
{% endif %}
diff --git a/src/Symfony/Bundle/WebProfilerBundle/Tests/Csp/ContentSecurityPolicyHandlerTest.php b/src/Symfony/Bundle/WebProfilerBundle/Tests/Csp/ContentSecurityPolicyHandlerTest.php
index acccc7cbfb6d2..349db2aaf75b4 100644
--- a/src/Symfony/Bundle/WebProfilerBundle/Tests/Csp/ContentSecurityPolicyHandlerTest.php
+++ b/src/Symfony/Bundle/WebProfilerBundle/Tests/Csp/ContentSecurityPolicyHandlerTest.php
@@ -41,7 +41,7 @@ public function testOnKernelResponse($nonce, $expectedNonce, Request $request, R
$this->assertFalse($response->headers->has('X-SymfonyProfiler-Style-Nonce'));
foreach ($expectedCsp as $header => $value) {
- $this->assertSame($value, $response->headers->get($header));
+ $this->assertSame($value, $response->headers->get($header), $header);
}
}
@@ -131,7 +131,7 @@ public function provideRequestAndResponsesForOnKernelResponse()
['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\'']),
- ['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],
+ ['Content-Security-Policy' => 'default-src \'self\' domain.com; script-src \'self\' \'unsafe-inline\'; script-src-elem \'self\' domain.com \'unsafe-inline\' \'nonce-'.$nonce.'\'; style-src \'self\' domain.com \'unsafe-inline\' \'nonce-'.$nonce.'\'; style-src-elem \'self\' domain.com \'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\' domain-report-only.com \'unsafe-inline\' \'nonce-'.$nonce.'\'; style-src \'self\' domain-report-only.com \'unsafe-inline\' \'nonce-'.$nonce.'\'; style-src-elem \'self\' domain-report-only.com \'unsafe-inline\' \'nonce-'.$nonce.'\'', 'X-Content-Security-Policy' => null],
],
[
$nonce,
diff --git a/src/Symfony/Bundle/WebServerBundle/Command/ServerLogCommand.php b/src/Symfony/Bundle/WebServerBundle/Command/ServerLogCommand.php
index 1eb1916a3f129..57feb65cc1f80 100644
--- a/src/Symfony/Bundle/WebServerBundle/Command/ServerLogCommand.php
+++ b/src/Symfony/Bundle/WebServerBundle/Command/ServerLogCommand.php
@@ -106,7 +106,7 @@ protected function execute(InputInterface $input, OutputInterface $output)
}
if (!$socket = stream_socket_server($host, $errno, $errstr)) {
- throw new RuntimeException(sprintf('Server start failed on "%s": %s %s.', $host, $errstr, $errno));
+ throw new RuntimeException(sprintf('Server start failed on "%s": '.$errstr.' '.$errno, $host));
}
foreach ($this->getLogs($socket) as $clientId => $message) {
diff --git a/src/Symfony/Component/Asset/VersionStrategy/JsonManifestVersionStrategy.php b/src/Symfony/Component/Asset/VersionStrategy/JsonManifestVersionStrategy.php
index 0130ed3d02866..99413dd1167bf 100644
--- a/src/Symfony/Component/Asset/VersionStrategy/JsonManifestVersionStrategy.php
+++ b/src/Symfony/Component/Asset/VersionStrategy/JsonManifestVersionStrategy.php
@@ -59,7 +59,7 @@ private function getManifestPath(string $path): ?string
$this->manifestData = json_decode(file_get_contents($this->manifestPath), true);
if (0 < json_last_error()) {
- throw new \RuntimeException(sprintf('Error parsing JSON from asset manifest file "%s" - %s.', $this->manifestPath, json_last_error_msg()));
+ throw new \RuntimeException(sprintf('Error parsing JSON from asset manifest file "%s": '.json_last_error_msg(), $this->manifestPath));
}
}
diff --git a/src/Symfony/Component/Cache/Adapter/AbstractAdapter.php b/src/Symfony/Component/Cache/Adapter/AbstractAdapter.php
index 79ecf3ce995d9..25ce2d923570f 100644
--- a/src/Symfony/Component/Cache/Adapter/AbstractAdapter.php
+++ b/src/Symfony/Component/Cache/Adapter/AbstractAdapter.php
@@ -13,7 +13,6 @@
use Psr\Log\LoggerAwareInterface;
use Psr\Log\LoggerInterface;
-use Psr\Log\NullLogger;
use Symfony\Component\Cache\CacheItem;
use Symfony\Component\Cache\Exception\InvalidArgumentException;
use Symfony\Component\Cache\ResettableInterface;
@@ -117,10 +116,12 @@ public static function createSystemCache($namespace, $defaultLifetime, $version,
return $opcache;
}
+ if (\in_array(\PHP_SAPI, ['cli', 'phpdbg'], true) && !filter_var(ini_get('apc.enable_cli'), FILTER_VALIDATE_BOOLEAN)) {
+ return $opcache;
+ }
+
$apcu = new ApcuAdapter($namespace, (int) $defaultLifetime / 5, $version);
- if ('cli' === \PHP_SAPI && !filter_var(ini_get('apc.enable_cli'), FILTER_VALIDATE_BOOLEAN)) {
- $apcu->setLogger(new NullLogger());
- } elseif (null !== $logger) {
+ if (null !== $logger) {
$apcu->setLogger($logger);
}
diff --git a/src/Symfony/Component/Cache/Adapter/ChainAdapter.php b/src/Symfony/Component/Cache/Adapter/ChainAdapter.php
index 63e97a8d0261e..05b8fc9c4be6e 100644
--- a/src/Symfony/Component/Cache/Adapter/ChainAdapter.php
+++ b/src/Symfony/Component/Cache/Adapter/ChainAdapter.php
@@ -51,6 +51,9 @@ public function __construct(array $adapters, int $defaultLifetime = 0)
if (!$adapter instanceof CacheItemPoolInterface) {
throw new InvalidArgumentException(sprintf('The class "%s" does not implement the "%s" interface.', \get_class($adapter), CacheItemPoolInterface::class));
}
+ if (\in_array(\PHP_SAPI, ['cli', 'phpdbg'], true) && $adapter instanceof ApcuAdapter && !filter_var(ini_get('apc.enable_cli'), FILTER_VALIDATE_BOOLEAN)) {
+ continue; // skip putting APCu in the chain when the backend is disabled
+ }
if ($adapter instanceof AdapterInterface) {
$this->adapters[] = $adapter;
diff --git a/src/Symfony/Component/Cache/Adapter/FilesystemTagAwareAdapter.php b/src/Symfony/Component/Cache/Adapter/FilesystemTagAwareAdapter.php
index d9a1ad39ac6ef..2ce9eeb977fc5 100644
--- a/src/Symfony/Component/Cache/Adapter/FilesystemTagAwareAdapter.php
+++ b/src/Symfony/Component/Cache/Adapter/FilesystemTagAwareAdapter.php
@@ -107,7 +107,7 @@ protected function doSave(array $values, ?int $lifetime, array $addTagData = [],
$file = $this->getFile($id);
- if (!@symlink($file, $this->getFile($id, true, $tagFolder))) {
+ if (!@symlink($file, $tagLink = $this->getFile($id, true, $tagFolder)) && !is_link($tagLink)) {
@unlink($file);
$failed[] = $id;
}
diff --git a/src/Symfony/Component/Cache/Adapter/TraceableAdapter.php b/src/Symfony/Component/Cache/Adapter/TraceableAdapter.php
index 7e9dac803c32d..c296c8847c5cf 100644
--- a/src/Symfony/Component/Cache/Adapter/TraceableAdapter.php
+++ b/src/Symfony/Component/Cache/Adapter/TraceableAdapter.php
@@ -247,15 +247,11 @@ public function prune()
*/
public function reset()
{
- if (!$this->pool instanceof ResetInterface) {
- return;
- }
- $event = $this->start(__FUNCTION__);
- try {
+ if ($this->pool instanceof ResetInterface) {
$this->pool->reset();
- } finally {
- $event->end = microtime(true);
}
+
+ $this->clearCalls();
}
/**
diff --git a/src/Symfony/Component/Cache/DependencyInjection/CacheCollectorPass.php b/src/Symfony/Component/Cache/DependencyInjection/CacheCollectorPass.php
index 6193d34798757..b534e5dc8a7fb 100644
--- a/src/Symfony/Component/Cache/DependencyInjection/CacheCollectorPass.php
+++ b/src/Symfony/Component/Cache/DependencyInjection/CacheCollectorPass.php
@@ -46,27 +46,38 @@ public function process(ContainerBuilder $container)
return;
}
- $collectorDefinition = $container->getDefinition($this->dataCollectorCacheId);
foreach ($container->findTaggedServiceIds($this->cachePoolTag) as $id => $attributes) {
- $definition = $container->getDefinition($id);
- if ($definition->isAbstract()) {
- continue;
+ $this->addToCollector($id, $container);
+
+ if (($attributes[0]['name'] ?? $id) !== $id) {
+ $this->addToCollector($attributes[0]['name'], $container);
}
+ }
+ }
- $recorder = new Definition(is_subclass_of($definition->getClass(), TagAwareAdapterInterface::class) ? TraceableTagAwareAdapter::class : TraceableAdapter::class);
- $recorder->setTags($definition->getTags());
+ private function addToCollector(string $id, ContainerBuilder $container)
+ {
+ $definition = $container->getDefinition($id);
+ if ($definition->isAbstract()) {
+ return;
+ }
+
+ $collectorDefinition = $container->getDefinition($this->dataCollectorCacheId);
+ $recorder = new Definition(is_subclass_of($definition->getClass(), TagAwareAdapterInterface::class) ? TraceableTagAwareAdapter::class : TraceableAdapter::class);
+ $recorder->setTags($definition->getTags());
+ if (!$definition->isPublic() || !$definition->isPrivate()) {
$recorder->setPublic($definition->isPublic());
- $recorder->setArguments([new Reference($innerId = $id.$this->cachePoolRecorderInnerSuffix)]);
+ }
+ $recorder->setArguments([new Reference($innerId = $id.$this->cachePoolRecorderInnerSuffix)]);
- $definition->setTags([]);
- $definition->setPublic(false);
+ $definition->setTags([]);
+ $definition->setPublic(false);
- $container->setDefinition($innerId, $definition);
- $container->setDefinition($id, $recorder);
+ $container->setDefinition($innerId, $definition);
+ $container->setDefinition($id, $recorder);
- // Tell the collector to add the new instance
- $collectorDefinition->addMethodCall('addInstance', [$id, new Reference($id)]);
- $collectorDefinition->setPublic(false);
- }
+ // Tell the collector to add the new instance
+ $collectorDefinition->addMethodCall('addInstance', [$id, new Reference($id)]);
+ $collectorDefinition->setPublic(false);
}
}
diff --git a/src/Symfony/Component/Cache/Tests/DependencyInjection/CacheCollectorPassTest.php b/src/Symfony/Component/Cache/Tests/DependencyInjection/CacheCollectorPassTest.php
index 7e77491de867b..8aff19fc3e14b 100644
--- a/src/Symfony/Component/Cache/Tests/DependencyInjection/CacheCollectorPassTest.php
+++ b/src/Symfony/Component/Cache/Tests/DependencyInjection/CacheCollectorPassTest.php
@@ -13,6 +13,7 @@
use PHPUnit\Framework\TestCase;
use Symfony\Component\Cache\Adapter\FilesystemAdapter;
+use Symfony\Component\Cache\Adapter\PhpArrayAdapter;
use Symfony\Component\Cache\Adapter\TagAwareAdapter;
use Symfony\Component\Cache\Adapter\TraceableAdapter;
use Symfony\Component\Cache\Adapter\TraceableTagAwareAdapter;
@@ -34,16 +35,29 @@ public function testProcess()
->addArgument(new Reference('fs'))
->addTag('cache.pool');
+ $container
+ ->register('.php.inner', PhpArrayAdapter::class)
+ ->addTag('cache.pool', ['name' => 'php']);
+ $container
+ ->register('php', TagAwareAdapter::class)
+ ->addArgument(new Reference('.php.inner'));
+
$collector = $container->register('data_collector.cache', CacheDataCollector::class);
(new CacheCollectorPass())->process($container);
$this->assertEquals([
['addInstance', ['fs', new Reference('fs')]],
['addInstance', ['tagged_fs', new Reference('tagged_fs')]],
+ ['addInstance', ['.php.inner', new Reference('.php.inner')]],
+ ['addInstance', ['php', new Reference('php')]],
], $collector->getMethodCalls());
$this->assertSame(TraceableAdapter::class, $container->findDefinition('fs')->getClass());
$this->assertSame(TraceableTagAwareAdapter::class, $container->getDefinition('tagged_fs')->getClass());
+
+ $this->assertSame(TraceableAdapter::class, $container->findDefinition('.php.inner')->getClass());
+ $this->assertSame(TraceableTagAwareAdapter::class, $container->getDefinition('php')->getClass());
+
$this->assertFalse($collector->isPublic(), 'The "data_collector.cache" should be private after processing');
}
}
diff --git a/src/Symfony/Component/Cache/Tests/Traits/TagAwareTestTrait.php b/src/Symfony/Component/Cache/Tests/Traits/TagAwareTestTrait.php
index 7f1544af5b41d..8b687b5a7564c 100644
--- a/src/Symfony/Component/Cache/Tests/Traits/TagAwareTestTrait.php
+++ b/src/Symfony/Component/Cache/Tests/Traits/TagAwareTestTrait.php
@@ -155,4 +155,42 @@ public function testGetMetadata()
$i = $pool->getItem('k');
$this->assertSame(['foo' => 'foo'], $i->getMetadata()[CacheItem::METADATA_TAGS]);
}
+
+ public function testRefreshAfterExpires()
+ {
+ $pool = $this->createCachePool();
+ $pool->clear();
+
+ $cacheItem = $pool->getItem('test');
+
+ $this->assertFalse($cacheItem->isHit());
+
+ // write cache with expires
+ $cacheItem->set('test');
+ $cacheItem->tag('1234');
+ $cacheItem->expiresAfter(1);
+
+ $pool->save($cacheItem);
+
+ $cacheItem = $pool->getItem('test');
+ $this->assertTrue($cacheItem->isHit());
+
+ // wait until expired
+ sleep(2);
+
+ // item should not longer be a hit
+ $cacheItem = $pool->getItem('test');
+ $this->assertFalse($cacheItem->isHit());
+
+ // update expired item
+ $cacheItem->set('test');
+ $cacheItem->tag('1234');
+ $cacheItem->expiresAfter(1);
+
+ $pool->save($cacheItem);
+
+ // item should be again a hit
+ $cacheItem = $pool->getItem('test');
+ $this->assertTrue($cacheItem->isHit());
+ }
}
diff --git a/src/Symfony/Component/Cache/Traits/MemcachedTrait.php b/src/Symfony/Component/Cache/Traits/MemcachedTrait.php
index 507bc1fbfb123..ca26f7e4ff7fc 100644
--- a/src/Symfony/Component/Cache/Traits/MemcachedTrait.php
+++ b/src/Symfony/Component/Cache/Traits/MemcachedTrait.php
@@ -303,7 +303,7 @@ private function checkResultCode($result)
return $result;
}
- throw new CacheException(sprintf('MemcachedAdapter client error: %s.', strtolower($this->client->getResultMessage())));
+ throw new CacheException('MemcachedAdapter client error: '.strtolower($this->client->getResultMessage()));
}
private function getClient(): \Memcached
diff --git a/src/Symfony/Component/Config/Definition/BaseNode.php b/src/Symfony/Component/Config/Definition/BaseNode.php
index ddf2f1155d6ba..edfb46e628347 100644
--- a/src/Symfony/Component/Config/Definition/BaseNode.php
+++ b/src/Symfony/Component/Config/Definition/BaseNode.php
@@ -438,7 +438,7 @@ final public function finalize($value)
throw $e;
} catch (\Exception $e) {
- throw new InvalidConfigurationException(sprintf('Invalid configuration for path "%s": %s.', $this->getPath(), $e->getMessage()), $e->getCode(), $e);
+ throw new InvalidConfigurationException(sprintf('Invalid configuration for path "%s": '.$e->getMessage(), $this->getPath()), $e->getCode(), $e);
}
}
diff --git a/src/Symfony/Component/Config/Tests/Resource/DirectoryResourceTest.php b/src/Symfony/Component/Config/Tests/Resource/DirectoryResourceTest.php
index aaadc8c4f430b..fbaed34a212a6 100644
--- a/src/Symfony/Component/Config/Tests/Resource/DirectoryResourceTest.php
+++ b/src/Symfony/Component/Config/Tests/Resource/DirectoryResourceTest.php
@@ -66,7 +66,7 @@ public function testGetPattern()
public function testResourceDoesNotExist()
{
$this->expectException('InvalidArgumentException');
- $this->expectExceptionMessageRegExp('/The directory ".*" does not exist./');
+ $this->expectExceptionMessageMatches('/The directory ".*" does not exist./');
new DirectoryResource('/____foo/foobar'.mt_rand(1, 999999));
}
diff --git a/src/Symfony/Component/Config/Tests/Resource/FileResourceTest.php b/src/Symfony/Component/Config/Tests/Resource/FileResourceTest.php
index f85a820fb95bb..e3a45566c2617 100644
--- a/src/Symfony/Component/Config/Tests/Resource/FileResourceTest.php
+++ b/src/Symfony/Component/Config/Tests/Resource/FileResourceTest.php
@@ -56,7 +56,7 @@ public function testToString()
public function testResourceDoesNotExist()
{
$this->expectException('InvalidArgumentException');
- $this->expectExceptionMessageRegExp('/The file ".*" does not exist./');
+ $this->expectExceptionMessageMatches('/The file ".*" does not exist./');
new FileResource('/____foo/foobar'.mt_rand(1, 999999));
}
diff --git a/src/Symfony/Component/DependencyInjection/Compiler/AbstractRecursivePass.php b/src/Symfony/Component/DependencyInjection/Compiler/AbstractRecursivePass.php
index c56feafbcfb03..75813ba4976b0 100644
--- a/src/Symfony/Component/DependencyInjection/Compiler/AbstractRecursivePass.php
+++ b/src/Symfony/Component/DependencyInjection/Compiler/AbstractRecursivePass.php
@@ -153,7 +153,7 @@ protected function getConstructor(Definition $definition, $required)
throw new RuntimeException(sprintf('Invalid service "%s": class "%s" does not exist.', $this->currentId, $class));
}
} catch (\ReflectionException $e) {
- throw new RuntimeException(sprintf('Invalid service "%s": %s.', $this->currentId, lcfirst(rtrim($e->getMessage(), '.'))));
+ throw new RuntimeException(sprintf('Invalid service "%s": '.lcfirst($e->getMessage()), $this->currentId));
}
if (!$r = $r->getConstructor()) {
if ($required) {
diff --git a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php
index 982472f5adf07..4a84cd1a24f11 100644
--- a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php
+++ b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php
@@ -1700,7 +1700,11 @@ private function dumpValue($value, bool $interpolate = true): string
if (!$v) {
continue;
}
- $definition = $this->container->findDefinition($id = (string) $v);
+ $id = (string) $v;
+ while ($this->container->hasAlias($id)) {
+ $id = (string) $this->container->getAlias($id);
+ }
+ $definition = $this->container->getDefinition($id);
$load = !($definition->hasErrors() && $e = $definition->getErrors()) ? $this->asFiles && !$this->inlineFactories && !$this->isHotPath($definition) : reset($e);
$serviceMap .= sprintf("\n %s => [%s, %s, %s, %s],",
$this->export($k),
diff --git a/src/Symfony/Component/DependencyInjection/Loader/Configurator/AbstractServiceConfigurator.php b/src/Symfony/Component/DependencyInjection/Loader/Configurator/AbstractServiceConfigurator.php
index 9d3305e8ee08c..2257edaef6daf 100644
--- a/src/Symfony/Component/DependencyInjection/Loader/Configurator/AbstractServiceConfigurator.php
+++ b/src/Symfony/Component/DependencyInjection/Loader/Configurator/AbstractServiceConfigurator.php
@@ -42,7 +42,7 @@ public function __destruct()
/**
* Registers a service.
*/
- final public function set(string $id, string $class = null): ServiceConfigurator
+ final public function set(?string $id, string $class = null): ServiceConfigurator
{
$this->__destruct();
diff --git a/src/Symfony/Component/DependencyInjection/Loader/Configurator/AliasConfigurator.php b/src/Symfony/Component/DependencyInjection/Loader/Configurator/AliasConfigurator.php
index cb00f58c049d4..8ac635758bb74 100644
--- a/src/Symfony/Component/DependencyInjection/Loader/Configurator/AliasConfigurator.php
+++ b/src/Symfony/Component/DependencyInjection/Loader/Configurator/AliasConfigurator.php
@@ -20,6 +20,7 @@ class AliasConfigurator extends AbstractServiceConfigurator
{
const FACTORY = 'alias';
+ use Traits\DeprecateTrait;
use Traits\PublicTrait;
public function __construct(ServicesConfigurator $parent, Alias $alias)
diff --git a/src/Symfony/Component/DependencyInjection/Loader/Configurator/InlineServiceConfigurator.php b/src/Symfony/Component/DependencyInjection/Loader/Configurator/InlineServiceConfigurator.php
index 362b374e55970..ea5db9778bad8 100644
--- a/src/Symfony/Component/DependencyInjection/Loader/Configurator/InlineServiceConfigurator.php
+++ b/src/Symfony/Component/DependencyInjection/Loader/Configurator/InlineServiceConfigurator.php
@@ -29,6 +29,10 @@ class InlineServiceConfigurator extends AbstractConfigurator
use Traits\ParentTrait;
use Traits\TagTrait;
+ private $id = '[inline]';
+ private $allowParent = true;
+ private $path = null;
+
public function __construct(Definition $definition)
{
$this->definition = $definition;
diff --git a/src/Symfony/Component/DependencyInjection/Loader/Configurator/PrototypeConfigurator.php b/src/Symfony/Component/DependencyInjection/Loader/Configurator/PrototypeConfigurator.php
index 3cd56e0f19a48..1b740cee74461 100644
--- a/src/Symfony/Component/DependencyInjection/Loader/Configurator/PrototypeConfigurator.php
+++ b/src/Symfony/Component/DependencyInjection/Loader/Configurator/PrototypeConfigurator.php
@@ -45,10 +45,13 @@ class PrototypeConfigurator extends AbstractServiceConfigurator
public function __construct(ServicesConfigurator $parent, PhpFileLoader $loader, Definition $defaults, string $namespace, string $resource, bool $allowParent)
{
$definition = new Definition();
- $definition->setPublic($defaults->isPublic());
+ if (!$defaults->isPublic() || !$defaults->isPrivate()) {
+ $definition->setPublic($defaults->isPublic());
+ }
$definition->setAutowired($defaults->isAutowired());
$definition->setAutoconfigured($defaults->isAutoconfigured());
- $definition->setBindings($defaults->getBindings());
+ // deep clone, to avoid multiple process of the same instance in the passes
+ $definition->setBindings(unserialize(serialize($defaults->getBindings())));
$definition->setChanges([]);
$this->loader = $loader;
diff --git a/src/Symfony/Component/DependencyInjection/Loader/Configurator/ServicesConfigurator.php b/src/Symfony/Component/DependencyInjection/Loader/Configurator/ServicesConfigurator.php
index f0fdde81c33a4..237099d08cd7e 100644
--- a/src/Symfony/Component/DependencyInjection/Loader/Configurator/ServicesConfigurator.php
+++ b/src/Symfony/Component/DependencyInjection/Loader/Configurator/ServicesConfigurator.php
@@ -83,13 +83,14 @@ final public function set(?string $id, string $class = null): ServiceConfigurato
$id = sprintf('.%d_%s', ++$this->anonymousCount, preg_replace('/^.*\\\\/', '', $class).'~'.$this->anonymousHash);
$definition->setPublic(false);
- } else {
- $definition->setPublic($defaults->isPublic());
+ } elseif (!$defaults->isPublic() || !$defaults->isPrivate()) {
+ $definition->setPublic($defaults->isPublic() && !$defaults->isPrivate());
}
$definition->setAutowired($defaults->isAutowired());
$definition->setAutoconfigured($defaults->isAutoconfigured());
- $definition->setBindings($defaults->getBindings());
+ // deep clone, to avoid multiple process of the same instance in the passes
+ $definition->setBindings(unserialize(serialize($defaults->getBindings())));
$definition->setChanges([]);
$configurator = new ServiceConfigurator($this->container, $this->instanceof, $allowParent, $this, $definition, $id, $defaults->getTags(), $this->path);
@@ -103,7 +104,10 @@ final public function set(?string $id, string $class = null): ServiceConfigurato
final public function alias(string $id, string $referencedId): AliasConfigurator
{
$ref = static::processValue($referencedId, true);
- $alias = new Alias((string) $ref, $this->defaults->isPublic());
+ $alias = new Alias((string) $ref);
+ if (!$this->defaults->isPublic() || !$this->defaults->isPrivate()) {
+ $alias->setPublic($this->defaults->isPublic());
+ }
$this->container->setAlias($id, $alias);
return new AliasConfigurator($this, $alias);
diff --git a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php
index a31959e456552..b35952e4522fb 100644
--- a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php
+++ b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php
@@ -299,7 +299,7 @@ private function parseDefaults(array &$content, string $file): array
private function isUsingShortSyntax(array $service): bool
{
foreach ($service as $key => $value) {
- if (\is_string($key) && ('' === $key || '$' !== $key[0])) {
+ if (\is_string($key) && ('' === $key || ('$' !== $key[0] && false === strpos($key, '\\')))) {
return false;
}
}
@@ -345,7 +345,7 @@ private function parseDefinition(string $id, $service, string $file, array $defa
if (isset($service['alias'])) {
$this->container->setAlias($id, $alias = new Alias($service['alias']));
- if (\array_key_exists('public', $service)) {
+ if (isset($service['public'])) {
$alias->setPublic($service['public']);
} elseif (isset($defaults['public'])) {
$alias->setPublic($defaults['public']);
@@ -690,7 +690,7 @@ protected function loadFile($file)
try {
$configuration = $this->yamlParser->parseFile($file, Yaml::PARSE_CONSTANT | Yaml::PARSE_CUSTOM_TAGS);
} catch (ParseException $e) {
- throw new InvalidArgumentException(sprintf('The file "%s" does not contain valid YAML: "%s".', $file, $e->getMessage()), 0, $e);
+ throw new InvalidArgumentException(sprintf('The file "%s" does not contain valid YAML: '.$e->getMessage(), $file), 0, $e);
}
return $this->validate($configuration, $file);
@@ -763,7 +763,7 @@ private function resolveServices($value, string $file, bool $isParameter = false
if (\is_array($argument) && isset($argument['tag']) && $argument['tag']) {
if ($diff = array_diff(array_keys($argument), ['tag', 'index_by', 'default_index_method', 'default_priority_method'])) {
- throw new InvalidArgumentException(sprintf('"!%s" tag contains unsupported key "%s"; supported ones are "tag", "index_by", "default_index_method", and "default_priority_method".', $value->getTag(), implode('"", "', $diff)));
+ throw new InvalidArgumentException(sprintf('"!%s" tag contains unsupported key "%s"; supported ones are "tag", "index_by", "default_index_method", and "default_priority_method".', $value->getTag(), implode('", "', $diff)));
}
$argument = new TaggedIteratorArgument($argument['tag'], $argument['index_by'] ?? null, $argument['default_index_method'] ?? null, $forLocator, $argument['default_priority_method'] ?? null);
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/RegisterServiceSubscribersPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/RegisterServiceSubscribersPassTest.php
index 77b1969f60b1c..d8e7d62d478f4 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/RegisterServiceSubscribersPassTest.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/RegisterServiceSubscribersPassTest.php
@@ -88,6 +88,7 @@ public function testNoAttributes()
CustomDefinition::class => new ServiceClosureArgument(new TypedReference(CustomDefinition::class, CustomDefinition::class, ContainerInterface::IGNORE_ON_INVALID_REFERENCE)),
'bar' => new ServiceClosureArgument(new TypedReference(CustomDefinition::class, CustomDefinition::class, ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, 'bar')),
'baz' => new ServiceClosureArgument(new TypedReference(CustomDefinition::class, CustomDefinition::class, ContainerInterface::IGNORE_ON_INVALID_REFERENCE, 'baz')),
+ 'late_alias' => new ServiceClosureArgument(new TypedReference(TestDefinition1::class, TestDefinition1::class, ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, 'late_alias')),
];
$this->assertEquals($expected, $container->getDefinition((string) $locator->getFactory()[0])->getArgument(0));
@@ -118,6 +119,7 @@ public function testWithAttributes()
CustomDefinition::class => new ServiceClosureArgument(new TypedReference(CustomDefinition::class, CustomDefinition::class, ContainerInterface::IGNORE_ON_INVALID_REFERENCE)),
'bar' => new ServiceClosureArgument(new TypedReference('bar', CustomDefinition::class, ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, 'bar')),
'baz' => new ServiceClosureArgument(new TypedReference(CustomDefinition::class, CustomDefinition::class, ContainerInterface::IGNORE_ON_INVALID_REFERENCE, 'baz')),
+ 'late_alias' => new ServiceClosureArgument(new TypedReference(TestDefinition1::class, TestDefinition1::class, ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, 'late_alias')),
];
$this->assertEquals($expected, $container->getDefinition((string) $locator->getFactory()[0])->getArgument(0));
@@ -260,6 +262,7 @@ public function testBinding()
CustomDefinition::class => new ServiceClosureArgument(new TypedReference(CustomDefinition::class, CustomDefinition::class, ContainerInterface::IGNORE_ON_INVALID_REFERENCE)),
'bar' => new ServiceClosureArgument(new TypedReference(CustomDefinition::class, CustomDefinition::class, ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, 'bar')),
'baz' => new ServiceClosureArgument(new TypedReference(CustomDefinition::class, CustomDefinition::class, ContainerInterface::IGNORE_ON_INVALID_REFERENCE, 'baz')),
+ 'late_alias' => new ServiceClosureArgument(new TypedReference(TestDefinition1::class, TestDefinition1::class, ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, 'late_alias')),
];
$this->assertEquals($expected, $container->getDefinition((string) $locator->getFactory()[0])->getArgument(0));
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveChildDefinitionsPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveChildDefinitionsPassTest.php
index dfe37445d4e88..dd58f90990310 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveChildDefinitionsPassTest.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveChildDefinitionsPassTest.php
@@ -400,7 +400,7 @@ protected function process(ContainerBuilder $container)
public function testProcessDetectsChildDefinitionIndirectCircularReference()
{
$this->expectException('Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException');
- $this->expectExceptionMessageRegExp('/^Circular reference detected for service "c", path: "c -> b -> a -> c"./');
+ $this->expectExceptionMessageMatches('/^Circular reference detected for service "c", path: "c -> b -> a -> c"./');
$container = new ContainerBuilder();
$container->register('a');
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveInstanceofConditionalsPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveInstanceofConditionalsPassTest.php
index ebd8f3df1024a..3acfbed776f1f 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveInstanceofConditionalsPassTest.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveInstanceofConditionalsPassTest.php
@@ -233,7 +233,7 @@ public function testProcessForAutoconfiguredCalls()
public function testProcessThrowsExceptionForArguments()
{
$this->expectException('Symfony\Component\DependencyInjection\Exception\InvalidArgumentException');
- $this->expectExceptionMessageRegExp('/Autoconfigured instanceof for type "PHPUnit[\\\\_]Framework[\\\\_]TestCase" defines arguments but these are not supported and should be removed\./');
+ $this->expectExceptionMessageMatches('/Autoconfigured instanceof for type "PHPUnit[\\\\_]Framework[\\\\_]TestCase" defines arguments but these are not supported and should be removed\./');
$container = new ContainerBuilder();
$container->registerForAutoconfiguration(parent::class)
->addArgument('bar');
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ValidateEnvPlaceholdersPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ValidateEnvPlaceholdersPassTest.php
index 457c7a7e5cf03..8b50689dc0f5b 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ValidateEnvPlaceholdersPassTest.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ValidateEnvPlaceholdersPassTest.php
@@ -105,7 +105,7 @@ public function testInvalidEnvInConfig()
public function testNulledEnvInConfig()
{
$this->expectException('Symfony\Component\Config\Definition\Exception\InvalidTypeException');
- $this->expectExceptionMessageRegexp('/^Invalid type for path "env_extension\.int_node"\. Expected "?int"?, but got (NULL|"null")\.$/');
+ $this->expectExceptionMessageMatches('/^Invalid type for path "env_extension\.int_node"\. Expected "?int"?, but got (NULL|"null")\.$/');
$container = new ContainerBuilder();
$container->setParameter('env(NULLED)', null);
$container->registerExtension(new EnvExtension());
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php
index 60f1ab1d4f070..51a88705d6fe6 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php
@@ -19,6 +19,8 @@
use Symfony\Component\DependencyInjection\Argument\RewindableGenerator;
use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
use Symfony\Component\DependencyInjection\Argument\ServiceLocator as ArgumentServiceLocator;
+use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
+use Symfony\Component\DependencyInjection\Compiler\PassConfig;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\ContainerInterface as SymfonyContainerInterface;
use Symfony\Component\DependencyInjection\Definition;
@@ -35,6 +37,7 @@
use Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition;
use Symfony\Component\DependencyInjection\Tests\Fixtures\ScalarFactory;
use Symfony\Component\DependencyInjection\Tests\Fixtures\StubbedTranslator;
+use Symfony\Component\DependencyInjection\Tests\Fixtures\TestDefinition1;
use Symfony\Component\DependencyInjection\Tests\Fixtures\TestServiceSubscriber;
use Symfony\Component\DependencyInjection\TypedReference;
use Symfony\Component\DependencyInjection\Variable;
@@ -906,6 +909,20 @@ public function testServiceSubscriber()
$container->register(CustomDefinition::class, CustomDefinition::class)
->setPublic(false);
+
+ $container->register(TestDefinition1::class, TestDefinition1::class)->setPublic(true);
+
+ $container->addCompilerPass(new class() implements CompilerPassInterface {
+ /**
+ * {@inheritdoc}
+ */
+ public function process(ContainerBuilder $container)
+ {
+ $container->setDefinition('late_alias', new Definition(TestDefinition1::class));
+ $container->setAlias(TestDefinition1::class, 'late_alias');
+ }
+ }, PassConfig::TYPE_AFTER_REMOVING);
+
$container->compile();
$dumper = new PhpDumper($container);
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/TestServiceSubscriber.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/TestServiceSubscriber.php
index 6876fc5cca53d..f4a4bdda6451e 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/TestServiceSubscriber.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/TestServiceSubscriber.php
@@ -22,6 +22,7 @@ public static function getSubscribedServices(): array
'?'.CustomDefinition::class,
'bar' => CustomDefinition::class,
'baz' => '?'.CustomDefinition::class,
+ 'late_alias' => TestDefinition1::class,
];
}
}
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/anonymous.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/anonymous.php
index 112b162bcaca9..65605bcccff89 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/anonymous.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/anonymous.php
@@ -7,7 +7,7 @@
use Symfony\Component\DependencyInjection\Tests\Fixtures\StdClassDecorator;
return function (ContainerConfigurator $c) {
- $s = $c->services();
+ $s = $c->services()->defaults()->public();
$s->set('decorated', stdClass::class);
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/basic.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/basic.php
index a9e250b9213a1..89132620ec3c8 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/basic.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/basic.php
@@ -5,7 +5,7 @@
use App\BarService;
return function (ContainerConfigurator $c) {
- $s = $c->services();
+ $s = $c->services()->defaults()->public();
$s->set(BarService::class)
->args([inline('FooClass')]);
};
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/child.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/child.php
index 8a5f2431d0abf..d2735bcfb1bfb 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/child.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/child.php
@@ -6,12 +6,14 @@
return function (ContainerConfigurator $c) {
$c->services()
- ->set('bar', 'Class1')
+ ->set('bar', 'Class1')->public()
->set(BarService::class)
+ ->public()
->abstract(true)
->lazy()
->set('foo')
->parent(BarService::class)
+ ->public()
->decorate('bar', 'b', 1)
->args([ref('b')])
->class('Class2')
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/defaults.expected.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/defaults.expected.yml
index a534f7267a078..2b389b694590a 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/defaults.expected.yml
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/defaults.expected.yml
@@ -18,7 +18,7 @@ services:
arguments: ['@bar']
bar:
class: Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\Foo
- public: false
+ public: true
tags:
- { name: t, a: b }
autowire: true
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/defaults.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/defaults.php
index 2889d3fbb6400..b04413d2102f3 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/defaults.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/defaults.php
@@ -14,7 +14,7 @@
->autowire()
->tag('t', ['a' => 'b'])
->bind(Foo::class, ref('bar'))
- ->private();
+ ->public();
$s->set(Foo::class)->args([ref('bar')])->public();
$s->set('bar', Foo::class)->call('setFoo')->autoconfigure(false);
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/instanceof.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/instanceof.php
index 0d6aac7a0051e..d9f62a67ec9a8 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/instanceof.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/instanceof.php
@@ -6,7 +6,7 @@
use Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype;
return function (ContainerConfigurator $c) {
- $s = $c->services();
+ $s = $c->services()->defaults()->public();
$s->instanceof(Prototype\Foo::class)
->property('p', 0)
->call('setFoo', [ref('foo')])
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/lazy_fqcn.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/lazy_fqcn.php
index 7cde4ef2d0699..d23f995993424 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/lazy_fqcn.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/lazy_fqcn.php
@@ -3,6 +3,6 @@
namespace Symfony\Component\DependencyInjection\Loader\Configurator;
return function (ContainerConfigurator $c) {
- $di = $c->services();
+ $di = $c->services()->defaults()->public();
$di->set('foo', 'stdClass')->lazy('SomeInterface');
};
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/object.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/object.php
index daf4682bf7efa..59c4d43fd2b11 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/object.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/object.php
@@ -7,7 +7,7 @@
return new class() {
public function __invoke(ContainerConfigurator $c)
{
- $s = $c->services();
+ $s = $c->services()->defaults()->public();
$s->set(BarService::class)
->args([inline('FooClass')]);
}
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/php7.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/php7.php
index 7711624e6f0f6..98134fb230409 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/php7.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/php7.php
@@ -9,7 +9,7 @@
('foo', 'Foo')
('bar', 'Bar')
;
- $c->services()
+ $c->services()->defaults()->public()
(Foo::class)
->arg('$bar', ref('bar'))
->public()
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/prototype.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/prototype.php
index 6a7d859df1fd6..f5baa0f642738 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/prototype.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/prototype.php
@@ -8,6 +8,7 @@
$di = $c->services()->defaults()
->tag('baz');
$di->load(Prototype::class.'\\', '../Prototype')
+ ->public()
->autoconfigure()
->exclude('../Prototype/{OtherDir,BadClasses,SinglyImplementedInterface}')
->factory('f')
@@ -17,6 +18,6 @@
->autoconfigure(false)
->tag('foo')
->parent('foo');
- $di->set('foo')->lazy()->abstract();
+ $di->set('foo')->lazy()->abstract()->public();
$di->get(Prototype\Foo::class)->lazy(false);
};
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/prototype_array.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/prototype_array.php
index 501baa3c10ab7..026ee6c1ab693 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/prototype_array.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/prototype_array.php
@@ -8,6 +8,7 @@
$di = $c->services()->defaults()
->tag('baz');
$di->load(Prototype::class.'\\', '../Prototype')
+ ->public()
->autoconfigure()
->exclude(['../Prototype/OtherDir', '../Prototype/BadClasses', '../Prototype/SinglyImplementedInterface'])
->factory('f')
@@ -17,6 +18,6 @@
->autoconfigure(false)
->tag('foo')
->parent('foo');
- $di->set('foo')->lazy()->abstract();
+ $di->set('foo')->lazy()->abstract()->public();
$di->get(Prototype\Foo::class)->lazy(false);
};
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/services9.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/services9.php
index 7c070ef64f450..8691d48efe257 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/services9.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/config/services9.php
@@ -16,8 +16,7 @@
$p->set('foo_class', FooClass::class)
->set('foo', 'bar');
- $s = $c->services()->defaults()
- ->public();
+ $s = $c->services()->defaults()->public();
$s->set('foo')
->args(['foo', ref('foo.baz'), ['%foo%' => 'foo is %foo%', 'foobar' => '%foo%'], true, ref('service_container')])
->class(FooClass::class)
@@ -127,12 +126,10 @@
->tag('foo');
$s->set('tagged_iterator', 'Bar')
- ->public()
->args([tagged_iterator('foo')]);
$s->set('runtime_error', 'stdClass')
- ->args([new Reference('errored_definition', ContainerInterface::RUNTIME_EXCEPTION_ON_INVALID_REFERENCE)])
- ->public();
+ ->args([new Reference('errored_definition', ContainerInterface::RUNTIME_EXCEPTION_ON_INVALID_REFERENCE)]);
$s->set('errored_definition', 'stdClass')->private();
$s->alias('alias_for_foo', 'foo')->private()->public();
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_subscriber.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_subscriber.php
index e184fad7bdf0c..caec312fdda41 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_subscriber.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_subscriber.php
@@ -27,9 +27,11 @@ public function __construct()
$this->methodMap = [
'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\TestServiceSubscriber' => 'getTestServiceSubscriberService',
'foo_service' => 'getFooServiceService',
+ 'late_alias' => 'getLateAliasService',
+ ];
+ $this->aliases = [
+ 'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\TestDefinition1' => 'late_alias',
];
-
- $this->aliases = [];
}
public function compile(): void
@@ -45,11 +47,13 @@ public function isCompiled(): bool
public function getRemovedIds(): array
{
return [
- '.service_locator.bPEFRiK' => true,
- '.service_locator.bPEFRiK.foo_service' => true,
+ '.service_locator.CpwjbIa' => true,
+ '.service_locator.CpwjbIa.foo_service' => true,
'Psr\\Container\\ContainerInterface' => true,
'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true,
'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\CustomDefinition' => true,
+ 'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\TestDefinition1' => true,
+ 'late_alias' => true,
];
}
@@ -75,14 +79,26 @@ protected function getFooServiceService()
'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\TestServiceSubscriber' => ['services', 'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\TestServiceSubscriber', 'getTestServiceSubscriberService', false],
'bar' => ['services', 'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\TestServiceSubscriber', 'getTestServiceSubscriberService', false],
'baz' => ['privates', 'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\CustomDefinition', 'getCustomDefinitionService', false],
+ 'late_alias' => ['services', 'late_alias', 'getLateAliasService', false],
], [
'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\CustomDefinition' => 'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\CustomDefinition',
'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\TestServiceSubscriber' => 'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\TestServiceSubscriber',
'bar' => 'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\CustomDefinition',
'baz' => 'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\CustomDefinition',
+ 'late_alias' => 'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\TestDefinition1',
]))->withContext('foo_service', $this));
}
+ /**
+ * Gets the public 'late_alias' shared service.
+ *
+ * @return \Symfony\Component\DependencyInjection\Tests\Fixtures\TestDefinition1
+ */
+ protected function getLateAliasService()
+ {
+ return $this->services['late_alias'] = new \Symfony\Component\DependencyInjection\Tests\Fixtures\TestDefinition1();
+ }
+
/**
* Gets the private 'Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition' shared service.
*
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_short_syntax.yml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_short_syntax.yml
new file mode 100644
index 0000000000000..16ddb6bdf241a
--- /dev/null
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/yaml/services_short_syntax.yml
@@ -0,0 +1,6 @@
+services:
+ foo_bar: [1, 2]
+
+ bar_foo:
+ $a: 'a'
+ App\Foo: 'foo'
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Loader/FileLoaderTest.php b/src/Symfony/Component/DependencyInjection/Tests/Loader/FileLoaderTest.php
index 57ea37e6f4964..61971f408c477 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Loader/FileLoaderTest.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/FileLoaderTest.php
@@ -216,7 +216,7 @@ public function testMissingParentClass()
public function testRegisterClassesWithBadPrefix()
{
$this->expectException('Symfony\Component\DependencyInjection\Exception\InvalidArgumentException');
- $this->expectExceptionMessageRegExp('/Expected to find class "Symfony\\\Component\\\DependencyInjection\\\Tests\\\Fixtures\\\Prototype\\\Bar" in file ".+" while importing services from resource "Prototype\/Sub\/\*", but it was not found\! Check the namespace prefix used with the resource/');
+ $this->expectExceptionMessageMatches('/Expected to find class "Symfony\\\Component\\\DependencyInjection\\\Tests\\\Fixtures\\\Prototype\\\Bar" in file ".+" while importing services from resource "Prototype\/Sub\/\*", but it was not found\! Check the namespace prefix used with the resource/');
$container = new ContainerBuilder();
$loader = new TestFileLoader($container, new FileLocator(self::$fixturesPath.'/Fixtures'));
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php b/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php
index 14ba4c7f6e567..e5868edad2cea 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php
@@ -378,7 +378,7 @@ public function testParseTagsWithoutNameThrowsException()
public function testParseTagWithEmptyNameThrowsException()
{
$this->expectException('Symfony\Component\DependencyInjection\Exception\InvalidArgumentException');
- $this->expectExceptionMessageRegExp('/The tag name for service ".+" in .* must be a non-empty string/');
+ $this->expectExceptionMessageMatches('/The tag name for service ".+" in .* must be a non-empty string/');
$container = new ContainerBuilder();
$loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml'));
$loader->load('tag_with_empty_name.xml');
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php b/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php
index f40fc22cc3452..fc87dd7168ccc 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php
@@ -50,7 +50,7 @@ public static function setUpBeforeClass(): void
public function testLoadUnExistFile()
{
$this->expectException('Symfony\Component\DependencyInjection\Exception\InvalidArgumentException');
- $this->expectExceptionMessageRegExp('/The file ".+" does not exist./');
+ $this->expectExceptionMessageMatches('/The file ".+" does not exist./');
$loader = new YamlFileLoader(new ContainerBuilder(), new FileLocator(self::$fixturesPath.'/ini'));
$r = new \ReflectionObject($loader);
$m = $r->getMethod('loadFile');
@@ -62,7 +62,7 @@ public function testLoadUnExistFile()
public function testLoadInvalidYamlFile()
{
$this->expectException('Symfony\Component\DependencyInjection\Exception\InvalidArgumentException');
- $this->expectExceptionMessageRegExp('/The file ".+" does not contain valid YAML./');
+ $this->expectExceptionMessageMatches('/The file ".+" does not contain valid YAML./');
$path = self::$fixturesPath.'/ini';
$loader = new YamlFileLoader(new ContainerBuilder(), new FileLocator($path));
$r = new \ReflectionObject($loader);
@@ -205,6 +205,17 @@ public function testLoadServices()
$this->assertEquals(['decorated', 'decorated.pif-pouf', 5, ContainerInterface::IGNORE_ON_INVALID_REFERENCE], $services['decorator_service_with_name_and_priority_and_on_invalid']->getDecoratedService());
}
+ public function testLoadShortSyntax()
+ {
+ $container = new ContainerBuilder();
+ $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml'));
+ $loader->load('services_short_syntax.yml');
+ $services = $container->getDefinitions();
+
+ $this->assertSame([1, 2], $services['foo_bar']->getArguments());
+ $this->assertSame(['$a' => 'a', 'App\Foo' => 'foo'], $services['bar_foo']->getArguments());
+ }
+
public function testDeprecatedAliases()
{
$container = new ContainerBuilder();
@@ -384,7 +395,7 @@ public function testLoadYamlOnlyWithKeys()
public function testTagWithEmptyNameThrowsException()
{
$this->expectException('Symfony\Component\DependencyInjection\Exception\InvalidArgumentException');
- $this->expectExceptionMessageRegExp('/The tag name for service ".+" in .+ must be a non-empty string/');
+ $this->expectExceptionMessageMatches('/The tag name for service ".+" in .+ must be a non-empty string/');
$loader = new YamlFileLoader(new ContainerBuilder(), new FileLocator(self::$fixturesPath.'/yaml'));
$loader->load('tag_name_empty_string.yml');
}
@@ -392,7 +403,7 @@ public function testTagWithEmptyNameThrowsException()
public function testTagWithNonStringNameThrowsException()
{
$this->expectException('Symfony\Component\DependencyInjection\Exception\InvalidArgumentException');
- $this->expectExceptionMessageRegExp('/The tag name for service ".+" in .+ must be a non-empty string/');
+ $this->expectExceptionMessageMatches('/The tag name for service ".+" in .+ must be a non-empty string/');
$loader = new YamlFileLoader(new ContainerBuilder(), new FileLocator(self::$fixturesPath.'/yaml'));
$loader->load('tag_name_no_string.yml');
}
@@ -493,7 +504,7 @@ public function testPrototypeWithNamespace()
public function testPrototypeWithNamespaceAndNoResource()
{
$this->expectException('Symfony\Component\DependencyInjection\Exception\InvalidArgumentException');
- $this->expectExceptionMessageRegExp('/A "resource" attribute must be set when the "namespace" attribute is set for service ".+" in .+/');
+ $this->expectExceptionMessageMatches('/A "resource" attribute must be set when the "namespace" attribute is set for service ".+" in .+/');
$container = new ContainerBuilder();
$loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml'));
$loader->load('services_prototype_namespace_without_resource.yml');
@@ -621,7 +632,7 @@ public function testDecoratedServicesWithWrongOnInvalidSyntaxThrowsException()
public function testInvalidTagsWithDefaults()
{
$this->expectException('Symfony\Component\DependencyInjection\Exception\InvalidArgumentException');
- $this->expectExceptionMessageRegExp('/Parameter "tags" must be an array for service "Foo\\\Bar" in ".+services31_invalid_tags\.yml"\. Check your YAML syntax./');
+ $this->expectExceptionMessageMatches('/Parameter "tags" must be an array for service "Foo\\\Bar" in ".+services31_invalid_tags\.yml"\. Check your YAML syntax./');
$loader = new YamlFileLoader(new ContainerBuilder(), new FileLocator(self::$fixturesPath.'/yaml'));
$loader->load('services31_invalid_tags.yml');
}
@@ -711,7 +722,7 @@ public function testAnonymousServicesInInstanceof()
public function testAnonymousServicesWithAliases()
{
$this->expectException('Symfony\Component\DependencyInjection\Exception\InvalidArgumentException');
- $this->expectExceptionMessageRegExp('/Creating an alias using the tag "!service" is not allowed in ".+anonymous_services_alias\.yml"\./');
+ $this->expectExceptionMessageMatches('/Creating an alias using the tag "!service" is not allowed in ".+anonymous_services_alias\.yml"\./');
$container = new ContainerBuilder();
$loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml'));
$loader->load('anonymous_services_alias.yml');
@@ -720,7 +731,7 @@ public function testAnonymousServicesWithAliases()
public function testAnonymousServicesInParameters()
{
$this->expectException('Symfony\Component\DependencyInjection\Exception\InvalidArgumentException');
- $this->expectExceptionMessageRegExp('/Using an anonymous service in a parameter is not allowed in ".+anonymous_services_in_parameters\.yml"\./');
+ $this->expectExceptionMessageMatches('/Using an anonymous service in a parameter is not allowed in ".+anonymous_services_in_parameters\.yml"\./');
$container = new ContainerBuilder();
$loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml'));
$loader->load('anonymous_services_in_parameters.yml');
@@ -739,7 +750,7 @@ public function testAutoConfigureInstanceof()
public function testEmptyDefaultsThrowsClearException()
{
$this->expectException('Symfony\Component\DependencyInjection\Exception\InvalidArgumentException');
- $this->expectExceptionMessageRegExp('/Service "_defaults" key must be an array, "NULL" given in ".+bad_empty_defaults\.yml"\./');
+ $this->expectExceptionMessageMatches('/Service "_defaults" key must be an array, "NULL" given in ".+bad_empty_defaults\.yml"\./');
$container = new ContainerBuilder();
$loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml'));
$loader->load('bad_empty_defaults.yml');
@@ -748,7 +759,7 @@ public function testEmptyDefaultsThrowsClearException()
public function testEmptyInstanceofThrowsClearException()
{
$this->expectException('Symfony\Component\DependencyInjection\Exception\InvalidArgumentException');
- $this->expectExceptionMessageRegExp('/Service "_instanceof" key must be an array, "NULL" given in ".+bad_empty_instanceof\.yml"\./');
+ $this->expectExceptionMessageMatches('/Service "_instanceof" key must be an array, "NULL" given in ".+bad_empty_instanceof\.yml"\./');
$container = new ContainerBuilder();
$loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml'));
$loader->load('bad_empty_instanceof.yml');
@@ -757,7 +768,7 @@ public function testEmptyInstanceofThrowsClearException()
public function testUnsupportedKeywordThrowsException()
{
$this->expectException('Symfony\Component\DependencyInjection\Exception\InvalidArgumentException');
- $this->expectExceptionMessageRegExp('/^The configuration key "private" is unsupported for definition "bar"/');
+ $this->expectExceptionMessageMatches('/^The configuration key "private" is unsupported for definition "bar"/');
$container = new ContainerBuilder();
$loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml'));
$loader->load('bad_keyword.yml');
@@ -766,7 +777,7 @@ public function testUnsupportedKeywordThrowsException()
public function testUnsupportedKeywordInServiceAliasThrowsException()
{
$this->expectException('Symfony\Component\DependencyInjection\Exception\InvalidArgumentException');
- $this->expectExceptionMessageRegExp('/^The configuration key "calls" is unsupported for the service "bar" which is defined as an alias/');
+ $this->expectExceptionMessageMatches('/^The configuration key "calls" is unsupported for the service "bar" which is defined as an alias/');
$container = new ContainerBuilder();
$loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml'));
$loader->load('bad_alias.yml');
diff --git a/src/Symfony/Component/ExpressionLanguage/Tests/ExpressionLanguageTest.php b/src/Symfony/Component/ExpressionLanguage/Tests/ExpressionLanguageTest.php
index 9c7b30dc225d9..ed8b6852a5dbc 100644
--- a/src/Symfony/Component/ExpressionLanguage/Tests/ExpressionLanguageTest.php
+++ b/src/Symfony/Component/ExpressionLanguage/Tests/ExpressionLanguageTest.php
@@ -231,7 +231,7 @@ public function testRegisterAfterEval($registerCallback)
public function testCallBadCallable()
{
$this->expectException('RuntimeException');
- $this->expectExceptionMessageRegExp('/Unable to call method "\w+" of object "\w+"./');
+ $this->expectExceptionMessageMatches('/Unable to call method "\w+" of object "\w+"./');
$el = new ExpressionLanguage();
$el->evaluate('foo.myfunction()', ['foo' => new \stdClass()]);
}
diff --git a/src/Symfony/Component/Filesystem/Filesystem.php b/src/Symfony/Component/Filesystem/Filesystem.php
index 9054d7be3d336..85c997109a733 100644
--- a/src/Symfony/Component/Filesystem/Filesystem.php
+++ b/src/Symfony/Component/Filesystem/Filesystem.php
@@ -102,7 +102,7 @@ public function mkdir($dirs, $mode = 0777)
if (!is_dir($dir)) {
// The directory was not created by a concurrent process. Let's throw an exception with a developer friendly error message if we have one
if (self::$lastError) {
- throw new IOException(sprintf('Failed to create "%s": %s.', $dir, self::$lastError), 0, null, $dir);
+ throw new IOException(sprintf('Failed to create "%s": '.self::$lastError, $dir), 0, null, $dir);
}
throw new IOException(sprintf('Failed to create "%s".', $dir), 0, null, $dir);
}
@@ -172,16 +172,16 @@ public function remove($files)
if (is_link($file)) {
// See https://bugs.php.net/52176
if (!(self::box('unlink', $file) || '\\' !== \DIRECTORY_SEPARATOR || self::box('rmdir', $file)) && file_exists($file)) {
- throw new IOException(sprintf('Failed to remove symlink "%s": %s.', $file, self::$lastError));
+ throw new IOException(sprintf('Failed to remove symlink "%s": '.self::$lastError, $file));
}
} elseif (is_dir($file)) {
$this->remove(new \FilesystemIterator($file, \FilesystemIterator::CURRENT_AS_PATHNAME | \FilesystemIterator::SKIP_DOTS));
if (!self::box('rmdir', $file) && file_exists($file)) {
- throw new IOException(sprintf('Failed to remove directory "%s": %s.', $file, self::$lastError));
+ throw new IOException(sprintf('Failed to remove directory "%s": '.self::$lastError, $file));
}
} elseif (!self::box('unlink', $file) && file_exists($file)) {
- throw new IOException(sprintf('Failed to remove file "%s": %s.', $file, self::$lastError));
+ throw new IOException(sprintf('Failed to remove file "%s": '.self::$lastError, $file));
}
}
}
diff --git a/src/Symfony/Component/Form/Extension/Core/Type/DateTimeType.php b/src/Symfony/Component/Form/Extension/Core/Type/DateTimeType.php
index 5d936ffb6b6e3..c271d098086c7 100644
--- a/src/Symfony/Component/Form/Extension/Core/Type/DateTimeType.php
+++ b/src/Symfony/Component/Form/Extension/Core/Type/DateTimeType.php
@@ -218,6 +218,14 @@ public function buildView(FormView $view, FormInterface $form, array $options)
// * the html5 is set to true
if ($options['html5'] && 'single_text' === $options['widget'] && self::HTML5_FORMAT === $options['format']) {
$view->vars['type'] = 'datetime-local';
+
+ // we need to force the browser to display the seconds by
+ // adding the HTML attribute step if not already defined.
+ // Otherwise the browser will not display and so not send the seconds
+ // therefore the value will always be considered as invalid.
+ if ($options['with_seconds'] && !isset($view->vars['attr']['step'])) {
+ $view->vars['attr']['step'] = 1;
+ }
}
}
diff --git a/src/Symfony/Component/Form/Extension/Core/Type/RepeatedType.php b/src/Symfony/Component/Form/Extension/Core/Type/RepeatedType.php
index ffb7520a582b6..6ed403523cb77 100644
--- a/src/Symfony/Component/Form/Extension/Core/Type/RepeatedType.php
+++ b/src/Symfony/Component/Form/Extension/Core/Type/RepeatedType.php
@@ -31,13 +31,16 @@ public function buildForm(FormBuilderInterface $builder, array $options)
$options['options']['error_bubbling'] = $options['error_bubbling'];
}
+ // children fields must always be mapped
+ $defaultOptions = ['mapped' => true];
+
$builder
->addViewTransformer(new ValueToDuplicatesTransformer([
$options['first_name'],
$options['second_name'],
]))
- ->add($options['first_name'], $options['type'], array_merge($options['options'], $options['first_options']))
- ->add($options['second_name'], $options['type'], array_merge($options['options'], $options['second_options']))
+ ->add($options['first_name'], $options['type'], array_merge($options['options'], $options['first_options'], $defaultOptions))
+ ->add($options['second_name'], $options['type'], array_merge($options['options'], $options['second_options'], $defaultOptions))
;
}
diff --git a/src/Symfony/Component/Form/Extension/Validator/Constraints/FormValidator.php b/src/Symfony/Component/Form/Extension/Validator/Constraints/FormValidator.php
index e0885eae6bf33..81d4c632e1f87 100644
--- a/src/Symfony/Component/Form/Extension/Validator/Constraints/FormValidator.php
+++ b/src/Symfony/Component/Form/Extension/Validator/Constraints/FormValidator.php
@@ -24,6 +24,8 @@
*/
class FormValidator extends ConstraintValidator
{
+ private $resolvedGroups;
+
/**
* {@inheritdoc}
*/
@@ -44,44 +46,70 @@ public function validate($form, Constraint $formConstraint)
if ($form->isSubmitted() && $form->isSynchronized()) {
// Validate the form data only if transformation succeeded
- $groups = self::getValidationGroups($form);
+ $groups = $this->getValidationGroups($form);
if (!$groups) {
return;
}
$data = $form->getData();
-
// Validate the data against its own constraints
- if ($form->isRoot() && (\is_object($data) || \is_array($data))) {
- if (($groups && \is_array($groups)) || ($groups instanceof GroupSequence && $groups->groups)) {
- $validator->atPath('data')->validate($form->getData(), null, $groups);
- }
- }
+ $validateDataGraph = $form->isRoot()
+ && (\is_object($data) || \is_array($data))
+ && (($groups && \is_array($groups)) || ($groups instanceof GroupSequence && $groups->groups))
+ ;
- // Validate the data against the constraints defined
- // in the form
+ // Validate the data against the constraints defined in the form
+ /** @var Constraint[] $constraints */
$constraints = $config->getOption('constraints', []);
if ($groups instanceof GroupSequence) {
- $validator->atPath('data')->validate($form->getData(), $constraints, $groups);
- // Otherwise validate a constraint only once for the first
- // matching group
- foreach ($groups as $group) {
- if (\in_array($group, $formConstraint->groups)) {
- $validator->atPath('data')->validate($form->getData(), $formConstraint, $group);
- if (\count($this->context->getViolations()) > 0) {
- break;
+ // Validate the data, the form AND nested fields in sequence
+ $violationsCount = $this->context->getViolations()->count();
+ $fieldPropertyPath = \is_object($data) ? 'children[%s]' : 'children%s';
+ $hasChildren = $form->count() > 0;
+ $this->resolvedGroups = $hasChildren ? new \SplObjectStorage() : null;
+
+ foreach ($groups->groups as $group) {
+ if ($validateDataGraph) {
+ $validator->atPath('data')->validate($data, null, $group);
+ }
+
+ if ($groupedConstraints = self::getConstraintsInGroups($constraints, $group)) {
+ $validator->atPath('data')->validate($data, $groupedConstraints, $group);
+ }
+
+ foreach ($form->all() as $field) {
+ if ($field->isSubmitted()) {
+ // remember to validate this field is one group only
+ // otherwise resolving the groups would reuse the same
+ // sequence recursively, thus some fields could fail
+ // in different steps without breaking early enough
+ $this->resolvedGroups[$field] = (array) $group;
+ $validator->atPath(sprintf($fieldPropertyPath, $field->getPropertyPath()))->validate($field, $formConstraint);
}
}
+
+ if ($violationsCount < $this->context->getViolations()->count()) {
+ break;
+ }
+ }
+
+ if ($hasChildren) {
+ // destroy storage at the end of the sequence to avoid memory leaks
+ $this->resolvedGroups = null;
}
} else {
+ if ($validateDataGraph) {
+ $validator->atPath('data')->validate($data, null, $groups);
+ }
+
$groupedConstraints = [];
foreach ($constraints as $constraint) {
// For the "Valid" constraint, validate the data in all groups
if ($constraint instanceof Valid) {
- $validator->atPath('data')->validate($form->getData(), $constraint, $groups);
+ $validator->atPath('data')->validate($data, $constraint, $groups);
continue;
}
@@ -101,7 +129,7 @@ public function validate($form, Constraint $formConstraint)
}
foreach ($groupedConstraints as $group => $constraint) {
- $validator->atPath('data')->validate($form->getData(), $constraint, $group);
+ $validator->atPath('data')->validate($data, $constraint, $group);
}
}
} elseif (!$form->isSynchronized()) {
@@ -159,7 +187,7 @@ public function validate($form, Constraint $formConstraint)
*
* @return string|GroupSequence|(string|GroupSequence)[] The validation groups
*/
- private static function getValidationGroups(FormInterface $form)
+ private function getValidationGroups(FormInterface $form)
{
// Determine the clicked button of the complete form tree
$clickedButton = null;
@@ -183,6 +211,10 @@ private static function getValidationGroups(FormInterface $form)
return self::resolveValidationGroups($groups, $form);
}
+ if (isset($this->resolvedGroups[$form])) {
+ return $this->resolvedGroups[$form];
+ }
+
$form = $form->getParent();
} while (null !== $form);
@@ -208,4 +240,11 @@ private static function resolveValidationGroups($groups, FormInterface $form)
return (array) $groups;
}
+
+ private static function getConstraintsInGroups($constraints, $group)
+ {
+ return array_filter($constraints, static function (Constraint $constraint) use ($group) {
+ return \in_array($group, $constraint->groups, true);
+ });
+ }
}
diff --git a/src/Symfony/Component/Form/Extension/Validator/EventListener/ValidationListener.php b/src/Symfony/Component/Form/Extension/Validator/EventListener/ValidationListener.php
index b9cc334256174..867a5768aee6c 100644
--- a/src/Symfony/Component/Form/Extension/Validator/EventListener/ValidationListener.php
+++ b/src/Symfony/Component/Form/Extension/Validator/EventListener/ValidationListener.php
@@ -50,8 +50,7 @@ public function validateForm(FormEvent $event)
foreach ($this->validator->validate($form) as $violation) {
// Allow the "invalid" constraint to be put onto
// non-synchronized forms
- // ConstraintViolation::getConstraint() must not expect to provide a constraint as long as Symfony\Component\Validator\ExecutionContext exists (before 3.0)
- $allowNonSynchronized = (null === $violation->getConstraint() || $violation->getConstraint() instanceof Form) && Form::NOT_SYNCHRONIZED_ERROR === $violation->getCode();
+ $allowNonSynchronized = $violation->getConstraint() instanceof Form && Form::NOT_SYNCHRONIZED_ERROR === $violation->getCode();
$this->violationMapper->mapViolation($violation, $form, $allowNonSynchronized);
}
diff --git a/src/Symfony/Component/Form/Form.php b/src/Symfony/Component/Form/Form.php
index d6073f3ed3b67..3981627e6b4b2 100644
--- a/src/Symfony/Component/Form/Form.php
+++ b/src/Symfony/Component/Form/Form.php
@@ -1056,7 +1056,7 @@ private function modelToNorm($value)
$value = $transformer->transform($value);
}
} catch (TransformationFailedException $exception) {
- throw new TransformationFailedException(sprintf('Unable to transform data for property path "%s": %s', $this->getPropertyPath(), $exception->getMessage()), $exception->getCode(), $exception, $exception->getInvalidMessage(), $exception->getInvalidMessageParameters());
+ throw new TransformationFailedException(sprintf('Unable to transform data for property path "%s": '.$exception->getMessage(), $this->getPropertyPath()), $exception->getCode(), $exception, $exception->getInvalidMessage(), $exception->getInvalidMessageParameters());
}
return $value;
@@ -1078,7 +1078,7 @@ private function normToModel($value)
$value = $transformers[$i]->reverseTransform($value);
}
} catch (TransformationFailedException $exception) {
- throw new TransformationFailedException(sprintf('Unable to reverse value for property path "%s": %s', $this->getPropertyPath(), $exception->getMessage()), $exception->getCode(), $exception, $exception->getInvalidMessage(), $exception->getInvalidMessageParameters());
+ throw new TransformationFailedException(sprintf('Unable to reverse value for property path "%s": '.$exception->getMessage(), $this->getPropertyPath()), $exception->getCode(), $exception, $exception->getInvalidMessage(), $exception->getInvalidMessageParameters());
}
return $value;
@@ -1107,7 +1107,7 @@ private function normToView($value)
$value = $transformer->transform($value);
}
} catch (TransformationFailedException $exception) {
- throw new TransformationFailedException(sprintf('Unable to transform value for property path "%s": %s', $this->getPropertyPath(), $exception->getMessage()), $exception->getCode(), $exception, $exception->getInvalidMessage(), $exception->getInvalidMessageParameters());
+ throw new TransformationFailedException(sprintf('Unable to transform value for property path "%s": '.$exception->getMessage(), $this->getPropertyPath()), $exception->getCode(), $exception, $exception->getInvalidMessage(), $exception->getInvalidMessageParameters());
}
return $value;
@@ -1131,7 +1131,7 @@ private function viewToNorm($value)
$value = $transformers[$i]->reverseTransform($value);
}
} catch (TransformationFailedException $exception) {
- throw new TransformationFailedException(sprintf('Unable to reverse value for property path "%s": %s', $this->getPropertyPath(), $exception->getMessage()), $exception->getCode(), $exception, $exception->getInvalidMessage(), $exception->getInvalidMessageParameters());
+ throw new TransformationFailedException(sprintf('Unable to reverse value for property path "%s": '.$exception->getMessage(), $this->getPropertyPath()), $exception->getCode(), $exception, $exception->getInvalidMessage(), $exception->getInvalidMessageParameters());
}
return $value;
diff --git a/src/Symfony/Component/Form/ResolvedFormType.php b/src/Symfony/Component/Form/ResolvedFormType.php
index 02a06c85e1161..fdffc946a5eeb 100644
--- a/src/Symfony/Component/Form/ResolvedFormType.php
+++ b/src/Symfony/Component/Form/ResolvedFormType.php
@@ -96,7 +96,7 @@ public function createBuilder(FormFactoryInterface $factory, $name, array $optio
try {
$options = $this->getOptionsResolver()->resolve($options);
} catch (ExceptionInterface $e) {
- throw new $e(sprintf('An error has occurred resolving the options of the form "%s": %s.', \get_class($this->getInnerType()), $e->getMessage()), $e->getCode(), $e);
+ throw new $e(sprintf('An error has occurred resolving the options of the form "%s": '.$e->getMessage(), \get_class($this->getInnerType())), $e->getCode(), $e);
}
// Should be decoupled from the specific option at some point
diff --git a/src/Symfony/Component/Form/Resources/config/validation.xml b/src/Symfony/Component/Form/Resources/config/validation.xml
index cbd586b915451..b2b935442d467 100644
--- a/src/Symfony/Component/Form/Resources/config/validation.xml
+++ b/src/Symfony/Component/Form/Resources/config/validation.xml
@@ -7,7 +7,7 @@
-
+
diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/CheckboxTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/CheckboxTypeTest.php
index 8391805b22666..e12347405dee9 100644
--- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/CheckboxTypeTest.php
+++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/CheckboxTypeTest.php
@@ -197,7 +197,7 @@ public function provideCustomFalseValues()
public function testDontAllowNonArrayFalseValues()
{
$this->expectException('Symfony\Component\OptionsResolver\Exception\InvalidOptionsException');
- $this->expectExceptionMessageRegExp('/"false_values" with value "invalid" is expected to be of type "array"/');
+ $this->expectExceptionMessageMatches('/"false_values" with value "invalid" is expected to be of type "array"/');
$this->factory->create(static::TESTED_TYPE, null, [
'false_values' => 'invalid',
]);
diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/DateTimeTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/DateTimeTypeTest.php
index b0c4f3549252d..b9012bdc699e4 100644
--- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/DateTimeTypeTest.php
+++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/DateTimeTypeTest.php
@@ -495,6 +495,37 @@ public function testDontPassHtml5TypeIfNotSingleText()
$this->assertArrayNotHasKey('type', $view->vars);
}
+ public function testSingleTextWidgetWithSecondsShouldHaveRightStepAttribute()
+ {
+ $view = $this->factory
+ ->create(static::TESTED_TYPE, null, [
+ 'widget' => 'single_text',
+ 'with_seconds' => true,
+ ])
+ ->createView()
+ ;
+
+ $this->assertArrayHasKey('step', $view->vars['attr']);
+ $this->assertEquals(1, $view->vars['attr']['step']);
+ }
+
+ public function testSingleTextWidgetWithSecondsShouldNotOverrideStepAttribute()
+ {
+ $view = $this->factory
+ ->create(static::TESTED_TYPE, null, [
+ 'widget' => 'single_text',
+ 'with_seconds' => true,
+ 'attr' => [
+ 'step' => 30,
+ ],
+ ])
+ ->createView()
+ ;
+
+ $this->assertArrayHasKey('step', $view->vars['attr']);
+ $this->assertEquals(30, $view->vars['attr']['step']);
+ }
+
public function testSingleTextWidgetWithCustomNonHtml5Format()
{
$form = $this->factory->create(static::TESTED_TYPE, new \DateTime('2019-02-13 19:12:13'), [
diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/RepeatedTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/RepeatedTypeTest.php
index 267511d88ccd4..a70f65021680f 100644
--- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/RepeatedTypeTest.php
+++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/RepeatedTypeTest.php
@@ -12,6 +12,7 @@
namespace Symfony\Component\Form\Tests\Extension\Core\Type;
use Symfony\Component\Form\Form;
+use Symfony\Component\Form\Tests\Fixtures\NotMappedType;
class RepeatedTypeTest extends BaseTypeTest
{
@@ -78,6 +79,41 @@ public function testSetRequired()
$this->assertFalse($form['second']->isRequired());
}
+ public function testMappedOverridesDefault()
+ {
+ $form = $this->factory->create(NotMappedType::class);
+ $this->assertFalse($form->getConfig()->getMapped());
+
+ $form = $this->factory->create(static::TESTED_TYPE, null, [
+ 'type' => NotMappedType::class,
+ ]);
+
+ $this->assertTrue($form['first']->getConfig()->getMapped());
+ $this->assertTrue($form['second']->getConfig()->getMapped());
+ }
+
+ /**
+ * @dataProvider notMappedConfigurationKeys
+ */
+ public function testNotMappedInnerIsOverridden($configurationKey)
+ {
+ $form = $this->factory->create(static::TESTED_TYPE, null, [
+ 'type' => TextTypeTest::TESTED_TYPE,
+ $configurationKey => ['mapped' => false],
+ ]);
+
+ $this->assertTrue($form['first']->getConfig()->getMapped());
+ $this->assertTrue($form['second']->getConfig()->getMapped());
+ }
+
+ public function notMappedConfigurationKeys()
+ {
+ return [
+ ['first_options'],
+ ['second_options'],
+ ];
+ }
+
public function testSetInvalidOptions()
{
$this->expectException('Symfony\Component\OptionsResolver\Exception\InvalidOptionsException');
diff --git a/src/Symfony/Component/Form/Tests/Extension/Validator/Constraints/FormValidatorTest.php b/src/Symfony/Component/Form/Tests/Extension/Validator/Constraints/FormValidatorTest.php
index 3f6294c53b3d6..782e1ed32749e 100644
--- a/src/Symfony/Component/Form/Tests/Extension/Validator/Constraints/FormValidatorTest.php
+++ b/src/Symfony/Component/Form/Tests/Extension/Validator/Constraints/FormValidatorTest.php
@@ -444,8 +444,8 @@ public function testHandleGroupSequenceValidationGroups()
$form = $this->getCompoundForm($object, $options);
$form->submit([]);
- $this->expectValidateAt(0, 'data', $object, new GroupSequence(['group1', 'group2']));
- $this->expectValidateAt(1, 'data', $object, new GroupSequence(['group1', 'group2']));
+ $this->expectValidateAt(0, 'data', $object, 'group1');
+ $this->expectValidateAt(1, 'data', $object, 'group2');
$this->validator->validate($form, new Form());
@@ -799,6 +799,39 @@ public function testCompositeConstraintValidatedInEachGroup()
$this->assertSame('data[field2]', $context->getViolations()[1]->getPropertyPath());
}
+ public function testCompositeConstraintValidatedInSequence()
+ {
+ $form = $this->getCompoundForm([], [
+ 'constraints' => [
+ new Collection([
+ 'field1' => new NotBlank([
+ 'groups' => ['field1'],
+ ]),
+ 'field2' => new NotBlank([
+ 'groups' => ['field2'],
+ ]),
+ ]),
+ ],
+ 'validation_groups' => new GroupSequence(['field1', 'field2']),
+ ])
+ ->add($this->getForm('field1'))
+ ->add($this->getForm('field2'))
+ ;
+
+ $form->submit([
+ 'field1' => '',
+ 'field2' => '',
+ ]);
+
+ $context = new ExecutionContext(Validation::createValidator(), $form, new IdentityTranslator());
+ $this->validator->initialize($context);
+ $this->validator->validate($form, new Form());
+
+ $this->assertCount(1, $context->getViolations());
+ $this->assertSame('This value should not be blank.', $context->getViolations()[0]->getMessage());
+ $this->assertSame('data[field1]', $context->getViolations()[0]->getPropertyPath());
+ }
+
protected function createValidator()
{
return new FormValidator();
@@ -821,7 +854,7 @@ private function getForm($name = 'name', $dataClass = null, array $options = [])
private function getCompoundForm($data, array $options = [])
{
- return $this->getBuilder('name', \get_class($data), $options)
+ return $this->getBuilder('name', \is_object($data) ? \get_class($data) : null, $options)
->setData($data)
->setCompound(true)
->setDataMapper(new PropertyPathMapper())
diff --git a/src/Symfony/Component/Form/Tests/Extension/Validator/Type/FormTypeValidatorExtensionTest.php b/src/Symfony/Component/Form/Tests/Extension/Validator/Type/FormTypeValidatorExtensionTest.php
index a920e3be5b3ac..0b1dab5485ecc 100644
--- a/src/Symfony/Component/Form/Tests/Extension/Validator/Type/FormTypeValidatorExtensionTest.php
+++ b/src/Symfony/Component/Form/Tests/Extension/Validator/Type/FormTypeValidatorExtensionTest.php
@@ -12,15 +12,19 @@
namespace Symfony\Component\Form\Tests\Extension\Validator\Type;
use Symfony\Component\Form\Extension\Validator\ValidatorExtension;
+use Symfony\Component\Form\Form;
use Symfony\Component\Form\Forms;
use Symfony\Component\Form\Test\Traits\ValidatorExtensionTrait;
use Symfony\Component\Form\Tests\Extension\Core\Type\FormTypeTest;
use Symfony\Component\Form\Tests\Extension\Core\Type\TextTypeTest;
-use Symfony\Component\Validator\Constraints\Email;
+use Symfony\Component\Form\Tests\Fixtures\Author;
use Symfony\Component\Validator\Constraints\GroupSequence;
use Symfony\Component\Validator\Constraints\Length;
+use Symfony\Component\Validator\Constraints\NotBlank;
use Symfony\Component\Validator\Constraints\Valid;
use Symfony\Component\Validator\ConstraintViolationList;
+use Symfony\Component\Validator\Mapping\ClassMetadata;
+use Symfony\Component\Validator\Mapping\Factory\MetadataFactoryInterface;
use Symfony\Component\Validator\Validation;
class FormTypeValidatorExtensionTest extends BaseValidatorExtensionTest
@@ -66,14 +70,71 @@ public function testGroupSequenceWithConstraintsOption()
->add('field', TextTypeTest::TESTED_TYPE, [
'constraints' => [
new Length(['min' => 10, 'groups' => ['First']] + $allowEmptyString),
- new Email(['groups' => ['Second']]),
+ new NotBlank(['groups' => ['Second']]),
],
])
;
$form->submit(['field' => 'wrong']);
- $this->assertCount(1, $form->getErrors(true));
+ $errors = $form->getErrors(true);
+
+ $this->assertCount(1, $errors);
+ $this->assertInstanceOf(Length::class, $errors[0]->getCause()->getConstraint());
+ }
+
+ public function testManyFieldsGroupSequenceWithConstraintsOption()
+ {
+ $allowEmptyString = property_exists(Length::class, 'allowEmptyString') ? ['allowEmptyString' => true] : [];
+
+ $formMetadata = new ClassMetadata(Form::class);
+ $authorMetadata = (new ClassMetadata(Author::class))
+ ->addPropertyConstraint('firstName', new NotBlank(['groups' => 'Second']))
+ ;
+ $metadataFactory = $this->createMock(MetadataFactoryInterface::class);
+ $metadataFactory->expects($this->any())
+ ->method('getMetadataFor')
+ ->willReturnCallback(static function ($classOrObject) use ($formMetadata, $authorMetadata) {
+ if (Author::class === $classOrObject || $classOrObject instanceof Author) {
+ return $authorMetadata;
+ }
+
+ if (Form::class === $classOrObject || $classOrObject instanceof Form) {
+ return $formMetadata;
+ }
+
+ return new ClassMetadata(\is_string($classOrObject) ? $classOrObject : \get_class($classOrObject));
+ })
+ ;
+
+ $validator = Validation::createValidatorBuilder()
+ ->setMetadataFactory($metadataFactory)
+ ->getValidator()
+ ;
+ $form = Forms::createFormFactoryBuilder()
+ ->addExtension(new ValidatorExtension($validator))
+ ->getFormFactory()
+ ->create(FormTypeTest::TESTED_TYPE, new Author(), (['validation_groups' => new GroupSequence(['First', 'Second'])]))
+ ->add('firstName', TextTypeTest::TESTED_TYPE)
+ ->add('lastName', TextTypeTest::TESTED_TYPE, [
+ 'constraints' => [
+ new Length(['min' => 10, 'groups' => ['First']] + $allowEmptyString),
+ ],
+ ])
+ ->add('australian', TextTypeTest::TESTED_TYPE, [
+ 'constraints' => [
+ new NotBlank(['groups' => ['Second']]),
+ ],
+ ])
+ ;
+
+ $form->submit(['firstName' => '', 'lastName' => 'wrong_1', 'australian' => '']);
+
+ $errors = $form->getErrors(true);
+
+ $this->assertCount(1, $errors);
+ $this->assertInstanceOf(Length::class, $errors[0]->getCause()->getConstraint());
+ $this->assertSame('children[lastName].data', $errors[0]->getCause()->getPropertyPath());
}
protected function createForm(array $options = [])
diff --git a/src/Symfony/Component/Form/Tests/Extension/Validator/ValidatorExtensionTest.php b/src/Symfony/Component/Form/Tests/Extension/Validator/ValidatorExtensionTest.php
index 136086a5e5ba8..383b7556d51b8 100644
--- a/src/Symfony/Component/Form/Tests/Extension/Validator/ValidatorExtensionTest.php
+++ b/src/Symfony/Component/Form/Tests/Extension/Validator/ValidatorExtensionTest.php
@@ -13,6 +13,8 @@
use PHPUnit\Framework\TestCase;
use Symfony\Component\Form\AbstractType;
+use Symfony\Component\Form\Extension\Core\Type\FormType;
+use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Validator\Constraints\Form as FormConstraint;
use Symfony\Component\Form\Extension\Validator\ValidatorExtension;
use Symfony\Component\Form\Extension\Validator\ValidatorTypeGuesser;
@@ -20,6 +22,8 @@
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormFactoryBuilder;
use Symfony\Component\OptionsResolver\OptionsResolver;
+use Symfony\Component\Validator\Constraints\GroupSequence;
+use Symfony\Component\Validator\Constraints\Length;
use Symfony\Component\Validator\Constraints\NotBlank;
use Symfony\Component\Validator\Mapping\CascadingStrategy;
use Symfony\Component\Validator\Mapping\ClassMetadata;
@@ -49,6 +53,8 @@ public function test2Dot5ValidationApi()
$this->assertCount(1, $metadata->getConstraints());
$this->assertInstanceOf(FormConstraint::class, $metadata->getConstraints()[0]);
+ $this->assertSame(CascadingStrategy::NONE, $metadata->cascadingStrategy);
+ $this->assertSame(TraversalStrategy::IMPLICIT, $metadata->traversalStrategy);
$this->assertSame(CascadingStrategy::CASCADE, $metadata->getPropertyMetadata('children')[0]->cascadingStrategy);
$this->assertSame(TraversalStrategy::IMPLICIT, $metadata->getPropertyMetadata('children')[0]->traversalStrategy);
}
@@ -86,7 +92,57 @@ public function testFieldConstraintsInvalidateFormIfFieldIsSubmitted()
$this->assertFalse($form->get('baz')->isValid());
}
- private function createForm($type)
+ public function testFieldsValidateInSequence()
+ {
+ $allowEmptyString = property_exists(Length::class, 'allowEmptyString') ? ['allowEmptyString' => true] : [];
+
+ $form = $this->createForm(FormType::class, null, [
+ 'validation_groups' => new GroupSequence(['group1', 'group2']),
+ ])
+ ->add('foo', TextType::class, [
+ 'constraints' => [new Length(['min' => 10, 'groups' => ['group1']] + $allowEmptyString)],
+ ])
+ ->add('bar', TextType::class, [
+ 'constraints' => [new NotBlank(['groups' => ['group2']])],
+ ])
+ ;
+
+ $form->submit(['foo' => 'invalid', 'bar' => null]);
+
+ $errors = $form->getErrors(true);
+
+ $this->assertCount(1, $errors);
+ $this->assertInstanceOf(Length::class, $errors[0]->getCause()->getConstraint());
+ }
+
+ public function testFieldsValidateInSequenceWithNestedGroupsArray()
+ {
+ $allowEmptyString = property_exists(Length::class, 'allowEmptyString') ? ['allowEmptyString' => true] : [];
+
+ $form = $this->createForm(FormType::class, null, [
+ 'validation_groups' => new GroupSequence([['group1', 'group2'], 'group3']),
+ ])
+ ->add('foo', TextType::class, [
+ 'constraints' => [new Length(['min' => 10, 'groups' => ['group1']] + $allowEmptyString)],
+ ])
+ ->add('bar', TextType::class, [
+ 'constraints' => [new Length(['min' => 10, 'groups' => ['group2']] + $allowEmptyString)],
+ ])
+ ->add('baz', TextType::class, [
+ 'constraints' => [new NotBlank(['groups' => ['group3']])],
+ ])
+ ;
+
+ $form->submit(['foo' => 'invalid', 'bar' => 'invalid', 'baz' => null]);
+
+ $errors = $form->getErrors(true);
+
+ $this->assertCount(2, $errors);
+ $this->assertInstanceOf(Length::class, $errors[0]->getCause()->getConstraint());
+ $this->assertInstanceOf(Length::class, $errors[1]->getCause()->getConstraint());
+ }
+
+ private function createForm($type, $data = null, array $options = [])
{
$validator = Validation::createValidatorBuilder()
->setMetadataFactory(new LazyLoadingMetadataFactory(new StaticMethodLoader()))
@@ -95,7 +151,7 @@ private function createForm($type)
$formFactoryBuilder->addExtension(new ValidatorExtension($validator));
$formFactory = $formFactoryBuilder->getFormFactory();
- return $formFactory->create($type);
+ return $formFactory->create($type, $data, $options);
}
}
diff --git a/src/Symfony/Component/Form/Tests/Fixtures/NotMappedType.php b/src/Symfony/Component/Form/Tests/Fixtures/NotMappedType.php
new file mode 100644
index 0000000000000..14c340b8917af
--- /dev/null
+++ b/src/Symfony/Component/Form/Tests/Fixtures/NotMappedType.php
@@ -0,0 +1,23 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form\Tests\Fixtures;
+
+use Symfony\Component\Form\AbstractType;
+use Symfony\Component\OptionsResolver\OptionsResolver;
+
+class NotMappedType extends AbstractType
+{
+ public function configureOptions(OptionsResolver $resolver)
+ {
+ $resolver->setDefault('mapped', false);
+ }
+}
diff --git a/src/Symfony/Component/HttpClient/CurlHttpClient.php b/src/Symfony/Component/HttpClient/CurlHttpClient.php
index ddc7f9d9f351c..2fcba7902ebef 100644
--- a/src/Symfony/Component/HttpClient/CurlHttpClient.php
+++ b/src/Symfony/Component/HttpClient/CurlHttpClient.php
@@ -141,12 +141,12 @@ public function request(string $method, string $url, array $options = []): Respo
CURLOPT_CERTINFO => $options['capture_peer_cert_chain'],
];
- if (1.0 === (float) $options['http_version']) {
+ if (\defined('CURL_VERSION_HTTP2') && (CURL_VERSION_HTTP2 & self::$curlVersion['features']) && ('https:' === $scheme || 2.0 === (float) $options['http_version'])) {
+ $curlopts[CURLOPT_HTTP_VERSION] = CURL_HTTP_VERSION_2_0;
+ } elseif (1.0 === (float) $options['http_version']) {
$curlopts[CURLOPT_HTTP_VERSION] = CURL_HTTP_VERSION_1_0;
- } elseif (1.1 === (float) $options['http_version'] || 'https:' !== $scheme) {
+ } elseif (1.1 === (float) $options['http_version']) {
$curlopts[CURLOPT_HTTP_VERSION] = CURL_HTTP_VERSION_1_1;
- } elseif (\defined('CURL_VERSION_HTTP2') && CURL_VERSION_HTTP2 & self::$curlVersion['features']) {
- $curlopts[CURLOPT_HTTP_VERSION] = CURL_HTTP_VERSION_2_0;
}
if (isset($options['auth_ntlm'])) {
diff --git a/src/Symfony/Component/HttpClient/HttpClientTrait.php b/src/Symfony/Component/HttpClient/HttpClientTrait.php
index e150e56a0c9c0..51856da9bfef4 100644
--- a/src/Symfony/Component/HttpClient/HttpClientTrait.php
+++ b/src/Symfony/Component/HttpClient/HttpClientTrait.php
@@ -343,11 +343,11 @@ private static function jsonEncode($value, int $flags = null, int $maxDepth = 51
try {
$value = json_encode($value, $flags | (\PHP_VERSION_ID >= 70300 ? JSON_THROW_ON_ERROR : 0), $maxDepth);
} catch (\JsonException $e) {
- throw new InvalidArgumentException(sprintf('Invalid value for "json" option: %s.', $e->getMessage()));
+ throw new InvalidArgumentException('Invalid value for "json" option: '.$e->getMessage());
}
if (\PHP_VERSION_ID < 70300 && JSON_ERROR_NONE !== json_last_error() && (false === $value || !($flags & JSON_PARTIAL_OUTPUT_ON_ERROR))) {
- throw new InvalidArgumentException(sprintf('Invalid value for "json" option: %s.', json_last_error_msg()));
+ throw new InvalidArgumentException('Invalid value for "json" option: '.json_last_error_msg());
}
return $value;
diff --git a/src/Symfony/Component/HttpClient/Response/ResponseTrait.php b/src/Symfony/Component/HttpClient/Response/ResponseTrait.php
index f49ca68338408..de4d7598b923e 100644
--- a/src/Symfony/Component/HttpClient/Response/ResponseTrait.php
+++ b/src/Symfony/Component/HttpClient/Response/ResponseTrait.php
@@ -153,11 +153,11 @@ public function toArray(bool $throw = true): array
try {
$content = json_decode($content, true, 512, JSON_BIGINT_AS_STRING | (\PHP_VERSION_ID >= 70300 ? JSON_THROW_ON_ERROR : 0));
} catch (\JsonException $e) {
- throw new JsonException(sprintf('%s for "%s".', $e->getMessage(), $this->getInfo('url')), $e->getCode());
+ throw new JsonException(sprintf($e->getMessage().' for "%s".', $this->getInfo('url')), $e->getCode());
}
if (\PHP_VERSION_ID < 70300 && JSON_ERROR_NONE !== json_last_error()) {
- throw new JsonException(sprintf('%s for "%s".', json_last_error_msg(), $this->getInfo('url')), json_last_error());
+ throw new JsonException(sprintf(json_last_error_msg().' for "%s".', $this->getInfo('url')), json_last_error());
}
if (!\is_array($content)) {
diff --git a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/AbstractSessionHandler.php b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/AbstractSessionHandler.php
index a196833cbf5d3..c4fa1fae206fa 100644
--- a/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/AbstractSessionHandler.php
+++ b/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/AbstractSessionHandler.php
@@ -71,6 +71,15 @@ public function validateId($sessionId)
$this->prefetchData = $this->read($sessionId);
$this->prefetchId = $sessionId;
+ if (\PHP_VERSION_ID < 70317 || (70400 <= \PHP_VERSION_ID && \PHP_VERSION_ID < 70405)) {
+ // work around https://bugs.php.net/79413
+ foreach (debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS) as $frame) {
+ if (!isset($frame['class']) && isset($frame['function']) && \in_array($frame['function'], ['session_regenerate_id', 'session_create_id'], true)) {
+ return '' === $this->prefetchData;
+ }
+ }
+ }
+
return '' !== $this->prefetchData;
}
diff --git a/src/Symfony/Component/HttpFoundation/Session/Storage/NativeSessionStorage.php b/src/Symfony/Component/HttpFoundation/Session/Storage/NativeSessionStorage.php
index 54933d953af3a..b6cce8165edb8 100644
--- a/src/Symfony/Component/HttpFoundation/Session/Storage/NativeSessionStorage.php
+++ b/src/Symfony/Component/HttpFoundation/Session/Storage/NativeSessionStorage.php
@@ -215,8 +215,10 @@ public function regenerate($destroy = false, $lifetime = null)
return false;
}
- if (null !== $lifetime) {
+ if (null !== $lifetime && $lifetime != ini_get('session.cookie_lifetime')) {
+ $this->save();
ini_set('session.cookie_lifetime', $lifetime);
+ $this->start();
}
if ($destroy) {
@@ -225,10 +227,6 @@ public function regenerate($destroy = false, $lifetime = null)
$isRegenerated = session_regenerate_id($destroy);
- // The reference to $_SESSION in session bags is lost in PHP7 and we need to re-create it.
- // @see https://bugs.php.net/70013
- $this->loadSession();
-
if (null !== $this->emulateSameSite) {
$originalCookie = SessionUtils::popSessionCookie(session_name(), session_id());
if (null !== $originalCookie) {
diff --git a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/Fixtures/regenerate.expected b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/Fixtures/regenerate.expected
index baa5f2f6f5cb0..d825f44f7cb86 100644
--- a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/Fixtures/regenerate.expected
+++ b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/Handler/Fixtures/regenerate.expected
@@ -11,6 +11,7 @@ validateId
read
doRead: abc|i:123;
read
+doRead: abc|i:123;
write
doWrite: abc|i:123;
diff --git a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/NativeSessionStorageTest.php b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/NativeSessionStorageTest.php
index ace6249394365..4cb0b2dab3885 100644
--- a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/NativeSessionStorageTest.php
+++ b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/NativeSessionStorageTest.php
@@ -120,6 +120,19 @@ public function testRegenerateDestroy()
$this->assertEquals(11, $storage->getBag('attributes')->get('legs'));
}
+ public function testRegenerateWithCustomLifetime()
+ {
+ $storage = $this->getStorage();
+ $storage->start();
+ $id = $storage->getId();
+ $lifetime = 999999;
+ $storage->getBag('attributes')->set('legs', 11);
+ $storage->regenerate(false, $lifetime);
+ $this->assertNotEquals($id, $storage->getId());
+ $this->assertEquals(11, $storage->getBag('attributes')->get('legs'));
+ $this->assertEquals($lifetime, ini_get('session.cookie_lifetime'));
+ }
+
public function testSessionGlobalIsUpToDateAfterIdRegeneration()
{
$storage = $this->getStorage();
diff --git a/src/Symfony/Component/HttpKernel/Controller/ControllerResolver.php b/src/Symfony/Component/HttpKernel/Controller/ControllerResolver.php
index feb69e54ac9fb..b3e21e5f26c21 100644
--- a/src/Symfony/Component/HttpKernel/Controller/ControllerResolver.php
+++ b/src/Symfony/Component/HttpKernel/Controller/ControllerResolver.php
@@ -64,7 +64,7 @@ public function getController(Request $request)
}
if (!\is_callable($controller)) {
- throw new \InvalidArgumentException(sprintf('The controller for URI "%s" is not callable. %s.', $request->getPathInfo(), $this->getControllerError($controller)));
+ throw new \InvalidArgumentException(sprintf('The controller for URI "%s" is not callable: '.$this->getControllerError($controller), $request->getPathInfo()));
}
return $controller;
@@ -72,7 +72,7 @@ public function getController(Request $request)
if (\is_object($controller)) {
if (!\is_callable($controller)) {
- throw new \InvalidArgumentException(sprintf('The controller for URI "%s" is not callable. %s.', $request->getPathInfo(), $this->getControllerError($controller)));
+ throw new \InvalidArgumentException(sprintf('The controller for URI "%s" is not callable: '.$this->getControllerError($controller), $request->getPathInfo()));
}
return $controller;
@@ -85,11 +85,11 @@ public function getController(Request $request)
try {
$callable = $this->createController($controller);
} catch (\InvalidArgumentException $e) {
- throw new \InvalidArgumentException(sprintf('The controller for URI "%s" is not callable. %s.', $request->getPathInfo(), $e->getMessage()), 0, $e);
+ throw new \InvalidArgumentException(sprintf('The controller for URI "%s" is not callable: '.$e->getMessage(), $request->getPathInfo()), 0, $e);
}
if (!\is_callable($callable)) {
- throw new \InvalidArgumentException(sprintf('The controller for URI "%s" is not callable. %s.', $request->getPathInfo(), $this->getControllerError($callable)));
+ throw new \InvalidArgumentException(sprintf('The controller for URI "%s" is not callable: '.$this->getControllerError($callable), $request->getPathInfo()));
}
return $callable;
diff --git a/src/Symfony/Component/HttpKernel/DataCollector/LoggerDataCollector.php b/src/Symfony/Component/HttpKernel/DataCollector/LoggerDataCollector.php
index 9314e432e150f..3bce904ccd2d2 100644
--- a/src/Symfony/Component/HttpKernel/DataCollector/LoggerDataCollector.php
+++ b/src/Symfony/Component/HttpKernel/DataCollector/LoggerDataCollector.php
@@ -179,7 +179,7 @@ private function sanitizeLogs(array $logs)
continue;
}
- $message = $log['message'];
+ $message = '_'.$log['message'];
$exception = $log['context']['exception'];
if ($exception instanceof SilencedErrorContext) {
diff --git a/src/Symfony/Component/HttpKernel/EventListener/DebugHandlersListener.php b/src/Symfony/Component/HttpKernel/EventListener/DebugHandlersListener.php
index dcb54ea891c4a..0e672a299dd41 100644
--- a/src/Symfony/Component/HttpKernel/EventListener/DebugHandlersListener.php
+++ b/src/Symfony/Component/HttpKernel/EventListener/DebugHandlersListener.php
@@ -15,6 +15,7 @@
use Symfony\Component\Console\ConsoleEvents;
use Symfony\Component\Console\Event\ConsoleEvent;
use Symfony\Component\Console\Output\ConsoleOutputInterface;
+use Symfony\Component\Debug\ErrorHandler as LegacyErrorHandler;
use Symfony\Component\Debug\Exception\FatalThrowableError;
use Symfony\Component\ErrorHandler\ErrorHandler;
use Symfony\Component\EventDispatcher\Event;
@@ -79,7 +80,7 @@ public function configure(Event $event = null)
restore_exception_handler();
if ($this->logger || null !== $this->throwAt) {
- if ($handler instanceof ErrorHandler) {
+ if ($handler instanceof ErrorHandler || $handler instanceof LegacyErrorHandler) {
if ($this->logger) {
$handler->setDefaultLogger($this->logger, $this->levels);
if (\is_array($this->levels)) {
@@ -138,7 +139,7 @@ public function configure(Event $event = null)
}
}
if ($this->exceptionHandler) {
- if ($handler instanceof ErrorHandler) {
+ if ($handler instanceof ErrorHandler || $handler instanceof LegacyErrorHandler) {
$handler->setExceptionHandler($this->exceptionHandler);
}
$this->exceptionHandler = null;
diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php
index 89198bd7cffcc..9cb3a034d6961 100644
--- a/src/Symfony/Component/HttpKernel/Kernel.php
+++ b/src/Symfony/Component/HttpKernel/Kernel.php
@@ -76,11 +76,11 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl
private static $freshCache = [];
- const VERSION = '4.4.7';
- const VERSION_ID = 40407;
+ const VERSION = '4.4.8';
+ const VERSION_ID = 40408;
const MAJOR_VERSION = 4;
const MINOR_VERSION = 4;
- const RELEASE_VERSION = 7;
+ const RELEASE_VERSION = 8;
const EXTRA_VERSION = '';
const END_OF_MAINTENANCE = '11/2022';
diff --git a/src/Symfony/Component/HttpKernel/Log/Logger.php b/src/Symfony/Component/HttpKernel/Log/Logger.php
index 6ea3b464f0d5e..e3badc3676e62 100644
--- a/src/Symfony/Component/HttpKernel/Log/Logger.php
+++ b/src/Symfony/Component/HttpKernel/Log/Logger.php
@@ -79,7 +79,7 @@ public function log($level, $message, array $context = [])
}
$formatter = $this->formatter;
- fwrite($this->handle, $formatter($level, $message, $context));
+ @fwrite($this->handle, $formatter($level, $message, $context));
}
private function format(string $level, string $message, array $context): string
diff --git a/src/Symfony/Component/HttpKernel/Tests/Controller/ControllerResolverTest.php b/src/Symfony/Component/HttpKernel/Tests/Controller/ControllerResolverTest.php
index 014b21e83aff3..d5ac4ad5c2609 100644
--- a/src/Symfony/Component/HttpKernel/Tests/Controller/ControllerResolverTest.php
+++ b/src/Symfony/Component/HttpKernel/Tests/Controller/ControllerResolverTest.php
@@ -173,17 +173,17 @@ public function getUndefinedControllers()
['foo', \Error::class, 'Class \'foo\' not found'],
['oof::bar', \Error::class, 'Class \'oof\' not found'],
[['oof', 'bar'], \Error::class, 'Class \'oof\' not found'],
- ['Symfony\Component\HttpKernel\Tests\Controller\ControllerTest::staticsAction', \InvalidArgumentException::class, 'The controller for URI "/" is not callable. Expected method "staticsAction" on class "Symfony\Component\HttpKernel\Tests\Controller\ControllerTest", did you mean "staticAction"?'],
- ['Symfony\Component\HttpKernel\Tests\Controller\ControllerTest::privateAction', \InvalidArgumentException::class, 'The controller for URI "/" is not callable. Method "privateAction" on class "Symfony\Component\HttpKernel\Tests\Controller\ControllerTest" should be public and non-abstract'],
- ['Symfony\Component\HttpKernel\Tests\Controller\ControllerTest::protectedAction', \InvalidArgumentException::class, 'The controller for URI "/" is not callable. Method "protectedAction" on class "Symfony\Component\HttpKernel\Tests\Controller\ControllerTest" should be public and non-abstract'],
- ['Symfony\Component\HttpKernel\Tests\Controller\ControllerTest::undefinedAction', \InvalidArgumentException::class, 'The controller for URI "/" is not callable. Expected method "undefinedAction" on class "Symfony\Component\HttpKernel\Tests\Controller\ControllerTest". Available methods: "publicAction", "staticAction"'],
- ['Symfony\Component\HttpKernel\Tests\Controller\ControllerTest', \InvalidArgumentException::class, 'The controller for URI "/" is not callable. Controller class "Symfony\Component\HttpKernel\Tests\Controller\ControllerTest" cannot be called without a method name. You need to implement "__invoke" or use one of the available methods: "publicAction", "staticAction".'],
- [[$controller, 'staticsAction'], \InvalidArgumentException::class, 'The controller for URI "/" is not callable. Expected method "staticsAction" on class "Symfony\Component\HttpKernel\Tests\Controller\ControllerTest", did you mean "staticAction"?'],
- [[$controller, 'privateAction'], \InvalidArgumentException::class, 'The controller for URI "/" is not callable. Method "privateAction" on class "Symfony\Component\HttpKernel\Tests\Controller\ControllerTest" should be public and non-abstract'],
- [[$controller, 'protectedAction'], \InvalidArgumentException::class, 'The controller for URI "/" is not callable. Method "protectedAction" on class "Symfony\Component\HttpKernel\Tests\Controller\ControllerTest" should be public and non-abstract'],
- [[$controller, 'undefinedAction'], \InvalidArgumentException::class, 'The controller for URI "/" is not callable. Expected method "undefinedAction" on class "Symfony\Component\HttpKernel\Tests\Controller\ControllerTest". Available methods: "publicAction", "staticAction"'],
- [$controller, \InvalidArgumentException::class, 'The controller for URI "/" is not callable. Controller class "Symfony\Component\HttpKernel\Tests\Controller\ControllerTest" cannot be called without a method name. You need to implement "__invoke" or use one of the available methods: "publicAction", "staticAction".'],
- [['a' => 'foo', 'b' => 'bar'], \InvalidArgumentException::class, 'The controller for URI "/" is not callable. Invalid array callable, expected [controller, method].'],
+ ['Symfony\Component\HttpKernel\Tests\Controller\ControllerTest::staticsAction', \InvalidArgumentException::class, 'The controller for URI "/" is not callable: Expected method "staticsAction" on class "Symfony\Component\HttpKernel\Tests\Controller\ControllerTest", did you mean "staticAction"?'],
+ ['Symfony\Component\HttpKernel\Tests\Controller\ControllerTest::privateAction', \InvalidArgumentException::class, 'The controller for URI "/" is not callable: Method "privateAction" on class "Symfony\Component\HttpKernel\Tests\Controller\ControllerTest" should be public and non-abstract'],
+ ['Symfony\Component\HttpKernel\Tests\Controller\ControllerTest::protectedAction', \InvalidArgumentException::class, 'The controller for URI "/" is not callable: Method "protectedAction" on class "Symfony\Component\HttpKernel\Tests\Controller\ControllerTest" should be public and non-abstract'],
+ ['Symfony\Component\HttpKernel\Tests\Controller\ControllerTest::undefinedAction', \InvalidArgumentException::class, 'The controller for URI "/" is not callable: Expected method "undefinedAction" on class "Symfony\Component\HttpKernel\Tests\Controller\ControllerTest". Available methods: "publicAction", "staticAction"'],
+ ['Symfony\Component\HttpKernel\Tests\Controller\ControllerTest', \InvalidArgumentException::class, 'The controller for URI "/" is not callable: Controller class "Symfony\Component\HttpKernel\Tests\Controller\ControllerTest" cannot be called without a method name. You need to implement "__invoke" or use one of the available methods: "publicAction", "staticAction".'],
+ [[$controller, 'staticsAction'], \InvalidArgumentException::class, 'The controller for URI "/" is not callable: Expected method "staticsAction" on class "Symfony\Component\HttpKernel\Tests\Controller\ControllerTest", did you mean "staticAction"?'],
+ [[$controller, 'privateAction'], \InvalidArgumentException::class, 'The controller for URI "/" is not callable: Method "privateAction" on class "Symfony\Component\HttpKernel\Tests\Controller\ControllerTest" should be public and non-abstract'],
+ [[$controller, 'protectedAction'], \InvalidArgumentException::class, 'The controller for URI "/" is not callable: Method "protectedAction" on class "Symfony\Component\HttpKernel\Tests\Controller\ControllerTest" should be public and non-abstract'],
+ [[$controller, 'undefinedAction'], \InvalidArgumentException::class, 'The controller for URI "/" is not callable: Expected method "undefinedAction" on class "Symfony\Component\HttpKernel\Tests\Controller\ControllerTest". Available methods: "publicAction", "staticAction"'],
+ [$controller, \InvalidArgumentException::class, 'The controller for URI "/" is not callable: Controller class "Symfony\Component\HttpKernel\Tests\Controller\ControllerTest" cannot be called without a method name. You need to implement "__invoke" or use one of the available methods: "publicAction", "staticAction".'],
+ [['a' => 'foo', 'b' => 'bar'], \InvalidArgumentException::class, 'The controller for URI "/" is not callable: Invalid array callable, expected [controller, method].'],
];
}
diff --git a/src/Symfony/Component/HttpKernel/Tests/DataCollector/LoggerDataCollectorTest.php b/src/Symfony/Component/HttpKernel/Tests/DataCollector/LoggerDataCollectorTest.php
index adfba5d4220ee..9c175397fced7 100644
--- a/src/Symfony/Component/HttpKernel/Tests/DataCollector/LoggerDataCollectorTest.php
+++ b/src/Symfony/Component/HttpKernel/Tests/DataCollector/LoggerDataCollectorTest.php
@@ -176,13 +176,15 @@ public function getCollectTestData()
[
['message' => 'foo3', 'context' => ['exception' => new \ErrorException('warning', 0, E_USER_WARNING)], 'priority' => 100, 'priorityName' => 'DEBUG'],
['message' => 'foo3', 'context' => ['exception' => new SilencedErrorContext(E_USER_WARNING, __FILE__, __LINE__)], 'priority' => 100, 'priorityName' => 'DEBUG'],
+ ['message' => '0', 'context' => ['exception' => new SilencedErrorContext(E_USER_WARNING, __FILE__, __LINE__)], 'priority' => 100, 'priorityName' => 'DEBUG'],
],
[
['message' => 'foo3', 'context' => ['exception' => ['warning', E_USER_WARNING]], 'priority' => 100, 'priorityName' => 'DEBUG'],
['message' => 'foo3', 'context' => ['exception' => [E_USER_WARNING]], 'priority' => 100, 'priorityName' => 'DEBUG', 'errorCount' => 1, 'scream' => true],
+ ['message' => '0', 'context' => ['exception' => [E_USER_WARNING]], 'priority' => 100, 'priorityName' => 'DEBUG', 'errorCount' => 1, 'scream' => true],
],
0,
- 1,
+ 2,
];
}
}
diff --git a/src/Symfony/Component/Intl/Data/Bundle/Reader/JsonBundleReader.php b/src/Symfony/Component/Intl/Data/Bundle/Reader/JsonBundleReader.php
index a56fd6af1c233..555dd377c9341 100644
--- a/src/Symfony/Component/Intl/Data/Bundle/Reader/JsonBundleReader.php
+++ b/src/Symfony/Component/Intl/Data/Bundle/Reader/JsonBundleReader.php
@@ -46,7 +46,7 @@ public function read($path, $locale)
$data = json_decode(file_get_contents($fileName), true);
if (null === $data) {
- throw new RuntimeException(sprintf('The resource bundle "%s" contains invalid JSON: %s.', $fileName, json_last_error_msg()));
+ throw new RuntimeException(sprintf('The resource bundle "%s" contains invalid JSON: '.json_last_error_msg(), $fileName));
}
return $data;
diff --git a/src/Symfony/Component/Ldap/Adapter/ExtLdap/Collection.php b/src/Symfony/Component/Ldap/Adapter/ExtLdap/Collection.php
index 573ab0ce99475..5531a7c435321 100644
--- a/src/Symfony/Component/Ldap/Adapter/ExtLdap/Collection.php
+++ b/src/Symfony/Component/Ldap/Adapter/ExtLdap/Collection.php
@@ -53,7 +53,7 @@ public function count()
foreach ($searches as $search) {
$searchCount = ldap_count_entries($con, $search);
if (false === $searchCount) {
- throw new LdapException(sprintf('Error while retrieving entry count: %s.', ldap_error($con)));
+ throw new LdapException('Error while retrieving entry count: '.ldap_error($con));
}
$count += $searchCount;
}
@@ -76,7 +76,7 @@ public function getIterator()
$current = ldap_first_entry($con, $search);
if (false === $current) {
- throw new LdapException(sprintf('Could not rewind entries array: %s.', ldap_error($con)));
+ throw new LdapException('Could not rewind entries array: '.ldap_error($con));
}
yield $this->getSingleEntry($con, $current);
@@ -123,7 +123,7 @@ private function getSingleEntry($con, $current): Entry
$attributes = ldap_get_attributes($con, $current);
if (false === $attributes) {
- throw new LdapException(sprintf('Could not fetch attributes: %s.', ldap_error($con)));
+ throw new LdapException('Could not fetch attributes: '.ldap_error($con));
}
$attributes = $this->cleanupAttributes($attributes);
@@ -131,7 +131,7 @@ private function getSingleEntry($con, $current): Entry
$dn = ldap_get_dn($con, $current);
if (false === $dn) {
- throw new LdapException(sprintf('Could not fetch DN: %s.', ldap_error($con)));
+ throw new LdapException('Could not fetch DN: '.ldap_error($con));
}
return new Entry($dn, $attributes);
diff --git a/src/Symfony/Component/Ldap/Adapter/ExtLdap/Connection.php b/src/Symfony/Component/Ldap/Adapter/ExtLdap/Connection.php
index 53fc5a86025d4..b25a6eb447c7e 100644
--- a/src/Symfony/Component/Ldap/Adapter/ExtLdap/Connection.php
+++ b/src/Symfony/Component/Ldap/Adapter/ExtLdap/Connection.php
@@ -140,11 +140,11 @@ private function connect()
}
if (false === $this->connection) {
- throw new LdapException(sprintf('Could not connect to Ldap server: %s.', ldap_error($this->connection)));
+ throw new LdapException('Could not connect to Ldap server: '.ldap_error($this->connection));
}
if ('tls' === $this->config['encryption'] && false === @ldap_start_tls($this->connection)) {
- throw new LdapException(sprintf('Could not initiate TLS connection: %s.', ldap_error($this->connection)));
+ throw new LdapException('Could not initiate TLS connection: '.ldap_error($this->connection));
}
}
diff --git a/src/Symfony/Component/Ldap/Adapter/ExtLdap/EntryManager.php b/src/Symfony/Component/Ldap/Adapter/ExtLdap/EntryManager.php
index 20055c2f3b24f..c08e2c1550303 100644
--- a/src/Symfony/Component/Ldap/Adapter/ExtLdap/EntryManager.php
+++ b/src/Symfony/Component/Ldap/Adapter/ExtLdap/EntryManager.php
@@ -38,7 +38,7 @@ public function add(Entry $entry)
$con = $this->getConnectionResource();
if (!@ldap_add($con, $entry->getDn(), $entry->getAttributes())) {
- throw new LdapException(sprintf('Could not add entry "%s": %s.', $entry->getDn(), ldap_error($con)));
+ throw new LdapException(sprintf('Could not add entry "%s": '.ldap_error($con), $entry->getDn()));
}
return $this;
@@ -52,7 +52,7 @@ public function update(Entry $entry)
$con = $this->getConnectionResource();
if (!@ldap_modify($con, $entry->getDn(), $entry->getAttributes())) {
- throw new LdapException(sprintf('Could not update entry "%s": %s.', $entry->getDn(), ldap_error($con)));
+ throw new LdapException(sprintf('Could not update entry "%s": '.ldap_error($con), $entry->getDn()));
}
}
@@ -64,7 +64,7 @@ public function remove(Entry $entry)
$con = $this->getConnectionResource();
if (!@ldap_delete($con, $entry->getDn())) {
- throw new LdapException(sprintf('Could not remove entry "%s": %s.', $entry->getDn(), ldap_error($con)));
+ throw new LdapException(sprintf('Could not remove entry "%s": '.ldap_error($con), $entry->getDn()));
}
}
@@ -79,7 +79,7 @@ public function addAttributeValues(Entry $entry, string $attribute, array $value
$con = $this->getConnectionResource();
if (!@ldap_mod_add($con, $entry->getDn(), [$attribute => $values])) {
- throw new LdapException(sprintf('Could not add values to entry "%s", attribute %s: %s.', $entry->getDn(), $attribute, ldap_error($con)));
+ throw new LdapException(sprintf('Could not add values to entry "%s", attribute %s: '.ldap_error($con), $entry->getDn(), $attribute));
}
}
@@ -94,7 +94,7 @@ public function removeAttributeValues(Entry $entry, string $attribute, array $va
$con = $this->getConnectionResource();
if (!@ldap_mod_del($con, $entry->getDn(), [$attribute => $values])) {
- throw new LdapException(sprintf('Could not remove values from entry "%s", attribute %s: %s.', $entry->getDn(), $attribute, ldap_error($con)));
+ throw new LdapException(sprintf('Could not remove values from entry "%s", attribute %s: '.ldap_error($con), $entry->getDn(), $attribute));
}
}
@@ -106,7 +106,7 @@ public function rename(Entry $entry, $newRdn, $removeOldRdn = true)
$con = $this->getConnectionResource();
if (!@ldap_rename($con, $entry->getDn(), $newRdn, null, $removeOldRdn)) {
- throw new LdapException(sprintf('Could not rename entry "%s" to "%s": %s.', $entry->getDn(), $newRdn, ldap_error($con)));
+ throw new LdapException(sprintf('Could not rename entry "%s" to "%s": '.ldap_error($con), $entry->getDn(), $newRdn));
}
}
@@ -122,7 +122,7 @@ public function move(Entry $entry, string $newParent)
$rdn = $this->parseRdnFromEntry($entry);
// deleteOldRdn does not matter here, since the Rdn will not be changing in the move.
if (!@ldap_rename($con, $entry->getDn(), $rdn, $newParent, true)) {
- throw new LdapException(sprintf('Could not move entry "%s" to "%s": %s.', $entry->getDn(), $newParent, ldap_error($con)));
+ throw new LdapException(sprintf('Could not move entry "%s" to "%s": '.ldap_error($con), $entry->getDn(), $newParent));
}
}
diff --git a/src/Symfony/Component/Ldap/Adapter/ExtLdap/Query.php b/src/Symfony/Component/Ldap/Adapter/ExtLdap/Query.php
index 533d0e617618b..af6edfcfcc9fc 100644
--- a/src/Symfony/Component/Ldap/Adapter/ExtLdap/Query.php
+++ b/src/Symfony/Component/Ldap/Adapter/ExtLdap/Query.php
@@ -49,7 +49,7 @@ public function __destruct()
continue;
}
if (!ldap_free_result($result)) {
- throw new LdapException(sprintf('Could not free results: %s.', ldap_error($con)));
+ throw new LdapException('Could not free results: '.ldap_error($con));
}
}
$this->results = null;
diff --git a/src/Symfony/Component/Lock/composer.json b/src/Symfony/Component/Lock/composer.json
index 8e82285b40f1e..d604d2bd6837d 100644
--- a/src/Symfony/Component/Lock/composer.json
+++ b/src/Symfony/Component/Lock/composer.json
@@ -21,7 +21,6 @@
},
"require-dev": {
"doctrine/dbal": "~2.5",
- "mongodb/mongodb": "~1.1",
"predis/predis": "~1.0"
},
"conflict": {
diff --git a/src/Symfony/Component/Lock/phpunit.xml.dist b/src/Symfony/Component/Lock/phpunit.xml.dist
index 96c3ea1903abe..4a066573f7d08 100644
--- a/src/Symfony/Component/Lock/phpunit.xml.dist
+++ b/src/Symfony/Component/Lock/phpunit.xml.dist
@@ -12,7 +12,6 @@
-
diff --git a/src/Symfony/Component/Mailer/Bridge/Amazon/Transport/SesApiTransport.php b/src/Symfony/Component/Mailer/Bridge/Amazon/Transport/SesApiTransport.php
index c9cd0ad8cc6f6..95cbdbfd987b7 100644
--- a/src/Symfony/Component/Mailer/Bridge/Amazon/Transport/SesApiTransport.php
+++ b/src/Symfony/Component/Mailer/Bridge/Amazon/Transport/SesApiTransport.php
@@ -65,7 +65,7 @@ protected function doSendApi(SentMessage $sentMessage, Email $email, Envelope $e
$result = new \SimpleXMLElement($response->getContent(false));
if (200 !== $response->getStatusCode()) {
- throw new HttpTransportException(sprintf('Unable to send an email: %s (code %d).', $result->Error->Message, $result->Error->Code), $response);
+ throw new HttpTransportException(sprintf('Unable to send an email: '.$result->Error->Message.' (code %d).', $result->Error->Code), $response);
}
$property = $payload['Action'].'Result';
diff --git a/src/Symfony/Component/Mailer/Bridge/Amazon/Transport/SesHttpTransport.php b/src/Symfony/Component/Mailer/Bridge/Amazon/Transport/SesHttpTransport.php
index 9cedbdf4c7f7c..f11e970f23cf5 100644
--- a/src/Symfony/Component/Mailer/Bridge/Amazon/Transport/SesHttpTransport.php
+++ b/src/Symfony/Component/Mailer/Bridge/Amazon/Transport/SesHttpTransport.php
@@ -65,7 +65,7 @@ protected function doSendHttp(SentMessage $message): ResponseInterface
$result = new \SimpleXMLElement($response->getContent(false));
if (200 !== $response->getStatusCode()) {
- throw new HttpTransportException(sprintf('Unable to send an email: %s (code %d).', $result->Error->Message, $result->Error->Code), $response);
+ throw new HttpTransportException(sprintf('Unable to send an email: '.$result->Error->Message.' (code %d).', $result->Error->Code), $response);
}
$message->setMessageId($result->SendRawEmailResult->MessageId);
diff --git a/src/Symfony/Component/Mailer/Bridge/Mailchimp/Transport/MandrillApiTransport.php b/src/Symfony/Component/Mailer/Bridge/Mailchimp/Transport/MandrillApiTransport.php
index 9b7c181b2943d..6ea7eae4d1eed 100644
--- a/src/Symfony/Component/Mailer/Bridge/Mailchimp/Transport/MandrillApiTransport.php
+++ b/src/Symfony/Component/Mailer/Bridge/Mailchimp/Transport/MandrillApiTransport.php
@@ -51,7 +51,7 @@ protected function doSendApi(SentMessage $sentMessage, Email $email, Envelope $e
$result = $response->toArray(false);
if (200 !== $response->getStatusCode()) {
if ('error' === ($result['status'] ?? false)) {
- throw new HttpTransportException(sprintf('Unable to send an email: %s (code %d).', $result['message'], $result['code']), $response);
+ throw new HttpTransportException(sprintf('Unable to send an email: '.$result['message'].' (code %d).', $result['code']), $response);
}
throw new HttpTransportException(sprintf('Unable to send an email (code %d).', $result['code']), $response);
diff --git a/src/Symfony/Component/Mailer/Bridge/Mailchimp/Transport/MandrillHttpTransport.php b/src/Symfony/Component/Mailer/Bridge/Mailchimp/Transport/MandrillHttpTransport.php
index 92f90b8563fa5..d2058799ec957 100644
--- a/src/Symfony/Component/Mailer/Bridge/Mailchimp/Transport/MandrillHttpTransport.php
+++ b/src/Symfony/Component/Mailer/Bridge/Mailchimp/Transport/MandrillHttpTransport.php
@@ -58,7 +58,7 @@ protected function doSendHttp(SentMessage $message): ResponseInterface
$result = $response->toArray(false);
if (200 !== $response->getStatusCode()) {
if ('error' === ($result['status'] ?? false)) {
- throw new HttpTransportException(sprintf('Unable to send an email: %s (code %d).', $result['message'], $result['code']), $response);
+ throw new HttpTransportException(sprintf('Unable to send an email: '.$result['message'].' (code %d).', $result['code']), $response);
}
throw new HttpTransportException(sprintf('Unable to send an email (code %d).', $result['code']), $response);
diff --git a/src/Symfony/Component/Mailer/Bridge/Mailgun/Transport/MailgunApiTransport.php b/src/Symfony/Component/Mailer/Bridge/Mailgun/Transport/MailgunApiTransport.php
index c8ed383fcc66b..2b6075c723eed 100644
--- a/src/Symfony/Component/Mailer/Bridge/Mailgun/Transport/MailgunApiTransport.php
+++ b/src/Symfony/Component/Mailer/Bridge/Mailgun/Transport/MailgunApiTransport.php
@@ -65,10 +65,10 @@ protected function doSendApi(SentMessage $sentMessage, Email $email, Envelope $e
$result = $response->toArray(false);
if (200 !== $response->getStatusCode()) {
if ('application/json' === $response->getHeaders(false)['content-type'][0]) {
- throw new HttpTransportException(sprintf('Unable to send an email: %s (code %d).', $result['message'], $response->getStatusCode()), $response);
+ throw new HttpTransportException(sprintf('Unable to send an email: '.$result['message'].' (code %d).', $response->getStatusCode()), $response);
}
- throw new HttpTransportException(sprintf('Unable to send an email: %s (code %d).', $response->getContent(false), $response->getStatusCode()), $response);
+ throw new HttpTransportException(sprintf('Unable to send an email: '.$response->getContent(false).' (code %d).', $response->getStatusCode()), $response);
}
$sentMessage->setMessageId($result['id']);
diff --git a/src/Symfony/Component/Mailer/Bridge/Mailgun/Transport/MailgunHttpTransport.php b/src/Symfony/Component/Mailer/Bridge/Mailgun/Transport/MailgunHttpTransport.php
index 3ed6f73369fc8..72fbaac1e77e3 100644
--- a/src/Symfony/Component/Mailer/Bridge/Mailgun/Transport/MailgunHttpTransport.php
+++ b/src/Symfony/Component/Mailer/Bridge/Mailgun/Transport/MailgunHttpTransport.php
@@ -67,10 +67,10 @@ protected function doSendHttp(SentMessage $message): ResponseInterface
$result = $response->toArray(false);
if (200 !== $response->getStatusCode()) {
if ('application/json' === $response->getHeaders(false)['content-type'][0]) {
- throw new HttpTransportException(sprintf('Unable to send an email: %s (code %d).', $result['message'], $response->getStatusCode()), $response);
+ throw new HttpTransportException(sprintf('Unable to send an email: '.$result['message'].' (code %d).', $response->getStatusCode()), $response);
}
- throw new HttpTransportException(sprintf('Unable to send an email: %s (code %d).', $response->getContent(false), $response->getStatusCode()), $response);
+ throw new HttpTransportException(sprintf('Unable to send an email: '.$response->getContent(false).' (code %d).', $response->getStatusCode()), $response);
}
$message->setMessageId($result['id']);
diff --git a/src/Symfony/Component/Mailer/Bridge/Postmark/Transport/PostmarkApiTransport.php b/src/Symfony/Component/Mailer/Bridge/Postmark/Transport/PostmarkApiTransport.php
index 352b2eded1568..a7c185264097f 100644
--- a/src/Symfony/Component/Mailer/Bridge/Postmark/Transport/PostmarkApiTransport.php
+++ b/src/Symfony/Component/Mailer/Bridge/Postmark/Transport/PostmarkApiTransport.php
@@ -54,7 +54,7 @@ protected function doSendApi(SentMessage $sentMessage, Email $email, Envelope $e
$result = $response->toArray(false);
if (200 !== $response->getStatusCode()) {
- throw new HttpTransportException(sprintf('Unable to send an email: %s (code %d).', $result['Message'], $result['ErrorCode']), $response);
+ throw new HttpTransportException(sprintf('Unable to send an email: '.$result['Message'].' (code %d).', $result['ErrorCode']), $response);
}
$sentMessage->setMessageId($result['MessageID']);
diff --git a/src/Symfony/Component/Mailer/Bridge/Sendgrid/Transport/SendgridApiTransport.php b/src/Symfony/Component/Mailer/Bridge/Sendgrid/Transport/SendgridApiTransport.php
index 550fd4bf331a5..aee333ddb16cf 100644
--- a/src/Symfony/Component/Mailer/Bridge/Sendgrid/Transport/SendgridApiTransport.php
+++ b/src/Symfony/Component/Mailer/Bridge/Sendgrid/Transport/SendgridApiTransport.php
@@ -53,7 +53,7 @@ protected function doSendApi(SentMessage $sentMessage, Email $email, Envelope $e
if (202 !== $response->getStatusCode()) {
$errors = $response->toArray(false);
- throw new HttpTransportException(sprintf('Unable to send an email: %s (code %d).', implode('; ', array_column($errors['errors'], 'message')), $response->getStatusCode()), $response);
+ throw new HttpTransportException(sprintf('Unable to send an email: '.implode('; ', array_column($errors['errors'], 'message')).' (code %d).', $response->getStatusCode()), $response);
}
$sentMessage->setMessageId($response->getHeaders(false)['x-message-id'][0]);
diff --git a/src/Symfony/Component/Mailer/EventListener/EnvelopeListener.php b/src/Symfony/Component/Mailer/EventListener/EnvelopeListener.php
index cbb3922a19a46..570a9e39551f5 100644
--- a/src/Symfony/Component/Mailer/EventListener/EnvelopeListener.php
+++ b/src/Symfony/Component/Mailer/EventListener/EnvelopeListener.php
@@ -14,6 +14,7 @@
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Mailer\Event\MessageEvent;
use Symfony\Component\Mime\Address;
+use Symfony\Component\Mime\Message;
/**
* Manipulates the Envelope of a Message.
@@ -43,6 +44,13 @@ public function onMessage(MessageEvent $event): void
{
if ($this->sender) {
$event->getEnvelope()->setSender($this->sender);
+
+ $message = $event->getMessage();
+ if ($message instanceof Message) {
+ if (!$message->getHeaders()->has('Sender') && !$message->getHeaders()->has('From')) {
+ $message->getHeaders()->addMailboxHeader('Sender', $this->sender->getAddress());
+ }
+ }
}
if ($this->recipients) {
diff --git a/src/Symfony/Component/Mailer/Tests/Transport/Smtp/SmtpTransportTest.php b/src/Symfony/Component/Mailer/Tests/Transport/Smtp/SmtpTransportTest.php
index 7ec8313612049..2c8d91e9e4402 100644
--- a/src/Symfony/Component/Mailer/Tests/Transport/Smtp/SmtpTransportTest.php
+++ b/src/Symfony/Component/Mailer/Tests/Transport/Smtp/SmtpTransportTest.php
@@ -13,6 +13,7 @@
use PHPUnit\Framework\TestCase;
use Symfony\Component\Mailer\Envelope;
+use Symfony\Component\Mailer\Exception\TransportException;
use Symfony\Component\Mailer\Transport\Smtp\SmtpTransport;
use Symfony\Component\Mailer\Transport\Smtp\Stream\AbstractStream;
use Symfony\Component\Mailer\Transport\Smtp\Stream\SocketStream;
@@ -43,6 +44,29 @@ public function testSendDoesNotPingBelowThreshold(): void
$this->assertNotContains("NOOP\r\n", $stream->getCommands());
}
+ public function testSendPingAfterTransportException(): void
+ {
+ $stream = new DummyStream();
+ $envelope = new Envelope(new Address('sender@example.org'), [new Address('recipient@example.org')]);
+
+ $transport = new SmtpTransport($stream);
+ $transport->send(new RawMessage('Message 1'), $envelope);
+ $stream->close();
+ $catch = false;
+
+ try {
+ $transport->send(new RawMessage('Message 2'), $envelope);
+ } catch (TransportException $exception) {
+ $catch = true;
+ }
+ $this->assertTrue($catch);
+ $this->assertTrue($stream->isClosed());
+
+ $transport->send(new RawMessage('Message 3'), $envelope);
+
+ $this->assertFalse($stream->isClosed());
+ }
+
public function testSendDoesPingAboveThreshold(): void
{
$stream = new DummyStream();
@@ -76,13 +100,23 @@ class DummyStream extends AbstractStream
*/
private $commands;
+ /**
+ * @var bool
+ */
+ private $closed = true;
+
public function initialize(): void
{
+ $this->closed = false;
$this->nextResponse = '220 localhost';
}
public function write(string $bytes, $debug = true): void
{
+ if ($this->closed) {
+ throw new TransportException('Unable to write bytes on the wire.');
+ }
+
$this->commands[] = $bytes;
if (0 === strpos($bytes, 'DATA')) {
@@ -120,4 +154,14 @@ protected function getReadConnectionDescription(): string
{
return 'null';
}
+
+ public function close(): void
+ {
+ $this->closed = true;
+ }
+
+ public function isClosed(): bool
+ {
+ return $this->closed;
+ }
}
diff --git a/src/Symfony/Component/Mailer/Tests/Transport/Smtp/Stream/SocketStreamTest.php b/src/Symfony/Component/Mailer/Tests/Transport/Smtp/Stream/SocketStreamTest.php
index d7912f9ccba2d..a67a1629d0169 100644
--- a/src/Symfony/Component/Mailer/Tests/Transport/Smtp/Stream/SocketStreamTest.php
+++ b/src/Symfony/Component/Mailer/Tests/Transport/Smtp/Stream/SocketStreamTest.php
@@ -19,7 +19,7 @@ class SocketStreamTest extends TestCase
public function testSocketErrorNoConnection()
{
$this->expectException('Symfony\Component\Mailer\Exception\TransportException');
- $this->expectExceptionMessageRegExp('/Connection refused|unable to connect/');
+ $this->expectExceptionMessageMatches('/Connection refused|unable to connect/');
$s = new SocketStream();
$s->setTimeout(0.1);
$s->setPort(9999);
@@ -29,7 +29,7 @@ public function testSocketErrorNoConnection()
public function testSocketErrorBeforeConnectError()
{
$this->expectException('Symfony\Component\Mailer\Exception\TransportException');
- $this->expectExceptionMessageRegExp('/no valid certs found cafile stream|Unable to find the socket transport "ssl"/');
+ $this->expectExceptionMessageMatches('/no valid certs found cafile stream|Unable to find the socket transport "ssl"/');
$s = new SocketStream();
$s->setStreamOptions([
'ssl' => [
diff --git a/src/Symfony/Component/Mailer/Transport/AbstractApiTransport.php b/src/Symfony/Component/Mailer/Transport/AbstractApiTransport.php
index e1a2d5eee2ac8..37b92a699bbc6 100644
--- a/src/Symfony/Component/Mailer/Transport/AbstractApiTransport.php
+++ b/src/Symfony/Component/Mailer/Transport/AbstractApiTransport.php
@@ -31,7 +31,7 @@ protected function doSendHttp(SentMessage $message): ResponseInterface
try {
$email = MessageConverter::toEmail($message->getOriginalMessage());
} catch (\Exception $e) {
- throw new RuntimeException(sprintf('Unable to send message with the "%s" transport: %s.', __CLASS__, $e->getMessage()), 0, $e);
+ throw new RuntimeException(sprintf('Unable to send message with the "%s" transport: '.$e->getMessage(), __CLASS__), 0, $e);
}
return $this->doSendApi($message, $email, $message->getEnvelope());
diff --git a/src/Symfony/Component/Mailer/Transport/AbstractTransport.php b/src/Symfony/Component/Mailer/Transport/AbstractTransport.php
index 1bc3fa12a5616..33f75fb3faa4f 100644
--- a/src/Symfony/Component/Mailer/Transport/AbstractTransport.php
+++ b/src/Symfony/Component/Mailer/Transport/AbstractTransport.php
@@ -63,10 +63,6 @@ public function send(RawMessage $message, Envelope $envelope = null): ?SentMessa
$envelope = $event->getEnvelope();
}
- if (!$envelope->getRecipients()) {
- return null;
- }
-
$message = new SentMessage($message, $envelope);
$this->doSend($message);
diff --git a/src/Symfony/Component/Mailer/Transport/Smtp/EsmtpTransport.php b/src/Symfony/Component/Mailer/Transport/Smtp/EsmtpTransport.php
index 3f68227ba4190..afa522ae97e13 100644
--- a/src/Symfony/Component/Mailer/Transport/Smtp/EsmtpTransport.php
+++ b/src/Symfony/Component/Mailer/Transport/Smtp/EsmtpTransport.php
@@ -106,6 +106,9 @@ protected function doHeloCommand(): void
/** @var SocketStream $stream */
$stream = $this->getStream();
+ // WARNING: !$stream->isTLS() is right, 100% sure :)
+ // if you think that the ! should be removed, read the code again
+ // if doing so "fixes" your issue then it probably means your SMTP server behaves incorrectly or is wrongly configured
if (!$stream->isTLS() && \defined('OPENSSL_VERSION_NUMBER') && \array_key_exists('STARTTLS', $capabilities)) {
$this->executeCommand("STARTTLS\r\n", [220]);
diff --git a/src/Symfony/Component/Mailer/Transport/Smtp/SmtpTransport.php b/src/Symfony/Component/Mailer/Transport/Smtp/SmtpTransport.php
index 091b5e2bc5a60..26f2057f941ae 100644
--- a/src/Symfony/Component/Mailer/Transport/Smtp/SmtpTransport.php
+++ b/src/Symfony/Component/Mailer/Transport/Smtp/SmtpTransport.php
@@ -206,12 +206,11 @@ protected function doSend(SentMessage $message): void
$this->stream->flush();
$this->executeCommand("\r\n.\r\n", [250]);
$message->appendDebug($this->stream->getDebug());
+ $this->lastMessageTime = microtime(true);
} catch (TransportExceptionInterface $e) {
$e->appendDebug($this->stream->getDebug());
-
+ $this->lastMessageTime = 0;
throw $e;
- } finally {
- $this->lastMessageTime = microtime(true);
}
}
diff --git a/src/Symfony/Component/Mailer/Transport/Smtp/Stream/ProcessStream.php b/src/Symfony/Component/Mailer/Transport/Smtp/Stream/ProcessStream.php
index 455f739a15faa..a8a8603807d27 100644
--- a/src/Symfony/Component/Mailer/Transport/Smtp/Stream/ProcessStream.php
+++ b/src/Symfony/Component/Mailer/Transport/Smtp/Stream/ProcessStream.php
@@ -41,7 +41,7 @@ public function initialize(): void
$this->stream = proc_open($this->command, $descriptorSpec, $pipes);
stream_set_blocking($pipes[2], false);
if ($err = stream_get_contents($pipes[2])) {
- throw new TransportException(sprintf('Process could not be started: %s.', $err));
+ throw new TransportException('Process could not be started: '.$err);
}
$this->in = &$pipes[0];
$this->out = &$pipes[1];
diff --git a/src/Symfony/Component/Mailer/Transport/Smtp/Stream/SocketStream.php b/src/Symfony/Component/Mailer/Transport/Smtp/Stream/SocketStream.php
index debeeb4b01cb9..5aa86296cbc03 100644
--- a/src/Symfony/Component/Mailer/Transport/Smtp/Stream/SocketStream.php
+++ b/src/Symfony/Component/Mailer/Transport/Smtp/Stream/SocketStream.php
@@ -135,7 +135,7 @@ public function initialize(): void
$streamContext = stream_context_create($options);
set_error_handler(function ($type, $msg) {
- throw new TransportException(sprintf('Connection could not be established with host "%s": %s.', $this->url, $msg));
+ throw new TransportException(sprintf('Connection could not be established with host "%s": '.$msg, $this->url));
});
try {
$this->stream = stream_socket_client($this->url, $errno, $errstr, $this->timeout, STREAM_CLIENT_CONNECT, $streamContext);
diff --git a/src/Symfony/Component/Messenger/Tests/Transport/Receiver/SingleMessageReceiverTest.php b/src/Symfony/Component/Messenger/Tests/Transport/Receiver/SingleMessageReceiverTest.php
index e8f5ef4ce1720..bfebfc5aa3f02 100644
--- a/src/Symfony/Component/Messenger/Tests/Transport/Receiver/SingleMessageReceiverTest.php
+++ b/src/Symfony/Component/Messenger/Tests/Transport/Receiver/SingleMessageReceiverTest.php
@@ -9,7 +9,7 @@
* file that was distributed with this source code.
*/
-namespace Symfony\Component\Messenger\Tests\Transport\Sender;
+namespace Symfony\Component\Messenger\Tests\Transport\Receiver;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Messenger\Envelope;
diff --git a/src/Symfony/Component/Messenger/Tests/Transport/RedisExt/ConnectionTest.php b/src/Symfony/Component/Messenger/Tests/Transport/RedisExt/ConnectionTest.php
index 0be034dd3de3d..e278cfb0ddf0e 100644
--- a/src/Symfony/Component/Messenger/Tests/Transport/RedisExt/ConnectionTest.php
+++ b/src/Symfony/Component/Messenger/Tests/Transport/RedisExt/ConnectionTest.php
@@ -62,6 +62,14 @@ public function testFromDsnWithOptions()
);
}
+ public function testFromDsnWithOptionsAndTrailingSlash()
+ {
+ $this->assertEquals(
+ Connection::fromDsn('redis://localhost/', ['stream' => 'queue', 'group' => 'group1', 'consumer' => 'consumer1', 'auto_setup' => false, 'serializer' => 2]),
+ Connection::fromDsn('redis://localhost/queue/group1/consumer1?serializer=2&auto_setup=0')
+ );
+ }
+
public function testFromDsnWithQueryOptions()
{
$this->assertEquals(
diff --git a/src/Symfony/Component/Messenger/Tests/Transport/Serialization/PhpSerializerTest.php b/src/Symfony/Component/Messenger/Tests/Transport/Serialization/PhpSerializerTest.php
index 6439873fe94cc..5076577b023fc 100644
--- a/src/Symfony/Component/Messenger/Tests/Transport/Serialization/PhpSerializerTest.php
+++ b/src/Symfony/Component/Messenger/Tests/Transport/Serialization/PhpSerializerTest.php
@@ -44,7 +44,7 @@ public function testDecodingFailsWithMissingBodyKey()
public function testDecodingFailsWithBadFormat()
{
$this->expectException(MessageDecodingFailedException::class);
- $this->expectExceptionMessageRegExp('/Could not decode/');
+ $this->expectExceptionMessageMatches('/Could not decode/');
$serializer = new PhpSerializer();
@@ -56,7 +56,7 @@ public function testDecodingFailsWithBadFormat()
public function testDecodingFailsWithBadClass()
{
$this->expectException(MessageDecodingFailedException::class);
- $this->expectExceptionMessageRegExp('/class "ReceivedSt0mp" not found/');
+ $this->expectExceptionMessageMatches('/class "ReceivedSt0mp" not found/');
$serializer = new PhpSerializer();
diff --git a/src/Symfony/Component/Messenger/Tests/Transport/Sync/SyncTransportFactoryTest.php b/src/Symfony/Component/Messenger/Tests/Transport/Sync/SyncTransportFactoryTest.php
index 021c7ae9706a4..63aa43fb7ca0f 100644
--- a/src/Symfony/Component/Messenger/Tests/Transport/Sync/SyncTransportFactoryTest.php
+++ b/src/Symfony/Component/Messenger/Tests/Transport/Sync/SyncTransportFactoryTest.php
@@ -9,7 +9,7 @@
* file that was distributed with this source code.
*/
-namespace Symfony\Component\Messenger\Tests\Transport\AmqpExt;
+namespace Symfony\Component\Messenger\Tests\Transport\Sync;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Messenger\MessageBusInterface;
diff --git a/src/Symfony/Component/Messenger/Tests/Transport/Sync/SyncTransportTest.php b/src/Symfony/Component/Messenger/Tests/Transport/Sync/SyncTransportTest.php
index 13549e27582a1..8ea4eb2e00522 100644
--- a/src/Symfony/Component/Messenger/Tests/Transport/Sync/SyncTransportTest.php
+++ b/src/Symfony/Component/Messenger/Tests/Transport/Sync/SyncTransportTest.php
@@ -9,7 +9,7 @@
* file that was distributed with this source code.
*/
-namespace Symfony\Component\Messenger\Tests\Transport\AmqpExt;
+namespace Symfony\Component\Messenger\Tests\Transport\Sync;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Messenger\Envelope;
diff --git a/src/Symfony/Component/Messenger/Transport/RedisExt/Connection.php b/src/Symfony/Component/Messenger/Transport/RedisExt/Connection.php
index 504fe0a10fa98..bb818512c2fda 100644
--- a/src/Symfony/Component/Messenger/Transport/RedisExt/Connection.php
+++ b/src/Symfony/Component/Messenger/Transport/RedisExt/Connection.php
@@ -56,11 +56,17 @@ public function __construct(array $configuration, array $connectionCredentials =
$this->connection->setOption(\Redis::OPT_SERIALIZER, $redisOptions['serializer'] ?? \Redis::SERIALIZER_PHP);
if (isset($connectionCredentials['auth']) && !$this->connection->auth($connectionCredentials['auth'])) {
- throw new InvalidArgumentException(sprintf('Redis connection failed: %s.', $redis->getLastError()));
+ throw new InvalidArgumentException('Redis connection failed: '.$redis->getLastError());
}
if (($dbIndex = $configuration['dbindex'] ?? self::DEFAULT_OPTIONS['dbindex']) && !$this->connection->select($dbIndex)) {
- throw new InvalidArgumentException(sprintf('Redis connection failed: %s.', $redis->getLastError()));
+ throw new InvalidArgumentException('Redis connection failed: '.$redis->getLastError());
+ }
+
+ foreach (['stream', 'group', 'consumer'] as $key) {
+ if (isset($configuration[$key]) && '' === $configuration[$key]) {
+ throw new InvalidArgumentException(sprintf('"%s" should be configured, got an empty string.', $key));
+ }
}
$this->stream = $configuration['stream'] ?? self::DEFAULT_OPTIONS['stream'];
@@ -77,7 +83,7 @@ public static function fromDsn(string $dsn, array $redisOptions = [], \Redis $re
throw new InvalidArgumentException(sprintf('The given Redis DSN "%s" is invalid.', $dsn));
}
- $pathParts = explode('/', $parsedUrl['path'] ?? '');
+ $pathParts = explode('/', rtrim($parsedUrl['path'] ?? '', '/'));
$stream = $pathParts[1] ?? $redisOptions['stream'] ?? null;
$group = $pathParts[2] ?? $redisOptions['group'] ?? null;
diff --git a/src/Symfony/Component/Messenger/Transport/Serialization/Serializer.php b/src/Symfony/Component/Messenger/Transport/Serialization/Serializer.php
index 8d466500fd9a6..22d48f7e23012 100644
--- a/src/Symfony/Component/Messenger/Transport/Serialization/Serializer.php
+++ b/src/Symfony/Component/Messenger/Transport/Serialization/Serializer.php
@@ -80,7 +80,7 @@ public function decode(array $encodedEnvelope): Envelope
try {
$message = $this->serializer->deserialize($encodedEnvelope['body'], $encodedEnvelope['headers']['type'], $this->format, $context);
} catch (ExceptionInterface $e) {
- throw new MessageDecodingFailedException(sprintf('Could not decode message: %s.', $e->getMessage()), $e->getCode(), $e);
+ throw new MessageDecodingFailedException('Could not decode message: '.$e->getMessage(), $e->getCode(), $e);
}
return new Envelope($message, $stamps);
@@ -118,7 +118,7 @@ private function decodeStamps(array $encodedEnvelope): array
try {
$stamps[] = $this->serializer->deserialize($value, substr($name, \strlen(self::STAMP_HEADER_PREFIX)).'[]', $this->format, $this->context);
} catch (ExceptionInterface $e) {
- throw new MessageDecodingFailedException(sprintf('Could not decode stamp: %s.', $e->getMessage()), $e->getCode(), $e);
+ throw new MessageDecodingFailedException('Could not decode stamp: '.$e->getMessage(), $e->getCode(), $e);
}
}
if ($stamps) {
diff --git a/src/Symfony/Component/Mime/Message.php b/src/Symfony/Component/Mime/Message.php
index 5b4e67f1dbc5b..94a49764e4912 100644
--- a/src/Symfony/Component/Mime/Message.php
+++ b/src/Symfony/Component/Mime/Message.php
@@ -74,7 +74,10 @@ public function getPreparedHeaders(): Headers
$headers = clone $this->headers;
if (!$headers->has('From')) {
- throw new LogicException('An email must have a "From" header.');
+ if (!$headers->has('Sender')) {
+ throw new LogicException('An email must have a "From" or a "Sender" header.');
+ }
+ $headers->addMailboxListHeader('From', [$headers->get('Sender')->getAddress()]);
}
$headers->addTextHeader('MIME-Version', '1.0');
@@ -119,8 +122,12 @@ public function toIterable(): iterable
public function ensureValidity()
{
- if (!$this->headers->has('From')) {
- throw new LogicException('An email must have a "From" header.');
+ if (!$this->headers->has('To')) {
+ throw new LogicException('An email must have a "To" header.');
+ }
+
+ if (!$this->headers->has('From') && !$this->headers->has('Sender')) {
+ throw new LogicException('An email must have a "From" or a "Sender" header.');
}
parent::ensureValidity();
@@ -133,7 +140,7 @@ public function generateMessageId(): string
} elseif ($this->headers->has('From')) {
$sender = $this->headers->get('From')->getAddresses()[0];
} else {
- throw new LogicException('An email must have a "From" or a "Sender" header to compute a Messsage ID.');
+ throw new LogicException('An email must have a "From" or a "Sender" header.');
}
return bin2hex(random_bytes(16)).strstr($sender->getAddress(), '@');
diff --git a/src/Symfony/Component/Mime/Tests/Crypto/SMimeSignerTest.php b/src/Symfony/Component/Mime/Tests/Crypto/SMimeSignerTest.php
index 0a86c3c90e1e7..5522bc6f55cd7 100644
--- a/src/Symfony/Component/Mime/Tests/Crypto/SMimeSignerTest.php
+++ b/src/Symfony/Component/Mime/Tests/Crypto/SMimeSignerTest.php
@@ -99,6 +99,7 @@ public function testSignedMessageWithBcc()
{
$message = (new Email())
->date(new \DateTime('2019-04-07 10:36:30', new \DateTimeZone('Europe/Paris')))
+ ->to('fabien@symfony.com')
->addBcc('fabien@symfony.com', 's.stok@rollerscapes.net')
->subject('I am your sign of fear')
->from('noreply@example.com')
@@ -115,8 +116,9 @@ public function testSignedMessageWithAttachments()
$message = new Email((new Headers())
->addDateHeader('Date', new \DateTime('2019-04-07 10:36:30', new \DateTimeZone('Europe/Paris')))
->addMailboxListHeader('From', ['fabien@symfony.com'])
+ ->addMailboxListHeader('To', ['fabien@symfony.com'])
);
- $message->html($content = 'html content ');
+ $message->html('html content ');
$message->text('text content');
$message->attach(fopen(__DIR__.'/../Fixtures/mimetypes/test', 'r'));
$message->attach(fopen(__DIR__.'/../Fixtures/mimetypes/test.gif', 'r'), 'test.gif');
diff --git a/src/Symfony/Component/Mime/Tests/EmailTest.php b/src/Symfony/Component/Mime/Tests/EmailTest.php
index bfd30053af09e..230df0791e15b 100644
--- a/src/Symfony/Component/Mime/Tests/EmailTest.php
+++ b/src/Symfony/Component/Mime/Tests/EmailTest.php
@@ -251,62 +251,62 @@ public function testGenerateBody()
$att = new DataPart($file = fopen(__DIR__.'/Fixtures/mimetypes/test', 'r'));
$img = new DataPart($image = fopen(__DIR__.'/Fixtures/mimetypes/test.gif', 'r'), 'test.gif');
- $e = (new Email())->from('me@example.com');
+ $e = (new Email())->from('me@example.com')->to('you@example.com');
$e->text('text content');
$this->assertEquals($text, $e->getBody());
$this->assertEquals('text content', $e->getTextBody());
- $e = (new Email())->from('me@example.com');
+ $e = (new Email())->from('me@example.com')->to('you@example.com');
$e->html('html content');
$this->assertEquals($html, $e->getBody());
$this->assertEquals('html content', $e->getHtmlBody());
- $e = (new Email())->from('me@example.com');
+ $e = (new Email())->from('me@example.com')->to('you@example.com');
$e->html('html content');
$e->text('text content');
$this->assertEquals(new AlternativePart($text, $html), $e->getBody());
- $e = (new Email())->from('me@example.com');
+ $e = (new Email())->from('me@example.com')->to('you@example.com');
$e->html('html content', 'iso-8859-1');
$e->text('text content', 'iso-8859-1');
$this->assertEquals('iso-8859-1', $e->getTextCharset());
$this->assertEquals('iso-8859-1', $e->getHtmlCharset());
$this->assertEquals(new AlternativePart(new TextPart('text content', 'iso-8859-1'), new TextPart('html content', 'iso-8859-1', 'html')), $e->getBody());
- $e = (new Email())->from('me@example.com');
+ $e = (new Email())->from('me@example.com')->to('you@example.com');
$e->attach($file);
$e->text('text content');
$this->assertEquals(new MixedPart($text, $att), $e->getBody());
- $e = (new Email())->from('me@example.com');
+ $e = (new Email())->from('me@example.com')->to('you@example.com');
$e->attach($file);
$e->html('html content');
$this->assertEquals(new MixedPart($html, $att), $e->getBody());
- $e = (new Email())->from('me@example.com');
+ $e = (new Email())->from('me@example.com')->to('you@example.com');
$e->attach($file);
$this->assertEquals(new MixedPart($att), $e->getBody());
- $e = (new Email())->from('me@example.com');
+ $e = (new Email())->from('me@example.com')->to('you@example.com');
$e->html('html content');
$e->text('text content');
$e->attach($file);
$this->assertEquals(new MixedPart(new AlternativePart($text, $html), $att), $e->getBody());
- $e = (new Email())->from('me@example.com');
+ $e = (new Email())->from('me@example.com')->to('you@example.com');
$e->html('html content');
$e->text('text content');
$e->attach($file);
$e->attach($image, 'test.gif');
$this->assertEquals(new MixedPart(new AlternativePart($text, $html), $att, $img), $e->getBody());
- $e = (new Email())->from('me@example.com');
+ $e = (new Email())->from('me@example.com')->to('you@example.com');
$e->text('text content');
$e->attach($file);
$e->attach($image, 'test.gif');
$this->assertEquals(new MixedPart($text, $att, $img), $e->getBody());
- $e = (new Email())->from('me@example.com');
+ $e = (new Email())->from('me@example.com')->to('you@example.com');
$e->html($content = 'html content ');
$e->text('text content');
$e->attach($file);
@@ -314,7 +314,7 @@ public function testGenerateBody()
$fullhtml = new TextPart($content, 'utf-8', 'html');
$this->assertEquals(new MixedPart(new AlternativePart($text, $fullhtml), $att, $img), $e->getBody());
- $e = (new Email())->from('me@example.com');
+ $e = (new Email())->from('me@example.com')->to('you@example.com');
$e->html($content = 'html content ');
$e->text('text content');
$e->attach($file);
@@ -334,7 +334,7 @@ public function testGenerateBody()
fwrite($r, $content);
rewind($r);
- $e = (new Email())->from('me@example.com');
+ $e = (new Email())->from('me@example.com')->to('you@example.com');
$e->html($r);
// embedding the same image twice results in one image only in the email
$e->embed($image, 'test.gif');
@@ -373,6 +373,7 @@ public function testSerialize()
$e = new Email();
$e->from('fabien@symfony.com');
+ $e->to('you@example.com');
$e->text($r);
$e->html($r);
$name = __DIR__.'/Fixtures/mimetypes/test';
diff --git a/src/Symfony/Component/Mime/Tests/MessageConverterTest.php b/src/Symfony/Component/Mime/Tests/MessageConverterTest.php
index 6a78086246377..a0e71a08a9416 100644
--- a/src/Symfony/Component/Mime/Tests/MessageConverterTest.php
+++ b/src/Symfony/Component/Mime/Tests/MessageConverterTest.php
@@ -21,7 +21,7 @@ class MessageConverterTest extends TestCase
public function testToEmail()
{
$file = file_get_contents(__DIR__.'/Fixtures/mimetypes/test.gif');
- $email = (new Email())->from('fabien@symfony.com');
+ $email = (new Email())->from('fabien@symfony.com')->to('you@example.com');
$this->assertSame($email, MessageConverter::toEmail($email));
$this->assertConversion((clone $email)->text('text content'));
diff --git a/src/Symfony/Component/Mime/Tests/Part/MessagePartTest.php b/src/Symfony/Component/Mime/Tests/Part/MessagePartTest.php
index 21a4eb03b1292..2713d5bc079c7 100644
--- a/src/Symfony/Component/Mime/Tests/Part/MessagePartTest.php
+++ b/src/Symfony/Component/Mime/Tests/Part/MessagePartTest.php
@@ -22,7 +22,7 @@ class MessagePartTest extends TestCase
{
public function testConstructor()
{
- $p = new MessagePart((new Email())->from('fabien@symfony.com')->text('content'));
+ $p = new MessagePart((new Email())->from('fabien@symfony.com')->to('you@example.com')->text('content'));
$this->assertStringContainsString('content', $p->getBody());
$this->assertStringContainsString('content', $p->bodyToString());
$this->assertStringContainsString('content', implode('', iterator_to_array($p->bodyToIterable())));
diff --git a/src/Symfony/Component/OptionsResolver/Tests/Debug/OptionsResolverIntrospectorTest.php b/src/Symfony/Component/OptionsResolver/Tests/Debug/OptionsResolverIntrospectorTest.php
index d58b120457b49..433d9a8a269d6 100644
--- a/src/Symfony/Component/OptionsResolver/Tests/Debug/OptionsResolverIntrospectorTest.php
+++ b/src/Symfony/Component/OptionsResolver/Tests/Debug/OptionsResolverIntrospectorTest.php
@@ -44,7 +44,7 @@ public function testGetDefaultThrowsOnNoConfiguredValue()
$resolver->setDefined($option = 'foo');
$debug = new OptionsResolverIntrospector($resolver);
- $this->assertSame('bar', $debug->getDefault($option));
+ $debug->getDefault($option);
}
public function testGetDefaultThrowsOnNotDefinedOption()
@@ -54,7 +54,7 @@ public function testGetDefaultThrowsOnNotDefinedOption()
$resolver = new OptionsResolver();
$debug = new OptionsResolverIntrospector($resolver);
- $this->assertSame('bar', $debug->getDefault('foo'));
+ $debug->getDefault('foo');
}
public function testGetLazyClosures()
@@ -75,7 +75,7 @@ public function testGetLazyClosuresThrowsOnNoConfiguredValue()
$resolver->setDefined($option = 'foo');
$debug = new OptionsResolverIntrospector($resolver);
- $this->assertSame('bar', $debug->getLazyClosures($option));
+ $debug->getLazyClosures($option);
}
public function testGetLazyClosuresThrowsOnNotDefinedOption()
@@ -85,7 +85,7 @@ public function testGetLazyClosuresThrowsOnNotDefinedOption()
$resolver = new OptionsResolver();
$debug = new OptionsResolverIntrospector($resolver);
- $this->assertSame('bar', $debug->getLazyClosures('foo'));
+ $debug->getLazyClosures('foo');
}
public function testGetAllowedTypes()
diff --git a/src/Symfony/Component/Process/Pipes/UnixPipes.php b/src/Symfony/Component/Process/Pipes/UnixPipes.php
index 603d726b47fc9..70fdd29574ec7 100644
--- a/src/Symfony/Component/Process/Pipes/UnixPipes.php
+++ b/src/Symfony/Component/Process/Pipes/UnixPipes.php
@@ -118,7 +118,7 @@ public function readAndWrite(bool $blocking, bool $close = false): array
$read[$type = array_search($pipe, $this->pipes, true)] = '';
do {
- $data = fread($pipe, self::CHUNK_SIZE);
+ $data = @fread($pipe, self::CHUNK_SIZE);
$read[$type] .= $data;
} while (isset($data[0]) && ($close || isset($data[self::CHUNK_SIZE - 1])));
diff --git a/src/Symfony/Component/Process/Pipes/WindowsPipes.php b/src/Symfony/Component/Process/Pipes/WindowsPipes.php
index 6d9976960ad1b..c548092c51fff 100644
--- a/src/Symfony/Component/Process/Pipes/WindowsPipes.php
+++ b/src/Symfony/Component/Process/Pipes/WindowsPipes.php
@@ -57,7 +57,7 @@ public function __construct($input, bool $haveReadSupport)
if (!$h = fopen($file.'.lock', 'w')) {
restore_error_handler();
- throw new RuntimeException(sprintf('A temporary file could not be opened to write the process output: %s.', $lastError));
+ throw new RuntimeException('A temporary file could not be opened to write the process output: '.$lastError);
}
if (!flock($h, LOCK_EX | LOCK_NB)) {
continue 2;
diff --git a/src/Symfony/Component/Process/Process.php b/src/Symfony/Component/Process/Process.php
index db4c2919dff59..81568a1c03409 100644
--- a/src/Symfony/Component/Process/Process.php
+++ b/src/Symfony/Component/Process/Process.php
@@ -1648,7 +1648,7 @@ private function replacePlaceholders(string $commandline, array $env)
{
return preg_replace_callback('/"\$\{:([_a-zA-Z]++[_a-zA-Z0-9]*+)\}"/', function ($matches) use ($commandline, $env) {
if (!isset($env[$matches[1]]) || false === $env[$matches[1]]) {
- throw new InvalidArgumentException(sprintf('Command line is missing a value for parameter "%s": %s.', $matches[1], $commandline));
+ throw new InvalidArgumentException(sprintf('Command line is missing a value for parameter "%s": '.$commandline, $matches[1]));
}
return $this->escapeArgument($env[$matches[1]]);
diff --git a/src/Symfony/Component/Process/Tests/ProcessTest.php b/src/Symfony/Component/Process/Tests/ProcessTest.php
index ecbf271aeb8cf..d7461d082f8c1 100644
--- a/src/Symfony/Component/Process/Tests/ProcessTest.php
+++ b/src/Symfony/Component/Process/Tests/ProcessTest.php
@@ -50,7 +50,7 @@ protected function tearDown(): void
public function testInvalidCwd()
{
$this->expectException('Symfony\Component\Process\Exception\RuntimeException');
- $this->expectExceptionMessageRegExp('/The provided cwd ".*" does not exist\./');
+ $this->expectExceptionMessageMatches('/The provided cwd ".*" does not exist\./');
try {
// Check that it works fine if the CWD exists
$cmd = new Process(['echo', 'test'], __DIR__);
@@ -1488,7 +1488,7 @@ public function testPreparedCommandWithQuoteInIt()
public function testPreparedCommandWithMissingValue()
{
$this->expectException('Symfony\Component\Process\Exception\InvalidArgumentException');
- $this->expectExceptionMessage('Command line is missing a value for parameter "abc": echo "${:abc}".');
+ $this->expectExceptionMessage('Command line is missing a value for parameter "abc": echo "${:abc}"');
$p = Process::fromShellCommandline('echo "${:abc}"');
$p->run(null, ['bcd' => 'BCD']);
}
@@ -1496,7 +1496,7 @@ public function testPreparedCommandWithMissingValue()
public function testPreparedCommandWithNoValues()
{
$this->expectException('Symfony\Component\Process\Exception\InvalidArgumentException');
- $this->expectExceptionMessage('Command line is missing a value for parameter "abc": echo "${:abc}".');
+ $this->expectExceptionMessage('Command line is missing a value for parameter "abc": echo "${:abc}"');
$p = Process::fromShellCommandline('echo "${:abc}"');
$p->run(null, []);
}
diff --git a/src/Symfony/Component/PropertyAccess/PropertyAccessor.php b/src/Symfony/Component/PropertyAccess/PropertyAccessor.php
index 01f948291ad5c..cac049a0a637e 100644
--- a/src/Symfony/Component/PropertyAccess/PropertyAccessor.php
+++ b/src/Symfony/Component/PropertyAccess/PropertyAccessor.php
@@ -385,7 +385,7 @@ private function readProperty(array $zval, string $property, bool $ignoreInvalid
} catch (\TypeError $e) {
// handle uninitialized properties in PHP >= 7
if (preg_match((sprintf('/^Return value of %s::%s\(\) must be of the type (\w+), null returned$/', preg_quote(\get_class($object)), $access[self::ACCESS_NAME])), $e->getMessage(), $matches)) {
- throw new AccessException(sprintf('The method "%s::%s()" returned "null", but expected type "%3$s". Have you forgotten to initialize a property or to make the return type nullable using "?%3$s" instead?', \get_class($object), $access[self::ACCESS_NAME], $matches[1]), 0, $e);
+ throw new AccessException(sprintf('The method "%s::%s()" returned "null", but expected type "%3$s". Did you forget to initialize a property or to make the return type nullable using "?%3$s"?', \get_class($object), $access[self::ACCESS_NAME], $matches[1]), 0, $e);
}
throw $e;
@@ -418,7 +418,7 @@ private function readProperty(array $zval, string $property, bool $ignoreInvalid
if (\PHP_VERSION_ID >= 70400 && preg_match('/^Typed property ([\w\\\]+)::\$(\w+) must not be accessed before initialization$/', $e->getMessage(), $matches)) {
$r = new \ReflectionProperty($matches[1], $matches[2]);
- throw new AccessException(sprintf('The property "%s::$%s" is not readable because it is typed "%3$s". You should either initialize it or make it nullable using "?%3$s" instead.', $r->getDeclaringClass()->getName(), $r->getName(), $r->getType()->getName()), 0, $e);
+ throw new AccessException(sprintf('The property "%s::$%s" is not readable because it is typed "%s". You should initialize it or declare a default value instead.', $r->getDeclaringClass()->getName(), $r->getName(), $r->getType()->getName()), 0, $e);
}
throw $e;
diff --git a/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorCollectionTest.php b/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorCollectionTest.php
index 09aebab87b135..94655b9762742 100644
--- a/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorCollectionTest.php
+++ b/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorCollectionTest.php
@@ -149,7 +149,7 @@ public function testSetValueCallsAdderAndRemoverForNestedCollections()
public function testSetValueFailsIfNoAdderNorRemoverFound()
{
$this->expectException('Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException');
- $this->expectExceptionMessageRegExp('/Could not determine access type for property "axes" in class "Mock_PropertyAccessorCollectionTest_CarNoAdderAndRemover_[^"]*"./');
+ $this->expectExceptionMessageMatches('/Could not determine access type for property "axes" in class "Mock_PropertyAccessorCollectionTest_CarNoAdderAndRemover_[^"]*"./');
$car = $this->getMockBuilder(__CLASS__.'_CarNoAdderAndRemover')->getMock();
$axesBefore = $this->getContainer([1 => 'second', 3 => 'fourth']);
$axesAfter = $this->getContainer([0 => 'first', 1 => 'second', 2 => 'third']);
@@ -188,7 +188,7 @@ public function testIsWritableReturnsFalseIfNoAdderNorRemoverExists()
public function testSetValueFailsIfAdderAndRemoverExistButValueIsNotTraversable()
{
$this->expectException('Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException');
- $this->expectExceptionMessageRegExp('/Could not determine access type for property "axes" in class "Symfony\\\\Component\\\\PropertyAccess\\\\Tests\\\\PropertyAccessorCollectionTest_Car[^"]*": The property "axes" in class "Symfony\\\\Component\\\\PropertyAccess\\\\Tests\\\\PropertyAccessorCollectionTest_Car[^"]*" can be defined with the methods "addAxis\(\)", "removeAxis\(\)" but the new value must be an array or an instance of \\\\Traversable, "string" given./');
+ $this->expectExceptionMessageMatches('/Could not determine access type for property "axes" in class "Symfony\\\\Component\\\\PropertyAccess\\\\Tests\\\\PropertyAccessorCollectionTest_Car[^"]*": The property "axes" in class "Symfony\\\\Component\\\\PropertyAccess\\\\Tests\\\\PropertyAccessorCollectionTest_Car[^"]*" can be defined with the methods "addAxis\(\)", "removeAxis\(\)" but the new value must be an array or an instance of \\\\Traversable, "string" given./');
$car = new PropertyAccessorCollectionTest_Car();
$this->propertyAccessor->setValue($car, 'axes', 'Not an array or Traversable');
diff --git a/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php b/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php
index 5304a807f1b75..eeabf7e9f8de1 100644
--- a/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php
+++ b/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php
@@ -139,7 +139,7 @@ public function testGetValueThrowsExceptionIfIndexNotFoundAndIndexExceptionsEnab
public function testGetValueThrowsExceptionIfUninitializedProperty()
{
$this->expectException('Symfony\Component\PropertyAccess\Exception\AccessException');
- $this->expectExceptionMessage('The property "Symfony\Component\PropertyAccess\Tests\Fixtures\UninitializedProperty::$uninitialized" is not readable because it is typed "string". You should either initialize it or make it nullable using "?string" instead.');
+ $this->expectExceptionMessage('The property "Symfony\Component\PropertyAccess\Tests\Fixtures\UninitializedProperty::$uninitialized" is not readable because it is typed "string". You should initialize it or declare a default value instead.');
$this->propertyAccessor->getValue(new UninitializedProperty(), 'uninitialized');
}
@@ -150,7 +150,7 @@ public function testGetValueThrowsExceptionIfUninitializedProperty()
public function testGetValueThrowsExceptionIfUninitializedPropertyWithGetter()
{
$this->expectException('Symfony\Component\PropertyAccess\Exception\AccessException');
- $this->expectExceptionMessage('The method "Symfony\Component\PropertyAccess\Tests\Fixtures\UninitializedPrivateProperty::getUninitialized()" returned "null", but expected type "array". Have you forgotten to initialize a property or to make the return type nullable using "?array" instead?');
+ $this->expectExceptionMessage('The method "Symfony\Component\PropertyAccess\Tests\Fixtures\UninitializedPrivateProperty::getUninitialized()" returned "null", but expected type "array". Did you forget to initialize a property or to make the return type nullable using "?array"?');
$this->propertyAccessor->getValue(new UninitializedPrivateProperty(), 'uninitialized');
}
@@ -768,7 +768,7 @@ public function testAdderAndRemoverArePreferredOverSetterForSameSingularAndPlura
public function testAdderWithoutRemover()
{
$this->expectException('Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException');
- $this->expectExceptionMessageRegExp('/.*The add method "addFoo" in class "Symfony\\\Component\\\PropertyAccess\\\Tests\\\Fixtures\\\TestAdderRemoverInvalidMethods" was found, but the corresponding remove method "removeFoo" was not found\./');
+ $this->expectExceptionMessageMatches('/.*The add method "addFoo" in class "Symfony\\\Component\\\PropertyAccess\\\Tests\\\Fixtures\\\TestAdderRemoverInvalidMethods" was found, but the corresponding remove method "removeFoo" was not found\./');
$object = new TestAdderRemoverInvalidMethods();
$this->propertyAccessor->setValue($object, 'foos', [1, 2]);
}
@@ -776,7 +776,7 @@ public function testAdderWithoutRemover()
public function testRemoverWithoutAdder()
{
$this->expectException('Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException');
- $this->expectExceptionMessageRegExp('/.*The remove method "removeBar" in class "Symfony\\\Component\\\PropertyAccess\\\Tests\\\Fixtures\\\TestAdderRemoverInvalidMethods" was found, but the corresponding add method "addBar" was not found\./');
+ $this->expectExceptionMessageMatches('/.*The remove method "removeBar" in class "Symfony\\\Component\\\PropertyAccess\\\Tests\\\Fixtures\\\TestAdderRemoverInvalidMethods" was found, but the corresponding add method "addBar" was not found\./');
$object = new TestAdderRemoverInvalidMethods();
$this->propertyAccessor->setValue($object, 'bars', [1, 2]);
}
@@ -784,7 +784,7 @@ public function testRemoverWithoutAdder()
public function testAdderAndRemoveNeedsTheExactParametersDefined()
{
$this->expectException('Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException');
- $this->expectExceptionMessageRegExp('/.*The method "addFoo" in class "Symfony\\\Component\\\PropertyAccess\\\Tests\\\Fixtures\\\TestAdderRemoverInvalidArgumentLength" requires 0 arguments, but should accept only 1\. The method "removeFoo" in class "Symfony\\\Component\\\PropertyAccess\\\Tests\\\Fixtures\\\TestAdderRemoverInvalidArgumentLength" requires 2 arguments, but should accept only 1\./');
+ $this->expectExceptionMessageMatches('/.*The method "addFoo" in class "Symfony\\\Component\\\PropertyAccess\\\Tests\\\Fixtures\\\TestAdderRemoverInvalidArgumentLength" requires 0 arguments, but should accept only 1\. The method "removeFoo" in class "Symfony\\\Component\\\PropertyAccess\\\Tests\\\Fixtures\\\TestAdderRemoverInvalidArgumentLength" requires 2 arguments, but should accept only 1\./');
$object = new TestAdderRemoverInvalidArgumentLength();
$this->propertyAccessor->setValue($object, 'foo', [1, 2]);
}
@@ -792,7 +792,7 @@ public function testAdderAndRemoveNeedsTheExactParametersDefined()
public function testSetterNeedsTheExactParametersDefined()
{
$this->expectException('Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException');
- $this->expectExceptionMessageRegExp('/.*The method "setBar" in class "Symfony\\\Component\\\PropertyAccess\\\Tests\\\Fixtures\\\TestAdderRemoverInvalidArgumentLength" requires 2 arguments, but should accept only 1\./');
+ $this->expectExceptionMessageMatches('/.*The method "setBar" in class "Symfony\\\Component\\\PropertyAccess\\\Tests\\\Fixtures\\\TestAdderRemoverInvalidArgumentLength" requires 2 arguments, but should accept only 1\./');
$object = new TestAdderRemoverInvalidArgumentLength();
$this->propertyAccessor->setValue($object, 'bar', [1, 2]);
}
@@ -800,7 +800,7 @@ public function testSetterNeedsTheExactParametersDefined()
public function testSetterNeedsPublicAccess()
{
$this->expectException('Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException');
- $this->expectExceptionMessageRegExp('/.*The method "setFoo" in class "Symfony\\\Component\\\PropertyAccess\\\Tests\\\Fixtures\\\TestClassSetValue" was found but does not have public access./');
+ $this->expectExceptionMessageMatches('/.*The method "setFoo" in class "Symfony\\\Component\\\PropertyAccess\\\Tests\\\Fixtures\\\TestClassSetValue" was found but does not have public access./');
$object = new TestClassSetValue(0);
$this->propertyAccessor->setValue($object, 'foo', 1);
}
diff --git a/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php b/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php
index 6de6517b726ef..ccb216b624e68 100644
--- a/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php
+++ b/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php
@@ -58,6 +58,9 @@ class ReflectionExtractor implements PropertyListExtractorInterface, PropertyTyp
private $enableConstructorExtraction;
private $accessFlags;
+ private $arrayMutatorPrefixesFirst;
+ private $arrayMutatorPrefixesLast;
+
/**
* @param string[]|null $mutatorPrefixes
* @param string[]|null $accessorPrefixes
@@ -70,6 +73,9 @@ public function __construct(array $mutatorPrefixes = null, array $accessorPrefix
$this->arrayMutatorPrefixes = null !== $arrayMutatorPrefixes ? $arrayMutatorPrefixes : self::$defaultArrayMutatorPrefixes;
$this->enableConstructorExtraction = $enableConstructorExtraction;
$this->accessFlags = $accessFlags;
+
+ $this->arrayMutatorPrefixesFirst = array_merge($this->arrayMutatorPrefixes, array_diff($this->mutatorPrefixes, $this->arrayMutatorPrefixes));
+ $this->arrayMutatorPrefixesLast = array_reverse($this->arrayMutatorPrefixesFirst);
}
/**
@@ -405,7 +411,9 @@ private function getMutatorMethod(string $class, string $property): ?array
$ucProperty = ucfirst($property);
$ucSingulars = (array) Inflector::singularize($ucProperty);
- foreach ($this->mutatorPrefixes as $prefix) {
+ $mutatorPrefixes = \in_array($ucProperty, $ucSingulars, true) ? $this->arrayMutatorPrefixesLast : $this->arrayMutatorPrefixesFirst;
+
+ foreach ($mutatorPrefixes as $prefix) {
$names = [$ucProperty];
if (\in_array($prefix, $this->arrayMutatorPrefixes)) {
$names = array_merge($names, $ucSingulars);
diff --git a/src/Symfony/Component/PropertyInfo/Tests/Extractor/PhpDocExtractorTest.php b/src/Symfony/Component/PropertyInfo/Tests/Extractor/PhpDocExtractorTest.php
index 3f48a1d828e1e..0d3c32206786e 100644
--- a/src/Symfony/Component/PropertyInfo/Tests/Extractor/PhpDocExtractorTest.php
+++ b/src/Symfony/Component/PropertyInfo/Tests/Extractor/PhpDocExtractorTest.php
@@ -9,7 +9,7 @@
* file that was distributed with this source code.
*/
-namespace Symfony\Component\PropertyInfo\Tests\PhpDocExtractor;
+namespace Symfony\Component\PropertyInfo\Tests\Extractor;
use phpDocumentor\Reflection\Types\Collection;
use PHPUnit\Framework\TestCase;
diff --git a/src/Symfony/Component/PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php b/src/Symfony/Component/PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php
index 45fd42c39a641..0fadd46413eec 100644
--- a/src/Symfony/Component/PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php
+++ b/src/Symfony/Component/PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php
@@ -70,6 +70,7 @@ public function testGetProperties()
'realParent',
'xTotals',
'YT',
+ 'date',
'c',
'd',
'e',
@@ -109,6 +110,7 @@ public function testGetPropertiesWithCustomPrefixes()
'foo4',
'foo5',
'files',
+ 'date',
'c',
'd',
'e',
@@ -173,6 +175,8 @@ public function typesProvider()
['staticSetter', null],
['self', [new Type(Type::BUILTIN_TYPE_OBJECT, false, 'Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy')]],
['realParent', [new Type(Type::BUILTIN_TYPE_OBJECT, false, 'Symfony\Component\PropertyInfo\Tests\Fixtures\ParentDummy')]],
+ ['date', [new Type(Type::BUILTIN_TYPE_OBJECT, false, \DateTime::class)]],
+ ['dates', [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_OBJECT, false, \DateTime::class))]],
];
}
diff --git a/src/Symfony/Component/PropertyInfo/Tests/Fixtures/Dummy.php b/src/Symfony/Component/PropertyInfo/Tests/Fixtures/Dummy.php
index ec4b75a099a73..bcec074438948 100644
--- a/src/Symfony/Component/PropertyInfo/Tests/Fixtures/Dummy.php
+++ b/src/Symfony/Component/PropertyInfo/Tests/Fixtures/Dummy.php
@@ -210,4 +210,12 @@ public function getXTotals()
public function getYT()
{
}
+
+ public function setDate(\DateTime $date)
+ {
+ }
+
+ public function addDate(\DateTime $date)
+ {
+ }
}
diff --git a/src/Symfony/Component/Routing/Annotation/Route.php b/src/Symfony/Component/Routing/Annotation/Route.php
index 8183b6fc55e97..52b208c41b067 100644
--- a/src/Symfony/Component/Routing/Annotation/Route.php
+++ b/src/Symfony/Component/Routing/Annotation/Route.php
@@ -31,9 +31,6 @@ class Route
private $methods = [];
private $schemes = [];
private $condition;
- private $locale;
- private $format;
- private $utf8;
/**
* @param array $data An array of key/value parameters
diff --git a/src/Symfony/Component/Routing/Loader/Configurator/ImportConfigurator.php b/src/Symfony/Component/Routing/Loader/Configurator/ImportConfigurator.php
index f11b7957525b1..0059a632a1977 100644
--- a/src/Symfony/Component/Routing/Loader/Configurator/ImportConfigurator.php
+++ b/src/Symfony/Component/Routing/Loader/Configurator/ImportConfigurator.php
@@ -13,6 +13,7 @@
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection;
+use Symfony\Component\Routing\RouteCompiler;
/**
* @author Nicolas Grekas
@@ -63,6 +64,7 @@ final public function prefix($prefix, bool $trailingSlashOnRoot = true): self
foreach ($prefix as $locale => $localePrefix) {
$localizedRoute = clone $route;
$localizedRoute->setDefault('_locale', $locale);
+ $localizedRoute->setRequirement('_locale', preg_quote($locale, RouteCompiler::REGEX_DELIMITER));
$localizedRoute->setDefault('_canonical_route', $name);
$localizedRoute->setPath($localePrefix.(!$trailingSlashOnRoot && '/' === $route->getPath() ? '' : $route->getPath()));
$this->route->add($name.'.'.$locale, $localizedRoute);
diff --git a/src/Symfony/Component/Routing/Loader/XmlFileLoader.php b/src/Symfony/Component/Routing/Loader/XmlFileLoader.php
index da8f34b293c19..31c614950e41a 100644
--- a/src/Symfony/Component/Routing/Loader/XmlFileLoader.php
+++ b/src/Symfony/Component/Routing/Loader/XmlFileLoader.php
@@ -211,6 +211,7 @@ protected function parseImport(RouteCollection $collection, \DOMElement $node, $
$localizedRoute = clone $route;
$localizedRoute->setPath($localePrefix.(!$trailingSlashOnRoot && '/' === $route->getPath() ? '' : $route->getPath()));
$localizedRoute->setDefault('_locale', $locale);
+ $localizedRoute->setRequirement('_locale', preg_quote($locale, RouteCompiler::REGEX_DELIMITER));
$localizedRoute->setDefault('_canonical_route', $name);
$subCollection->add($name.'.'.$locale, $localizedRoute);
}
@@ -314,9 +315,9 @@ private function parseConfigs(\DOMElement $node, string $path): array
if ($controller = $node->getAttribute('controller')) {
if (isset($defaults['_controller'])) {
- $name = $node->hasAttribute('id') ? sprintf('"%s"', $node->getAttribute('id')) : sprintf('the "%s" tag', $node->tagName);
+ $name = $node->hasAttribute('id') ? sprintf('"%s".', $node->getAttribute('id')) : sprintf('the "%s" tag.', $node->tagName);
- throw new \InvalidArgumentException(sprintf('The routing file "%s" must not specify both the "controller" attribute and the defaults key "_controller" for %s.', $path, $name));
+ throw new \InvalidArgumentException(sprintf('The routing file "%s" must not specify both the "controller" attribute and the defaults key "_controller" for ', $path).$name);
}
$defaults['_controller'] = $controller;
diff --git a/src/Symfony/Component/Routing/Loader/YamlFileLoader.php b/src/Symfony/Component/Routing/Loader/YamlFileLoader.php
index 868da9bd30b4b..8d4b9abdb10fa 100644
--- a/src/Symfony/Component/Routing/Loader/YamlFileLoader.php
+++ b/src/Symfony/Component/Routing/Loader/YamlFileLoader.php
@@ -216,6 +216,7 @@ protected function parseImport(RouteCollection $collection, array $config, $path
foreach ($prefix as $locale => $localePrefix) {
$localizedRoute = clone $route;
$localizedRoute->setDefault('_locale', $locale);
+ $localizedRoute->setRequirement('_locale', preg_quote($locale, RouteCompiler::REGEX_DELIMITER));
$localizedRoute->setDefault('_canonical_route', $name);
$localizedRoute->setPath($localePrefix.(!$trailingSlashOnRoot && '/' === $route->getPath() ? '' : $route->getPath()));
$subCollection->add($name.'.'.$locale, $localizedRoute);
diff --git a/src/Symfony/Component/Routing/README.md b/src/Symfony/Component/Routing/README.md
index a16d9d7fcbbbb..03b258ec8203b 100644
--- a/src/Symfony/Component/Routing/README.md
+++ b/src/Symfony/Component/Routing/README.md
@@ -3,10 +3,48 @@ Routing Component
The Routing component maps an HTTP request to a set of configuration variables.
+Getting Started
+---------------
+
+```
+$ composer require symfony/routing
+```
+
+```php
+use App\Controller\BlogController;
+use Symfony\Component\Routing\Generator\UrlGenerator;
+use Symfony\Component\Routing\Matcher\UrlMatcher;
+use Symfony\Component\Routing\RequestContext;
+use Symfony\Component\Routing\Route;
+use Symfony\Component\Routing\RouteCollection;
+
+$route = new Route('/blog/{slug}', ['_controller' => BlogController::class]);
+$routes = new RouteCollection();
+$routes->add('blog_show', $route);
+
+$context = new RequestContext();
+
+// Routing can match routes with incoming requests
+$matcher = new UrlMatcher($routes, $context);
+$parameters = $matcher->match('/blog/lorem-ipsum');
+// $parameters = [
+// '_controller' => 'App\Controller\BlogController',
+// 'slug' => 'lorem-ipsum',
+// '_route' => 'blog_show'
+// ]
+
+// Routing can also generate URLs for a given route
+$generator = new UrlGenerator($routes, $context);
+$url = $generator->generate('blog_show', [
+ 'slug' => 'my-blog-post',
+]);
+// $url = '/blog/my-blog-post'
+```
+
Resources
---------
- * [Documentation](https://symfony.com/doc/current/components/routing.html)
+ * [Documentation](https://symfony.com/doc/current/routing.html)
* [Contributing](https://symfony.com/doc/current/contributing/index.html)
* [Report issues](https://github.com/symfony/symfony/issues) and
[send Pull Requests](https://github.com/symfony/symfony/pulls)
diff --git a/src/Symfony/Component/Routing/Tests/Fixtures/php_dsl_sub_i18n.php b/src/Symfony/Component/Routing/Tests/Fixtures/php_dsl_sub_i18n.php
index e79edc869d2de..e6d846dd463cf 100644
--- a/src/Symfony/Component/Routing/Tests/Fixtures/php_dsl_sub_i18n.php
+++ b/src/Symfony/Component/Routing/Tests/Fixtures/php_dsl_sub_i18n.php
@@ -8,4 +8,6 @@
$add('foo', ['fr' => '/foo']);
$add('bar', ['fr' => '/bar']);
+
+ $routes->add('non_localized', '/non-localized');
};
diff --git a/src/Symfony/Component/Routing/Tests/Loader/PhpFileLoaderTest.php b/src/Symfony/Component/Routing/Tests/Loader/PhpFileLoaderTest.php
index 789848c66021a..b84d5ff3543be 100644
--- a/src/Symfony/Component/Routing/Tests/Loader/PhpFileLoaderTest.php
+++ b/src/Symfony/Component/Routing/Tests/Loader/PhpFileLoaderTest.php
@@ -234,6 +234,7 @@ public function testRoutingI18nConfigurator()
$expectedCollection->add('baz.en', (new Route('/baz'))->setDefaults(['_locale' => 'en', '_canonical_route' => 'baz'])->setRequirement('_locale', 'en'));
$expectedCollection->add('c_foo.fr', (new Route('/ench/pub/foo'))->setDefaults(['_locale' => 'fr', '_canonical_route' => 'c_foo'])->setRequirement('_locale', 'fr'));
$expectedCollection->add('c_bar.fr', (new Route('/ench/pub/bar'))->setDefaults(['_locale' => 'fr', '_canonical_route' => 'c_bar'])->setRequirement('_locale', 'fr'));
+ $expectedCollection->add('non_localized.fr', (new Route('/ench/non-localized'))->setDefaults(['_locale' => 'fr', '_canonical_route' => 'non_localized'])->setRequirement('_locale', 'fr'));
$expectedCollection->addResource(new FileResource(realpath(__DIR__.'/../Fixtures/php_dsl_sub_i18n.php')));
$expectedCollection->addResource(new FileResource(realpath(__DIR__.'/../Fixtures/php_dsl_i18n.php')));
diff --git a/src/Symfony/Component/Routing/Tests/Loader/XmlFileLoaderTest.php b/src/Symfony/Component/Routing/Tests/Loader/XmlFileLoaderTest.php
index 66d54fc985c4c..383cda0176952 100644
--- a/src/Symfony/Component/Routing/Tests/Loader/XmlFileLoaderTest.php
+++ b/src/Symfony/Component/Routing/Tests/Loader/XmlFileLoaderTest.php
@@ -201,6 +201,9 @@ public function testLocalizedImportsOfNotLocalizedRoutes()
$this->assertEquals('/le-prefix/suffix', $routeCollection->get('imported.fr')->getPath());
$this->assertEquals('/the-prefix/suffix', $routeCollection->get('imported.en')->getPath());
+
+ $this->assertSame('fr', $routeCollection->get('imported.fr')->getRequirement('_locale'));
+ $this->assertSame('en', $routeCollection->get('imported.en')->getRequirement('_locale'));
}
/**
@@ -439,7 +442,7 @@ public function testLoadRouteWithControllerSetInDefaults()
public function testOverrideControllerInDefaults()
{
$this->expectException('InvalidArgumentException');
- $this->expectExceptionMessageRegExp('/The routing file "[^"]*" must not specify both the "controller" attribute and the defaults key "_controller" for "app_blog"/');
+ $this->expectExceptionMessageMatches('/The routing file "[^"]*" must not specify both the "controller" attribute and the defaults key "_controller" for "app_blog"/');
$loader = new XmlFileLoader(new FileLocator([__DIR__.'/../Fixtures/controller']));
$loader->load('override_defaults.xml');
}
@@ -471,7 +474,7 @@ public function provideFilesImportingRoutesWithControllers()
public function testImportWithOverriddenController()
{
$this->expectException('InvalidArgumentException');
- $this->expectExceptionMessageRegExp('/The routing file "[^"]*" must not specify both the "controller" attribute and the defaults key "_controller" for the "import" tag/');
+ $this->expectExceptionMessageMatches('/The routing file "[^"]*" must not specify both the "controller" attribute and the defaults key "_controller" for the "import" tag/');
$loader = new XmlFileLoader(new FileLocator([__DIR__.'/../Fixtures/controller']));
$loader->load('import_override_defaults.xml');
}
diff --git a/src/Symfony/Component/Routing/Tests/Loader/YamlFileLoaderTest.php b/src/Symfony/Component/Routing/Tests/Loader/YamlFileLoaderTest.php
index 52c21c287fcf6..e5571b0b7a6eb 100644
--- a/src/Symfony/Component/Routing/Tests/Loader/YamlFileLoaderTest.php
+++ b/src/Symfony/Component/Routing/Tests/Loader/YamlFileLoaderTest.php
@@ -144,7 +144,7 @@ public function testLoadRouteWithControllerSetInDefaults()
public function testOverrideControllerInDefaults()
{
$this->expectException('InvalidArgumentException');
- $this->expectExceptionMessageRegExp('/The routing file "[^"]*" must not specify both the "controller" key and the defaults key "_controller" for "app_blog"/');
+ $this->expectExceptionMessageMatches('/The routing file "[^"]*" must not specify both the "controller" key and the defaults key "_controller" for "app_blog"/');
$loader = new YamlFileLoader(new FileLocator([__DIR__.'/../Fixtures/controller']));
$loader->load('override_defaults.yml');
}
@@ -176,7 +176,7 @@ public function provideFilesImportingRoutesWithControllers()
public function testImportWithOverriddenController()
{
$this->expectException('InvalidArgumentException');
- $this->expectExceptionMessageRegExp('/The routing file "[^"]*" must not specify both the "controller" key and the defaults key "_controller" for "_static"/');
+ $this->expectExceptionMessageMatches('/The routing file "[^"]*" must not specify both the "controller" key and the defaults key "_controller" for "_static"/');
$loader = new YamlFileLoader(new FileLocator([__DIR__.'/../Fixtures/controller']));
$loader->load('import_override_defaults.yml');
}
@@ -334,6 +334,9 @@ public function testImportingNonLocalizedRoutesWithLocales()
$this->assertCount(2, $routes);
$this->assertEquals('/nl/imported', $routes->get('imported.nl')->getPath());
$this->assertEquals('/en/imported', $routes->get('imported.en')->getPath());
+
+ $this->assertSame('nl', $routes->get('imported.nl')->getRequirement('_locale'));
+ $this->assertSame('en', $routes->get('imported.en')->getRequirement('_locale'));
}
public function testImportingRoutesWithOfficialLocales()
diff --git a/src/Symfony/Component/Security/Core/Authentication/Provider/LdapBindAuthenticationProvider.php b/src/Symfony/Component/Security/Core/Authentication/Provider/LdapBindAuthenticationProvider.php
index 2c9dc44a17735..a7bd1e087d688 100644
--- a/src/Symfony/Component/Security/Core/Authentication/Provider/LdapBindAuthenticationProvider.php
+++ b/src/Symfony/Component/Security/Core/Authentication/Provider/LdapBindAuthenticationProvider.php
@@ -83,14 +83,13 @@ protected function checkAuthentication(UserInterface $user, UsernamePasswordToke
}
try {
- $username = $this->ldap->escape($username, '', LdapInterface::ESCAPE_DN);
-
if ($this->queryString) {
if ('' !== $this->searchDn && '' !== $this->searchPassword) {
$this->ldap->bind($this->searchDn, $this->searchPassword);
} else {
@trigger_error('Using the "query_string" config without using a "search_dn" and a "search_password" is deprecated since Symfony 4.4 and will throw an exception in Symfony 5.0.', E_USER_DEPRECATED);
}
+ $username = $this->ldap->escape($username, '', LdapInterface::ESCAPE_FILTER);
$query = str_replace('{username}', $username, $this->queryString);
$result = $this->ldap->query($this->dnString, $query)->execute();
if (1 !== $result->count()) {
@@ -99,6 +98,7 @@ protected function checkAuthentication(UserInterface $user, UsernamePasswordToke
$dn = $result[0]->getDn();
} else {
+ $username = $this->ldap->escape($username, '', LdapInterface::ESCAPE_DN);
$dn = str_replace('{username}', $username, $this->dnString);
}
diff --git a/src/Symfony/Component/Security/Core/Authentication/Token/Storage/TokenStorage.php b/src/Symfony/Component/Security/Core/Authentication/Token/Storage/TokenStorage.php
index bf491797aa25d..dd8a5ae00420a 100644
--- a/src/Symfony/Component/Security/Core/Authentication/Token/Storage/TokenStorage.php
+++ b/src/Symfony/Component/Security/Core/Authentication/Token/Storage/TokenStorage.php
@@ -49,6 +49,11 @@ public function setToken(TokenInterface $token = null)
@trigger_error(sprintf('Not implementing the "%s::getRoleNames()" method in "%s" is deprecated since Symfony 4.3.', TokenInterface::class, \get_class($token)), E_USER_DEPRECATED);
}
+ if ($token) {
+ // ensure any initializer is called
+ $this->getToken();
+ }
+
$this->initializer = null;
$this->token = $token;
}
diff --git a/src/Symfony/Component/Security/Core/Authentication/Token/Storage/UsageTrackingTokenStorage.php b/src/Symfony/Component/Security/Core/Authentication/Token/Storage/UsageTrackingTokenStorage.php
index 3ce8913aa4fbb..b90d5ab28b635 100644
--- a/src/Symfony/Component/Security/Core/Authentication/Token/Storage/UsageTrackingTokenStorage.php
+++ b/src/Symfony/Component/Security/Core/Authentication/Token/Storage/UsageTrackingTokenStorage.php
@@ -52,6 +52,11 @@ public function getToken(): ?TokenInterface
public function setToken(TokenInterface $token = null): void
{
$this->storage->setToken($token);
+
+ if ($token && $this->enableUsageTracking) {
+ // increments the internal session usage index
+ $this->sessionLocator->get('session')->getMetadataBag();
+ }
}
public function enableUsageTracking(): void
diff --git a/src/Symfony/Component/Security/Core/Authorization/TraceableAccessDecisionManager.php b/src/Symfony/Component/Security/Core/Authorization/TraceableAccessDecisionManager.php
index 7690b3e2264bc..7a9c4d0cfb23a 100644
--- a/src/Symfony/Component/Security/Core/Authorization/TraceableAccessDecisionManager.php
+++ b/src/Symfony/Component/Security/Core/Authorization/TraceableAccessDecisionManager.php
@@ -47,8 +47,10 @@ public function __construct(AccessDecisionManagerInterface $manager)
/**
* {@inheritdoc}
+ *
+ * @param bool $allowMultipleAttributes Whether to allow passing multiple values to the $attributes array
*/
- public function decide(TokenInterface $token, array $attributes, $object = null): bool
+ public function decide(TokenInterface $token, array $attributes, $object = null/*, bool $allowMultipleAttributes = false*/): bool
{
$currentDecisionLog = [
'attributes' => $attributes,
@@ -58,7 +60,7 @@ public function decide(TokenInterface $token, array $attributes, $object = null)
$this->currentLog[] = &$currentDecisionLog;
- $result = $this->manager->decide($token, $attributes, $object);
+ $result = $this->manager->decide($token, $attributes, $object, 3 < \func_num_args() && func_get_arg(3));
$currentDecisionLog['result'] = $result;
diff --git a/src/Symfony/Component/Security/Core/Encoder/EncoderFactory.php b/src/Symfony/Component/Security/Core/Encoder/EncoderFactory.php
index cad50180d181e..a6dbf71a1d1f3 100644
--- a/src/Symfony/Component/Security/Core/Encoder/EncoderFactory.php
+++ b/src/Symfony/Component/Security/Core/Encoder/EncoderFactory.php
@@ -72,10 +72,10 @@ private function createEncoder(array $config, bool $isExtra = false): PasswordEn
$config = $this->getEncoderConfigFromAlgorithm($config);
}
if (!isset($config['class'])) {
- throw new \InvalidArgumentException(sprintf('"class" must be set in %s.', json_encode($config)));
+ throw new \InvalidArgumentException('"class" must be set in '.json_encode($config));
}
if (!isset($config['arguments'])) {
- throw new \InvalidArgumentException(sprintf('"arguments" must be set in %s.', json_encode($config)));
+ throw new \InvalidArgumentException('"arguments" must be set in '.json_encode($config));
}
$encoder = new $config['class'](...$config['arguments']);
diff --git a/src/Symfony/Component/Security/Core/Tests/Authentication/Provider/LdapBindAuthenticationProviderTest.php b/src/Symfony/Component/Security/Core/Tests/Authentication/Provider/LdapBindAuthenticationProviderTest.php
index 893c8909719fa..fcc950dbe622b 100644
--- a/src/Symfony/Component/Security/Core/Tests/Authentication/Provider/LdapBindAuthenticationProviderTest.php
+++ b/src/Symfony/Component/Security/Core/Tests/Authentication/Provider/LdapBindAuthenticationProviderTest.php
@@ -111,16 +111,16 @@ public function testQueryForDn()
;
$ldap = $this->getMockBuilder(LdapInterface::class)->getMock();
+ $ldap
+ ->expects($this->at(0))
+ ->method('bind')
+ ->with('elsa', 'test1234A$');
$ldap
->expects($this->once())
->method('escape')
->with('foo', '')
->willReturn('foo')
;
- $ldap
- ->expects($this->at(1))
- ->method('bind')
- ->with('elsa', 'test1234A$');
$ldap
->expects($this->once())
->method('query')
@@ -151,16 +151,16 @@ public function testQueryWithUserForDn()
;
$ldap = $this->getMockBuilder(LdapInterface::class)->getMock();
+ $ldap
+ ->expects($this->at(0))
+ ->method('bind')
+ ->with('elsa', 'test1234A$');
$ldap
->expects($this->once())
->method('escape')
->with('foo', '')
->willReturn('foo')
;
- $ldap
- ->expects($this->at(1))
- ->method('bind')
- ->with('elsa', 'test1234A$');
$ldap
->expects($this->once())
->method('query')
@@ -195,7 +195,7 @@ public function testEmptyQueryResultShouldThrowAnException()
$ldap = $this->getMockBuilder(LdapInterface::class)->getMock();
$ldap
- ->expects($this->at(1))
+ ->expects($this->at(0))
->method('bind')
->with('elsa', 'test1234A$');
$ldap
diff --git a/src/Symfony/Component/Security/Guard/Tests/Provider/GuardAuthenticationProviderTest.php b/src/Symfony/Component/Security/Guard/Tests/Provider/GuardAuthenticationProviderTest.php
index 816bd097309e8..e00fb12dfb392 100644
--- a/src/Symfony/Component/Security/Guard/Tests/Provider/GuardAuthenticationProviderTest.php
+++ b/src/Symfony/Component/Security/Guard/Tests/Provider/GuardAuthenticationProviderTest.php
@@ -190,7 +190,7 @@ public function testSupportsChecksGuardAuthenticatorsTokenOrigin()
public function testAuthenticateFailsOnNonOriginatingToken()
{
$this->expectException('Symfony\Component\Security\Core\Exception\AuthenticationException');
- $this->expectExceptionMessageRegExp('/second_firewall_0/');
+ $this->expectExceptionMessageMatches('/second_firewall_0/');
$authenticatorA = $this->getMockBuilder(AuthenticatorInterface::class)->getMock();
$authenticators = [$authenticatorA];
diff --git a/src/Symfony/Component/Security/Http/Firewall/SwitchUserListener.php b/src/Symfony/Component/Security/Http/Firewall/SwitchUserListener.php
index 21bd1bc1cdb0b..a6718a7232824 100644
--- a/src/Symfony/Component/Security/Http/Firewall/SwitchUserListener.php
+++ b/src/Symfony/Component/Security/Http/Firewall/SwitchUserListener.php
@@ -168,7 +168,7 @@ private function attemptSwitchUser(Request $request, string $username): ?TokenIn
try {
$this->provider->loadUserByUsername($nonExistentUsername);
- } catch (AuthenticationException $e) {
+ } catch (\Exception $e) {
}
} catch (AuthenticationException $e) {
$this->provider->loadUserByUsername($currentUsername);
diff --git a/src/Symfony/Component/Security/Http/Tests/Firewall/ContextListenerTest.php b/src/Symfony/Component/Security/Http/Tests/Firewall/ContextListenerTest.php
index 82a5f917d98ee..7e89c0b1b36fb 100644
--- a/src/Symfony/Component/Security/Http/Tests/Firewall/ContextListenerTest.php
+++ b/src/Symfony/Component/Security/Http/Tests/Firewall/ContextListenerTest.php
@@ -411,9 +411,9 @@ protected function runSessionOnKernelResponse($newToken, $original = null)
private function handleEventWithPreviousSession($userProviders, UserInterface $user = null, RememberMeServicesInterface $rememberMeServices = null)
{
- $user = $user ?: new User('foo', 'bar');
+ $tokenUser = $user ?: new User('foo', 'bar');
$session = new Session(new MockArraySessionStorage());
- $session->set('_security_context_key', serialize(new UsernamePasswordToken($user, '', 'context_key', ['ROLE_USER'])));
+ $session->set('_security_context_key', serialize(new UsernamePasswordToken($tokenUser, '', 'context_key', ['ROLE_USER'])));
$request = new Request();
$request->setSession($session);
@@ -442,6 +442,10 @@ private function handleEventWithPreviousSession($userProviders, UserInterface $u
$listener(new RequestEvent($this->getMockBuilder(HttpKernelInterface::class)->getMock(), $request, HttpKernelInterface::MASTER_REQUEST));
if (null !== $usageIndex) {
+ if (null !== $user) {
+ ++$usageIndex;
+ }
+
$this->assertSame($usageIndex, $session->getUsageIndex());
$tokenStorage->getToken();
$this->assertSame(1 + $usageIndex, $session->getUsageIndex());
diff --git a/src/Symfony/Component/Security/Http/composer.json b/src/Symfony/Component/Security/Http/composer.json
index 699ffcf7032b3..6b23f5cc8e311 100644
--- a/src/Symfony/Component/Security/Http/composer.json
+++ b/src/Symfony/Component/Security/Http/composer.json
@@ -17,7 +17,7 @@
],
"require": {
"php": "^7.1.3",
- "symfony/security-core": "^4.4.7",
+ "symfony/security-core": "^4.4.8",
"symfony/http-foundation": "^3.4.40|^4.4.7|^5.0.7",
"symfony/http-kernel": "^4.4",
"symfony/property-access": "^3.4|^4.0|^5.0"
diff --git a/src/Symfony/Component/Serializer/Encoder/XmlEncoder.php b/src/Symfony/Component/Serializer/Encoder/XmlEncoder.php
index 138f9280dd598..fbfa7574693bf 100644
--- a/src/Symfony/Component/Serializer/Encoder/XmlEncoder.php
+++ b/src/Symfony/Component/Serializer/Encoder/XmlEncoder.php
@@ -473,7 +473,7 @@ private function buildXml(\DOMNode $parentNode, $data, string $xmlRootNodeName =
return $this->appendNode($parentNode, $data, 'data');
}
- throw new NotEncodableValueException(sprintf('An unexpected value could not be serialized: %s.', !\is_resource($data) ? var_export($data, true) : sprintf('%s resource', get_resource_type($data))));
+ throw new NotEncodableValueException('An unexpected value could not be serialized: '.(!\is_resource($data) ? var_export($data, true) : sprintf('%s resource', get_resource_type($data))));
}
/**
diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php
index be4f9c47ae729..4eb8468360435 100644
--- a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php
+++ b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php
@@ -359,7 +359,7 @@ public function denormalize($data, $type, $format = null, array $context = [])
try {
$this->setAttributeValue($object, $attribute, $value, $format, $context);
} catch (InvalidArgumentException $e) {
- throw new NotNormalizableValueException(sprintf('Failed to denormalize attribute "%s" value for class "%s": %s.', $attribute, $type, $e->getMessage()), $e->getCode(), $e);
+ throw new NotNormalizableValueException(sprintf('Failed to denormalize attribute "%s" value for class "%s": '.$e->getMessage(), $attribute, $type), $e->getCode(), $e);
}
}
@@ -482,7 +482,7 @@ private function validateAndDenormalize(string $currentClass, string $attribute,
*/
protected function denormalizeParameter(\ReflectionClass $class, \ReflectionParameter $parameter, $parameterName, $parameterData, array $context, $format = null)
{
- if (null === $this->propertyTypeExtractor || null === $types = $this->propertyTypeExtractor->getTypes($class->getName(), $parameterName)) {
+ if (null === $this->propertyTypeExtractor || null === $this->propertyTypeExtractor->getTypes($class->getName(), $parameterName)) {
return parent::denormalizeParameter($class, $parameter, $parameterName, $parameterData, $context, $format);
}
@@ -617,6 +617,7 @@ private function getCacheKey(?string $format, array $context)
unset($context[$key]);
}
unset($context[self::EXCLUDE_FROM_CACHE_KEY]);
+ unset($context[self::OBJECT_TO_POPULATE]);
unset($context['cache_key']); // avoid artificially different keys
try {
diff --git a/src/Symfony/Component/Serializer/Serializer.php b/src/Symfony/Component/Serializer/Serializer.php
index 94770484738f0..3f2461cf96a09 100644
--- a/src/Symfony/Component/Serializer/Serializer.php
+++ b/src/Symfony/Component/Serializer/Serializer.php
@@ -173,7 +173,7 @@ public function normalize($data, $format = null, array $context = [])
throw new NotNormalizableValueException(sprintf('Could not normalize object of type "%s", no supporting normalizer found.', \get_class($data)));
}
- throw new NotNormalizableValueException(sprintf('An unexpected value could not be normalized: %s.', !\is_resource($data) ? var_export($data, true) : sprintf('%s resource', get_resource_type($data))));
+ throw new NotNormalizableValueException('An unexpected value could not be normalized: '.(!\is_resource($data) ? var_export($data, true) : sprintf('%s resource', get_resource_type($data))));
}
/**
diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php
index 3de0ac086a5eb..01b8950cb4c98 100644
--- a/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php
+++ b/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php
@@ -174,6 +174,14 @@ public function testDenormalizeStringCollectionDecodedFromXmlWithTwoChildren()
$this->assertEquals('bar', $stringCollection->children[1]);
}
+ public function testDenormalizeNotSerializableObjectToPopulate()
+ {
+ $normalizer = new AbstractObjectNormalizerDummy();
+ $normalizedData = $normalizer->denormalize(['foo' => 'foo'], Dummy::class, null, [AbstractObjectNormalizer::OBJECT_TO_POPULATE => new NotSerializable()]);
+
+ $this->assertSame('foo', $normalizedData->foo);
+ }
+
private function getDenormalizerForStringCollection()
{
$extractor = $this->getMockBuilder(PhpDocExtractor::class)->getMock();
@@ -448,3 +456,15 @@ public function setSerializer(SerializerInterface $serializer)
$this->serializer = $serializer;
}
}
+
+class NotSerializable
+{
+ public function __sleep()
+ {
+ if (class_exists(\Error::class)) {
+ throw new \Error('not serializable');
+ }
+
+ throw new \Exception('not serializable');
+ }
+}
diff --git a/src/Symfony/Component/Translation/Formatter/IntlFormatter.php b/src/Symfony/Component/Translation/Formatter/IntlFormatter.php
index ad4a45b95b5c0..9101a63aa2ee9 100644
--- a/src/Symfony/Component/Translation/Formatter/IntlFormatter.php
+++ b/src/Symfony/Component/Translation/Formatter/IntlFormatter.php
@@ -40,7 +40,7 @@ public function formatIntl(string $message, string $locale, array $parameters =
try {
$this->cache[$locale][$message] = $formatter = new \MessageFormatter($locale, $message);
} catch (\IntlException $e) {
- throw new InvalidArgumentException(sprintf('Invalid message format (error #%d): %s.', intl_get_error_code(), intl_get_error_message()), 0, $e);
+ throw new InvalidArgumentException(sprintf('Invalid message format (error #%d): '.intl_get_error_message(), intl_get_error_code()), 0, $e);
}
}
@@ -52,7 +52,7 @@ public function formatIntl(string $message, string $locale, array $parameters =
}
if (false === $message = $formatter->format($parameters)) {
- throw new InvalidArgumentException(sprintf('Unable to format message (error #%s): %s.', $formatter->getErrorCode(), $formatter->getErrorMessage()));
+ throw new InvalidArgumentException(sprintf('Unable to format message (error #%s): '.$formatter->getErrorMessage(), $formatter->getErrorCode()));
}
return $message;
diff --git a/src/Symfony/Component/Translation/Loader/JsonFileLoader.php b/src/Symfony/Component/Translation/Loader/JsonFileLoader.php
index fe6747e8aabd4..9f15dbc628893 100644
--- a/src/Symfony/Component/Translation/Loader/JsonFileLoader.php
+++ b/src/Symfony/Component/Translation/Loader/JsonFileLoader.php
@@ -30,7 +30,7 @@ protected function loadResource($resource)
$messages = json_decode($data, true);
if (0 < $errorCode = json_last_error()) {
- throw new InvalidResourceException(sprintf('Error parsing JSON - %s.', $this->getJSONErrorMessage($errorCode)));
+ throw new InvalidResourceException('Error parsing JSON: '.$this->getJSONErrorMessage($errorCode));
}
}
diff --git a/src/Symfony/Component/Translation/Loader/XliffFileLoader.php b/src/Symfony/Component/Translation/Loader/XliffFileLoader.php
index aa36ebcc483ea..d7741a071007c 100644
--- a/src/Symfony/Component/Translation/Loader/XliffFileLoader.php
+++ b/src/Symfony/Component/Translation/Loader/XliffFileLoader.php
@@ -53,12 +53,12 @@ private function extract($resource, MessageCatalogue $catalogue, string $domain)
try {
$dom = XmlUtils::loadFile($resource);
} catch (\InvalidArgumentException $e) {
- throw new InvalidResourceException(sprintf('Unable to load "%s": %s.', $resource, $e->getMessage()), $e->getCode(), $e);
+ throw new InvalidResourceException(sprintf('Unable to load "%s": '.$e->getMessage(), $resource), $e->getCode(), $e);
}
$xliffVersion = XliffUtils::getVersionNumber($dom);
if ($errors = XliffUtils::validateSchema($dom)) {
- throw new InvalidResourceException(sprintf('Invalid resource provided: "%s"; Errors: %s.', $resource, XliffUtils::getErrorsAsString($errors)));
+ throw new InvalidResourceException(sprintf('Invalid resource provided: "%s"; Errors: '.XliffUtils::getErrorsAsString($errors), $resource));
}
if ('1.2' === $xliffVersion) {
diff --git a/src/Symfony/Component/Translation/Tests/Loader/JsonFileLoaderTest.php b/src/Symfony/Component/Translation/Tests/Loader/JsonFileLoaderTest.php
index d264bb16b29d9..c5a9ca64d4d7f 100644
--- a/src/Symfony/Component/Translation/Tests/Loader/JsonFileLoaderTest.php
+++ b/src/Symfony/Component/Translation/Tests/Loader/JsonFileLoaderTest.php
@@ -50,7 +50,7 @@ public function testLoadNonExistingResource()
public function testParseException()
{
$this->expectException('Symfony\Component\Translation\Exception\InvalidResourceException');
- $this->expectExceptionMessage('Error parsing JSON - Syntax error, malformed JSON');
+ $this->expectExceptionMessage('Error parsing JSON: Syntax error, malformed JSON');
$loader = new JsonFileLoader();
$resource = __DIR__.'/../fixtures/malformed.json';
$loader->load($resource, 'en', 'domain1');
diff --git a/src/Symfony/Component/Validator/Constraints/AbstractComparisonValidator.php b/src/Symfony/Component/Validator/Constraints/AbstractComparisonValidator.php
index 3b3aba0450f85..e06f714bbd2ea 100644
--- a/src/Symfony/Component/Validator/Constraints/AbstractComparisonValidator.php
+++ b/src/Symfony/Component/Validator/Constraints/AbstractComparisonValidator.php
@@ -55,7 +55,7 @@ public function validate($value, Constraint $constraint)
try {
$comparedValue = $this->getPropertyAccessor()->getValue($object, $path);
} catch (NoSuchPropertyException $e) {
- throw new ConstraintDefinitionException(sprintf('Invalid property path "%s" provided to "%s" constraint: %s.', $path, \get_class($constraint), $e->getMessage()), 0, $e);
+ throw new ConstraintDefinitionException(sprintf('Invalid property path "%s" provided to "%s" constraint: '.$e->getMessage(), $path, \get_class($constraint)), 0, $e);
}
} else {
$comparedValue = $constraint->value;
diff --git a/src/Symfony/Component/Validator/Constraints/BicValidator.php b/src/Symfony/Component/Validator/Constraints/BicValidator.php
index c49986561e980..651d76dcf1c00 100644
--- a/src/Symfony/Component/Validator/Constraints/BicValidator.php
+++ b/src/Symfony/Component/Validator/Constraints/BicValidator.php
@@ -138,7 +138,7 @@ public function validate($value, Constraint $constraint)
try {
$iban = $this->getPropertyAccessor()->getValue($object, $path);
} catch (NoSuchPropertyException $e) {
- throw new ConstraintDefinitionException(sprintf('Invalid property path "%s" provided to "%s" constraint: %s.', $path, \get_class($constraint), $e->getMessage()), 0, $e);
+ throw new ConstraintDefinitionException(sprintf('Invalid property path "%s" provided to "%s" constraint: '.$e->getMessage(), $path, \get_class($constraint)), 0, $e);
}
}
if (!$iban) {
diff --git a/src/Symfony/Component/Validator/Constraints/CallbackValidator.php b/src/Symfony/Component/Validator/Constraints/CallbackValidator.php
index ef1c10b181ed3..751c2b9c115ed 100644
--- a/src/Symfony/Component/Validator/Constraints/CallbackValidator.php
+++ b/src/Symfony/Component/Validator/Constraints/CallbackValidator.php
@@ -40,7 +40,7 @@ public function validate($object, Constraint $constraint)
if (isset($method[0]) && \is_object($method[0])) {
$method[0] = \get_class($method[0]);
}
- throw new ConstraintDefinitionException(sprintf('%s targeted by Callback constraint is not a valid callable.', json_encode($method)));
+ throw new ConstraintDefinitionException(json_encode($method).' targeted by Callback constraint is not a valid callable.');
}
$method($object, $this->context, $constraint->payload);
diff --git a/src/Symfony/Component/Validator/Constraints/Composite.php b/src/Symfony/Component/Validator/Constraints/Composite.php
index bd7030ee27a44..b14276c3bbf1a 100644
--- a/src/Symfony/Component/Validator/Constraints/Composite.php
+++ b/src/Symfony/Component/Validator/Constraints/Composite.php
@@ -88,7 +88,8 @@ public function __construct($options = null)
}
}
- $this->groups = array_keys($mergedGroups);
+ // prevent empty composite constraint to have empty groups
+ $this->groups = array_keys($mergedGroups) ?: [self::DEFAULT_GROUP];
$this->$compositeOption = $nestedConstraints;
return;
diff --git a/src/Symfony/Component/Validator/Constraints/RangeValidator.php b/src/Symfony/Component/Validator/Constraints/RangeValidator.php
index 9b7c40ce5ae9a..3cfd43f998676 100644
--- a/src/Symfony/Component/Validator/Constraints/RangeValidator.php
+++ b/src/Symfony/Component/Validator/Constraints/RangeValidator.php
@@ -157,7 +157,7 @@ private function getLimit($propertyPath, $default, Constraint $constraint)
try {
return $this->getPropertyAccessor()->getValue($object, $propertyPath);
} catch (NoSuchPropertyException $e) {
- throw new ConstraintDefinitionException(sprintf('Invalid property path "%s" provided to "%s" constraint: %s.', $propertyPath, \get_class($constraint), $e->getMessage()), 0, $e);
+ throw new ConstraintDefinitionException(sprintf('Invalid property path "%s" provided to "%s" constraint: '.$e->getMessage(), $propertyPath, \get_class($constraint)), 0, $e);
}
}
diff --git a/src/Symfony/Component/Validator/Constraints/UrlValidator.php b/src/Symfony/Component/Validator/Constraints/UrlValidator.php
index 43c21f5768118..77d7354946653 100644
--- a/src/Symfony/Component/Validator/Constraints/UrlValidator.php
+++ b/src/Symfony/Component/Validator/Constraints/UrlValidator.php
@@ -24,7 +24,7 @@ class UrlValidator extends ConstraintValidator
{
const PATTERN = '~^
(%s):// # protocol
- (([\_\.\pL\pN-]+:)?([\_\.\pL\pN-]+)@)? # basic auth
+ (((?:[\_\.\pL\pN-]|%%[0-9A-Fa-f]{2})+:)?((?:[\_\.\pL\pN-]|%%[0-9A-Fa-f]{2})+)@)? # basic auth
(
([\pL\pN\pS\-\_\.])+(\.?([\pL\pN]|xn\-\-[\pL\pN-]+)+\.?) # a domain name
| # or
diff --git a/src/Symfony/Component/Validator/Context/ExecutionContext.php b/src/Symfony/Component/Validator/Context/ExecutionContext.php
index c18cc0212e491..a197168bf72ab 100644
--- a/src/Symfony/Component/Validator/Context/ExecutionContext.php
+++ b/src/Symfony/Component/Validator/Context/ExecutionContext.php
@@ -21,6 +21,7 @@
use Symfony\Component\Validator\Mapping\MetadataInterface;
use Symfony\Component\Validator\Mapping\PropertyMetadataInterface;
use Symfony\Component\Validator\Util\PropertyPath;
+use Symfony\Component\Validator\Validator\LazyProperty;
use Symfony\Component\Validator\Validator\ValidatorInterface;
use Symfony\Component\Validator\Violation\ConstraintViolationBuilder;
use Symfony\Component\Validator\Violation\ConstraintViolationBuilderInterface;
@@ -192,7 +193,7 @@ public function addViolation($message, array $parameters = [])
$parameters,
$this->root,
$this->propertyPath,
- $this->value,
+ $this->getValue(),
null,
null,
$this->constraint
@@ -211,7 +212,7 @@ public function buildViolation($message, array $parameters = []): ConstraintViol
$parameters,
$this->root,
$this->propertyPath,
- $this->value,
+ $this->getValue(),
$this->translator,
$this->translationDomain
);
@@ -246,6 +247,10 @@ public function getRoot()
*/
public function getValue()
{
+ if ($this->value instanceof LazyProperty) {
+ return $this->value->getPropertyValue();
+ }
+
return $this->value;
}
diff --git a/src/Symfony/Component/Validator/Mapping/Factory/LazyLoadingMetadataFactory.php b/src/Symfony/Component/Validator/Mapping/Factory/LazyLoadingMetadataFactory.php
index 38df595f5516c..642d8e6e9f0fb 100644
--- a/src/Symfony/Component/Validator/Mapping/Factory/LazyLoadingMetadataFactory.php
+++ b/src/Symfony/Component/Validator/Mapping/Factory/LazyLoadingMetadataFactory.php
@@ -138,34 +138,25 @@ public function getMetadataFor($value)
private function mergeConstraints(ClassMetadata $metadata)
{
+ if ($metadata->getReflectionClass()->isInterface()) {
+ return;
+ }
+
// Include constraints from the parent class
if ($parent = $metadata->getReflectionClass()->getParentClass()) {
$metadata->mergeConstraints($this->getMetadataFor($parent->name));
}
- $interfaces = $metadata->getReflectionClass()->getInterfaces();
-
- $interfaces = array_filter($interfaces, function (\ReflectionClass $interface) use ($parent, $interfaces) {
- $interfaceName = $interface->getName();
-
- if ($parent && $parent->implementsInterface($interfaceName)) {
- return false;
- }
-
- foreach ($interfaces as $i) {
- if ($i !== $interface && $i->implementsInterface($interfaceName)) {
- return false;
- }
- }
-
- return true;
- });
-
// Include constraints from all directly implemented interfaces
- foreach ($interfaces as $interface) {
+ foreach ($metadata->getReflectionClass()->getInterfaces() as $interface) {
if ('Symfony\Component\Validator\GroupSequenceProviderInterface' === $interface->name) {
continue;
}
+
+ if ($parent && \in_array($interface->getName(), $parent->getInterfaceNames(), true)) {
+ continue;
+ }
+
$metadata->mergeConstraints($this->getMetadataFor($interface->name));
}
}
diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.ru.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.ru.xlf
index 80911a9902910..3c03fd8525cca 100644
--- a/src/Symfony/Component/Validator/Resources/translations/validators.ru.xlf
+++ b/src/Symfony/Component/Validator/Resources/translations/validators.ru.xlf
@@ -370,6 +370,18 @@
This value is not a valid hostname.
Значение не является корректным именем хоста.
+
+ The number of elements in this collection should be a multiple of {{ compared_value }}.
+ Количество элементов в этой коллекции должно быть кратным {{ compared_value }}.
+
+
+ This value should satisfy at least one of the following constraints:
+ Значение должно удовлетворять как минимум одному из следующих ограничений:
+
+
+ Each element of this collection should satisfy its own set of constraints.
+ Каждый элемент этой коллекции должен удовлетворять своему собственному набору ограничений.
+