123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832 |
- <?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\Validator;
- use Symfony\Component\Validator\Constraint;
- use Symfony\Component\Validator\Constraints\GroupSequence;
- use Symfony\Component\Validator\ConstraintValidatorFactoryInterface;
- use Symfony\Component\Validator\Context\ExecutionContext;
- use Symfony\Component\Validator\Context\ExecutionContextInterface;
- use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
- use Symfony\Component\Validator\Exception\NoSuchMetadataException;
- use Symfony\Component\Validator\Exception\RuntimeException;
- use Symfony\Component\Validator\Exception\UnsupportedMetadataException;
- use Symfony\Component\Validator\Exception\ValidatorException;
- use Symfony\Component\Validator\Mapping\CascadingStrategy;
- use Symfony\Component\Validator\Mapping\ClassMetadataInterface;
- use Symfony\Component\Validator\Mapping\GenericMetadata;
- use Symfony\Component\Validator\Mapping\MetadataInterface;
- use Symfony\Component\Validator\Mapping\PropertyMetadataInterface;
- use Symfony\Component\Validator\Mapping\TraversalStrategy;
- use Symfony\Component\Validator\MetadataFactoryInterface;
- use Symfony\Component\Validator\ObjectInitializerInterface;
- use Symfony\Component\Validator\Util\PropertyPath;
- /**
- * Recursive implementation of {@link ContextualValidatorInterface}.
- *
- * @author Bernhard Schussek <bschussek@gmail.com>
- */
- class RecursiveContextualValidator implements ContextualValidatorInterface
- {
- private $context;
- private $defaultPropertyPath;
- private $defaultGroups;
- private $metadataFactory;
- private $validatorFactory;
- 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 $context, MetadataFactoryInterface $metadataFactory, ConstraintValidatorFactoryInterface $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();
- $previousConstraint = null;
- if ($this->context instanceof ExecutionContext || method_exists($this->context, 'getConstraint')) {
- $previousConstraint = $this->context->getConstraint();
- }
- // 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,
- $previousObject,
- \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);
- if (null !== $previousConstraint) {
- $this->context->setConstraint($previousConstraint);
- }
- 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 "\Symfony\Component\Validator\Mapping\ClassMetadataInterface", 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 "\Symfony\Component\Validator\Mapping\ClassMetadataInterface", 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 string|GroupSequence|(string|GroupSequence)[] $groups The groups to normalize
- *
- * @return (string|GroupSequence)[] 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|GroupSequence)[] $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, $traversalStrategy, ExecutionContextInterface $context)
- {
- try {
- $classMetadata = $this->metadataFactory->getMetadataFor($object);
- if (!$classMetadata instanceof ClassMetadataInterface) {
- throw new UnsupportedMetadataException(sprintf('The metadata factory should return instances of "Symfony\Component\Validator\Mapping\ClassMetadataInterface", 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 iterable $collection The collection
- * @param string $propertyPath The current property path
- * @param (string|GroupSequence)[] $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, $stopRecursion, ExecutionContextInterface $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|GroupSequence)[] $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, $cacheKey, ClassMetadataInterface $metadata = null, $propertyPath, array $groups, $cascadedGroups, $traversalStrategy, ExecutionContextInterface $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 \Symfony\Component\Validator\GroupSequenceProviderInterface $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 (0 === \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 "Symfony\Component\Validator\Mapping\PropertyMetadataInterface", got: "%s".', \is_object($propertyMetadata) ? \get_class($propertyMetadata) : \gettype($propertyMetadata)));
- }
- $propertyValue = $propertyMetadata->getPropertyValue($object);
- $this->validateGenericNode(
- $propertyValue,
- $object,
- $cacheKey.':'.$propertyName,
- $propertyMetadata,
- PropertyPath::append($propertyPath, $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|GroupSequence)[] $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, $cacheKey, MetadataInterface $metadata = null, $propertyPath, array $groups, $cascadedGroups, $traversalStrategy, ExecutionContextInterface $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 (0 === \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 = null !== $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, $cacheKey, MetadataInterface $metadata = null, $propertyPath, $traversalStrategy, GroupSequence $groupSequence, $cascadedGroup, ExecutionContextInterface $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, $cacheKey, MetadataInterface $metadata, $group, ExecutionContextInterface $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);
- }
- }
- }
|