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 @@
- Ova vrednost bi trebala biti pozitivna ili nula.
+ Ova vrednost bi trebala biti negativna ili nula.
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']]