vendor/symfony/src/Symfony/Component/Validator/GraphWalker.php
changeset 0 7f95f8617b0b
equal deleted inserted replaced
-1:000000000000 0:7f95f8617b0b
       
     1 <?php
       
     2 
       
     3 /*
       
     4  * This file is part of the Symfony package.
       
     5  *
       
     6  * (c) Fabien Potencier <fabien@symfony.com>
       
     7  *
       
     8  * For the full copyright and license information, please view the LICENSE
       
     9  * file that was distributed with this source code.
       
    10  */
       
    11 
       
    12 namespace Symfony\Component\Validator;
       
    13 
       
    14 use Symfony\Component\Validator\ConstraintValidatorFactoryInterface;
       
    15 use Symfony\Component\Validator\Constraint;
       
    16 use Symfony\Component\Validator\Constraints\All;
       
    17 use Symfony\Component\Validator\Constraints\Valid;
       
    18 use Symfony\Component\Validator\Exception\UnexpectedTypeException;
       
    19 use Symfony\Component\Validator\Mapping\ClassMetadataFactoryInterface;
       
    20 use Symfony\Component\Validator\Mapping\ClassMetadata;
       
    21 use Symfony\Component\Validator\Mapping\MemberMetadata;
       
    22 
       
    23 /**
       
    24  * Responsible for walking over and initializing validation on different
       
    25  * types of items.
       
    26  *
       
    27  * @author Fabien Potencier <fabien@symfony.com>
       
    28  * @author Bernhard Schussek <bernhard.schussek@symfony.com>
       
    29  */
       
    30 class GraphWalker
       
    31 {
       
    32     protected $context;
       
    33     protected $validatorFactory;
       
    34     protected $metadataFactory;
       
    35     protected $validatorInitializers = array();
       
    36     protected $validatedObjects = array();
       
    37 
       
    38     public function __construct($root, ClassMetadataFactoryInterface $metadataFactory, ConstraintValidatorFactoryInterface $factory, array $validatorInitializers = array())
       
    39     {
       
    40         $this->context = new ExecutionContext($root, $this, $metadataFactory);
       
    41         $this->validatorFactory = $factory;
       
    42         $this->metadataFactory = $metadataFactory;
       
    43         $this->validatorInitializers = $validatorInitializers;
       
    44     }
       
    45 
       
    46     /**
       
    47      * @return ConstraintViolationList
       
    48      */
       
    49     public function getViolations()
       
    50     {
       
    51         return $this->context->getViolations();
       
    52     }
       
    53 
       
    54     /**
       
    55      * Initialize validation on the given object using the given metadata
       
    56      * instance and validation group.
       
    57      *
       
    58      * @param ClassMetadata $metadata
       
    59      * @param object        $object       The object to validate
       
    60      * @param string        $group        The validator group to use for validation
       
    61      * @param string        $propertyPath
       
    62      */
       
    63     public function walkObject(ClassMetadata $metadata, $object, $group, $propertyPath)
       
    64     {
       
    65         foreach ($this->validatorInitializers as $initializer) {
       
    66             if (!$initializer instanceof ObjectInitializerInterface) {
       
    67                 throw new \LogicException('Validator initializers must implement ObjectInitializerInterface.');
       
    68             }
       
    69             $initializer->initialize($object);
       
    70         }
       
    71 
       
    72         $this->context->setCurrentClass($metadata->getClassName());
       
    73 
       
    74         if ($group === Constraint::DEFAULT_GROUP && $metadata->hasGroupSequence()) {
       
    75             $groups = $metadata->getGroupSequence();
       
    76             foreach ($groups as $group) {
       
    77                 $this->walkObjectForGroup($metadata, $object, $group, $propertyPath, Constraint::DEFAULT_GROUP);
       
    78 
       
    79                 if (count($this->getViolations()) > 0) {
       
    80                     break;
       
    81                 }
       
    82             }
       
    83         } else {
       
    84             $this->walkObjectForGroup($metadata, $object, $group, $propertyPath);
       
    85         }
       
    86     }
       
    87 
       
    88     protected function walkObjectForGroup(ClassMetadata $metadata, $object, $group, $propertyPath, $propagatedGroup = null)
       
    89     {
       
    90         $hash = spl_object_hash($object);
       
    91 
       
    92         // Exit, if the object is already validated for the current group
       
    93         if (isset($this->validatedObjects[$hash])) {
       
    94             if (isset($this->validatedObjects[$hash][$group])) {
       
    95                 return;
       
    96             }
       
    97         } else {
       
    98             $this->validatedObjects[$hash] = array();
       
    99         }
       
   100 
       
   101         // Remember validating this object before starting and possibly
       
   102         // traversing the object graph
       
   103         $this->validatedObjects[$hash][$group] = true;
       
   104 
       
   105         foreach ($metadata->findConstraints($group) as $constraint) {
       
   106             $this->walkConstraint($constraint, $object, $group, $propertyPath);
       
   107         }
       
   108 
       
   109         if (null !== $object) {
       
   110             foreach ($metadata->getConstrainedProperties() as $property) {
       
   111                 $localPropertyPath = empty($propertyPath) ? $property : $propertyPath.'.'.$property;
       
   112 
       
   113                 $this->walkProperty($metadata, $property, $object, $group, $localPropertyPath, $propagatedGroup);
       
   114             }
       
   115         }
       
   116     }
       
   117 
       
   118     public function walkProperty(ClassMetadata $metadata, $property, $object, $group, $propertyPath, $propagatedGroup = null)
       
   119     {
       
   120         foreach ($metadata->getMemberMetadatas($property) as $member) {
       
   121             $this->walkMember($member, $member->getValue($object), $group, $propertyPath, $propagatedGroup);
       
   122         }
       
   123     }
       
   124 
       
   125     public function walkPropertyValue(ClassMetadata $metadata, $property, $value, $group, $propertyPath)
       
   126     {
       
   127         foreach ($metadata->getMemberMetadatas($property) as $member) {
       
   128             $this->walkMember($member, $value, $group, $propertyPath);
       
   129         }
       
   130     }
       
   131 
       
   132     protected function walkMember(MemberMetadata $metadata, $value, $group, $propertyPath, $propagatedGroup = null)
       
   133     {
       
   134         $this->context->setCurrentProperty($metadata->getPropertyName());
       
   135 
       
   136         foreach ($metadata->findConstraints($group) as $constraint) {
       
   137             $this->walkConstraint($constraint, $value, $group, $propertyPath);
       
   138         }
       
   139 
       
   140         if ($metadata->isCascaded()) {
       
   141             $this->walkReference($value, $propagatedGroup ?: $group, $propertyPath, $metadata->isCollectionCascaded());
       
   142         }
       
   143     }
       
   144 
       
   145     public function walkReference($value, $group, $propertyPath, $traverse)
       
   146     {
       
   147         if (null !== $value) {
       
   148             if (!is_object($value) && !is_array($value)) {
       
   149                 throw new UnexpectedTypeException($value, 'object or array');
       
   150             }
       
   151 
       
   152             if ($traverse && (is_array($value) || $value instanceof \Traversable)) {
       
   153                 foreach ($value as $key => $element) {
       
   154                     // Ignore any scalar values in the collection
       
   155                     if (is_object($element) || is_array($element)) {
       
   156                         $this->walkReference($element, $group, $propertyPath.'['.$key.']', $traverse);
       
   157                     }
       
   158                 }
       
   159             }
       
   160 
       
   161             if (is_object($value)) {
       
   162                 $metadata = $this->metadataFactory->getClassMetadata(get_class($value));
       
   163                 $this->walkObject($metadata, $value, $group, $propertyPath);
       
   164             }
       
   165         }
       
   166     }
       
   167 
       
   168     public function walkConstraint(Constraint $constraint, $value, $group, $propertyPath)
       
   169     {
       
   170         $validator = $this->validatorFactory->getInstance($constraint);
       
   171 
       
   172         $this->context->setPropertyPath($propertyPath);
       
   173         $this->context->setGroup($group);
       
   174 
       
   175         $validator->initialize($this->context);
       
   176 
       
   177         if (!$validator->isValid($value, $constraint)) {
       
   178             // Resetting the property path. This is needed because some
       
   179             // validators, like CollectionValidator, use the walker internally
       
   180             // and so change the context.
       
   181             $this->context->setPropertyPath($propertyPath);
       
   182 
       
   183             $this->context->addViolation(
       
   184                 $validator->getMessageTemplate(),
       
   185                 $validator->getMessageParameters(),
       
   186                 $value
       
   187             );
       
   188         }
       
   189     }
       
   190 }