View source
<?php
namespace Drupal\Core\TypedData\Validation;
use Drupal\Core\Entity\Plugin\DataType\EntityAdapter;
use Drupal\Core\TypedData\ComplexDataInterface;
use Drupal\Core\TypedData\ListInterface;
use Drupal\Core\TypedData\TypedDataInterface;
use Drupal\Core\TypedData\TypedDataManagerInterface;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidatorFactoryInterface;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
use Symfony\Component\Validator\Mapping\Factory\MetadataFactoryInterface;
use Symfony\Component\Validator\Util\PropertyPath;
class RecursiveContextualValidator implements ContextualValidatorInterface {
protected $context;
protected $metadataFactory;
protected $constraintValidatorFactory;
public function __construct(ExecutionContextInterface $context, MetadataFactoryInterface $metadata_factory, ConstraintValidatorFactoryInterface $validator_factory, TypedDataManagerInterface $typed_data_manager) {
$this->context = $context;
$this->metadataFactory = $metadata_factory;
$this->constraintValidatorFactory = $validator_factory;
$this->typedDataManager = $typed_data_manager;
}
public function atPath($path) {
return $this;
}
public function validate($data, $constraints = NULL, $groups = NULL, $is_root_call = TRUE) {
if (isset($groups)) {
throw new \LogicException('Passing custom groups is not supported.');
}
if (!$data instanceof TypedDataInterface) {
throw new \InvalidArgumentException('The passed value must be a typed data object.');
}
if (isset($constraints) && !is_array($constraints)) {
$constraints = [
$constraints,
];
}
$this
->validateNode($data, $constraints, $is_root_call);
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());
$typed_data_manager = method_exists($data, 'getTypedDataManager') ? $data
->getTypedDataManager() : $this->typedDataManager;
$value = $typed_data_manager
->getCanonicalRepresentation($data);
$constraints_given = isset($constraints);
$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 (($data instanceof ListInterface || $data instanceof ComplexDataInterface) && !$data
->isEmpty() && !($data instanceof EntityAdapter && $constraints_given)) {
foreach ($data as $name => $property) {
$this
->validateNode($property);
}
}
$this->context
->setNode($previous_value, $previous_object, $previous_metadata, $previous_path);
return $this;
}
protected function validateConstraints($value, $cache_key, $constraints) {
foreach ($constraints as $constraint) {
if (isset($cache_key)) {
$constraint_hash = spl_object_hash($constraint);
if ($this->context
->isConstraintValidated($cache_key, $constraint_hash)) {
continue;
}
$this->context
->markConstraintAsValidated($cache_key, $constraint_hash);
}
$this->context
->setConstraint($constraint);
$validator = $this->constraintValidatorFactory
->getInstance($constraint);
$validator
->initialize($this->context);
$validator
->validate($value, $constraint);
}
}
public function getViolations() {
return $this->context
->getViolations();
}
public function validateProperty($object, $propertyName, $groups = NULL) {
if (isset($groups)) {
throw new \LogicException('Passing custom groups is not supported.');
}
if (!is_object($object)) {
throw new \InvalidArgumentException('Passing class name is not supported.');
}
elseif (!$object instanceof TypedDataInterface) {
throw new \InvalidArgumentException('The passed in object has to be typed data.');
}
elseif (!$object instanceof ListInterface && !$object instanceof ComplexDataInterface) {
throw new \InvalidArgumentException('Passed data does not contain properties.');
}
return $this
->validateNode($object
->get($propertyName), NULL, TRUE);
}
public function validatePropertyValue($object, $property_name, $value, $groups = NULL) {
if (!is_object($object)) {
throw new \InvalidArgumentException('Passing class name is not supported.');
}
elseif (!$object instanceof TypedDataInterface) {
throw new \InvalidArgumentException('The passed in object has to be typed data.');
}
elseif (!$object instanceof ListInterface && !$object instanceof ComplexDataInterface) {
throw new \InvalidArgumentException('Passed data does not contain properties.');
}
$data = $object
->get($property_name);
$metadata = $this->metadataFactory
->getMetadataFor($data);
$constraints = $metadata
->findConstraints(Constraint::DEFAULT_GROUP);
return $this
->validate($value, $constraints, $groups, TRUE);
}
}