vendor/symfony/src/Symfony/Component/Validator/GraphWalker.php
changeset 0 7f95f8617b0b
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/symfony/src/Symfony/Component/Validator/GraphWalker.php	Sat Sep 24 15:40:41 2011 +0200
@@ -0,0 +1,190 @@
+<?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\Validator;
+
+use Symfony\Component\Validator\ConstraintValidatorFactoryInterface;
+use Symfony\Component\Validator\Constraint;
+use Symfony\Component\Validator\Constraints\All;
+use Symfony\Component\Validator\Constraints\Valid;
+use Symfony\Component\Validator\Exception\UnexpectedTypeException;
+use Symfony\Component\Validator\Mapping\ClassMetadataFactoryInterface;
+use Symfony\Component\Validator\Mapping\ClassMetadata;
+use Symfony\Component\Validator\Mapping\MemberMetadata;
+
+/**
+ * Responsible for walking over and initializing validation on different
+ * types of items.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ * @author Bernhard Schussek <bernhard.schussek@symfony.com>
+ */
+class GraphWalker
+{
+    protected $context;
+    protected $validatorFactory;
+    protected $metadataFactory;
+    protected $validatorInitializers = array();
+    protected $validatedObjects = array();
+
+    public function __construct($root, ClassMetadataFactoryInterface $metadataFactory, ConstraintValidatorFactoryInterface $factory, array $validatorInitializers = array())
+    {
+        $this->context = new ExecutionContext($root, $this, $metadataFactory);
+        $this->validatorFactory = $factory;
+        $this->metadataFactory = $metadataFactory;
+        $this->validatorInitializers = $validatorInitializers;
+    }
+
+    /**
+     * @return ConstraintViolationList
+     */
+    public function getViolations()
+    {
+        return $this->context->getViolations();
+    }
+
+    /**
+     * Initialize validation on the given object using the given metadata
+     * instance and validation group.
+     *
+     * @param ClassMetadata $metadata
+     * @param object        $object       The object to validate
+     * @param string        $group        The validator group to use for validation
+     * @param string        $propertyPath
+     */
+    public function walkObject(ClassMetadata $metadata, $object, $group, $propertyPath)
+    {
+        foreach ($this->validatorInitializers as $initializer) {
+            if (!$initializer instanceof ObjectInitializerInterface) {
+                throw new \LogicException('Validator initializers must implement ObjectInitializerInterface.');
+            }
+            $initializer->initialize($object);
+        }
+
+        $this->context->setCurrentClass($metadata->getClassName());
+
+        if ($group === Constraint::DEFAULT_GROUP && $metadata->hasGroupSequence()) {
+            $groups = $metadata->getGroupSequence();
+            foreach ($groups as $group) {
+                $this->walkObjectForGroup($metadata, $object, $group, $propertyPath, Constraint::DEFAULT_GROUP);
+
+                if (count($this->getViolations()) > 0) {
+                    break;
+                }
+            }
+        } else {
+            $this->walkObjectForGroup($metadata, $object, $group, $propertyPath);
+        }
+    }
+
+    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);
+        }
+
+        if (null !== $object) {
+            foreach ($metadata->getConstrainedProperties() as $property) {
+                $localPropertyPath = empty($propertyPath) ? $property : $propertyPath.'.'.$property;
+
+                $this->walkProperty($metadata, $property, $object, $group, $localPropertyPath, $propagatedGroup);
+            }
+        }
+    }
+
+    public function walkProperty(ClassMetadata $metadata, $property, $object, $group, $propertyPath, $propagatedGroup = null)
+    {
+        foreach ($metadata->getMemberMetadatas($property) as $member) {
+            $this->walkMember($member, $member->getValue($object), $group, $propertyPath, $propagatedGroup);
+        }
+    }
+
+    public function walkPropertyValue(ClassMetadata $metadata, $property, $value, $group, $propertyPath)
+    {
+        foreach ($metadata->getMemberMetadatas($property) as $member) {
+            $this->walkMember($member, $value, $group, $propertyPath);
+        }
+    }
+
+    protected function walkMember(MemberMetadata $metadata, $value, $group, $propertyPath, $propagatedGroup = null)
+    {
+        $this->context->setCurrentProperty($metadata->getPropertyName());
+
+        foreach ($metadata->findConstraints($group) as $constraint) {
+            $this->walkConstraint($constraint, $value, $group, $propertyPath);
+        }
+
+        if ($metadata->isCascaded()) {
+            $this->walkReference($value, $propagatedGroup ?: $group, $propertyPath, $metadata->isCollectionCascaded());
+        }
+    }
+
+    public function walkReference($value, $group, $propertyPath, $traverse)
+    {
+        if (null !== $value) {
+            if (!is_object($value) && !is_array($value)) {
+                throw new UnexpectedTypeException($value, 'object or array');
+            }
+
+            if ($traverse && (is_array($value) || $value instanceof \Traversable)) {
+                foreach ($value as $key => $element) {
+                    // Ignore any scalar values in the collection
+                    if (is_object($element) || is_array($element)) {
+                        $this->walkReference($element, $group, $propertyPath.'['.$key.']', $traverse);
+                    }
+                }
+            }
+
+            if (is_object($value)) {
+                $metadata = $this->metadataFactory->getClassMetadata(get_class($value));
+                $this->walkObject($metadata, $value, $group, $propertyPath);
+            }
+        }
+    }
+
+    public function walkConstraint(Constraint $constraint, $value, $group, $propertyPath)
+    {
+        $validator = $this->validatorFactory->getInstance($constraint);
+
+        $this->context->setPropertyPath($propertyPath);
+        $this->context->setGroup($group);
+
+        $validator->initialize($this->context);
+
+        if (!$validator->isValid($value, $constraint)) {
+            // Resetting the property path. This is needed because some
+            // validators, like CollectionValidator, use the walker internally
+            // and so change the context.
+            $this->context->setPropertyPath($propertyPath);
+
+            $this->context->addViolation(
+                $validator->getMessageTemplate(),
+                $validator->getMessageParameters(),
+                $value
+            );
+        }
+    }
+}