Quellcode durchsuchen

[Validator] Each object is now only validated once for a given group

Bernhard Schussek vor 14 Jahren
Ursprung
Commit
2d7c47e488

+ 21 - 5
src/Symfony/Component/Validator/GraphWalker.php

@@ -32,6 +32,7 @@ class GraphWalker
     protected $context;
     protected $validatorFactory;
     protected $metadataFactory;
+    protected $validatedObjects = array();
 
     public function __construct($root, ClassMetadataFactoryInterface $metadataFactory, ConstraintValidatorFactoryInterface $factory)
     {
@@ -57,26 +58,41 @@ class GraphWalker
      * @param  string $group The validator group to use for validation
      * @param  string $propertyPath
      */
-    public function walkClass(ClassMetadata $metadata, $object, $group, $propertyPath)
+    public function walkObject(ClassMetadata $metadata, $object, $group, $propertyPath)
     {
         $this->context->setCurrentClass($metadata->getClassName());
 
         if ($group === Constraint::DEFAULT_GROUP && $metadata->hasGroupSequence()) {
             $groups = $metadata->getGroupSequence();
             foreach ($groups as $group) {
-                $this->walkClassForGroup($metadata, $object, $group, $propertyPath, Constraint::DEFAULT_GROUP);
+                $this->walkObjectForGroup($metadata, $object, $group, $propertyPath, Constraint::DEFAULT_GROUP);
 
                 if (count($this->getViolations()) > 0) {
                     break;
                 }
             }
         } else {
-            $this->walkClassForGroup($metadata, $object, $group, $propertyPath);
+            $this->walkObjectForGroup($metadata, $object, $group, $propertyPath);
         }
     }
 
-    protected function walkClassForGroup(ClassMetadata $metadata, $object, $group, $propertyPath, $propagatedGroup = null)
+    protected function walkObjectForGroup(ClassMetadata $metadata, $object, $group, $propertyPath, $propagatedGroup = null)
     {
+        $hash = spl_object_hash($object);
+
+        // Exit, if the object is already validated for the current group
+        if (isset($this->validatedObjects[$hash])) {
+            if (isset($this->validatedObjects[$hash][$group])) {
+                return;
+            }
+        } else {
+            $this->validatedObjects[$hash] = array();
+        }
+
+        // Remember validating this object before starting and possibly
+        // traversing the object graph
+        $this->validatedObjects[$hash][$group] = true;
+
         foreach ($metadata->findConstraints($group) as $constraint) {
             $this->walkConstraint($constraint, $object, $group, $propertyPath);
         }
@@ -128,7 +144,7 @@ class GraphWalker
                 throw new UnexpectedTypeException($value, 'object or array');
             } else {
                 $metadata = $this->metadataFactory->getClassMetadata(get_class($value));
-                $this->walkClass($metadata, $value, $group, $propertyPath);
+                $this->walkObject($metadata, $value, $group, $propertyPath);
             }
         }
     }

+ 1 - 1
src/Symfony/Component/Validator/Validator.php

@@ -46,7 +46,7 @@ class Validator implements ValidatorInterface
         $metadata = $this->metadataFactory->getClassMetadata(get_class($object));
 
         $walk = function(GraphWalker $walker, $group) use ($metadata, $object) {
-            return $walker->walkClass($metadata, $object, $group, '');
+            return $walker->walkObject($metadata, $object, $group, '');
         };
 
         return $this->validateGraph($object, $walk, $groups);

+ 47 - 12
tests/Symfony/Tests/Component/Validator/GraphWalkerTest.php

@@ -46,34 +46,69 @@ class GraphWalkerTest extends \PHPUnit_Framework_TestCase
         $this->metadata = new ClassMetadata(self::CLASSNAME);
     }
 
-    public function testWalkClassValidatesConstraints()
+    public function testWalkObjectValidatesConstraints()
     {
         $this->metadata->addConstraint(new ConstraintA());
 
-        $this->walker->walkClass($this->metadata, new Entity(), 'Default', '');
+        $this->walker->walkObject($this->metadata, new Entity(), 'Default', '');
 
         $this->assertEquals(1, count($this->walker->getViolations()));
     }
 
-    public function testWalkClassValidatesPropertyConstraints()
+    public function testWalkObjectTwiceValidatesConstraintsOnce()
+    {
+        $this->metadata->addConstraint(new ConstraintA());
+
+        $entity = new Entity();
+
+        $this->walker->walkObject($this->metadata, $entity, 'Default', '');
+        $this->walker->walkObject($this->metadata, $entity, 'Default', '');
+
+        $this->assertEquals(1, count($this->walker->getViolations()));
+    }
+
+    public function testWalkDifferentObjectsValidatesTwice()
+    {
+        $this->metadata->addConstraint(new ConstraintA());
+
+        $this->walker->walkObject($this->metadata, new Entity(), 'Default', '');
+        $this->walker->walkObject($this->metadata, new Entity(), 'Default', '');
+
+        $this->assertEquals(2, count($this->walker->getViolations()));
+    }
+
+    public function testWalkObjectTwiceInDifferentGroupsValidatesTwice()
+    {
+        $this->metadata->addConstraint(new ConstraintA());
+        $this->metadata->addConstraint(new ConstraintA(array('groups' => 'Custom')));
+
+        $entity = new Entity();
+
+        $this->walker->walkObject($this->metadata, $entity, 'Default', '');
+        $this->walker->walkObject($this->metadata, $entity, 'Custom', '');
+
+        $this->assertEquals(2, count($this->walker->getViolations()));
+    }
+
+    public function testWalkObjectValidatesPropertyConstraints()
     {
         $this->metadata->addPropertyConstraint('firstName', new ConstraintA());
 
-        $this->walker->walkClass($this->metadata, new Entity(), 'Default', '');
+        $this->walker->walkObject($this->metadata, new Entity(), 'Default', '');
 
         $this->assertEquals(1, count($this->walker->getViolations()));
     }
 
-    public function testWalkClassValidatesGetterConstraints()
+    public function testWalkObjectValidatesGetterConstraints()
     {
         $this->metadata->addGetterConstraint('lastName', new ConstraintA());
 
-        $this->walker->walkClass($this->metadata, new Entity(), 'Default', '');
+        $this->walker->walkObject($this->metadata, new Entity(), 'Default', '');
 
         $this->assertEquals(1, count($this->walker->getViolations()));
     }
 
-    public function testWalkClassInDefaultGroupTraversesGroupSequence()
+    public function testWalkObjectInDefaultGroupTraversesGroupSequence()
     {
         $entity = new Entity();
 
@@ -85,7 +120,7 @@ class GraphWalkerTest extends \PHPUnit_Framework_TestCase
         )));
         $this->metadata->setGroupSequence(array('First', $this->metadata->getDefaultGroup()));
 
-        $this->walker->walkClass($this->metadata, $entity, 'Default', '');
+        $this->walker->walkObject($this->metadata, $entity, 'Default', '');
 
         // After validation of group "First" failed, no more group was
         // validated
@@ -101,7 +136,7 @@ class GraphWalkerTest extends \PHPUnit_Framework_TestCase
         $this->assertEquals($violations, $this->walker->getViolations());
     }
 
-    public function testWalkClassInGroupSequencePropagatesDefaultGroup()
+    public function testWalkObjectInGroupSequencePropagatesDefaultGroup()
     {
         $entity = new Entity();
         $entity->reference = new Reference();
@@ -117,7 +152,7 @@ class GraphWalkerTest extends \PHPUnit_Framework_TestCase
         )));
         $this->factory->addClassMetadata($referenceMetadata);
 
-        $this->walker->walkClass($this->metadata, $entity, 'Default', '');
+        $this->walker->walkObject($this->metadata, $entity, 'Default', '');
 
         // The validation of the reference's FailingConstraint in group
         // "Default" was launched
@@ -133,7 +168,7 @@ class GraphWalkerTest extends \PHPUnit_Framework_TestCase
         $this->assertEquals($violations, $this->walker->getViolations());
     }
 
-    public function testWalkClassInOtherGroupTraversesNoGroupSequence()
+    public function testWalkObjectInOtherGroupTraversesNoGroupSequence()
     {
         $entity = new Entity();
 
@@ -145,7 +180,7 @@ class GraphWalkerTest extends \PHPUnit_Framework_TestCase
         )));
         $this->metadata->setGroupSequence(array('First', $this->metadata->getDefaultGroup()));
 
-        $this->walker->walkClass($this->metadata, $entity, $this->metadata->getDefaultGroup(), '');
+        $this->walker->walkObject($this->metadata, $entity, $this->metadata->getDefaultGroup(), '');
 
         // Only group "Second" was validated
         $violations = new ConstraintViolationList();