/** * Validates a Typed Data node in the validation tree. * * If no constraints are passed, the data is validated against the * constraints specified in its data definition. If the data is complex or a * list and no constraints are passed, the contained properties or list items * are validated recursively. * * @param \Drupal\Core\TypedData\TypedDataInterface $data * The data to validated. * @param \Symfony\Component\Validator\Constraint[]|null $constraints * (optional) If set, an array of constraints to validate. * @param bool $is_root_call * (optional) Whether its the most upper call in the type data tree. * * @return $this */ protected function validateNode(TypedDataInterface $data, $constraints = NULL, $is_root_call = FALSE) { $previous_value = $this->context->getValue(); $previous_object = $this->context->getObject(); $previous_metadata = $this->context->getMetadata(); $previous_path = $this->context->getPropertyPath(); $metadata = $this->metadataFactory->getMetadataFor($data); $cache_key = spl_object_hash($data); $property_path = $is_root_call ? '' : PropertyPath::append($previous_path, $data->getName()); // Pass the canonical representation of the data as validated value to // constraint validators, such that they do not have to care about Typed // Data. $value = $this->typedDataManager->getCanonicalRepresentation($data); $this->context->setNode($value, $data, $metadata, $property_path); if (isset($constraints) || !$this->context->isGroupValidated($cache_key, Constraint::DEFAULT_GROUP)) { if (!isset($constraints)) { $this->context->markGroupAsValidated($cache_key, Constraint::DEFAULT_GROUP); $constraints = $metadata->findConstraints(Constraint::DEFAULT_GROUP); } $this->validateConstraints($value, $cache_key, $constraints); } // If the data is a list or complex data, validate the contained list items // or properties. However, do not recurse if the data is empty. if (($data instanceof ListInterface || $data instanceof ComplexDataInterface) && !$data->isEmpty()) { foreach ($data as $name => $property) { $this->validateNode($property); } } $this->context->setNode($previous_value, $previous_object, $previous_metadata, $previous_path); return $this; }
protected function setPropertyPath($propertyPath) { $this->propertyPath = $propertyPath; switch ($this->getApiVersion()) { case Validation::API_VERSION_2_4: $this->context = $this->createContext(); $this->validator->initialize($this->context); break; case Validation::API_VERSION_2_5: case Validation::API_VERSION_2_5_BC: $this->context->setNode($this->value, $this->object, $this->metadata, $this->propertyPath); break; } }
/** * 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, $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 = 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() }
protected function setPropertyPath($propertyPath) { $this->propertyPath = $propertyPath; $this->context->setNode($this->value, $this->object, $this->metadata, $this->propertyPath); }