@@ -102,6 +102,8 @@ public function validate($value, $constraints = null, $groups = null)
102102
103103 $ this ->traverseGenericNode (
104104 $ value ,
105+ is_object ($ value ) ? spl_object_hash ($ value ) : null ,
106+ null ,
105107 null ,
106108 $ metadata ,
107109 $ this ->defaultPropertyPath ,
@@ -116,23 +118,24 @@ public function validate($value, $constraints = null, $groups = null)
116118
117119 if (is_object ($ value )) {
118120 $ this ->cascadeObject (
119- $ value ,
120- $ this ->defaultPropertyPath ,
121- $ groups ,
122- TraversalStrategy::IMPLICIT ,
123- $ this ->context
121+ $ value ,
122+ spl_object_hash ($ value ),
123+ $ this ->defaultPropertyPath ,
124+ $ groups ,
125+ TraversalStrategy::IMPLICIT ,
126+ $ this ->context
124127 );
125128
126129 return $ this ;
127130 }
128131
129132 if (is_array ($ value )) {
130133 $ this ->cascadeCollection (
131- $ value ,
132- $ this ->defaultPropertyPath ,
133- $ groups ,
134- TraversalStrategy::IMPLICIT ,
135- $ this ->context
134+ $ value ,
135+ $ this ->defaultPropertyPath ,
136+ $ groups ,
137+ TraversalStrategy::IMPLICIT ,
138+ $ this ->context
136139 );
137140
138141 return $ this ;
@@ -148,9 +151,9 @@ public function validate($value, $constraints = null, $groups = null)
148151 /**
149152 * {@inheritdoc}
150153 */
151- public function validateProperty ($ object , $ propertyName , $ groups = null )
154+ public function validateProperty ($ container , $ propertyName , $ groups = null )
152155 {
153- $ classMetadata = $ this ->metadataFactory ->getMetadataFor ($ object );
156+ $ classMetadata = $ this ->metadataFactory ->getMetadataFor ($ container );
154157
155158 if (!$ classMetadata instanceof ClassMetadataInterface) {
156159 // Cannot be UnsupportedMetadataException because of BC with
@@ -165,13 +168,16 @@ public function validateProperty($object, $propertyName, $groups = null)
165168
166169 $ propertyMetadatas = $ classMetadata ->getPropertyMetadata ($ propertyName );
167170 $ groups = $ groups ? $ this ->normalizeGroups ($ groups ) : $ this ->defaultGroups ;
171+ $ containerHash = spl_object_hash ($ container );
168172
169173 foreach ($ propertyMetadatas as $ propertyMetadata ) {
170- $ propertyValue = $ propertyMetadata ->getPropertyValue ($ object );
174+ $ propertyValue = $ propertyMetadata ->getPropertyValue ($ container );
171175
172176 $ this ->traverseGenericNode (
173177 $ propertyValue ,
174- $ object ,
178+ is_object ($ propertyValue ) ? spl_object_hash ($ propertyValue ) : null ,
179+ $ container ,
180+ $ containerHash ,
175181 $ propertyMetadata ,
176182 PropertyPath::append ($ this ->defaultPropertyPath , $ propertyName ),
177183 $ groups ,
@@ -187,9 +193,9 @@ public function validateProperty($object, $propertyName, $groups = null)
187193 /**
188194 * {@inheritdoc}
189195 */
190- public function validatePropertyValue ($ object , $ propertyName , $ value , $ groups = null )
196+ public function validatePropertyValue ($ container , $ propertyName , $ value , $ groups = null )
191197 {
192- $ classMetadata = $ this ->metadataFactory ->getMetadataFor ($ object );
198+ $ classMetadata = $ this ->metadataFactory ->getMetadataFor ($ container );
193199
194200 if (!$ classMetadata instanceof ClassMetadataInterface) {
195201 // Cannot be UnsupportedMetadataException because of BC with
@@ -204,11 +210,14 @@ public function validatePropertyValue($object, $propertyName, $value, $groups =
204210
205211 $ propertyMetadatas = $ classMetadata ->getPropertyMetadata ($ propertyName );
206212 $ groups = $ groups ? $ this ->normalizeGroups ($ groups ) : $ this ->defaultGroups ;
213+ $ containerHash = spl_object_hash ($ container );
207214
208215 foreach ($ propertyMetadatas as $ propertyMetadata ) {
209216 $ this ->traverseGenericNode (
210217 $ value ,
211- $ object ,
218+ is_object ($ value ) ? spl_object_hash ($ value ) : null ,
219+ $ container ,
220+ $ containerHash ,
212221 $ propertyMetadata ,
213222 PropertyPath::append ($ this ->defaultPropertyPath , $ propertyName ),
214223 $ groups ,
@@ -266,12 +275,12 @@ protected function normalizeGroups($groups)
266275 * @see CollectionNode
267276 * @see TraversalStrategy
268277 */
269- private function traverseClassNode ($ value , ClassMetadataInterface $ metadata = null , $ propertyPath , array $ groups , $ cascadedGroups , $ traversalStrategy , ExecutionContextInterface $ context )
278+ private function traverseClassNode ($ value , $ valueHash , ClassMetadataInterface $ metadata = null , $ propertyPath , array $ groups , $ cascadedGroups , $ traversalStrategy , ExecutionContextInterface $ context )
270279 {
271280 // Replace "Default" group by group sequence, if appropriate
272281 $ groups = $ this ->replaceDefaultGroup ($ value , $ metadata , $ groups );
273282
274- $ groups = $ this ->validateNode ($ value , $ value , $ metadata , $ propertyPath , $ groups , $ traversalStrategy , $ context );
283+ $ groups = $ this ->validateNode ($ value , $ valueHash , null , null , $ metadata , $ propertyPath , $ groups , $ traversalStrategy , $ context );
275284
276285 if (0 === count ($ groups )) {
277286 return ;
@@ -288,9 +297,13 @@ private function traverseClassNode($value, ClassMetadataInterface $metadata = nu
288297 ));
289298 }
290299
300+ $ propertyValue = $ propertyMetadata ->getPropertyValue ($ value );
301+
291302 $ this ->traverseGenericNode (
292- $ propertyMetadata ->getPropertyValue ($ value ),
303+ $ propertyValue ,
304+ is_object ($ propertyValue ) ? spl_object_hash ($ propertyValue ) : null ,
293305 $ value ,
306+ $ valueHash ,
294307 $ propertyMetadata ,
295308 $ propertyPath
296309 ? $ propertyPath .'. ' .$ propertyName
@@ -392,6 +405,7 @@ private function cascadeCollection($collection, $propertyPath, array $groups, $t
392405 if (is_object ($ value )) {
393406 $ this ->cascadeObject (
394407 $ value ,
408+ spl_object_hash ($ value ),
395409 $ propertyPath .'[ ' .$ key .'] ' ,
396410 $ groups ,
397411 $ traversalStrategy ,
@@ -418,9 +432,9 @@ private function cascadeCollection($collection, $propertyPath, array $groups, $t
418432 * @param Node $node The node
419433 * @param ExecutionContextInterface $context The current execution context
420434 */
421- private function traverseGenericNode ($ value , $ object , MetadataInterface $ metadata = null , $ propertyPath , array $ groups , $ cascadedGroups , $ traversalStrategy , ExecutionContextInterface $ context )
435+ private function traverseGenericNode ($ value , $ valueHash , $ container , $ containerHash , MetadataInterface $ metadata = null , $ propertyPath , array $ groups , $ cascadedGroups , $ traversalStrategy , ExecutionContextInterface $ context )
422436 {
423- $ groups = $ this ->validateNode ($ value , $ object , $ metadata , $ propertyPath , $ groups , $ traversalStrategy , $ context );
437+ $ groups = $ this ->validateNode ($ value , $ valueHash , $ container , $ containerHash , $ metadata , $ propertyPath , $ groups , $ traversalStrategy , $ context );
424438
425439 if (0 === count ($ groups )) {
426440 return ;
@@ -467,6 +481,7 @@ private function traverseGenericNode($value, $object, MetadataInterface $metadat
467481 // (BC with Symfony < 2.5)
468482 $ this ->cascadeObject (
469483 $ value ,
484+ $ valueHash ,
470485 $ propertyPath ,
471486 $ cascadedGroups ,
472487 $ traversalStrategy ,
@@ -492,7 +507,7 @@ private function traverseGenericNode($value, $object, MetadataInterface $metadat
492507 * traversal of the object, a new collection node is put on the stack.
493508 * Otherwise, an exception is thrown.
494509 *
495- * @param object $object The object to cascade
510+ * @param object $container The object to cascade
496511 * @param string $propertyPath The current property path
497512 * @param string[] $groups The validated groups
498513 * @param integer $traversalStrategy The strategy for traversing the
@@ -507,10 +522,10 @@ private function traverseGenericNode($value, $object, MetadataInterface $metadat
507522 * metadata factory does not implement
508523 * {@link ClassMetadataInterface}
509524 */
510- private function cascadeObject ($ object , $ propertyPath , array $ groups , $ traversalStrategy , ExecutionContextInterface $ context )
525+ private function cascadeObject ($ container , $ containerHash , $ propertyPath , array $ groups , $ traversalStrategy , ExecutionContextInterface $ context )
511526 {
512527 try {
513- $ classMetadata = $ this ->metadataFactory ->getMetadataFor ($ object );
528+ $ classMetadata = $ this ->metadataFactory ->getMetadataFor ($ container );
514529
515530 if (!$ classMetadata instanceof ClassMetadataInterface) {
516531 throw new UnsupportedMetadataException (sprintf (
@@ -522,7 +537,8 @@ private function cascadeObject($object, $propertyPath, array $groups, $traversal
522537 }
523538
524539 $ this ->traverseClassNode (
525- $ object ,
540+ $ container ,
541+ $ containerHash ,
526542 $ classMetadata ,
527543 $ propertyPath ,
528544 $ groups ,
@@ -532,7 +548,7 @@ private function cascadeObject($object, $propertyPath, array $groups, $traversal
532548 );
533549 } catch (NoSuchMetadataException $ e ) {
534550 // Rethrow if not Traversable
535- if (!$ object instanceof \Traversable) {
551+ if (!$ container instanceof \Traversable) {
536552 throw $ e ;
537553 }
538554
@@ -542,7 +558,7 @@ private function cascadeObject($object, $propertyPath, array $groups, $traversal
542558 }
543559
544560 $ this ->cascadeCollection (
545- $ object ,
561+ $ container ,
546562 $ propertyPath ,
547563 $ groups ,
548564 $ traversalStrategy ,
@@ -563,14 +579,12 @@ private function cascadeObject($object, $propertyPath, array $groups, $traversal
563579 *
564580 * @return array The groups in which the successor nodes should be validated
565581 */
566- public function validateNode ($ value , $ object , MetadataInterface $ metadata = null , $ propertyPath , array $ groups , $ traversalStrategy , ExecutionContextInterface $ context )
582+ public function validateNode ($ value , $ valueHash , $ container , $ containerHash , MetadataInterface $ metadata = null , $ propertyPath , array $ groups , $ traversalStrategy , ExecutionContextInterface $ context )
567583 {
568584 $ context ->setValue ($ value );
569585 $ context ->setMetadata ($ metadata );
570586 $ context ->setPropertyPath ($ propertyPath );
571587
572- $ objectHash = is_object ($ object ) ? spl_object_hash ($ object ) : null ;
573-
574588 // if group (=[<G1,G2>,G3,G4]) contains group sequence (=<G1,G2>)
575589 // then call traverse() with each entry of the group sequence and abort
576590 // if necessary (G1, G2)
@@ -587,20 +601,20 @@ public function validateNode($value, $object, MetadataInterface $metadata = null
587601 // Use the object hash for group sequences
588602 $ groupHash = is_object ($ group ) ? spl_object_hash ($ group ) : $ group ;
589603
590- if ($ context ->isObjectValidatedForGroup ($ objectHash , $ groupHash )) {
604+ if ($ context ->isObjectValidatedForGroup ($ valueHash , $ groupHash )) {
591605 // Skip this group when validating the successor nodes
592606 // (property and/or collection nodes)
593607 unset($ groups [$ key ]);
594608
595609 continue ;
596610 }
597611
598- $ context ->markObjectAsValidatedForGroup ($ objectHash , $ groupHash );
612+ $ context ->markObjectAsValidatedForGroup ($ valueHash , $ groupHash );
599613 }
600614
601615 if ($ group instanceof GroupSequence) {
602616 // Traverse group sequence until a violation is generated
603- $ this ->stepThroughGroupSequence ($ value , $ object , $ metadata , $ propertyPath , $ traversalStrategy , $ group , $ context );
617+ $ this ->stepThroughGroupSequence ($ value , $ valueHash , $ container , $ containerHash , $ metadata , $ propertyPath , $ traversalStrategy , $ group , $ context );
604618
605619 // Skip the group sequence when validating successor nodes
606620 unset($ groups [$ key ]);
@@ -609,7 +623,7 @@ public function validateNode($value, $object, MetadataInterface $metadata = null
609623 }
610624
611625 // Validate normal group
612- $ this ->validateNodeForGroup ($ value , $ objectHash , $ metadata , $ group , $ context );
626+ $ this ->validateNodeForGroup ($ value , $ valueHash , $ containerHash , $ metadata , $ group , $ context );
613627 }
614628
615629 return $ groups ;
@@ -625,7 +639,7 @@ public function validateNode($value, $object, MetadataInterface $metadata = null
625639 * @param GroupSequence $groupSequence The group sequence
626640 * @param ExecutionContextInterface $context The execution context
627641 */
628- private function stepThroughGroupSequence ($ value , $ object , MetadataInterface $ metadata = null , $ propertyPath , $ traversalStrategy , GroupSequence $ groupSequence , ExecutionContextInterface $ context )
642+ private function stepThroughGroupSequence ($ value , $ valueHash , $ container , $ containerHash , MetadataInterface $ metadata = null , $ propertyPath , $ traversalStrategy , GroupSequence $ groupSequence , ExecutionContextInterface $ context )
629643 {
630644 $ violationCount = count ($ context ->getViolations ());
631645
@@ -640,6 +654,7 @@ private function stepThroughGroupSequence($value, $object, MetadataInterface $me
640654 if ($ metadata instanceof ClassMetadataInterface) {
641655 $ this ->traverseClassNode (
642656 $ value ,
657+ $ valueHash ,
643658 $ metadata ,
644659 $ propertyPath ,
645660 $ groups ,
@@ -650,7 +665,9 @@ private function stepThroughGroupSequence($value, $object, MetadataInterface $me
650665 } else {
651666 $ this ->traverseGenericNode (
652667 $ value ,
653- $ object ,
668+ $ valueHash ,
669+ $ container ,
670+ $ containerHash ,
654671 $ metadata ,
655672 $ propertyPath ,
656673 $ groups ,
@@ -673,36 +690,38 @@ private function stepThroughGroupSequence($value, $object, MetadataInterface $me
673690 * @param Node $node The validated node
674691 * @param string $group The group to validate
675692 * @param ExecutionContextInterface $context The execution context
676- * @param string $objectHash The hash of the node's
693+ * @param string $containerHash The hash of the node's
677694 * object (if any)
678695 *
679696 * @throws \Exception
680697 */
681- private function validateNodeForGroup ($ value , $ objectHash , MetadataInterface $ metadata = null , $ group , ExecutionContextInterface $ context )
698+ private function validateNodeForGroup ($ value , $ valueHash , $ containerHash , MetadataInterface $ metadata = null , $ group , ExecutionContextInterface $ context )
682699 {
683700 $ context ->setGroup ($ group );
684701
702+ $ propertyName = $ metadata instanceof PropertyMetadataInterface
703+ ? $ metadata ->getPropertyName ()
704+ : null ;
705+
685706 foreach ($ metadata ->findConstraints ($ group ) as $ constraint ) {
686707 // Prevent duplicate validation of constraints, in the case
687708 // that constraints belong to multiple validated groups
688- if (null !== $ objectHash ) {
709+ if (null !== $ propertyName ) {
689710 $ constraintHash = spl_object_hash ($ constraint );
690711
691- if ($ metadata instanceof ClassMetadataInterface) {
692- if ($ context ->isClassConstraintValidated ($ objectHash , $ constraintHash )) {
693- continue ;
694- }
695-
696- $ context ->markClassConstraintAsValidated ($ objectHash , $ constraintHash );
697- } elseif ($ metadata instanceof PropertyMetadataInterface) {
698- $ propertyName = $ metadata ->getPropertyName ();
712+ if ($ context ->isPropertyConstraintValidated ($ containerHash , $ propertyName , $ constraintHash )) {
713+ continue ;
714+ }
699715
700- if ( $ context ->isPropertyConstraintValidated ( $ objectHash , $ propertyName , $ constraintHash )) {
701- continue ;
702- }
716+ $ context ->markPropertyConstraintAsValidated ( $ containerHash , $ propertyName , $ constraintHash );
717+ } elseif ( null !== $ valueHash ) {
718+ $ constraintHash = spl_object_hash ( $ constraint );
703719
704- $ context ->markPropertyConstraintAsValidated ($ objectHash , $ propertyName , $ constraintHash );
720+ if ($ context ->isClassConstraintValidated ($ valueHash , $ constraintHash )) {
721+ continue ;
705722 }
723+
724+ $ context ->markClassConstraintAsValidated ($ valueHash , $ constraintHash );
706725 }
707726
708727 $ validator = $ this ->validatorFactory ->getInstance ($ constraint );
0 commit comments