Вход Регистрация
Файл: symfony-2.7/src/Symfony/Component/Validator/Validator/RecursiveContextualValidator.php
Строк: 978
<?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 SymfonyComponentValidatorValidator;

use 
SymfonyComponentValidatorConstraint;
use 
SymfonyComponentValidatorConstraintsGroupSequence;
use 
SymfonyComponentValidatorConstraintValidatorFactoryInterface;
use 
SymfonyComponentValidatorContextExecutionContextInterface;
use 
SymfonyComponentValidatorExceptionConstraintDefinitionException;
use 
SymfonyComponentValidatorExceptionNoSuchMetadataException;
use 
SymfonyComponentValidatorExceptionRuntimeException;
use 
SymfonyComponentValidatorExceptionUnsupportedMetadataException;
use 
SymfonyComponentValidatorExceptionValidatorException;
use 
SymfonyComponentValidatorMappingCascadingStrategy;
use 
SymfonyComponentValidatorMappingClassMetadataInterface;
use 
SymfonyComponentValidatorMappingGenericMetadata;
use 
SymfonyComponentValidatorMappingMetadataInterface;
use 
SymfonyComponentValidatorMappingPropertyMetadataInterface;
use 
SymfonyComponentValidatorMappingTraversalStrategy;
use 
SymfonyComponentValidatorMetadataFactoryInterface;
use 
SymfonyComponentValidatorObjectInitializerInterface;
use 
SymfonyComponentValidatorUtilPropertyPath;

/**
 * Recursive implementation of {@link ContextualValidatorInterface}.
 *
 * @since  2.5
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
class RecursiveContextualValidator implements ContextualValidatorInterface
{
    
/**
     * @var ExecutionContextInterface
     */
    
private $context;

    
/**
     * @var MetadataFactoryInterface
     */
    
private $metadataFactory;

    
/**
     * @var ConstraintValidatorFactoryInterface
     */
    
private $validatorFactory;

    
/**
     * @var ObjectInitializerInterface[]
     */
    
private $objectInitializers;

    
/**
     * Creates a validator for the given context.
     *
     * @param ExecutionContextInterface           $context            The execution context
     * @param MetadataFactoryInterface            $metadataFactory    The factory for
     *                                                                fetching the metadata
     *                                                                of validated objects
     * @param ConstraintValidatorFactoryInterface $validatorFactory   The factory for creating
     *                                                                constraint validators
     * @param ObjectInitializerInterface[]        $objectInitializers The object initializers
     */
    
public function __construct(ExecutionContextInterface $contextMetadataFactoryInterface $metadataFactoryConstraintValidatorFactoryInterface $validatorFactory, array $objectInitializers = array())
    {
        
$this->context $context;
        
$this->defaultPropertyPath $context->getPropertyPath();
        
$this->defaultGroups = array($context->getGroup() ?: Constraint::DEFAULT_GROUP);
        
$this->metadataFactory $metadataFactory;
        
$this->validatorFactory $validatorFactory;
        
$this->objectInitializers $objectInitializers;
    }

    
/**
     * {@inheritdoc}
     */
    
public function atPath($path)
    {
        
$this->defaultPropertyPath $this->context->getPropertyPath($path);

        return 
$this;
    }

    
/**
     * {@inheritdoc}
     */
    
public function validate($value$constraints null$groups null)
    {
        
$groups $groups $this->normalizeGroups($groups) : $this->defaultGroups;

        
$previousValue $this->context->getValue();
        
$previousObject $this->context->getObject();
        
$previousMetadata $this->context->getMetadata();
        
$previousPath $this->context->getPropertyPath();
        
$previousGroup $this->context->getGroup();

        
// If explicit constraints are passed, validate the value against
        // those constraints
        
if (null !== $constraints) {
            
// You can pass a single constraint or an array of constraints
            // Make sure to deal with an array in the rest of the code
            
if (!is_array($constraints)) {
                
$constraints = array($constraints);
            }

            
$metadata = new GenericMetadata();
            
$metadata->addConstraints($constraints);

            
$this->validateGenericNode(
                
$value,
                
null,
                
is_object($value) ? spl_object_hash($value) : null,
                
$metadata,
                
$this->defaultPropertyPath,
                
$groups,
                
null,
                
TraversalStrategy::IMPLICIT,
                
$this->context
            
);

            
$this->context->setNode($previousValue$previousObject$previousMetadata$previousPath);
            
$this->context->setGroup($previousGroup);

            return 
$this;
        }

        
// If an object is passed without explicit constraints, validate that
        // object against the constraints defined for the object's class
        
if (is_object($value)) {
            
$this->validateObject(
                
$value,
                
$this->defaultPropertyPath,
                
$groups,
                
TraversalStrategy::IMPLICIT,
                
$this->context
            
);

            
$this->context->setNode($previousValue$previousObject$previousMetadata$previousPath);
            
$this->context->setGroup($previousGroup);

            return 
$this;
        }

        
// If an array is passed without explicit constraints, validate each
        // object in the array
        
if (is_array($value)) {
            
$this->validateEachObjectIn(
                
$value,
                
$this->defaultPropertyPath,
                
$groups,
                
true,
                
$this->context
            
);

            
$this->context->setNode($previousValue$previousObject$previousMetadata$previousPath);
            
$this->context->setGroup($previousGroup);

            return 
$this;
        }

        throw new 
RuntimeException(sprintf(
            
'Cannot validate values of type "%s" automatically. Please '.
            
'provide a constraint.',
            
gettype($value)
        ));
    }

    
/**
     * {@inheritdoc}
     */
    
public function validateProperty($object$propertyName$groups null)
    {
        
$classMetadata $this->metadataFactory->getMetadataFor($object);

        if (!
$classMetadata instanceof ClassMetadataInterface) {
            
// Cannot be UnsupportedMetadataException because of BC with
            // Symfony < 2.5
            
throw new ValidatorException(sprintf(
                
'The metadata factory should return instances of '.
                
'"SymfonyComponentValidatorMappingClassMetadataInterface", '.
                
'got: "%s".',
                
is_object($classMetadata) ? get_class($classMetadata) : gettype($classMetadata)
            ));
        }

        
$propertyMetadatas $classMetadata->getPropertyMetadata($propertyName);
        
$groups $groups $this->normalizeGroups($groups) : $this->defaultGroups;
        
$cacheKey spl_object_hash($object);
        
$propertyPath PropertyPath::append($this->defaultPropertyPath$propertyName);

        
$previousValue $this->context->getValue();
        
$previousObject $this->context->getObject();
        
$previousMetadata $this->context->getMetadata();
        
$previousPath $this->context->getPropertyPath();
        
$previousGroup $this->context->getGroup();

        foreach (
$propertyMetadatas as $propertyMetadata) {
            
$propertyValue $propertyMetadata->getPropertyValue($object);

            
$this->validateGenericNode(
                
$propertyValue,
                
$object,
                
$cacheKey.':'.$propertyName,
                
$propertyMetadata,
                
$propertyPath,
                
$groups,
                
null,
                
TraversalStrategy::IMPLICIT,
                
$this->context
            
);
        }

        
$this->context->setNode($previousValue$previousObject$previousMetadata$previousPath);
        
$this->context->setGroup($previousGroup);

        return 
$this;
    }

    
/**
     * {@inheritdoc}
     */
    
public function validatePropertyValue($objectOrClass$propertyName$value$groups null)
    {
        
$classMetadata $this->metadataFactory->getMetadataFor($objectOrClass);

        if (!
$classMetadata instanceof ClassMetadataInterface) {
            
// Cannot be UnsupportedMetadataException because of BC with
            // Symfony < 2.5
            
throw new ValidatorException(sprintf(
                
'The metadata factory should return instances of '.
                
'"SymfonyComponentValidatorMappingClassMetadataInterface", '.
                
'got: "%s".',
                
is_object($classMetadata) ? get_class($classMetadata) : gettype($classMetadata)
            ));
        }

        
$propertyMetadatas $classMetadata->getPropertyMetadata($propertyName);
        
$groups $groups $this->normalizeGroups($groups) : $this->defaultGroups;

        if (
is_object($objectOrClass)) {
            
$object $objectOrClass;
            
$cacheKey spl_object_hash($objectOrClass);
            
$propertyPath PropertyPath::append($this->defaultPropertyPath$propertyName);
        } else {
            
// $objectOrClass contains a class name
            
$object null;
            
$cacheKey null;
            
$propertyPath $this->defaultPropertyPath;
        }

        
$previousValue $this->context->getValue();
        
$previousObject $this->context->getObject();
        
$previousMetadata $this->context->getMetadata();
        
$previousPath $this->context->getPropertyPath();
        
$previousGroup $this->context->getGroup();

        foreach (
$propertyMetadatas as $propertyMetadata) {
            
$this->validateGenericNode(
                
$value,
                
$object,
                
$cacheKey.':'.$propertyName,
                
$propertyMetadata,
                
$propertyPath,
                
$groups,
                
null,
                
TraversalStrategy::IMPLICIT,
                
$this->context
            
);
        }

        
$this->context->setNode($previousValue$previousObject$previousMetadata$previousPath);
        
$this->context->setGroup($previousGroup);

        return 
$this;
    }

    
/**
     * {@inheritdoc}
     */
    
public function getViolations()
    {
        return 
$this->context->getViolations();
    }

    
/**
     * Normalizes the given group or list of groups to an array.
     *
     * @param mixed $groups The groups to normalize
     *
     * @return array A group array
     */
    
protected function normalizeGroups($groups)
    {
        if (
is_array($groups)) {
            return 
$groups;
        }

        return array(
$groups);
    }
    
/**
     * Validates an object against the constraints defined for its class.
     *
     * If no metadata is available for the class, but the class is an instance
     * of {@link Traversable} and the selected traversal strategy allows
     * traversal, the object will be iterated and each nested object will be
     * validated instead.
     *
     * @param object                    $object            The object to cascade
     * @param string                    $propertyPath      The current property path
     * @param string[]                  $groups            The validated groups
     * @param int                       $traversalStrategy The strategy for traversing the
     *                                                     cascaded object
     * @param ExecutionContextInterface $context           The current execution context
     *
     * @throws NoSuchMetadataException      If the object has no associated metadata
     *                                      and does not implement {@link Traversable}
     *                                      or if traversal is disabled via the
     *                                      $traversalStrategy argument
     * @throws UnsupportedMetadataException If the metadata returned by the
     *                                      metadata factory does not implement
     *                                      {@link ClassMetadataInterface}
     */
    
private function validateObject($object$propertyPath, array $groups$traversalStrategyExecutionContextInterface $context)
    {
        try {
            
$classMetadata $this->metadataFactory->getMetadataFor($object);

            if (!
$classMetadata instanceof ClassMetadataInterface) {
                throw new 
UnsupportedMetadataException(sprintf(
                    
'The metadata factory should return instances of '.
                    
'"SymfonyComponentValidatorMappingClassMetadataInterface", '.
                    
'got: "%s".',
                    
is_object($classMetadata) ? get_class($classMetadata) : gettype($classMetadata)
                ));
            }

            
$this->validateClassNode(
                
$object,
                
spl_object_hash($object),
                
$classMetadata,
                
$propertyPath,
                
$groups,
                
null,
                
$traversalStrategy,
                
$context
            
);
        } catch (
NoSuchMetadataException $e) {
            
// Rethrow if not Traversable
            
if (!$object instanceof Traversable) {
                throw 
$e;
            }

            
// Rethrow unless IMPLICIT or TRAVERSE
            
if (!($traversalStrategy & (TraversalStrategy::IMPLICIT TraversalStrategy::TRAVERSE))) {
                throw 
$e;
            }

            
$this->validateEachObjectIn(
                
$object,
                
$propertyPath,
                
$groups,
                
$traversalStrategy TraversalStrategy::STOP_RECURSION,
                
$context
            
);
        }
    }

    
/**
     * Validates each object in a collection against the constraints defined
     * for their classes.
     *
     * If the parameter $recursive is set to true, nested {@link Traversable}
     * objects are iterated as well. Nested arrays are always iterated,
     * regardless of the value of $recursive.
     *
     * @param array|Traversable        $collection    The collection
     * @param string                    $propertyPath  The current property path
     * @param string[]                  $groups        The validated groups
     * @param bool                      $stopRecursion Whether to disable
     *                                                 recursive iteration. For
     *                                                 backwards compatibility
     *                                                 with Symfony < 2.5.
     * @param ExecutionContextInterface $context       The current execution context
     *
     * @see ClassNode
     * @see CollectionNode
     */
    
private function validateEachObjectIn($collection$propertyPath, array $groups$stopRecursionExecutionContextInterface $context)
    {
        if (
$stopRecursion) {
            
$traversalStrategy TraversalStrategy::NONE;
        } else {
            
$traversalStrategy TraversalStrategy::IMPLICIT;
        }

        foreach (
$collection as $key => $value) {
            if (
is_array($value)) {
                
// Arrays are always cascaded, independent of the specified
                // traversal strategy
                // (BC with Symfony < 2.5)
                
$this->validateEachObjectIn(
                    
$value,
                    
$propertyPath.'['.$key.']',
                    
$groups,
                    
$stopRecursion,
                    
$context
                
);

                continue;
            }

            
// Scalar and null values in the collection are ignored
            // (BC with Symfony < 2.5)
            
if (is_object($value)) {
                
$this->validateObject(
                    
$value,
                    
$propertyPath.'['.$key.']',
                    
$groups,
                    
$traversalStrategy,
                    
$context
                
);
            }
        }
    }

    
/**
     * Validates a class node.
     *
     * A class node is a combination of an object with a {@link ClassMetadataInterface}
     * instance. Each class node (conceptionally) has zero or more succeeding
     * property nodes:
     *
     *     (Article:class node)
     *                
     *        ($title:property node)
     *
     * This method validates the passed objects against all constraints defined
     * at class level. It furthermore triggers the validation of each of the
     * class' properties against the constraints for that property.
     *
     * If the selected traversal strategy allows traversal, the object is
     * iterated and each nested object is validated against its own constraints.
     * The object is not traversed if traversal is disabled in the class
     * metadata.
     *
     * If the passed groups contain the group "Default", the validator will
     * check whether the "Default" group has been replaced by a group sequence
     * in the class metadata. If this is the case, the group sequence is
     * validated instead.
     *
     * @param object                    $object            The validated object
     * @param string                    $cacheKey          The key for caching
     *                                                     the validated object
     * @param ClassMetadataInterface    $metadata          The class metadata of
     *                                                     the object
     * @param string                    $propertyPath      The property path leading
     *                                                     to the object
     * @param string[]                  $groups            The groups in which the
     *                                                     object should be validated
     * @param string[]|null             $cascadedGroups    The groups in which
     *                                                     cascaded objects should
     *                                                     be validated
     * @param int                       $traversalStrategy The strategy used for
     *                                                     traversing the object
     * @param ExecutionContextInterface $context           The current execution context
     *
     * @throws UnsupportedMetadataException  If a property metadata does not
     *                                       implement {@link PropertyMetadataInterface}
     * @throws ConstraintDefinitionException If traversal was enabled but the
     *                                       object does not implement
     *                                       {@link Traversable}
     *
     * @see TraversalStrategy
     */
    
private function validateClassNode($object$cacheKeyClassMetadataInterface $metadata null$propertyPath, array $groups$cascadedGroups$traversalStrategyExecutionContextInterface $context)
    {
        
$context->setNode($object$object$metadata$propertyPath);

        if (!
$context->isObjectInitialized($cacheKey)) {
            foreach (
$this->objectInitializers as $initializer) {
                
$initializer->initialize($object);
            }

            
$context->markObjectAsInitialized($cacheKey);
        }

        foreach (
$groups as $key => $group) {
            
// If the "Default" group is replaced by a group sequence, remember
            // to cascade the "Default" group when traversing the group
            // sequence
            
$defaultOverridden false;

            
// Use the object hash for group sequences
            
$groupHash is_object($group) ? spl_object_hash($group) : $group;

            if (
$context->isGroupValidated($cacheKey$groupHash)) {
                
// Skip this group when validating the properties and when
                // traversing the object
                
unset($groups[$key]);

                continue;
            }

            
$context->markGroupAsValidated($cacheKey$groupHash);

            
// Replace the "Default" group by the group sequence defined
            // for the class, if applicable.
            // This is done after checking the cache, so that
            // spl_object_hash() isn't called for this sequence and
            // "Default" is used instead in the cache. This is useful
            // if the getters below return different group sequences in
            // every call.
            
if (Constraint::DEFAULT_GROUP === $group) {
                if (
$metadata->hasGroupSequence()) {
                    
// The group sequence is statically defined for the class
                    
$group $metadata->getGroupSequence();
                    
$defaultOverridden true;
                } elseif (
$metadata->isGroupSequenceProvider()) {
                    
// The group sequence is dynamically obtained from the validated
                    // object
                    /** @var SymfonyComponentValidatorGroupSequenceProviderInterface $object */
                    
$group $object->getGroupSequence();
                    
$defaultOverridden true;

                    if (!
$group instanceof GroupSequence) {
                        
$group = new GroupSequence($group);
                    }
                }
            }

            
// If the groups (=[<G1,G2>,G3,G4]) contain a group sequence
            // (=<G1,G2>), then call validateClassNode() with each entry of the
            // group sequence and abort if necessary (G1, G2)
            
if ($group instanceof GroupSequence) {
                
$this->stepThroughGroupSequence(
                     
$object,
                     
$object,
                     
$cacheKey,
                     
$metadata,
                     
$propertyPath,
                     
$traversalStrategy,
                     
$group,
                     
$defaultOverridden Constraint::DEFAULT_GROUP null,
                     
$context
                
);

                
// Skip the group sequence when validating properties, because
                // stepThroughGroupSequence() already validates the properties
                
unset($groups[$key]);

                continue;
            }

            
$this->validateInGroup($object$cacheKey$metadata$group$context);
        }

        
// If no more groups should be validated for the property nodes,
        // we can safely quit
        
if (=== count($groups)) {
            return;
        }

        
// Validate all properties against their constraints
        
foreach ($metadata->getConstrainedProperties() as $propertyName) {
            
// If constraints are defined both on the getter of a property as
            // well as on the property itself, then getPropertyMetadata()
            // returns two metadata objects, not just one
            
foreach ($metadata->getPropertyMetadata($propertyName) as $propertyMetadata) {
                if (!
$propertyMetadata instanceof PropertyMetadataInterface) {
                    throw new 
UnsupportedMetadataException(sprintf(
                        
'The property metadata instances should implement '.
                        
'"SymfonyComponentValidatorMappingPropertyMetadataInterface", '.
                        
'got: "%s".',
                        
is_object($propertyMetadata) ? get_class($propertyMetadata) : gettype($propertyMetadata)
                    ));
                }

                
$propertyValue $propertyMetadata->getPropertyValue($object);

                
$this->validateGenericNode(
                    
$propertyValue,
                    
$object,
                    
$cacheKey.':'.$propertyName,
                    
$propertyMetadata,
                    
$propertyPath
                        
$propertyPath.'.'.$propertyName
                        
$propertyName,
                    
$groups,
                    
$cascadedGroups,
                    
TraversalStrategy::IMPLICIT,
                    
$context
                
);
            }
        }

        
// If no specific traversal strategy was requested when this method
        // was called, use the traversal strategy of the class' metadata
        
if ($traversalStrategy TraversalStrategy::IMPLICIT) {
            
// Keep the STOP_RECURSION flag, if it was set
            
$traversalStrategy $metadata->getTraversalStrategy()
                | (
$traversalStrategy TraversalStrategy::STOP_RECURSION);
        }

        
// Traverse only if IMPLICIT or TRAVERSE
        
if (!($traversalStrategy & (TraversalStrategy::IMPLICIT TraversalStrategy::TRAVERSE))) {
            return;
        }

        
// If IMPLICIT, stop unless we deal with a Traversable
        
if ($traversalStrategy TraversalStrategy::IMPLICIT && !$object instanceof Traversable) {
            return;
        }

        
// If TRAVERSE, fail if we have no Traversable
        
if (!$object instanceof Traversable) {
            
// Must throw a ConstraintDefinitionException for backwards
            // compatibility reasons with Symfony < 2.5
            
throw new ConstraintDefinitionException(sprintf(
                
'Traversal was enabled for "%s", but this class '.
                
'does not implement "Traversable".',
                
get_class($object)
            ));
        }

        
$this->validateEachObjectIn(
            
$object,
            
$propertyPath,
            
$groups,
            
$traversalStrategy TraversalStrategy::STOP_RECURSION,
            
$context
        
);
    }

    
/**
     * Validates a node that is not a class node.
     *
     * Currently, two such node types exist:
     *
     *  - property nodes, which consist of the value of an object's
     *    property together with a {@link PropertyMetadataInterface} instance
     *  - generic nodes, which consist of a value and some arbitrary
     *    constraints defined in a {@link MetadataInterface} container
     *
     * In both cases, the value is validated against all constraints defined
     * in the passed metadata object. Then, if the value is an instance of
     * {@link Traversable} and the selected traversal strategy permits it,
     * the value is traversed and each nested object validated against its own
     * constraints. Arrays are always traversed.
     *
     * @param mixed                     $value             The validated value
     * @param object|null               $object            The current object
     * @param string                    $cacheKey          The key for caching
     *                                                     the validated value
     * @param MetadataInterface         $metadata          The metadata of the
     *                                                     value
     * @param string                    $propertyPath      The property path leading
     *                                                     to the value
     * @param string[]                  $groups            The groups in which the
     *                                                     value should be validated
     * @param string[]|null             $cascadedGroups    The groups in which
     *                                                     cascaded objects should
     *                                                     be validated
     * @param int                       $traversalStrategy The strategy used for
     *                                                     traversing the value
     * @param ExecutionContextInterface $context           The current execution context
     *
     * @see TraversalStrategy
     */
    
private function validateGenericNode($value$object$cacheKeyMetadataInterface $metadata null$propertyPath, array $groups$cascadedGroups$traversalStrategyExecutionContextInterface $context)
    {
        
$context->setNode($value$object$metadata$propertyPath);

        foreach (
$groups as $key => $group) {
            if (
$group instanceof GroupSequence) {
                
$this->stepThroughGroupSequence(
                     
$value,
                     
$object,
                     
$cacheKey,
                     
$metadata,
                     
$propertyPath,
                     
$traversalStrategy,
                     
$group,
                     
null,
                     
$context
                
);

                
// Skip the group sequence when cascading, as the cascading
                // logic is already done in stepThroughGroupSequence()
                
unset($groups[$key]);

                continue;
            }

            
$this->validateInGroup($value$cacheKey$metadata$group$context);
        }

        if (
=== count($groups)) {
            return;
        }

        if (
null === $value) {
            return;
        }

        
$cascadingStrategy $metadata->getCascadingStrategy();

        
// Quit unless we have an array or a cascaded object
        
if (!is_array($value) && !($cascadingStrategy CascadingStrategy::CASCADE)) {
            return;
        }

        
// If no specific traversal strategy was requested when this method
        // was called, use the traversal strategy of the node's metadata
        
if ($traversalStrategy TraversalStrategy::IMPLICIT) {
            
// Keep the STOP_RECURSION flag, if it was set
            
$traversalStrategy $metadata->getTraversalStrategy()
                | (
$traversalStrategy TraversalStrategy::STOP_RECURSION);
        }

        
// The $cascadedGroups property is set, if the "Default" group is
        // overridden by a group sequence
        // See validateClassNode()
        
$cascadedGroups count($cascadedGroups) > 0
            
$cascadedGroups
            
$groups;

        if (
is_array($value)) {
            
// Arrays are always traversed, independent of the specified
            // traversal strategy
            // (BC with Symfony < 2.5)
            
$this->validateEachObjectIn(
                
$value,
                
$propertyPath,
                
$cascadedGroups,
                
$traversalStrategy TraversalStrategy::STOP_RECURSION,
                
$context
            
);

            return;
        }

        
// If the value is a scalar, pass it anyway, because we want
        // a NoSuchMetadataException to be thrown in that case
        // (BC with Symfony < 2.5)
        
$this->validateObject(
            
$value,
            
$propertyPath,
            
$cascadedGroups,
            
$traversalStrategy,
            
$context
        
);

        
// Currently, the traversal strategy can only be TRAVERSE for a
        // generic node if the cascading strategy is CASCADE. Thus, traversable
        // objects will always be handled within validateObject() and there's
        // nothing more to do here.

        // see GenericMetadata::addConstraint()
    
}

    
/**
     * Sequentially validates a node's value in each group of a group sequence.
     *
     * If any of the constraints generates a violation, subsequent groups in the
     * group sequence are skipped.
     *
     * @param mixed                     $value             The validated value
     * @param object|null               $object            The current object
     * @param string                    $cacheKey          The key for caching
     *                                                     the validated value
     * @param MetadataInterface         $metadata          The metadata of the
     *                                                     value
     * @param string                    $propertyPath      The property path leading
     *                                                     to the value
     * @param int                       $traversalStrategy The strategy used for
     *                                                     traversing the value
     * @param GroupSequence             $groupSequence     The group sequence
     * @param string[]|null             $cascadedGroup     The group that should
     *                                                     be passed to cascaded
     *                                                     objects instead of
     *                                                     the group sequence
     * @param ExecutionContextInterface $context           The execution context
     */
    
private function stepThroughGroupSequence($value$object$cacheKeyMetadataInterface $metadata null$propertyPath$traversalStrategyGroupSequence $groupSequence$cascadedGroupExecutionContextInterface $context)
    {
        
$violationCount count($context->getViolations());
        
$cascadedGroups $cascadedGroup ? array($cascadedGroup) : null;

        foreach (
$groupSequence->groups as $groupInSequence) {
            
$groups = array($groupInSequence);

            if (
$metadata instanceof ClassMetadataInterface) {
                
$this->validateClassNode(
                     
$value,
                     
$cacheKey,
                     
$metadata,
                     
$propertyPath,
                     
$groups,
                     
$cascadedGroups,
                     
$traversalStrategy,
                     
$context
                
);
            } else {
                
$this->validateGenericNode(
                     
$value,
                     
$object,
                     
$cacheKey,
                     
$metadata,
                     
$propertyPath,
                     
$groups,
                     
$cascadedGroups,
                     
$traversalStrategy,
                     
$context
                
);
            }

            
// Abort sequence validation if a violation was generated
            
if (count($context->getViolations()) > $violationCount) {
                break;
            }
        }
    }

    
/**
     * Validates a node's value against all constraints in the given group.
     *
     * @param mixed                     $value    The validated value
     * @param string                    $cacheKey The key for caching the
     *                                            validated value
     * @param MetadataInterface         $metadata The metadata of the value
     * @param string                    $group    The group to validate
     * @param ExecutionContextInterface $context  The execution context
     */
    
private function validateInGroup($value$cacheKeyMetadataInterface $metadata$groupExecutionContextInterface $context)
    {
        
$context->setGroup($group);

        foreach (
$metadata->findConstraints($group) as $constraint) {
            
// Prevent duplicate validation of constraints, in the case
            // that constraints belong to multiple validated groups
            
if (null !== $cacheKey) {
                
$constraintHash spl_object_hash($constraint);

                if (
$context->isConstraintValidated($cacheKey$constraintHash)) {
                    continue;
                }

                
$context->markConstraintAsValidated($cacheKey$constraintHash);
            }

            
$context->setConstraint($constraint);

            
$validator $this->validatorFactory->getInstance($constraint);
            
$validator->initialize($context);
            
$validator->validate($value$constraint);
        }
    }
}
Онлайн: 1
Реклама