You are here

private function RecursiveContextualValidator::validateClassNode in Plug 7

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.

Parameters

object $object The validated object:

string $cacheKey The key for caching: the validated object

ClassMetadataInterface $metadata The class metadata of: the object

string $propertyPath The property path leading: to the object

string[] $groups The groups in which the: object should be validated

string[]|null $cascadedGroups The groups in which: cascaded objects should be validated

int $traversalStrategy The strategy used for: traversing the object

ExecutionContextInterface $context The current execution context:

Throws

UnsupportedMetadataException If a property metadata does not implement {@link PropertyMetadataInterface}

ConstraintDefinitionException If traversal was enabled but the object does not implement {@link \Traversable}

See also

TraversalStrategy

2 calls to RecursiveContextualValidator::validateClassNode()
RecursiveContextualValidator::stepThroughGroupSequence in lib/Symfony/validator/Symfony/Component/Validator/Validator/RecursiveContextualValidator.php
Sequentially validates a node's value in each group of a group sequence.
RecursiveContextualValidator::validateObject in lib/Symfony/validator/Symfony/Component/Validator/Validator/RecursiveContextualValidator.php
Validates an object against the constraints defined for its class.

File

lib/Symfony/validator/Symfony/Component/Validator/Validator/RecursiveContextualValidator.php, line 484

Class

RecursiveContextualValidator
Recursive implementation of {@link ContextualValidatorInterface}.

Namespace

Symfony\Component\Validator\Validator

Code

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);
}