Skip to content

Commit

Permalink
Merge branch '5.1'
Browse files Browse the repository at this point in the history
* 5.1:
  [Validator] RangeTest: fix expected deprecation
  Fix bad merge
  [Yaml] Fix for #36624; Allow PHP constant as first key in block
  Use PHPUnit 9.3 on php 8.
  fix mapping errors from unmapped forms
  [Validator] Add target guards for Composite nested constraints
  • Loading branch information
fabpot committed Aug 13, 2020
2 parents a45428c + 2b0fdc9 commit da60fbe
Show file tree
Hide file tree
Showing 12 changed files with 215 additions and 26 deletions.
6 changes: 5 additions & 1 deletion phpunit
Expand Up @@ -12,7 +12,11 @@ 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');
}
putenv('SYMFONY_PHPUNIT_VERSION=8.3');
if (\PHP_VERSION_ID >= 80000) {
putenv('SYMFONY_PHPUNIT_VERSION=9.3');
} else {
putenv('SYMFONY_PHPUNIT_VERSION=8.3');
}
} elseif (\PHP_VERSION_ID >= 70000) {
putenv('SYMFONY_PHPUNIT_VERSION=6.5');
}
Expand Down
Expand Up @@ -231,13 +231,6 @@ private function reconstructPath(ViolationPath $violationPath, FormInterface $or
// Form inherits its parent data
// Cut the piece out of the property path and proceed
$propertyPathBuilder->remove($i);
} elseif (!$scope->getConfig()->getMapped()) {
// Form is not mapped
// Set the form as new origin and strip everything
// we have so far in the path
$origin = $scope;
$propertyPathBuilder->remove(0, $i + 1);
$i = 0;
} else {
/* @var \Symfony\Component\PropertyAccess\PropertyPathInterface $propertyPath */
$propertyPath = $scope->getPropertyPath();
Expand Down
@@ -0,0 +1,16 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\Form\Tests\Extension\Validator\ViolationMapper\Fixtures;

class Issue
{
}
Expand Up @@ -22,6 +22,7 @@
use Symfony\Component\Form\FormConfigBuilder;
use Symfony\Component\Form\FormError;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\Tests\Extension\Validator\ViolationMapper\Fixtures\Issue;
use Symfony\Component\PropertyAccess\PropertyPath;
use Symfony\Component\Validator\ConstraintViolation;
use Symfony\Component\Validator\ConstraintViolationInterface;
Expand Down Expand Up @@ -70,12 +71,12 @@ protected function setUp(): void
$this->params = ['foo' => 'bar'];
}

protected function getForm($name = 'name', $propertyPath = null, $dataClass = null, $errorMapping = [], $inheritData = false, $synchronized = true)
protected function getForm($name = 'name', $propertyPath = null, $dataClass = null, $errorMapping = [], $inheritData = false, $synchronized = true, array $options = [])
{
$config = new FormConfigBuilder($name, $dataClass, $this->dispatcher, [
'error_mapping' => $errorMapping,
]);
$config->setMapped(true);
] + $options);
$config->setMapped(isset($options['mapped']) ? $options['mapped'] : true);
$config->setInheritData($inheritData);
$config->setPropertyPath($propertyPath);
$config->setCompound(true);
Expand All @@ -91,12 +92,9 @@ function () { throw new TransformationFailedException(); }
return new Form($config);
}

/**
* @param $propertyPath
*/
protected function getConstraintViolation($propertyPath): ConstraintViolation
protected function getConstraintViolation($propertyPath, $root = null): ConstraintViolation
{
return new ConstraintViolation($this->message, $this->messageTemplate, $this->params, null, $propertyPath, null);
return new ConstraintViolation($this->message, $this->messageTemplate, $this->params, $root, $propertyPath, null);
}

protected function getFormError(ConstraintViolationInterface $violation, FormInterface $form): FormError
Expand All @@ -107,6 +105,33 @@ protected function getFormError(ConstraintViolationInterface $violation, FormInt
return $error;
}

public function testMappingErrorsWhenFormIsNotMapped()
{
$form = $this->getForm('name', null, Issue::class, [
'child1' => 'child2',
]);

$violation = $this->getConstraintViolation('children[child1].data', $form);

$child1Form = $this->getForm('child1', null, null, [], false, true, [
'mapped' => false,
]);
$child2Form = $this->getForm('child2', null, null, [], false, true, [
'mapped' => false,
]);

$form->add($child1Form);
$form->add($child2Form);

$form->submit([]);

$this->mapper->mapViolation($violation, $form);

$this->assertCount(0, $form->getErrors());
$this->assertCount(0, $form->get('child1')->getErrors());
$this->assertCount(1, $form->get('child2')->getErrors());
}

public function testMapToFormInheritingParentDataIfDataDoesNotMatch()
{
$violation = $this->getConstraintViolation('children[address].data.foo');
Expand Down
11 changes: 11 additions & 0 deletions src/Symfony/Component/Validator/Constraints/Composite.php
Expand Up @@ -136,6 +136,17 @@ public function addImplicitGroupName($group)
*/
abstract protected function getCompositeOption();

/**
* @internal Used by metadata
*
* @return Constraint[]
*/
public function getNestedContraints()
{
/* @var Constraint[] $nestedConstraints */
return $this->{$this->getCompositeOption()};
}

/**
* Initializes the nested constraints.
*
Expand Down
18 changes: 15 additions & 3 deletions src/Symfony/Component/Validator/Mapping/ClassMetadata.php
Expand Up @@ -13,6 +13,7 @@

use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\Constraints\Cascade;
use Symfony\Component\Validator\Constraints\Composite;
use Symfony\Component\Validator\Constraints\GroupSequence;
use Symfony\Component\Validator\Constraints\Traverse;
use Symfony\Component\Validator\Constraints\Valid;
Expand Down Expand Up @@ -186,9 +187,7 @@ public function getDefaultGroup()
*/
public function addConstraint(Constraint $constraint)
{
if (!\in_array(Constraint::CLASS_CONSTRAINT, (array) $constraint->getTargets())) {
throw new ConstraintDefinitionException(sprintf('The constraint "%s" cannot be put on classes.', get_debug_type($constraint)));
}
$this->checkConstraint($constraint);

if ($constraint instanceof Traverse) {
if ($constraint->traverse) {
Expand Down Expand Up @@ -502,4 +501,17 @@ private function addPropertyMetadata(PropertyMetadataInterface $metadata)

$this->members[$property][] = $metadata;
}

private function checkConstraint(Constraint $constraint)
{
if (!\in_array(Constraint::CLASS_CONSTRAINT, (array) $constraint->getTargets(), true)) {
throw new ConstraintDefinitionException(sprintf('The constraint "%s" cannot be put on classes.', get_debug_type($constraint)));
}

if ($constraint instanceof Composite) {
foreach ($constraint->getNestedContraints() as $nestedContraint) {
$this->checkConstraint($nestedContraint);
}
}
}
}
18 changes: 15 additions & 3 deletions src/Symfony/Component/Validator/Mapping/MemberMetadata.php
Expand Up @@ -12,6 +12,7 @@
namespace Symfony\Component\Validator\Mapping;

use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\Constraints\Composite;
use Symfony\Component\Validator\Exception\ConstraintDefinitionException;

/**
Expand Down Expand Up @@ -71,9 +72,7 @@ public function __construct(string $class, string $name, string $property)
*/
public function addConstraint(Constraint $constraint)
{
if (!\in_array(Constraint::PROPERTY_CONSTRAINT, (array) $constraint->getTargets())) {
throw new ConstraintDefinitionException(sprintf('The constraint "%s" cannot be put on properties or getters.', get_debug_type($constraint)));
}
$this->checkConstraint($constraint);

parent::addConstraint($constraint);

Expand Down Expand Up @@ -181,4 +180,17 @@ public function getReflectionMember($objectOrClassName)
* @return \ReflectionMethod|\ReflectionProperty The reflection instance
*/
abstract protected function newReflectionMember($objectOrClassName);

private function checkConstraint(Constraint $constraint)
{
if (!\in_array(Constraint::PROPERTY_CONSTRAINT, (array) $constraint->getTargets(), true)) {
throw new ConstraintDefinitionException(sprintf('The constraint "%s" cannot be put on properties or getters.', get_debug_type($constraint)));
}

if ($constraint instanceof Composite) {
foreach ($constraint->getNestedContraints() as $nestedContraint) {
$this->checkConstraint($nestedContraint);
}
}
}
}
Expand Up @@ -52,7 +52,7 @@ public function provideDeprecationTriggeredIfMinMaxAndMinMessageOrMaxMessageSet(

/**
* @group legacy
* @expectedDeprecation Since symfony/validator 4.4: minMessage and maxMessage are deprecated when min and max options are set together. Use notInRangeMessage instead.
* @expectedDeprecation Since symfony/validator 4.4: "minMessage" and "maxMessage" are deprecated when the "min" and "max" options are both set. Use "notInRangeMessage" instead.
* @dataProvider provideDeprecationTriggeredIfMinMaxAndMinMessageOrMaxMessageSet
*/
public function testDeprecationTriggeredIfMinMaxAndMinMessageOrMaxMessageSet(array $options, bool $expectedDeprecatedMinMessageSet, bool $expectedDeprecatedMaxMessageSet)
Expand Down
Expand Up @@ -14,11 +14,13 @@
use PHPUnit\Framework\TestCase;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\Constraints\Cascade;
use Symfony\Component\Validator\Constraints\Composite;
use Symfony\Component\Validator\Constraints\Valid;
use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
use Symfony\Component\Validator\Mapping\CascadingStrategy;
use Symfony\Component\Validator\Mapping\ClassMetadata;
use Symfony\Component\Validator\Tests\Fixtures\CascadingEntity;
use Symfony\Component\Validator\Tests\Fixtures\ClassConstraint;
use Symfony\Component\Validator\Tests\Fixtures\ConstraintA;
use Symfony\Component\Validator\Tests\Fixtures\ConstraintB;
use Symfony\Component\Validator\Tests\Fixtures\PropertyConstraint;
Expand Down Expand Up @@ -56,6 +58,20 @@ public function testAddConstraintRequiresClassConstraints()
$this->metadata->addConstraint(new PropertyConstraint());
}

public function testAddCompositeConstraintRejectsNestedPropertyConstraints()
{
$this->expectException(ConstraintDefinitionException::class);
$this->expectExceptionMessage('The constraint "Symfony\Component\Validator\Tests\Fixtures\PropertyConstraint" cannot be put on classes.');

$this->metadata->addConstraint(new ClassCompositeConstraint([new PropertyConstraint()]));
}

public function testAddCompositeConstraintAcceptsNestedClassConstraints()
{
$this->metadata->addConstraint($constraint = new ClassCompositeConstraint([new ClassConstraint()]));
$this->assertSame($this->metadata->getConstraints(), [$constraint]);
}

public function testAddPropertyConstraints()
{
$this->metadata->addPropertyConstraint('firstName', new ConstraintA());
Expand Down Expand Up @@ -347,3 +363,23 @@ public function testCascadeConstraint()
], $metadata->getConstrainedProperties());
}
}

class ClassCompositeConstraint extends Composite
{
public $nested;

public function getDefaultOption()
{
return $this->getCompositeOption();
}

protected function getCompositeOption()
{
return 'nested';
}

public function getTargets()
{
return [self::CLASS_CONSTRAINT];
}
}
Expand Up @@ -12,11 +12,16 @@
namespace Symfony\Component\Validator\Tests\Mapping;

use PHPUnit\Framework\TestCase;
use Symfony\Component\Validator\Constraints\Collection;
use Symfony\Component\Validator\Constraints\Composite;
use Symfony\Component\Validator\Constraints\Required;
use Symfony\Component\Validator\Constraints\Valid;
use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
use Symfony\Component\Validator\Mapping\MemberMetadata;
use Symfony\Component\Validator\Tests\Fixtures\ClassConstraint;
use Symfony\Component\Validator\Tests\Fixtures\ConstraintA;
use Symfony\Component\Validator\Tests\Fixtures\ConstraintB;
use Symfony\Component\Validator\Tests\Fixtures\PropertyConstraint;

class MemberMetadataTest extends TestCase
{
Expand All @@ -43,6 +48,34 @@ public function testAddConstraintRequiresClassConstraints()
$this->metadata->addConstraint(new ClassConstraint());
}

public function testAddCompositeConstraintRejectsNestedClassConstraints()
{
$this->expectException(ConstraintDefinitionException::class);
$this->expectExceptionMessage('The constraint "Symfony\Component\Validator\Tests\Fixtures\ClassConstraint" cannot be put on properties or getters.');

$this->metadata->addConstraint(new PropertyCompositeConstraint([new ClassConstraint()]));
}

public function testAddCompositeConstraintRejectsDeepNestedClassConstraints()
{
$this->expectException(ConstraintDefinitionException::class);
$this->expectExceptionMessage('The constraint "Symfony\Component\Validator\Tests\Fixtures\ClassConstraint" cannot be put on properties or getters.');

$this->metadata->addConstraint(new Collection(['field1' => new Required([new ClassConstraint()])]));
}

public function testAddCompositeConstraintAcceptsNestedPropertyConstraints()
{
$this->metadata->addConstraint($constraint = new PropertyCompositeConstraint([new PropertyConstraint()]));
$this->assertSame($this->metadata->getConstraints(), [$constraint]);
}

public function testAddCompositeConstraintAcceptsDeepNestedPropertyConstraints()
{
$this->metadata->addConstraint($constraint = new Collection(['field1' => new Required([new PropertyConstraint()])]));
$this->assertSame($this->metadata->getConstraints(), [$constraint]);
}

public function testSerialize()
{
$this->metadata->addConstraint(new ConstraintA(['property1' => 'A']));
Expand Down Expand Up @@ -82,3 +115,18 @@ protected function newReflectionMember($object): object
{
}
}

class PropertyCompositeConstraint extends Composite
{
public $nested;

public function getDefaultOption()
{
return $this->getCompositeOption();
}

protected function getCompositeOption()
{
return 'nested';
}
}
8 changes: 6 additions & 2 deletions src/Symfony/Component/Yaml/Parser.php
Expand Up @@ -179,8 +179,12 @@ private function doParse(string $value, int $flags)
$this->parseBlock($this->getRealCurrentLineNb() + 1, $this->getNextEmbedBlock(null, true), $flags)
);
} else {
if (isset($values['leadspaces'])
&& self::preg_match('#^(?P<key>'.Inline::REGEX_QUOTED_STRING.'|[^ \'"\{\[].*?) *\:(\s+(?P<value>.+?))?\s*$#u', $this->trimTag($values['value']), $matches)
if (
isset($values['leadspaces'])
&& (
'!' === $values['value'][0]
|| self::preg_match('#^(?P<key>'.Inline::REGEX_QUOTED_STRING.'|[^ \'"\{\[].*?) *\:(\s+(?P<value>.+?))?\s*$#u', $this->trimTag($values['value']), $matches)
)
) {
// this is a compact notation element, add to next block and parse
$block = $values['value'];
Expand Down

0 comments on commit da60fbe

Please sign in to comment.