diff --git a/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit.php b/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit.php index e59b6670f0..3cc7f5fa0e 100644 --- a/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit.php +++ b/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit.php @@ -148,8 +148,8 @@ if ('disabled' === $getEnvVar('SYMFONY_DEPRECATIONS_HELPER')) { } $COMPOSER = file_exists($COMPOSER = $oldPwd.'/composer.phar') - || ($COMPOSER = rtrim('\\' === \DIRECTORY_SEPARATOR ? preg_replace('/[\r\n].*/', '', `where.exe composer.phar`) : `which composer.phar 2> /dev/null`)) - || ($COMPOSER = rtrim('\\' === \DIRECTORY_SEPARATOR ? preg_replace('/[\r\n].*/', '', `where.exe composer`) : `which composer 2> /dev/null`)) + || ($COMPOSER = rtrim('\\' === \DIRECTORY_SEPARATOR ? preg_replace('/[\r\n].*/', '', `where.exe composer.phar 2> NUL`) : `which composer.phar 2> /dev/null`)) + || ($COMPOSER = rtrim('\\' === \DIRECTORY_SEPARATOR ? preg_replace('/[\r\n].*/', '', `where.exe composer 2> NUL`) : `which composer 2> /dev/null`)) || file_exists($COMPOSER = rtrim('\\' === \DIRECTORY_SEPARATOR ? `git rev-parse --show-toplevel 2> NUL` : `git rev-parse --show-toplevel 2> /dev/null`).\DIRECTORY_SEPARATOR.'composer.phar') ? ('#!/usr/bin/env php' === file_get_contents($COMPOSER, false, null, 0, 18) ? $PHP : '').' '.escapeshellarg($COMPOSER) // detect shell wrappers by looking at the shebang : 'composer'; @@ -176,9 +176,9 @@ if (!file_exists("$PHPUNIT_DIR/$PHPUNIT_VERSION_DIR/phpunit") || $configurationH @mkdir($PHPUNIT_DIR, 0777, true); chdir($PHPUNIT_DIR); if (file_exists("$PHPUNIT_VERSION_DIR")) { - passthru(sprintf('\\' === \DIRECTORY_SEPARATOR ? 'rmdir /S /Q %s > NUL' : 'rm -rf %s', "$PHPUNIT_VERSION_DIR.old")); + passthru(sprintf('\\' === \DIRECTORY_SEPARATOR ? 'rmdir /S /Q %s 2> NUL' : 'rm -rf %s', escapeshellarg("$PHPUNIT_VERSION_DIR.old"))); rename("$PHPUNIT_VERSION_DIR", "$PHPUNIT_VERSION_DIR.old"); - passthru(sprintf('\\' === \DIRECTORY_SEPARATOR ? 'rmdir /S /Q %s' : 'rm -rf %s', "$PHPUNIT_VERSION_DIR.old")); + passthru(sprintf('\\' === \DIRECTORY_SEPARATOR ? 'rmdir /S /Q %s' : 'rm -rf %s', escapeshellarg("$PHPUNIT_VERSION_DIR.old"))); } $info = []; @@ -307,10 +307,15 @@ EOPHP // This is useful for static analytics tools such as PHPStan having to load PHPUnit's classes // and for other testing libraries such as Behat using PHPUnit's assertions. chdir($PHPUNIT_DIR); -if (file_exists('phpunit')) { - @unlink('phpunit'); +if ('\\' === \DIRECTORY_SEPARATOR) { + passthru('rmdir /S /Q phpunit 2> NUL'); + passthru(sprintf('mklink /j phpunit %s > NUL 2>&1', escapeshellarg($PHPUNIT_VERSION_DIR))); +} else { + if (file_exists('phpunit')) { + @unlink('phpunit'); + } + @symlink($PHPUNIT_VERSION_DIR, 'phpunit'); } -@symlink($PHPUNIT_VERSION_DIR, 'phpunit'); chdir($oldPwd); if ($PHPUNIT_VERSION < 8.0) { diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index 32c7f7e5cc..37d8d64b90 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -2109,6 +2109,12 @@ class FrameworkExtension extends Extension ->setPublic($pool['public']) ; + if (method_exists(TagAwareAdapter::class, 'setLogger')) { + $container + ->getDefinition($name) + ->addMethodCall('setLogger', [new Reference('logger', ContainerInterface::IGNORE_ON_INVALID_REFERENCE)]); + } + $pool['name'] = $tagAwareId = $name; $pool['public'] = false; $name = '.'.$name.'.inner'; diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/cache.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/cache.php index 44e0c450f4..a060c13f93 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/cache.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/cache.php @@ -35,6 +35,11 @@ $container->loadFromExtension('framework', [ 'redis://foo' => 'cache.adapter.redis', ], ], + 'cache.ccc' => [ + 'adapter' => 'cache.adapter.array', + 'default_lifetime' => 410, + 'tags' => true, + ], 'cache.redis_tag_aware.foo' => [ 'adapter' => 'cache.adapter.redis_tag_aware', ], diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/cache.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/cache.xml index 7f04adc965..2750715f6b 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/cache.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/cache.xml @@ -18,6 +18,7 @@ + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/cache.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/cache.yml index 91f4d25fff..8c9e10b82e 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/cache.yml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/cache.yml @@ -25,6 +25,10 @@ framework: - cache.adapter.array - cache.adapter.filesystem - {name: cache.adapter.redis, provider: 'redis://foo'} + cache.ccc: + adapter: cache.adapter.array + default_lifetime: 410 + tags: true cache.redis_tag_aware.foo: adapter: cache.adapter.redis_tag_aware cache.redis_tag_aware.foo2: diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php index 18f56e394c..2c6713a38a 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php @@ -30,6 +30,7 @@ use Symfony\Component\Cache\Adapter\FilesystemAdapter; use Symfony\Component\Cache\Adapter\ProxyAdapter; use Symfony\Component\Cache\Adapter\RedisAdapter; use Symfony\Component\Cache\Adapter\RedisTagAwareAdapter; +use Symfony\Component\Cache\Adapter\TagAwareAdapter; use Symfony\Component\Cache\DependencyInjection\CachePoolPass; use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument; @@ -1537,6 +1538,17 @@ abstract class FrameworkExtensionTest extends TestCase 12, ]; $this->assertEquals($expected, $chain->getArguments()); + + // Test "tags: true" wrapping logic + $tagAwareDefinition = $container->getDefinition('cache.ccc'); + $this->assertSame(TagAwareAdapter::class, $tagAwareDefinition->getClass()); + $this->assertCachePoolServiceDefinitionIsCreated($container, (string) $tagAwareDefinition->getArgument(0), 'cache.adapter.array', 410); + + if (method_exists(TagAwareAdapter::class, 'setLogger')) { + $this->assertEquals([ + ['setLogger', [new Reference('logger', ContainerInterface::IGNORE_ON_INVALID_REFERENCE)]], + ], $tagAwareDefinition->getMethodCalls()); + } } public function testRedisTagAwareAdapter() @@ -1948,6 +1960,9 @@ abstract class FrameworkExtensionTest extends TestCase case 'cache.adapter.redis': $this->assertSame(RedisAdapter::class, $parentDefinition->getClass()); break; + case 'cache.adapter.array': + $this->assertSame(ArrayAdapter::class, $parentDefinition->getClass()); + break; default: $this->fail('Unresolved adapter: '.$adapter); } diff --git a/src/Symfony/Component/Cache/Adapter/TagAwareAdapter.php b/src/Symfony/Component/Cache/Adapter/TagAwareAdapter.php index 9e2dba2738..6f23a2a035 100644 --- a/src/Symfony/Component/Cache/Adapter/TagAwareAdapter.php +++ b/src/Symfony/Component/Cache/Adapter/TagAwareAdapter.php @@ -13,6 +13,8 @@ namespace Symfony\Component\Cache\Adapter; use Psr\Cache\CacheItemInterface; use Psr\Cache\InvalidArgumentException; +use Psr\Log\LoggerAwareInterface; +use Psr\Log\LoggerAwareTrait; use Symfony\Component\Cache\CacheItem; use Symfony\Component\Cache\PruneableInterface; use Symfony\Component\Cache\ResettableInterface; @@ -23,11 +25,12 @@ use Symfony\Contracts\Cache\TagAwareCacheInterface; /** * @author Nicolas Grekas */ -class TagAwareAdapter implements TagAwareAdapterInterface, TagAwareCacheInterface, PruneableInterface, ResettableInterface +class TagAwareAdapter implements TagAwareAdapterInterface, TagAwareCacheInterface, PruneableInterface, ResettableInterface, LoggerAwareInterface { public const TAGS_PREFIX = "\0tags\0"; use ContractsTrait; + use LoggerAwareTrait; use ProxyTrait; private $deferred = []; diff --git a/src/Symfony/Component/Cache/LockRegistry.php b/src/Symfony/Component/Cache/LockRegistry.php index d8929bebd3..38c0a6cea7 100644 --- a/src/Symfony/Component/Cache/LockRegistry.php +++ b/src/Symfony/Component/Cache/LockRegistry.php @@ -82,7 +82,7 @@ final class LockRegistry public static function compute(callable $callback, ItemInterface $item, bool &$save, CacheInterface $pool, \Closure $setMetadata = null, LoggerInterface $logger = null) { - $key = self::$files ? crc32($item->getKey()) % \count(self::$files) : -1; + $key = self::$files ? abs(crc32($item->getKey())) % \count(self::$files) : -1; if ($key < 0 || (self::$lockedFiles[$key] ?? false) || !$lock = self::open($key)) { return $callback($item, $save); diff --git a/src/Symfony/Component/Cache/Tests/Adapter/TagAwareAdapterTest.php b/src/Symfony/Component/Cache/Tests/Adapter/TagAwareAdapterTest.php index e96db881d0..9a45adaa36 100644 --- a/src/Symfony/Component/Cache/Tests/Adapter/TagAwareAdapterTest.php +++ b/src/Symfony/Component/Cache/Tests/Adapter/TagAwareAdapterTest.php @@ -14,6 +14,7 @@ namespace Symfony\Component\Cache\Tests\Adapter; use PHPUnit\Framework\MockObject\MockObject; use Psr\Cache\CacheItemInterface; use Psr\Cache\CacheItemPoolInterface; +use Psr\Log\LoggerInterface; use Symfony\Component\Cache\Adapter\AdapterInterface; use Symfony\Component\Cache\Adapter\ArrayAdapter; use Symfony\Component\Cache\Adapter\FilesystemAdapter; @@ -197,6 +198,20 @@ class TagAwareAdapterTest extends AdapterTestCase $this->assertFalse($item->isHit()); } + public function testLog() + { + $logger = $this->createMock(LoggerInterface::class); + $logger + ->expects($this->atLeastOnce()) + ->method($this->anything()); + + $cache = new TagAwareAdapter(new ArrayAdapter()); + $cache->setLogger($logger); + + // Computing will produce at least one log + $cache->get('foo', static function (): string { return 'ccc'; }); + } + /** * @return MockObject|PruneableCacheInterface */ diff --git a/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php b/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php index 6d73fa67f2..c2899bbd78 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php @@ -187,13 +187,14 @@ class ChoiceType extends AbstractType } if ($options['multiple']) { - $builder->addEventListener(FormEvents::POST_SUBMIT, function (FormEvent $event) use (&$unknownValues) { + $messageTemplate = $options['invalid_message'] ?? 'The value {{ value }} is not valid.'; + + $builder->addEventListener(FormEvents::POST_SUBMIT, function (FormEvent $event) use (&$unknownValues, $messageTemplate) { // Throw exception if unknown values were submitted if (\count($unknownValues) > 0) { $form = $event->getForm(); - $clientDataAsString = is_scalar($form->getViewData()) ? (string) $form->getViewData() : \gettype($form->getViewData()); - $messageTemplate = 'The value {{ value }} is not valid.'; + $clientDataAsString = is_scalar($form->getViewData()) ? (string) $form->getViewData() : (\is_array($form->getViewData()) ? implode('", "', array_keys($unknownValues)) : \gettype($form->getViewData())); if (null !== $this->translator) { $message = $this->translator->trans($messageTemplate, ['{{ value }}' => $clientDataAsString], 'validators'); @@ -201,7 +202,7 @@ class ChoiceType extends AbstractType $message = strtr($messageTemplate, ['{{ value }}' => $clientDataAsString]); } - $form->addError(new FormError($message, $messageTemplate, ['{{ value }}' => $clientDataAsString], null, new TransformationFailedException(sprintf('The choices "%s" do not exist in the choice list.', implode('", "', array_keys($unknownValues)))))); + $form->addError(new FormError($message, $messageTemplate, ['{{ value }}' => $clientDataAsString], null, new TransformationFailedException(sprintf('The choices "%s" do not exist in the choice list.', $clientDataAsString)))); } }); diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypeTest.php index 4cf9937f04..50b259f58e 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypeTest.php @@ -1861,6 +1861,32 @@ class ChoiceTypeTest extends BaseTypeTest $this->assertSame('name[]', $view->vars['full_name']); } + public function testInvalidMessageAwarenessForMultiple() + { + $form = $this->factory->create(static::TESTED_TYPE, null, [ + 'multiple' => true, + 'expanded' => false, + 'choices' => $this->choices, + 'invalid_message' => 'You are not able to use value "{{ value }}"', + ]); + + $form->submit(['My invalid choice']); + $this->assertEquals("ERROR: You are not able to use value \"My invalid choice\"\n", (string) $form->getErrors(true)); + } + + public function testInvalidMessageAwarenessForMultipleWithoutScalarOrArrayViewData() + { + $form = $this->factory->create(static::TESTED_TYPE, null, [ + 'multiple' => true, + 'expanded' => false, + 'choices' => $this->choices, + 'invalid_message' => 'You are not able to use value "{{ value }}"', + ]); + + $form->submit(new \stdClass()); + $this->assertEquals("ERROR: You are not able to use value \"stdClass\"\n", (string) $form->getErrors(true)); + } + // https://github.com/symfony/symfony/issues/3298 public function testInitializeWithEmptyChoices() { diff --git a/src/Symfony/Component/HttpClient/Response/HttplugPromise.php b/src/Symfony/Component/HttpClient/Response/HttplugPromise.php index 2231464a22..fbef8e29ca 100644 --- a/src/Symfony/Component/HttpClient/Response/HttplugPromise.php +++ b/src/Symfony/Component/HttpClient/Response/HttplugPromise.php @@ -53,10 +53,8 @@ final class HttplugPromise implements HttplugPromiseInterface /** * {@inheritdoc} - * - * @return Psr7ResponseInterface|mixed */ - public function wait($unwrap = true) + public function wait($unwrap = true): ?Psr7ResponseInterface { $result = $this->promise->wait($unwrap); diff --git a/src/Symfony/Component/HttpKernel/EventListener/AbstractSessionListener.php b/src/Symfony/Component/HttpKernel/EventListener/AbstractSessionListener.php index 9b8891f79e..eae5fb6bab 100644 --- a/src/Symfony/Component/HttpKernel/EventListener/AbstractSessionListener.php +++ b/src/Symfony/Component/HttpKernel/EventListener/AbstractSessionListener.php @@ -56,14 +56,13 @@ abstract class AbstractSessionListener implements EventSubscriberInterface return; } - $session = null; $request = $event->getRequest(); if (!$request->hasSession()) { $sess = null; $request->setSessionFactory(function () use (&$sess) { return $sess ?? $sess = $this->getSession(); }); } - $session = $session ?? ($this->container && $this->container->has('initialized_session') ? $this->container->get('initialized_session') : null); + $session = $this->container && $this->container->has('initialized_session') ? $this->container->get('initialized_session') : null; $this->sessionUsageStack[] = $session instanceof Session ? $session->getUsageIndex() : 0; } diff --git a/src/Symfony/Component/Routing/Route.php b/src/Symfony/Component/Routing/Route.php index ce52a5696f..d52ed4240a 100644 --- a/src/Symfony/Component/Routing/Route.php +++ b/src/Symfony/Component/Routing/Route.php @@ -539,15 +539,15 @@ class Route implements \Serializable return $pattern; } - return preg_replace_callback('#\{(!?\w++)(<.*?>)?(\?[^\}]*+)?\}#', function ($m) { - if (isset($m[3][0])) { - $this->setDefault($m[1], '?' !== $m[3] ? substr($m[3], 1) : null); + return preg_replace_callback('#\{(!?)(\w++)(<.*?>)?(\?[^\}]*+)?\}#', function ($m) { + if (isset($m[4][0])) { + $this->setDefault($m[2], '?' !== $m[4] ? substr($m[4], 1) : null); } - if (isset($m[2][0])) { - $this->setRequirement($m[1], substr($m[2], 1, -1)); + if (isset($m[3][0])) { + $this->setRequirement($m[2], substr($m[3], 1, -1)); } - return '{'.$m[1].'}'; + return '{'.$m[1].$m[2].'}'; }, $pattern); } diff --git a/src/Symfony/Component/Routing/Tests/RouteTest.php b/src/Symfony/Component/Routing/Tests/RouteTest.php index 5dc12c8759..63ae8d952a 100644 --- a/src/Symfony/Component/Routing/Tests/RouteTest.php +++ b/src/Symfony/Component/Routing/Tests/RouteTest.php @@ -50,6 +50,14 @@ class RouteTest extends TestCase $this->assertEquals($route, $route->setPath(''), '->setPath() implements a fluent interface'); $route->setPath('//path'); $this->assertEquals('/path', $route->getPath(), '->setPath() does not allow two slashes "//" at the beginning of the path as it would be confused with a network path when generating the path from the route'); + $route->setPath('/path/{!foo}'); + $this->assertEquals('/path/{!foo}', $route->getPath(), '->setPath() keeps ! to pass important params'); + $route->setPath('/path/{bar<\w++>}'); + $this->assertEquals('/path/{bar}', $route->getPath(), '->setPath() removes inline requirements'); + $route->setPath('/path/{foo?value}'); + $this->assertEquals('/path/{foo}', $route->getPath(), '->setPath() removes inline defaults'); + $route->setPath('/path/{!bar<\d+>?value}'); + $this->assertEquals('/path/{!bar}', $route->getPath(), '->setPath() removes all inline settings'); } public function testOptions() @@ -221,17 +229,20 @@ class RouteTest extends TestCase $this->assertEquals((new Route('/foo/{bar}'))->setDefault('bar', null), new Route('/foo/{bar?}')); $this->assertEquals((new Route('/foo/{bar}'))->setDefault('bar', 'baz'), new Route('/foo/{bar?baz}')); $this->assertEquals((new Route('/foo/{bar}'))->setDefault('bar', 'baz'), new Route('/foo/{bar?baz}')); - $this->assertEquals((new Route('/foo/{!bar}'))->setDefault('!bar', 'baz'), new Route('/foo/{!bar?baz}')); + $this->assertEquals((new Route('/foo/{!bar}'))->setDefault('bar', 'baz'), new Route('/foo/{!bar?baz}')); $this->assertEquals((new Route('/foo/{bar}'))->setDefault('bar', 'baz'), new Route('/foo/{bar?}', ['bar' => 'baz'])); $this->assertEquals((new Route('/foo/{bar}'))->setRequirement('bar', '.*'), new Route('/foo/{bar<.*>}')); $this->assertEquals((new Route('/foo/{bar}'))->setRequirement('bar', '>'), new Route('/foo/{bar<>>}')); $this->assertEquals((new Route('/foo/{bar}'))->setRequirement('bar', '\d+'), new Route('/foo/{bar<.*>}', [], ['bar' => '\d+'])); $this->assertEquals((new Route('/foo/{bar}'))->setRequirement('bar', '[a-z]{2}'), new Route('/foo/{bar<[a-z]{2}>}')); + $this->assertEquals((new Route('/foo/{!bar}'))->setRequirement('bar', '\d+'), new Route('/foo/{!bar<\d+>}')); $this->assertEquals((new Route('/foo/{bar}'))->setDefault('bar', null)->setRequirement('bar', '.*'), new Route('/foo/{bar<.*>?}')); $this->assertEquals((new Route('/foo/{bar}'))->setDefault('bar', '<>')->setRequirement('bar', '>'), new Route('/foo/{bar<>>?<>}')); + $this->assertEquals((new Route('/{foo}/{!bar}'))->setDefaults(['bar' => '<>', 'foo' => '\\'])->setRequirements(['bar' => '\\', 'foo' => '.']), new Route('/{foo<.>?\}/{!bar<\>?<>}')); + $this->assertEquals((new Route('/'))->setHost('{bar}')->setDefault('bar', null), (new Route('/'))->setHost('{bar?}')); $this->assertEquals((new Route('/'))->setHost('{bar}')->setDefault('bar', 'baz'), (new Route('/'))->setHost('{bar?baz}')); $this->assertEquals((new Route('/'))->setHost('{bar}')->setDefault('bar', 'baz'), (new Route('/'))->setHost('{bar?baz}')); diff --git a/src/Symfony/Component/Yaml/Parser.php b/src/Symfony/Component/Yaml/Parser.php index b394e82d13..797d626ac7 100644 --- a/src/Symfony/Component/Yaml/Parser.php +++ b/src/Symfony/Component/Yaml/Parser.php @@ -1233,6 +1233,10 @@ class Parser $offset = $cursor; $cursor += strcspn($this->currentLine, '[]{},: ', $cursor); + if ($cursor === $offset) { + throw new ParseException('Malformed unquoted YAML string.'); + } + return substr($this->currentLine, $offset, $cursor - $offset); } diff --git a/src/Symfony/Component/Yaml/Tests/ParserTest.php b/src/Symfony/Component/Yaml/Tests/ParserTest.php index 3822ce7d0c..b4e972bc59 100644 --- a/src/Symfony/Component/Yaml/Tests/ParserTest.php +++ b/src/Symfony/Component/Yaml/Tests/ParserTest.php @@ -2675,6 +2675,25 @@ YAML; ); } + public function testThrowExceptionIfInvalidAdditionalClosingTagOccurs() + { + $yaml = '{ + "object": { + "array": [ + "a", + "b", + "c" + ] + ], + } + }'; + + $this->expectException(ParseException::class); + $this->expectExceptionMessage('Malformed unquoted YAML string at line 8 (near " ],").'); + + $this->parser->parse($yaml); + } + public function testWhitespaceAtEndOfLine() { $yaml = "\nfoo:\n arguments: [ '@bar' ] \n"; diff --git a/src/Symfony/Contracts/HttpClient/ResponseInterface.php b/src/Symfony/Contracts/HttpClient/ResponseInterface.php index 85e7fddd6c..df7148816e 100644 --- a/src/Symfony/Contracts/HttpClient/ResponseInterface.php +++ b/src/Symfony/Contracts/HttpClient/ResponseInterface.php @@ -102,8 +102,8 @@ interface ResponseInterface * * Other info SHOULD be named after curl_getinfo()'s associative return value. * - * @return array|mixed An array of all available info, or one of them when $type is - * provided, or null when an unsupported type is requested + * @return mixed An array of all available info, or one of them when $type is + * provided, or null when an unsupported type is requested */ public function getInfo(string $type = null); }