diff --git a/phpunit b/phpunit
index 2b4412dc4d..e1b1aea0e4 100755
--- a/phpunit
+++ b/phpunit
@@ -12,10 +12,10 @@ if (!getenv('SYMFONY_PHPUNIT_VERSION')) {
if (false === getenv('SYMFONY_PHPUNIT_REMOVE_RETURN_TYPEHINT') && false !== strpos(@file_get_contents(__DIR__.'/src/Symfony/Component/HttpKernel/Kernel.php'), 'const MAJOR_VERSION = 3;')) {
putenv('SYMFONY_PHPUNIT_REMOVE_RETURN_TYPEHINT=1');
}
- if (\PHP_VERSION_ID >= 80000) {
- putenv('SYMFONY_PHPUNIT_VERSION=9.3');
+ if (\PHP_VERSION_ID < 70300) {
+ putenv('SYMFONY_PHPUNIT_VERSION=8.5');
} else {
- putenv('SYMFONY_PHPUNIT_VERSION=8.3');
+ putenv('SYMFONY_PHPUNIT_VERSION=9.3');
}
} elseif (\PHP_VERSION_ID >= 70000) {
putenv('SYMFONY_PHPUNIT_VERSION=6.5');
diff --git a/src/Symfony/Bridge/PhpUnit/Legacy/CoverageListenerTrait.php b/src/Symfony/Bridge/PhpUnit/Legacy/CoverageListenerTrait.php
index bd8a452777..c1b276cea2 100644
--- a/src/Symfony/Bridge/PhpUnit/Legacy/CoverageListenerTrait.php
+++ b/src/Symfony/Bridge/PhpUnit/Legacy/CoverageListenerTrait.php
@@ -109,7 +109,7 @@ class CoverageListenerTrait
// Exclude internal classes; PHPUnit 9.1+ is picky about tests covering, say, a \RuntimeException
$covers = array_filter($covers, function ($class) {
- $reflector = new ReflectionClass($class);
+ $reflector = new \ReflectionClass($class);
return $reflector->isUserDefined();
});
diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php
index eab898b882..0070353a71 100644
--- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php
+++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php
@@ -1197,7 +1197,7 @@ class Configuration implements ConfigurationInterface
return $middleware;
}
if (1 < \count($middleware)) {
- throw new \InvalidArgumentException(sprintf('Invalid middleware at path "framework.messenger": a map with a single factory id as key and its arguments as value was expected, %s given.', json_encode($middleware)));
+ throw new \InvalidArgumentException('Invalid middleware at path "framework.messenger": a map with a single factory id as key and its arguments as value was expected, '.json_encode($middleware).' given.');
}
return [
diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd b/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd
index e2bc23f936..815efd8870 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd
+++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd
@@ -331,7 +331,7 @@
-
+
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/workflows.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/workflows.php
index 7c7f7ed0b4..d0abe507e5 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/workflows.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/workflows.php
@@ -10,6 +10,10 @@ $container->loadFromExtension('framework', [
FrameworkExtensionTest::class,
],
'initial_marking' => ['draft'],
+ 'metadata' => [
+ 'title' => 'article workflow',
+ 'description' => 'workflow for articles'
+ ],
'places' => [
'draft',
'wait_for_journalist',
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/workflows.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/workflows.xml
index 0c6a638df4..290ab50e7d 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/workflows.xml
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/workflows.xml
@@ -35,6 +35,10 @@
approved_by_spellchecker
published
+
+ article workflow
+ workflow for articles
+
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/workflows.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/workflows.yml
index 225106383d..e4ac9c0189 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/workflows.yml
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/workflows.yml
@@ -5,6 +5,9 @@ framework:
supports:
- Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTest
initial_marking: [draft]
+ metadata:
+ title: article workflow
+ description: workflow for articles
places:
# simple format
- draft
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php
index fd0b154198..c6f158eb8c 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php
@@ -54,6 +54,7 @@ use Symfony\Component\Translation\DependencyInjection\TranslatorPass;
use Symfony\Component\Validator\DependencyInjection\AddConstraintValidatorsPass;
use Symfony\Component\Validator\Mapping\Loader\PropertyInfoLoader;
use Symfony\Component\Workflow;
+use Symfony\Component\Workflow\Metadata\InMemoryMetadataStore;
abstract class FrameworkExtensionTest extends TestCase
{
@@ -231,6 +232,12 @@ abstract class FrameworkExtensionTest extends TestCase
);
$this->assertCount(4, $workflowDefinition->getArgument(1));
$this->assertSame(['draft'], $workflowDefinition->getArgument(2));
+ $metadataStoreDefinition = $workflowDefinition->getArgument(3);
+ $this->assertSame(InMemoryMetadataStore::class, $metadataStoreDefinition->getClass());
+ $this->assertSame([
+ 'title' => 'article workflow',
+ 'description' => 'workflow for articles',
+ ], $metadataStoreDefinition->getArgument(0));
$this->assertTrue($container->hasDefinition('state_machine.pull_request'), 'State machine is registered as a service');
$this->assertSame('state_machine.abstract', $container->getDefinition('state_machine.pull_request')->getParent());
@@ -255,7 +262,7 @@ abstract class FrameworkExtensionTest extends TestCase
$metadataStoreDefinition = $stateMachineDefinition->getArgument(3);
$this->assertInstanceOf(Definition::class, $metadataStoreDefinition);
- $this->assertSame(Workflow\Metadata\InMemoryMetadataStore::class, $metadataStoreDefinition->getClass());
+ $this->assertSame(InMemoryMetadataStore::class, $metadataStoreDefinition->getClass());
$workflowMetadata = $metadataStoreDefinition->getArgument(0);
$this->assertSame(['title' => 'workflow title'], $workflowMetadata);
diff --git a/src/Symfony/Component/Cache/CacheItem.php b/src/Symfony/Component/Cache/CacheItem.php
index 3f121766fe..deb23ad343 100644
--- a/src/Symfony/Component/Cache/CacheItem.php
+++ b/src/Symfony/Component/Cache/CacheItem.php
@@ -119,9 +119,10 @@ final class CacheItem implements ItemInterface
$tags = [$tags];
}
foreach ($tags as $tag) {
- if (!\is_string($tag)) {
- throw new InvalidArgumentException(sprintf('Cache tag must be string, "%s" given.', get_debug_type($tag)));
+ if (!\is_string($tag) && !(\is_object($tag) && method_exists($tag, '__toString'))) {
+ throw new InvalidArgumentException(sprintf('Cache tag must be string or object that implements __toString(), "%s" given.', \is_object($tag) ? \get_class($tag) : \gettype($tag)));
}
+ $tag = (string) $tag;
if (isset($this->newMetadata[self::METADATA_TAGS][$tag])) {
continue;
}
diff --git a/src/Symfony/Component/Cache/Tests/CacheItemTest.php b/src/Symfony/Component/Cache/Tests/CacheItemTest.php
index 3b756f571f..01914e4a36 100644
--- a/src/Symfony/Component/Cache/Tests/CacheItemTest.php
+++ b/src/Symfony/Component/Cache/Tests/CacheItemTest.php
@@ -13,6 +13,7 @@ namespace Symfony\Component\Cache\Tests;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Cache\CacheItem;
+use Symfony\Component\Cache\Tests\Fixtures\StringableTag;
class CacheItemTest extends TestCase
{
@@ -61,9 +62,11 @@ class CacheItemTest extends TestCase
$this->assertSame($item, $item->tag('foo'));
$this->assertSame($item, $item->tag(['bar', 'baz']));
+ $this->assertSame($item, $item->tag(new StringableTag('qux')));
+ $this->assertSame($item, $item->tag([new StringableTag('quux'), new StringableTag('quuux')]));
(\Closure::bind(function () use ($item) {
- $this->assertSame(['foo' => 'foo', 'bar' => 'bar', 'baz' => 'baz'], $item->newMetadata[CacheItem::METADATA_TAGS]);
+ $this->assertSame(['foo' => 'foo', 'bar' => 'bar', 'baz' => 'baz', 'qux' => 'qux', 'quux' => 'quux', 'quuux' => 'quuux'], $item->newMetadata[CacheItem::METADATA_TAGS]);
}, $this, CacheItem::class))();
}
diff --git a/src/Symfony/Component/Cache/Tests/Fixtures/StringableTag.php b/src/Symfony/Component/Cache/Tests/Fixtures/StringableTag.php
new file mode 100644
index 0000000000..caaf55c026
--- /dev/null
+++ b/src/Symfony/Component/Cache/Tests/Fixtures/StringableTag.php
@@ -0,0 +1,30 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Cache\Tests\Fixtures;
+
+class StringableTag
+{
+ /**
+ * @var string
+ */
+ private $tag;
+
+ public function __construct(string $tag)
+ {
+ $this->tag = $tag;
+ }
+
+ public function __toString(): string
+ {
+ return $this->tag;
+ }
+}
diff --git a/src/Symfony/Component/Console/Application.php b/src/Symfony/Component/Console/Application.php
index c42609d8c7..784af89b31 100644
--- a/src/Symfony/Component/Console/Application.php
+++ b/src/Symfony/Component/Console/Application.php
@@ -107,8 +107,10 @@ class Application implements ResetInterface
*/
public function run(InputInterface $input = null, OutputInterface $output = null)
{
- putenv('LINES='.$this->terminal->getHeight());
- putenv('COLUMNS='.$this->terminal->getWidth());
+ if (\function_exists('putenv')) {
+ @putenv('LINES='.$this->terminal->getHeight());
+ @putenv('COLUMNS='.$this->terminal->getWidth());
+ }
if (null === $input) {
$input = new ArgvInput();
@@ -891,7 +893,9 @@ class Application implements ResetInterface
$input->setInteractive(false);
}
- putenv('SHELL_VERBOSITY='.$shellVerbosity);
+ if (\function_exists('putenv')) {
+ @putenv('SHELL_VERBOSITY='.$shellVerbosity);
+ }
$_ENV['SHELL_VERBOSITY'] = $shellVerbosity;
$_SERVER['SHELL_VERBOSITY'] = $shellVerbosity;
}
diff --git a/src/Symfony/Component/EventDispatcher/Tests/Debug/TraceableEventDispatcherTest.php b/src/Symfony/Component/EventDispatcher/Tests/Debug/TraceableEventDispatcherTest.php
index 99627a71a9..bcb14d34a6 100644
--- a/src/Symfony/Component/EventDispatcher/Tests/Debug/TraceableEventDispatcherTest.php
+++ b/src/Symfony/Component/EventDispatcher/Tests/Debug/TraceableEventDispatcherTest.php
@@ -12,6 +12,7 @@
namespace Symfony\Component\EventDispatcher\Tests\Debug;
use PHPUnit\Framework\TestCase;
+use Symfony\Component\ErrorHandler\BufferingLogger;
use Symfony\Component\EventDispatcher\Debug\TraceableEventDispatcher;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
@@ -192,41 +193,57 @@ class TraceableEventDispatcherTest extends TestCase
public function testLogger()
{
- $logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
+ $logger = new BufferingLogger();
$dispatcher = new EventDispatcher();
$tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch(), $logger);
$tdispatcher->addListener('foo', $listener1 = function () {});
$tdispatcher->addListener('foo', $listener2 = function () {});
- $logger->expects($this->exactly(2))
- ->method('debug')
- ->withConsecutive(
- ['Notified event "{event}" to listener "{listener}".', ['event' => 'foo', 'listener' => 'closure']],
- ['Notified event "{event}" to listener "{listener}".', ['event' => 'foo', 'listener' => 'closure']]
- );
-
$tdispatcher->dispatch(new Event(), 'foo');
+
+ $this->assertSame([
+ [
+ 'debug',
+ 'Notified event "{event}" to listener "{listener}".',
+ ['event' => 'foo', 'listener' => 'closure'],
+ ],
+ [
+ 'debug',
+ 'Notified event "{event}" to listener "{listener}".',
+ ['event' => 'foo', 'listener' => 'closure'],
+ ],
+ ], $logger->cleanLogs());
}
public function testLoggerWithStoppedEvent()
{
- $logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
+ $logger = new BufferingLogger();
$dispatcher = new EventDispatcher();
$tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch(), $logger);
$tdispatcher->addListener('foo', $listener1 = function (Event $event) { $event->stopPropagation(); });
$tdispatcher->addListener('foo', $listener2 = function () {});
- $logger->expects($this->exactly(3))
- ->method('debug')
- ->withConsecutive(
- ['Notified event "{event}" to listener "{listener}".', ['event' => 'foo', 'listener' => 'closure']],
- ['Listener "{listener}" stopped propagation of the event "{event}".', ['event' => 'foo', 'listener' => 'closure']],
- ['Listener "{listener}" was not called for event "{event}".', ['event' => 'foo', 'listener' => 'closure']]
- );
-
$tdispatcher->dispatch(new Event(), 'foo');
+
+ $this->assertSame([
+ [
+ 'debug',
+ 'Notified event "{event}" to listener "{listener}".',
+ ['event' => 'foo', 'listener' => 'closure'],
+ ],
+ [
+ 'debug',
+ 'Listener "{listener}" stopped propagation of the event "{event}".',
+ ['event' => 'foo', 'listener' => 'closure'],
+ ],
+ [
+ 'debug',
+ 'Listener "{listener}" was not called for event "{event}".',
+ ['event' => 'foo', 'listener' => 'closure'],
+ ],
+ ], $logger->cleanLogs());
}
public function testDispatchCallListeners()
diff --git a/src/Symfony/Component/EventDispatcher/composer.json b/src/Symfony/Component/EventDispatcher/composer.json
index a67f66a42a..7fb244fa6a 100644
--- a/src/Symfony/Component/EventDispatcher/composer.json
+++ b/src/Symfony/Component/EventDispatcher/composer.json
@@ -25,6 +25,7 @@
"symfony/dependency-injection": "^4.4|^5.0",
"symfony/expression-language": "^4.4|^5.0",
"symfony/config": "^4.4|^5.0",
+ "symfony/error-handler": "^4.4|^5.0",
"symfony/http-foundation": "^4.4|^5.0",
"symfony/service-contracts": "^1.1|^2",
"symfony/stopwatch": "^4.4|^5.0",
diff --git a/src/Symfony/Component/Form/Extension/Core/Type/BaseType.php b/src/Symfony/Component/Form/Extension/Core/Type/BaseType.php
index ac371c61c5..3b5a3a01b5 100644
--- a/src/Symfony/Component/Form/Extension/Core/Type/BaseType.php
+++ b/src/Symfony/Component/Form/Extension/Core/Type/BaseType.php
@@ -11,6 +11,7 @@
namespace Symfony\Component\Form\Extension\Core\Type;
+use Symfony\Component\Form\AbstractRendererEngine;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormInterface;
@@ -112,7 +113,7 @@ abstract class BaseType extends AbstractType
// collection form have different types (dynamically), they should
// be rendered differently.
// https://github.com/symfony/symfony/issues/5038
- 'cache_key' => $uniqueBlockPrefix.'_'.$form->getConfig()->getType()->getBlockPrefix(),
+ AbstractRendererEngine::CACHE_KEY_VAR => $uniqueBlockPrefix.'_'.$form->getConfig()->getType()->getBlockPrefix(),
]);
}
diff --git a/src/Symfony/Component/HttpClient/CurlHttpClient.php b/src/Symfony/Component/HttpClient/CurlHttpClient.php
index b7f814ed14..5174ad053a 100644
--- a/src/Symfony/Component/HttpClient/CurlHttpClient.php
+++ b/src/Symfony/Component/HttpClient/CurlHttpClient.php
@@ -267,7 +267,14 @@ final class CurlHttpClient implements HttpClientInterface, LoggerAwareInterface,
}
if ($options['bindto']) {
- $curlopts[file_exists($options['bindto']) ? \CURLOPT_UNIX_SOCKET_PATH : \CURLOPT_INTERFACE] = $options['bindto'];
+ if (file_exists($options['bindto'])) {
+ $curlopts[\CURLOPT_UNIX_SOCKET_PATH] = $options['bindto'];
+ } elseif (preg_match('/^(.*):(\d+)$/', $options['bindto'], $matches)) {
+ $curlopts[\CURLOPT_INTERFACE] = $matches[1];
+ $curlopts[\CURLOPT_LOCALPORT] = $matches[2];
+ } else {
+ $curlopts[\CURLOPT_INTERFACE] = $options['bindto'];
+ }
}
if (0 < $options['max_duration']) {
diff --git a/src/Symfony/Component/HttpClient/HttpClientTrait.php b/src/Symfony/Component/HttpClient/HttpClientTrait.php
index b001f28c18..d9037c832a 100644
--- a/src/Symfony/Component/HttpClient/HttpClientTrait.php
+++ b/src/Symfony/Component/HttpClient/HttpClientTrait.php
@@ -198,6 +198,16 @@ trait HttpClientTrait
continue;
}
+ if ('auth_ntlm' === $name) {
+ if (!\extension_loaded('curl')) {
+ $msg = 'try installing the "curl" extension to use "%s" instead.';
+ } else {
+ $msg = 'try using "%s" instead.';
+ }
+
+ throw new InvalidArgumentException(sprintf('Option "auth_ntlm" is not supported by "%s", '.$msg, __CLASS__, CurlHttpClient::class));
+ }
+
$alternatives = [];
foreach ($defaultOptions as $key => $v) {
@@ -206,10 +216,6 @@ trait HttpClientTrait
}
}
- if ('auth_ntlm' === $name) {
- throw new InvalidArgumentException(sprintf('Option "auth_ntlm" is not supported by "%s", try using CurlHttpClient instead.', __CLASS__));
- }
-
throw new InvalidArgumentException(sprintf('Unsupported option "%s" passed to "%s", did you mean "%s"?', $name, __CLASS__, implode('", "', $alternatives ?: array_keys($defaultOptions))));
}
diff --git a/src/Symfony/Component/HttpClient/Tests/CurlHttpClientTest.php b/src/Symfony/Component/HttpClient/Tests/CurlHttpClientTest.php
index e0a12a49a5..3f51b73603 100644
--- a/src/Symfony/Component/HttpClient/Tests/CurlHttpClientTest.php
+++ b/src/Symfony/Component/HttpClient/Tests/CurlHttpClientTest.php
@@ -34,6 +34,74 @@ class CurlHttpClientTest extends HttpClientTestCase
return new CurlHttpClient(['verify_peer' => false, 'verify_host' => false]);
}
+ public function testBindToPort()
+ {
+ $client = $this->getHttpClient(__FUNCTION__);
+ $response = $client->request('GET', 'http://localhost:8057', ['bindto' => '127.0.0.1:9876']);
+ $response->getStatusCode();
+
+ $r = new \ReflectionProperty($response, 'handle');
+ $r->setAccessible(true);
+
+ $curlInfo = curl_getinfo($r->getValue($response));
+
+ self::assertSame('127.0.0.1', $curlInfo['local_ip']);
+ self::assertSame(9876, $curlInfo['local_port']);
+ }
+
+ /**
+ * @requires PHP 7.2.17
+ */
+ public function testHttp2PushVulcain()
+ {
+ $client = $this->getVulcainClient();
+ $logger = new TestLogger();
+ $client->setLogger($logger);
+
+ $responseAsArray = $client->request('GET', 'https://127.0.0.1:3000/json', [
+ 'headers' => [
+ 'Preload' => '/documents/*/id',
+ ],
+ ])->toArray();
+
+ foreach ($responseAsArray['documents'] as $document) {
+ $client->request('GET', 'https://127.0.0.1:3000'.$document['id'])->toArray();
+ }
+
+ $client->reset();
+
+ $expected = [
+ 'Request: "GET https://127.0.0.1:3000/json"',
+ 'Queueing pushed response: "https://127.0.0.1:3000/json/1"',
+ 'Queueing pushed response: "https://127.0.0.1:3000/json/2"',
+ 'Queueing pushed response: "https://127.0.0.1:3000/json/3"',
+ 'Response: "200 https://127.0.0.1:3000/json"',
+ 'Accepting pushed response: "GET https://127.0.0.1:3000/json/1"',
+ 'Response: "200 https://127.0.0.1:3000/json/1"',
+ 'Accepting pushed response: "GET https://127.0.0.1:3000/json/2"',
+ 'Response: "200 https://127.0.0.1:3000/json/2"',
+ 'Accepting pushed response: "GET https://127.0.0.1:3000/json/3"',
+ 'Response: "200 https://127.0.0.1:3000/json/3"',
+ ];
+ $this->assertSame($expected, $logger->logs);
+ }
+
+ /**
+ * @requires PHP 7.2.17
+ */
+ public function testHttp2PushVulcainWithUnusedResponse()
+ {
+ $client = $this->getVulcainClient();
+ $logger = new TestLogger();
+ $client->setLogger($logger);
+
+ $responseAsArray = $client->request('GET', 'https://127.0.0.1:3000/json', [
+ 'headers' => [
+ 'Preload' => '/documents/*/id',
+ ],
+ ])->toArray();
+ }
+
public function testTimeoutIsNotAFatalError()
{
if ('\\' === \DIRECTORY_SEPARATOR) {
diff --git a/src/Symfony/Component/HttpKernel/HttpClientKernel.php b/src/Symfony/Component/HttpKernel/HttpClientKernel.php
index a5e207d546..818082f439 100644
--- a/src/Symfony/Component/HttpKernel/HttpClientKernel.php
+++ b/src/Symfony/Component/HttpKernel/HttpClientKernel.php
@@ -35,7 +35,7 @@ final class HttpClientKernel implements HttpKernelInterface
public function __construct(HttpClientInterface $client = null)
{
- if (!class_exists(HttpClient::class)) {
+ if (null === $client && !class_exists(HttpClient::class)) {
throw new \LogicException(sprintf('You cannot use "%s" as the HttpClient component is not installed. Try running "composer require symfony/http-client".', __CLASS__));
}
@@ -53,7 +53,6 @@ final class HttpClientKernel implements HttpKernelInterface
$response = $this->client->request($request->getMethod(), $request->getUri(), [
'headers' => $headers,
'body' => $body,
- 'max_redirects' => 0,
] + $request->attributes->get('http_client_options', []));
$response = new Response($response->getContent(!$catch), $response->getStatusCode(), $response->getHeaders(!$catch));
diff --git a/src/Symfony/Component/HttpKernel/Tests/HttpClientKernelTest.php b/src/Symfony/Component/HttpKernel/Tests/HttpClientKernelTest.php
new file mode 100644
index 0000000000..2b904bf9a2
--- /dev/null
+++ b/src/Symfony/Component/HttpKernel/Tests/HttpClientKernelTest.php
@@ -0,0 +1,46 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpKernel\Tests;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpKernel\HttpClientKernel;
+use Symfony\Contracts\HttpClient\HttpClientInterface;
+use Symfony\Contracts\HttpClient\ResponseInterface;
+
+class HttpClientKernelTest extends TestCase
+{
+ public function testHandlePassesMaxRedirectsHttpClientOption()
+ {
+ $request = new Request();
+ $request->attributes->set('http_client_options', ['max_redirects' => 50]);
+
+ $response = $this->getMockBuilder(ResponseInterface::class)->getMock();
+ $response->expects($this->once())->method('getStatusCode')->willReturn(200);
+
+ $client = $this->getMockBuilder(HttpClientInterface::class)->getMock();
+ $client
+ ->expects($this->once())
+ ->method('request')
+ ->willReturnCallback(function (string $method, string $uri, array $options) use ($request, $response) {
+ $this->assertSame($request->getMethod(), $method);
+ $this->assertSame($request->getUri(), $uri);
+ $this->assertArrayHasKey('max_redirects', $options);
+ $this->assertSame(50, $options['max_redirects']);
+
+ return $response;
+ });
+
+ $kernel = new HttpClientKernel($client);
+ $kernel->handle($request);
+ }
+}
diff --git a/src/Symfony/Component/HttpKernel/composer.json b/src/Symfony/Component/HttpKernel/composer.json
index c016af2369..d29435754f 100644
--- a/src/Symfony/Component/HttpKernel/composer.json
+++ b/src/Symfony/Component/HttpKernel/composer.json
@@ -20,6 +20,7 @@
"symfony/deprecation-contracts": "^2.1",
"symfony/error-handler": "^4.4|^5.0",
"symfony/event-dispatcher": "^5.0",
+ "symfony/http-client-contracts": "^1.1|^2",
"symfony/http-foundation": "^4.4|^5.0",
"symfony/polyfill-ctype": "^1.8",
"symfony/polyfill-php73": "^1.9",
diff --git a/src/Symfony/Component/Translation/Tests/TranslatorTest.php b/src/Symfony/Component/Translation/Tests/TranslatorTest.php
index 72a4f9055a..ed1bb9dba5 100644
--- a/src/Symfony/Component/Translation/Tests/TranslatorTest.php
+++ b/src/Symfony/Component/Translation/Tests/TranslatorTest.php
@@ -19,6 +19,19 @@ use Symfony\Component\Translation\Translator;
class TranslatorTest extends TestCase
{
+ private $defaultLocale;
+
+ protected function setUp(): void
+ {
+ $this->defaultLocale = \Locale::getDefault();
+ \Locale::setDefault('en');
+ }
+
+ protected function tearDown(): void
+ {
+ \Locale::setDefault($this->defaultLocale);
+ }
+
/**
* @dataProvider getInvalidLocalesTests
*/
diff --git a/src/Symfony/Component/Translation/Translator.php b/src/Symfony/Component/Translation/Translator.php
index c92abf1383..b6799b7daf 100644
--- a/src/Symfony/Component/Translation/Translator.php
+++ b/src/Symfony/Component/Translation/Translator.php
@@ -158,7 +158,7 @@ class Translator implements TranslatorInterface, TranslatorBagInterface, LocaleA
*/
public function getLocale()
{
- return $this->locale;
+ return $this->locale ?? \Locale::getDefault();
}
/**
diff --git a/src/Symfony/Component/Validator/Constraints/TimezoneValidator.php b/src/Symfony/Component/Validator/Constraints/TimezoneValidator.php
index cf52239d0c..ab6b4eed62 100644
--- a/src/Symfony/Component/Validator/Constraints/TimezoneValidator.php
+++ b/src/Symfony/Component/Validator/Constraints/TimezoneValidator.php
@@ -78,7 +78,11 @@ class TimezoneValidator extends ConstraintValidator
private static function getPhpTimezones(int $zone, string $countryCode = null): array
{
if (null !== $countryCode) {
- return @\DateTimeZone::listIdentifiers($zone, $countryCode) ?: [];
+ try {
+ return @\DateTimeZone::listIdentifiers($zone, $countryCode) ?: [];
+ } catch (\ValueError $e) {
+ return [];
+ }
}
return \DateTimeZone::listIdentifiers($zone);
diff --git a/src/Symfony/Component/VarDumper/Caster/SplCaster.php b/src/Symfony/Component/VarDumper/Caster/SplCaster.php
index f1854a50ad..49cca4fd0c 100644
--- a/src/Symfony/Component/VarDumper/Caster/SplCaster.php
+++ b/src/Symfony/Component/VarDumper/Caster/SplCaster.php
@@ -110,6 +110,14 @@ class SplCaster
$a[$prefix.'⚠'] = 'The parent constructor was not called: the object is in an invalid state';
+ return $a;
+ } catch (\Error $e) {
+ if ('Object not initialized' !== $e->getMessage()) {
+ throw $e;
+ }
+
+ $a[$prefix.'⚠'] = 'The parent constructor was not called: the object is in an invalid state';
+
return $a;
}
}
diff --git a/src/Symfony/Component/Yaml/Inline.php b/src/Symfony/Component/Yaml/Inline.php
index c862d8c798..3086ba0bf4 100644
--- a/src/Symfony/Component/Yaml/Inline.php
+++ b/src/Symfony/Component/Yaml/Inline.php
@@ -90,7 +90,7 @@ class Inline
}
// some comments are allowed at the end
- if (preg_replace('/\s+#.*$/A', '', substr($value, $i))) {
+ if (preg_replace('/\s*#.*$/A', '', substr($value, $i))) {
throw new ParseException(sprintf('Unexpected characters near "%s".', substr($value, $i)), self::$parsedLineNumber + 1, $value, self::$parsedFilename);
}
diff --git a/src/Symfony/Component/Yaml/Parser.php b/src/Symfony/Component/Yaml/Parser.php
index ff6ffa3100..690c202983 100644
--- a/src/Symfony/Component/Yaml/Parser.php
+++ b/src/Symfony/Component/Yaml/Parser.php
@@ -104,6 +104,7 @@ class Parser
$this->refs = [];
$this->skippedLineNumbers = [];
$this->locallySkippedLineNumbers = [];
+ $this->totalNumberOfLines = null;
}
return $data;
@@ -728,7 +729,7 @@ class Parser
if (\in_array($value[0], ['!', '|', '>'], true) && self::preg_match('/^(?:'.self::TAG_PATTERN.' +)?'.self::BLOCK_SCALAR_HEADER_PATTERN.'$/', $value, $matches)) {
$modifiers = isset($matches['modifiers']) ? $matches['modifiers'] : '';
- $data = $this->parseBlockScalar($matches['separator'], preg_replace('#\d+#', '', $modifiers), (int) abs((int) $modifiers));
+ $data = $this->parseBlockScalar($matches['separator'], preg_replace('#\d+#', '', $modifiers), abs((int) $modifiers));
if ('' !== $matches['tag'] && '!' !== $matches['tag']) {
if ('!!binary' === $matches['tag']) {
diff --git a/src/Symfony/Component/Yaml/Tests/InlineTest.php b/src/Symfony/Component/Yaml/Tests/InlineTest.php
index 84cc2c4846..9e7d072934 100644
--- a/src/Symfony/Component/Yaml/Tests/InlineTest.php
+++ b/src/Symfony/Component/Yaml/Tests/InlineTest.php
@@ -837,6 +837,16 @@ class InlineTest extends TestCase
self::assertSame('-0123456789', Inline::parse('-0123456789'));
}
+ public function testParseCommentNotPrefixedBySpaces()
+ {
+ self::assertSame('foo', Inline::parse('"foo"#comment'));
+ }
+
+ public function testParseUnquotedStringContainingHashTagNotPrefixedBySpace()
+ {
+ self::assertSame('foo#nocomment', Inline::parse('foo#nocomment'));
+ }
+
/**
* @dataProvider unquotedExclamationMarkThrowsProvider
*/
diff --git a/src/Symfony/Component/Yaml/Tests/ParserTest.php b/src/Symfony/Component/Yaml/Tests/ParserTest.php
index 4dfc5b58ce..fcc56fb134 100644
--- a/src/Symfony/Component/Yaml/Tests/ParserTest.php
+++ b/src/Symfony/Component/Yaml/Tests/ParserTest.php
@@ -2437,6 +2437,39 @@ YAML;
$this->parser->parse($yaml)
);
}
+
+ /**
+ * This is a regression test for a bug where a YAML block with a nested multiline string using | was parsed without
+ * a trailing \n when a shorter YAML document was parsed before.
+ *
+ * When a shorter document was parsed before, the nested string did not have a \n at the end of the string, because
+ * the Parser thought it was the end of the file, even though it is not.
+ */
+ public function testParsingMultipleDocuments()
+ {
+ $shortDocument = 'foo: bar';
+ $longDocument = << ['b' => "row\nrow2\n"], 'c' => 'd'];
+
+ // The parser was not used before, so there is a new line after row2
+ $this->assertSame($expected, $this->parser->parse($longDocument));
+
+ $parser = new Parser();
+ // The first parsing set and fixed the totalNumberOfLines in the Parser before, so parsing the short document here
+ // to reproduce the issue. If the issue would not have been fixed, the next assertion will fail
+ $parser->parse($shortDocument);
+
+ // After the total number of lines has been rset the result will be the same as if a new parser was used
+ // (before, there was no \n after row2)
+ $this->assertSame($expected, $parser->parse($longDocument));
+ }
}
class B