diff --git a/src/Symfony/Bundle/WebProfilerBundle/Csp/ContentSecurityPolicyHandler.php b/src/Symfony/Bundle/WebProfilerBundle/Csp/ContentSecurityPolicyHandler.php index 7c111a8ba3..a409404f8c 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Csp/ContentSecurityPolicyHandler.php +++ b/src/Symfony/Bundle/WebProfilerBundle/Csp/ContentSecurityPolicyHandler.php @@ -129,12 +129,11 @@ class ContentSecurityPolicyHandler continue; } if (!isset($headers[$header][$type])) { - if (isset($headers[$header]['default-src'])) { - $headers[$header][$type] = $headers[$header]['default-src']; - } else { - // If there is no script-src/style-src and no default-src, no additional rules required. + if (null === $fallback = $this->getDirectiveFallback($directives, $type)) { continue; } + + $headers[$header][$type] = $fallback; } $ruleIsSet = true; if (!\in_array('\'unsafe-inline\'', $headers[$header][$type], true)) { @@ -199,9 +198,7 @@ class ContentSecurityPolicyHandler { if (isset($directivesSet[$type])) { $directives = $directivesSet[$type]; - } elseif (isset($directivesSet['default-src'])) { - $directives = $directivesSet['default-src']; - } else { + } elseif (null === $directives = $this->getDirectiveFallback($directivesSet, $type)) { return false; } @@ -225,6 +222,16 @@ class ContentSecurityPolicyHandler return false; } + private function getDirectiveFallback(array $directiveSet, $type) + { + if (\in_array($type, ['script-src-elem', 'style-src-elem'], true) || !isset($directiveSet['default-src'])) { + // Let the browser fallback on it's own + return null; + } + + return $directiveSet['default-src']; + } + /** * Retrieves the Content-Security-Policy headers (either X-Content-Security-Policy or Content-Security-Policy) from * a response. diff --git a/src/Symfony/Bundle/WebProfilerBundle/Tests/Csp/ContentSecurityPolicyHandlerTest.php b/src/Symfony/Bundle/WebProfilerBundle/Tests/Csp/ContentSecurityPolicyHandlerTest.php index 349db2aaf7..3afe8a95fc 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Tests/Csp/ContentSecurityPolicyHandlerTest.php +++ b/src/Symfony/Bundle/WebProfilerBundle/Tests/Csp/ContentSecurityPolicyHandlerTest.php @@ -131,7 +131,14 @@ class ContentSecurityPolicyHandlerTest extends TestCase ['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\'; 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], + ['Content-Security-Policy' => 'default-src \'self\' domain.com; script-src \'self\' \'unsafe-inline\'; style-src \'self\' domain.com \'unsafe-inline\' \'nonce-'.$nonce.'\'', 'Content-Security-Policy-Report-Only' => 'default-src \'self\' domain-report-only.com; script-src \'self\' \'unsafe-inline\'; style-src \'self\' domain-report-only.com \'unsafe-inline\' \'nonce-'.$nonce.'\'', 'X-Content-Security-Policy' => null], + ], + [ + $nonce, + ['csp_script_nonce' => $nonce, 'csp_style_nonce' => $nonce], + $this->createRequest(), + $this->createResponse(['Content-Security-Policy' => 'default-src \'self\' domain.com; script-src \'self\' \'unsafe-inline\'; script-src-elem \'self\'; style-src \'self\' \'unsafe-inline\'; style-src-elem \'self\'', 'Content-Security-Policy-Report-Only' => 'default-src \'self\' domain-report-only.com; script-src \'self\' \'unsafe-inline\'; script-src-elem \'self\'; style-src \'self\' \'unsafe-inline\'; style-src-elem \'self\'']), + ['Content-Security-Policy' => 'default-src \'self\' domain.com; script-src \'self\' \'unsafe-inline\'; script-src-elem \'self\' \'unsafe-inline\' \'nonce-'.$nonce.'\'; style-src \'self\' \'unsafe-inline\'; style-src-elem \'self\' \'unsafe-inline\' \'nonce-'.$nonce.'\'', 'Content-Security-Policy-Report-Only' => 'default-src \'self\' domain-report-only.com; script-src \'self\' \'unsafe-inline\'; script-src-elem \'self\' \'unsafe-inline\' \'nonce-'.$nonce.'\'; style-src \'self\' \'unsafe-inline\'; style-src-elem \'self\' \'unsafe-inline\' \'nonce-'.$nonce.'\'', 'X-Content-Security-Policy' => null], ], [ $nonce, diff --git a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php index b35952e452..de934edd2e 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php +++ b/src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php @@ -690,7 +690,7 @@ class YamlFileLoader extends FileLoader 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: '.$e->getMessage(), $file), 0, $e); + throw new InvalidArgumentException(sprintf('The file "%s" does not contain valid YAML', $file).': '.$e->getMessage(), 0, $e); } return $this->validate($configuration, $file); diff --git a/src/Symfony/Component/Filesystem/Filesystem.php b/src/Symfony/Component/Filesystem/Filesystem.php index 85c997109a..ae9fceedb6 100644 --- a/src/Symfony/Component/Filesystem/Filesystem.php +++ b/src/Symfony/Component/Filesystem/Filesystem.php @@ -454,28 +454,19 @@ class Filesystem $startPath = str_replace('\\', '/', $startPath); } - $stripDriveLetter = function ($path) { - if (\strlen($path) > 2 && ':' === $path[1] && '/' === $path[2] && ctype_alpha($path[0])) { - return substr($path, 2); - } - - return $path; + $splitDriveLetter = function ($path) { + return (\strlen($path) > 2 && ':' === $path[1] && '/' === $path[2] && ctype_alpha($path[0])) + ? [substr($path, 2), strtoupper($path[0])] + : [$path, null]; }; - $endPath = $stripDriveLetter($endPath); - $startPath = $stripDriveLetter($startPath); - - // Split the paths into arrays - $startPathArr = explode('/', trim($startPath, '/')); - $endPathArr = explode('/', trim($endPath, '/')); - - $normalizePathArray = function ($pathSegments) { + $splitPath = function ($path) { $result = []; - foreach ($pathSegments as $segment) { + foreach (explode('/', trim($path, '/')) as $segment) { if ('..' === $segment) { array_pop($result); - } elseif ('.' !== $segment) { + } elseif ('.' !== $segment && '' !== $segment) { $result[] = $segment; } } @@ -483,8 +474,16 @@ class Filesystem return $result; }; - $startPathArr = $normalizePathArray($startPathArr); - $endPathArr = $normalizePathArray($endPathArr); + list($endPath, $endDriveLetter) = $splitDriveLetter($endPath); + list($startPath, $startDriveLetter) = $splitDriveLetter($startPath); + + $startPathArr = $splitPath($startPath); + $endPathArr = $splitPath($endPath); + + if ($endDriveLetter && $startDriveLetter && $endDriveLetter != $startDriveLetter) { + // End path is on another drive, so no relative path exists + return $endDriveLetter.':/'.($endPathArr ? implode('/', $endPathArr).'/' : ''); + } // Find for which directory the common path stops $index = 0; diff --git a/src/Symfony/Component/Filesystem/Tests/FilesystemTest.php b/src/Symfony/Component/Filesystem/Tests/FilesystemTest.php index c85860be7f..d47f8b7b68 100644 --- a/src/Symfony/Component/Filesystem/Tests/FilesystemTest.php +++ b/src/Symfony/Component/Filesystem/Tests/FilesystemTest.php @@ -1107,10 +1107,14 @@ class FilesystemTest extends FilesystemTestCase ['/../aa/bb/cc', '/aa/dd/..', 'bb/cc/'], ['/../../aa/../bb/cc', '/aa/dd/..', '../bb/cc/'], ['C:/aa/bb/cc', 'C:/aa/dd/..', 'bb/cc/'], + ['C:/aa/bb/cc', 'c:/aa/dd/..', 'bb/cc/'], ['c:/aa/../bb/cc', 'c:/aa/dd/..', '../bb/cc/'], ['C:/aa/bb/../../cc', 'C:/aa/../dd/..', 'cc/'], ['C:/../aa/bb/cc', 'C:/aa/dd/..', 'bb/cc/'], ['C:/../../aa/../bb/cc', 'C:/aa/dd/..', '../bb/cc/'], + ['D:/', 'C:/aa/../bb/cc', 'D:/'], + ['D:/aa/bb', 'C:/aa', 'D:/aa/bb/'], + ['D:/../../aa/../bb/cc', 'C:/aa/dd/..', 'D:/bb/cc/'], ]; if ('\\' === \DIRECTORY_SEPARATOR) { diff --git a/src/Symfony/Component/Form/Form.php b/src/Symfony/Component/Form/Form.php index 3981627e6b..79db067223 100644 --- a/src/Symfony/Component/Form/Form.php +++ b/src/Symfony/Component/Form/Form.php @@ -959,7 +959,7 @@ class Form implements \IteratorAggregate, FormInterface, ClearableErrorsInterfac * * @return FormInterface The child form * - * @throws \OutOfBoundsException if the named child does not exist + * @throws OutOfBoundsException if the named child does not exist */ public function offsetGet($name) { diff --git a/src/Symfony/Component/Form/FormInterface.php b/src/Symfony/Component/Form/FormInterface.php index 9be5008182..6c3719f6da 100644 --- a/src/Symfony/Component/Form/FormInterface.php +++ b/src/Symfony/Component/Form/FormInterface.php @@ -62,7 +62,7 @@ interface FormInterface extends \ArrayAccess, \Traversable, \Countable * * @return self * - * @throws \OutOfBoundsException if the named child does not exist + * @throws Exception\OutOfBoundsException if the named child does not exist */ public function get($name); diff --git a/src/Symfony/Component/Inflector/Tests/InflectorTest.php b/src/Symfony/Component/Inflector/Tests/InflectorTest.php index 4f4cd45c94..9a93125dd4 100644 --- a/src/Symfony/Component/Inflector/Tests/InflectorTest.php +++ b/src/Symfony/Component/Inflector/Tests/InflectorTest.php @@ -294,16 +294,16 @@ class InflectorTest extends TestCase /** * @dataProvider singularizeProvider */ - public function testSingularize($plural, $singular) + public function testSingularize($plural, $expectedSingular) { - $single = Inflector::singularize($plural); - if (\is_string($singular) && \is_array($single)) { - $this->fail("--- Expected\n`string`: ".$singular."\n+++ Actual\n`array`: ".implode(', ', $single)); - } elseif (\is_array($singular) && \is_string($single)) { - $this->fail("--- Expected\n`array`: ".implode(', ', $singular)."\n+++ Actual\n`string`: ".$single); + $singular = Inflector::singularize($plural); + if (\is_string($expectedSingular) && \is_array($singular)) { + $this->fail("--- Expected\n`string`: ".$expectedSingular."\n+++ Actual\n`array`: ".implode(', ', $singular)); + } elseif (\is_array($expectedSingular) && \is_string($singular)) { + $this->fail("--- Expected\n`array`: ".implode(', ', $expectedSingular)."\n+++ Actual\n`string`: ".$singular); } - $this->assertEquals($singular, $single); + $this->assertEquals($expectedSingular, $singular); } /** diff --git a/src/Symfony/Component/Routing/Loader/YamlFileLoader.php b/src/Symfony/Component/Routing/Loader/YamlFileLoader.php index 8d4b9abdb1..44cee21419 100644 --- a/src/Symfony/Component/Routing/Loader/YamlFileLoader.php +++ b/src/Symfony/Component/Routing/Loader/YamlFileLoader.php @@ -62,7 +62,7 @@ class YamlFileLoader extends FileLoader try { $parsedConfig = $this->yamlParser->parseFile($path, Yaml::PARSE_CONSTANT); } catch (ParseException $e) { - throw new \InvalidArgumentException(sprintf('The file "%s" does not contain valid YAML.', $path), 0, $e); + throw new \InvalidArgumentException(sprintf('The file "%s" does not contain valid YAML', $path).': '.$e->getMessage(), 0, $e); } $collection = new RouteCollection(); diff --git a/src/Symfony/Component/Translation/Loader/YamlFileLoader.php b/src/Symfony/Component/Translation/Loader/YamlFileLoader.php index 9502e167cd..6325a1f225 100644 --- a/src/Symfony/Component/Translation/Loader/YamlFileLoader.php +++ b/src/Symfony/Component/Translation/Loader/YamlFileLoader.php @@ -42,7 +42,7 @@ class YamlFileLoader extends FileLoader try { $messages = $this->yamlParser->parseFile($resource, Yaml::PARSE_CONSTANT); } catch (ParseException $e) { - throw new InvalidResourceException(sprintf('Error parsing YAML, invalid file "%s".', $resource), 0, $e); + throw new InvalidResourceException(sprintf('The file "%s" does not contain valid YAML', $resource).': '.$e->getMessage(), 0, $e); } if (null !== $messages && !\is_array($messages)) { diff --git a/src/Symfony/Component/Validator/Constraints/EmailValidator.php b/src/Symfony/Component/Validator/Constraints/EmailValidator.php index ef4760ffc9..cb16d08f37 100644 --- a/src/Symfony/Component/Validator/Constraints/EmailValidator.php +++ b/src/Symfony/Component/Validator/Constraints/EmailValidator.php @@ -80,6 +80,9 @@ class EmailValidator extends ConstraintValidator } $value = (string) $value; + if ('' === $value) { + return; + } if (null !== $constraint->normalizer) { $value = ($constraint->normalizer)($value); diff --git a/src/Symfony/Component/Validator/Mapping/Loader/YamlFileLoader.php b/src/Symfony/Component/Validator/Mapping/Loader/YamlFileLoader.php index 3170a2b977..f2f2fa55b2 100644 --- a/src/Symfony/Component/Validator/Mapping/Loader/YamlFileLoader.php +++ b/src/Symfony/Component/Validator/Mapping/Loader/YamlFileLoader.php @@ -114,7 +114,7 @@ class YamlFileLoader extends FileLoader try { $classes = $this->yamlParser->parseFile($path, Yaml::PARSE_CONSTANT); } catch (ParseException $e) { - throw new \InvalidArgumentException(sprintf('The file "%s" does not contain valid YAML.', $path), 0, $e); + throw new \InvalidArgumentException(sprintf('The file "%s" does not contain valid YAML', $path).': '.$e->getMessage(), 0, $e); } // empty file diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.sr_Latn.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.sr_Latn.xlf index 20dff43c6d..43d2070ab7 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.sr_Latn.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.sr_Latn.xlf @@ -352,7 +352,7 @@ This value should be either negative or zero. - Ova vrednost bi trebala biti pozitivna ili nula. + Ova vrednost bi trebala biti negativna ili nula. This value is not a valid timezone. diff --git a/src/Symfony/Component/Validator/Tests/Constraints/EmailValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/EmailValidatorTest.php index f201f38a84..08a3c4ee76 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/EmailValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/EmailValidatorTest.php @@ -60,6 +60,13 @@ class EmailValidatorTest extends ConstraintValidatorTestCase $this->assertNoViolation(); } + public function testObjectEmptyStringIsValid() + { + $this->validator->validate(new EmptyEmailObject(), new Email()); + + $this->assertNoViolation(); + } + public function testExpectsStringCompatibleType() { $this->expectException('Symfony\Component\Validator\Exception\UnexpectedValueException'); @@ -411,3 +418,11 @@ class EmailValidatorTest extends ConstraintValidatorTestCase ]; } } + +class EmptyEmailObject +{ + public function __toString() + { + return ''; + } +} diff --git a/src/Symfony/Component/Validator/Tests/Fixtures/Entity.php b/src/Symfony/Component/Validator/Tests/Fixtures/Entity.php index 16ba8a718e..673e62bae7 100644 --- a/src/Symfony/Component/Validator/Tests/Fixtures/Entity.php +++ b/src/Symfony/Component/Validator/Tests/Fixtures/Entity.php @@ -53,6 +53,11 @@ class Entity extends EntityParent implements EntityInterfaceB $this->internal = $internal; } + public function getFirstName() + { + return $this->firstName; + } + public function getInternal() { return $this->internal.' from getter'; @@ -141,4 +146,9 @@ class Entity extends EntityParent implements EntityInterfaceB { $this->childB = $childB; } + + public function getReference() + { + return $this->reference; + } } diff --git a/src/Symfony/Component/Validator/Tests/Validator/AbstractValidatorTest.php b/src/Symfony/Component/Validator/Tests/Validator/AbstractValidatorTest.php index c4c86e9eb6..e9bad07096 100644 --- a/src/Symfony/Component/Validator/Tests/Validator/AbstractValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Validator/AbstractValidatorTest.php @@ -32,6 +32,8 @@ abstract class AbstractValidatorTest extends TestCase const REFERENCE_CLASS = 'Symfony\Component\Validator\Tests\Fixtures\Reference'; + const LAZY_PROPERTY = 'Symfony\Component\Validator\Validator\LazyProperty'; + /** * @var FakeMetadataFactory */ @@ -54,6 +56,7 @@ abstract class AbstractValidatorTest extends TestCase $this->referenceMetadata = new ClassMetadata(self::REFERENCE_CLASS); $this->metadataFactory->addMetadata($this->metadata); $this->metadataFactory->addMetadata($this->referenceMetadata); + $this->metadataFactory->addMetadata(new ClassMetadata(self::LAZY_PROPERTY)); } protected function tearDown(): void @@ -510,7 +513,10 @@ abstract class AbstractValidatorTest extends TestCase $this->validate($entity); } - public function testArrayReference() + /** + * @dataProvider getConstraintMethods + */ + public function testArrayReference($constraintMethod) { $entity = new Entity(); $entity->reference = ['key' => new Reference()]; @@ -528,7 +534,7 @@ abstract class AbstractValidatorTest extends TestCase $context->addViolation('Message %param%', ['%param%' => 'value']); }; - $this->metadata->addPropertyConstraint('reference', new Valid()); + $this->metadata->$constraintMethod('reference', new Valid()); $this->referenceMetadata->addConstraint(new Callback([ 'callback' => $callback, 'groups' => 'Group', @@ -548,8 +554,10 @@ abstract class AbstractValidatorTest extends TestCase $this->assertNull($violations[0]->getCode()); } - // https://github.com/symfony/symfony/issues/6246 - public function testRecursiveArrayReference() + /** + * @dataProvider getConstraintMethods + */ + public function testRecursiveArrayReference($constraintMethod) { $entity = new Entity(); $entity->reference = [2 => ['key' => new Reference()]]; @@ -567,7 +575,7 @@ abstract class AbstractValidatorTest extends TestCase $context->addViolation('Message %param%', ['%param%' => 'value']); }; - $this->metadata->addPropertyConstraint('reference', new Valid()); + $this->metadata->$constraintMethod('reference', new Valid()); $this->referenceMetadata->addConstraint(new Callback([ 'callback' => $callback, 'groups' => 'Group', @@ -611,7 +619,10 @@ abstract class AbstractValidatorTest extends TestCase $this->assertCount(0, $violations); } - public function testArrayTraversalCannotBeDisabled() + /** + * @dataProvider getConstraintMethods + */ + public function testArrayTraversalCannotBeDisabled($constraintMethod) { $entity = new Entity(); $entity->reference = ['key' => new Reference()]; @@ -620,7 +631,7 @@ abstract class AbstractValidatorTest extends TestCase $context->addViolation('Message %param%', ['%param%' => 'value']); }; - $this->metadata->addPropertyConstraint('reference', new Valid([ + $this->metadata->$constraintMethod('reference', new Valid([ 'traverse' => false, ])); $this->referenceMetadata->addConstraint(new Callback($callback)); @@ -631,7 +642,10 @@ abstract class AbstractValidatorTest extends TestCase $this->assertCount(1, $violations); } - public function testRecursiveArrayTraversalCannotBeDisabled() + /** + * @dataProvider getConstraintMethods + */ + public function testRecursiveArrayTraversalCannotBeDisabled($constraintMethod) { $entity = new Entity(); $entity->reference = [2 => ['key' => new Reference()]]; @@ -640,9 +654,10 @@ abstract class AbstractValidatorTest extends TestCase $context->addViolation('Message %param%', ['%param%' => 'value']); }; - $this->metadata->addPropertyConstraint('reference', new Valid([ + $this->metadata->$constraintMethod('reference', new Valid([ 'traverse' => false, ])); + $this->referenceMetadata->addConstraint(new Callback($callback)); $violations = $this->validate($entity); @@ -651,12 +666,15 @@ abstract class AbstractValidatorTest extends TestCase $this->assertCount(1, $violations); } - public function testIgnoreScalarsDuringArrayTraversal() + /** + * @dataProvider getConstraintMethods + */ + public function testIgnoreScalarsDuringArrayTraversal($constraintMethod) { $entity = new Entity(); $entity->reference = ['string', 1234]; - $this->metadata->addPropertyConstraint('reference', new Valid()); + $this->metadata->$constraintMethod('reference', new Valid()); $violations = $this->validate($entity); @@ -664,12 +682,15 @@ abstract class AbstractValidatorTest extends TestCase $this->assertCount(0, $violations); } - public function testIgnoreNullDuringArrayTraversal() + /** + * @dataProvider getConstraintMethods + */ + public function testIgnoreNullDuringArrayTraversal($constraintMethod) { $entity = new Entity(); $entity->reference = [null]; - $this->metadata->addPropertyConstraint('reference', new Valid()); + $this->metadata->$constraintMethod('reference', new Valid()); $violations = $this->validate($entity); @@ -1218,6 +1239,14 @@ abstract class AbstractValidatorTest extends TestCase } } + public function getConstraintMethods() + { + return [ + ['addPropertyConstraint'], + ['addGetterConstraint'], + ]; + } + public function getTestReplaceDefaultGroup() { return [ diff --git a/src/Symfony/Component/Validator/Validator/RecursiveContextualValidator.php b/src/Symfony/Component/Validator/Validator/RecursiveContextualValidator.php index 4497ea2989..6a92ddbacf 100644 --- a/src/Symfony/Component/Validator/Validator/RecursiveContextualValidator.php +++ b/src/Symfony/Component/Validator/Validator/RecursiveContextualValidator.php @@ -634,6 +634,10 @@ class RecursiveContextualValidator implements ContextualValidatorInterface // See validateClassNode() $cascadedGroups = null !== $cascadedGroups && \count($cascadedGroups) > 0 ? $cascadedGroups : $groups; + if ($value instanceof LazyProperty) { + $value = $value->getPropertyValue(); + } + if (\is_array($value)) { // Arrays are always traversed, independent of the specified // traversal strategy diff --git a/src/Symfony/Component/Yaml/Parser.php b/src/Symfony/Component/Yaml/Parser.php index 7894e53b44..3a68b07f61 100644 --- a/src/Symfony/Component/Yaml/Parser.php +++ b/src/Symfony/Component/Yaml/Parser.php @@ -618,8 +618,14 @@ class Parser } $isItUnindentedCollection = $this->isStringUnIndentedCollectionItem(); + $isItComment = $this->isCurrentLineComment(); while ($this->moveToNextLine()) { + if ($isItComment && !$isItUnindentedCollection) { + $isItUnindentedCollection = $this->isStringUnIndentedCollectionItem(); + $isItComment = $this->isCurrentLineComment(); + } + $indent = $this->getCurrentLineIndentation(); if ($isItUnindentedCollection && !$this->isCurrentLineEmpty() && !$this->isStringUnIndentedCollectionItem() && $newIndent === $indent) { diff --git a/src/Symfony/Component/Yaml/Tests/Fixtures/sfComments.yml b/src/Symfony/Component/Yaml/Tests/Fixtures/sfComments.yml index af3ab38597..4b0c91c9b1 100644 --- a/src/Symfony/Component/Yaml/Tests/Fixtures/sfComments.yml +++ b/src/Symfony/Component/Yaml/Tests/Fixtures/sfComments.yml @@ -74,3 +74,17 @@ yaml: | 'foo #': baz php: | ['foo #' => 'baz'] +--- +test: Comment before first item in unindented collection +brief: > + Comment directly before unindented collection is allowed +yaml: | + collection1: + # comment + - a + - b + collection2: + - a + - b +php: | + ['collection1' => ['a', 'b'], 'collection2' => ['a', 'b']]