Issue #2343035 by fago, dawehner, larowlan, ianthomas_uk, cilefen, hussainweb, Miroling, jibran, beejeebus, dashaforbes, webmozart, klausi: Upgrade validator integration for Symfony versions 2.5+
parent
d57ee5f9a3
commit
373d925dff
|
@ -14,7 +14,7 @@
|
|||
"symfony/http-kernel": "2.6.*",
|
||||
"symfony/routing": "2.6.*",
|
||||
"symfony/serializer": "2.6.*",
|
||||
"symfony/validator": "2.6.*",
|
||||
"symfony/validator": "2.6.*@dev",
|
||||
"symfony/process": "2.6.*",
|
||||
"symfony/yaml": "2.6.*",
|
||||
"twig/twig": "1.18.*",
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
"Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"hash": "c523fe48318d98a520d2bc45286106e2",
|
||||
"hash": "ba1a97bf2c0bcef4fb771231a4c1fbdb",
|
||||
"packages": [
|
||||
{
|
||||
"name": "behat/mink",
|
||||
|
@ -461,7 +461,7 @@
|
|||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://github.com/doctrine/inflector/archive/v1.0.zip",
|
||||
"url": "https://api.github.com/repos/doctrine/inflector/zipball/54b8333d2a5682afdc690060c1cf384ba9f47f08",
|
||||
"reference": "v1.0",
|
||||
"shasum": ""
|
||||
},
|
||||
|
@ -578,7 +578,7 @@
|
|||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://github.com/doctrine/lexer/archive/v1.0.zip",
|
||||
"url": "https://api.github.com/repos/doctrine/lexer/zipball/2f708a85bb3aab5d99dab8be435abd73e0b18acb",
|
||||
"reference": "v1.0",
|
||||
"shasum": ""
|
||||
},
|
||||
|
@ -1530,12 +1530,12 @@
|
|||
"version": "1.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/php-fig/log",
|
||||
"url": "https://github.com/php-fig/log.git",
|
||||
"reference": "1.0.0"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://github.com/php-fig/log/archive/1.0.0.zip",
|
||||
"url": "https://api.github.com/repos/php-fig/log/zipball/fe0936ee26643249e916849d48e3a51d5f5e278b",
|
||||
"reference": "1.0.0",
|
||||
"shasum": ""
|
||||
},
|
||||
|
@ -2891,17 +2891,17 @@
|
|||
},
|
||||
{
|
||||
"name": "symfony/validator",
|
||||
"version": "v2.6.6",
|
||||
"version": "2.6.x-dev",
|
||||
"target-dir": "Symfony/Component/Validator",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/Validator.git",
|
||||
"reference": "85d9b42fe71bf88e7a1e5dec2094605dc9fbff28"
|
||||
"reference": "6bb1b474d25cb80617d8da6cb14c955ba914e495"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/Validator/zipball/85d9b42fe71bf88e7a1e5dec2094605dc9fbff28",
|
||||
"reference": "85d9b42fe71bf88e7a1e5dec2094605dc9fbff28",
|
||||
"url": "https://api.github.com/repos/symfony/Validator/zipball/6bb1b474d25cb80617d8da6cb14c955ba914e495",
|
||||
"reference": "6bb1b474d25cb80617d8da6cb14c955ba914e495",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -2948,18 +2948,18 @@
|
|||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "http://symfony.com/contributors"
|
||||
},
|
||||
{
|
||||
"name": "Fabien Potencier",
|
||||
"email": "fabien@symfony.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"description": "Symfony Validator Component",
|
||||
"homepage": "http://symfony.com",
|
||||
"time": "2015-03-30 15:54:10"
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2015-05-05 01:29:27"
|
||||
},
|
||||
{
|
||||
"name": "symfony/yaml",
|
||||
|
@ -3221,7 +3221,9 @@
|
|||
"packages-dev": [],
|
||||
"aliases": [],
|
||||
"minimum-stability": "stable",
|
||||
"stability-flags": [],
|
||||
"stability-flags": {
|
||||
"symfony/validator": 20
|
||||
},
|
||||
"prefer-stable": false,
|
||||
"prefer-lowest": false,
|
||||
"platform": {
|
||||
|
|
|
@ -13,11 +13,11 @@ use Drupal\Core\Cache\CacheBackendInterface;
|
|||
use Drupal\Core\DependencyInjection\ClassResolverInterface;
|
||||
use Drupal\Core\Extension\ModuleHandlerInterface;
|
||||
use Drupal\Core\Plugin\DefaultPluginManager;
|
||||
use Drupal\Core\TypedData\Validation\MetadataFactory;
|
||||
use Drupal\Core\TypedData\Validation\ExecutionContextFactory;
|
||||
use Drupal\Core\TypedData\Validation\RecursiveValidator;
|
||||
use Drupal\Core\Validation\ConstraintManager;
|
||||
use Drupal\Core\Validation\ConstraintValidatorFactory;
|
||||
use Drupal\Core\Validation\DrupalTranslator;
|
||||
use Symfony\Component\Validator\Validation;
|
||||
use Symfony\Component\Validator\Validator\ValidatorInterface;
|
||||
|
||||
/**
|
||||
|
@ -28,7 +28,7 @@ class TypedDataManager extends DefaultPluginManager {
|
|||
/**
|
||||
* The validator used for validating typed data.
|
||||
*
|
||||
* @var \Symfony\Component\Validator\ValidatorInterface
|
||||
* @var \Symfony\Component\Validator\Validator\ValidatorInterface
|
||||
*/
|
||||
protected $validator;
|
||||
|
||||
|
@ -331,12 +331,11 @@ class TypedDataManager extends DefaultPluginManager {
|
|||
*/
|
||||
public function getValidator() {
|
||||
if (!isset($this->validator)) {
|
||||
$this->validator = Validation::createValidatorBuilder()
|
||||
->setMetadataFactory(new MetadataFactory($this))
|
||||
->setTranslator(new DrupalTranslator())
|
||||
->setConstraintValidatorFactory(new ConstraintValidatorFactory($this->classResolver))
|
||||
->setApiVersion(Validation::API_VERSION_2_4)
|
||||
->getValidator();
|
||||
$this->validator = new RecursiveValidator(
|
||||
new ExecutionContextFactory(new DrupalTranslator()),
|
||||
new ConstraintValidatorFactory($this->classResolver),
|
||||
$this
|
||||
);
|
||||
}
|
||||
return $this->validator;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,254 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Core\TypedData\Validation;
|
||||
|
||||
use Symfony\Component\Translation\TranslatorInterface;
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
use Symfony\Component\Validator\ConstraintViolation;
|
||||
use Symfony\Component\Validator\ConstraintViolationList;
|
||||
use Symfony\Component\Validator\Util\PropertyPath;
|
||||
use Symfony\Component\Validator\Violation\ConstraintViolationBuilderInterface;
|
||||
|
||||
/**
|
||||
* Defines a constraint violation builder for the Typed Data validator.
|
||||
*
|
||||
* We do not use the builder provided by Symfony as it is marked internal.
|
||||
*
|
||||
* @codingStandardsIgnoreStart
|
||||
*/
|
||||
class ConstraintViolationBuilder implements ConstraintViolationBuilderInterface {
|
||||
|
||||
/**
|
||||
* The list of violations.
|
||||
*
|
||||
* @var \Symfony\Component\Validator\ConstraintViolationList
|
||||
*/
|
||||
protected $violations;
|
||||
|
||||
/**
|
||||
* The violation message.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $message;
|
||||
|
||||
/**
|
||||
* The message parameters.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $parameters;
|
||||
|
||||
/**
|
||||
* The root path.
|
||||
*
|
||||
* @var mixed
|
||||
*/
|
||||
protected $root;
|
||||
|
||||
/**
|
||||
* The invalid value caused the violation.
|
||||
*
|
||||
* @var mixed
|
||||
*/
|
||||
protected $invalidValue;
|
||||
|
||||
/**
|
||||
* The property path.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $propertyPath;
|
||||
|
||||
/**
|
||||
* The translator.
|
||||
*
|
||||
* @var \Symfony\Component\Translation\TranslatorInterface
|
||||
*/
|
||||
protected $translator;
|
||||
|
||||
/**
|
||||
* The translation domain.
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
protected $translationDomain;
|
||||
|
||||
/**
|
||||
* The number used
|
||||
* @var int|null
|
||||
*/
|
||||
protected $plural;
|
||||
|
||||
/**
|
||||
* @var Constraint
|
||||
*/
|
||||
protected $constraint;
|
||||
|
||||
/**
|
||||
* @var mixed
|
||||
*/
|
||||
protected $code;
|
||||
|
||||
/**
|
||||
* @var mixed
|
||||
*/
|
||||
protected $cause;
|
||||
|
||||
/**
|
||||
* Constructs a new ConstraintViolationBuilder instance.
|
||||
*
|
||||
* @param \Symfony\Component\Validator\ConstraintViolationList $violations
|
||||
* The violation list.
|
||||
* @param \Symfony\Component\Validator\Constraint $constraint
|
||||
* The constraint.
|
||||
* @param string $message
|
||||
* The message.
|
||||
* @param array $parameters
|
||||
* The message parameters.
|
||||
* @param mixed $root
|
||||
* The root.
|
||||
* @param string $propertyPath
|
||||
* The property string.
|
||||
* @param mixed $invalidValue
|
||||
* The invalid value.
|
||||
* @param \Symfony\Component\Translation\TranslatorInterface $translator
|
||||
* The translator.
|
||||
* @param null $translationDomain
|
||||
* (optional) The translation domain.
|
||||
*/
|
||||
public function __construct(ConstraintViolationList $violations, Constraint $constraint, $message, array $parameters, $root, $propertyPath, $invalidValue, TranslatorInterface $translator, $translationDomain = null)
|
||||
{
|
||||
$this->violations = $violations;
|
||||
$this->message = $message;
|
||||
$this->parameters = $parameters;
|
||||
$this->root = $root;
|
||||
$this->propertyPath = $propertyPath;
|
||||
$this->invalidValue = $invalidValue;
|
||||
$this->translator = $translator;
|
||||
$this->translationDomain = $translationDomain;
|
||||
$this->constraint = $constraint;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function atPath($path)
|
||||
{
|
||||
$this->propertyPath = PropertyPath::append($this->propertyPath, $path);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setParameter($key, $value)
|
||||
{
|
||||
$this->parameters[$key] = $value;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setParameters(array $parameters)
|
||||
{
|
||||
$this->parameters = $parameters;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setTranslationDomain($translationDomain)
|
||||
{
|
||||
$this->translationDomain = $translationDomain;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setInvalidValue($invalidValue)
|
||||
{
|
||||
$this->invalidValue = $invalidValue;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setPlural($number)
|
||||
{
|
||||
$this->plural = $number;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setCode($code)
|
||||
{
|
||||
$this->code = $code;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setCause($cause)
|
||||
{
|
||||
$this->cause = $cause;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addViolation()
|
||||
{
|
||||
if (null === $this->plural) {
|
||||
$translatedMessage = $this->translator->trans(
|
||||
$this->message,
|
||||
$this->parameters,
|
||||
$this->translationDomain
|
||||
);
|
||||
} else {
|
||||
try {
|
||||
$translatedMessage = $this->translator->transChoice(
|
||||
$this->message,
|
||||
$this->plural,
|
||||
$this->parameters,
|
||||
$this->translationDomain#
|
||||
);
|
||||
} catch (\InvalidArgumentException $e) {
|
||||
$translatedMessage = $this->translator->trans(
|
||||
$this->message,
|
||||
$this->parameters,
|
||||
$this->translationDomain
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$this->violations->add(new ConstraintViolation(
|
||||
$translatedMessage,
|
||||
$this->message,
|
||||
$this->parameters,
|
||||
$this->root,
|
||||
$this->propertyPath,
|
||||
$this->invalidValue,
|
||||
$this->plural,
|
||||
$this->code,
|
||||
$this->constraint,
|
||||
$this->cause
|
||||
));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\TypedData\Validation\RecursiveContextualValidatorInterface.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\TypedData\Validation;
|
||||
|
||||
use Symfony\Component\Validator\Validator\ContextualValidatorInterface as ContextualValidatorInterfaceBase;
|
||||
|
||||
/**
|
||||
* Extends the contextual validator validate method by a new parameter.
|
||||
*/
|
||||
interface ContextualValidatorInterface extends ContextualValidatorInterfaceBase {
|
||||
|
||||
/**
|
||||
* Validates a value against a constraint or a list of constraints.
|
||||
*
|
||||
* If no constraint is passed, the constraint
|
||||
* \Symfony\Component\Validator\Constraints\Valid is assumed.
|
||||
*
|
||||
* @param mixed $value
|
||||
* The value to validate
|
||||
* @param \Symfony\Component\Validator\Constraint|\Symfony\Component\Validator\Constraint[] $constraints
|
||||
* The constraint(s) to validate against.
|
||||
* @param array|null $groups
|
||||
* The validation groups to validate, defaults to "Default".
|
||||
* @param bool $is_root_call
|
||||
* (optional) Whether its the most upper call in the typed data tree.
|
||||
*
|
||||
* @see \Symfony\Component\Validator\Constraints\Valid
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function validate($value, $constraints = NULL, $groups = NULL, $is_root_call = TRUE);
|
||||
|
||||
}
|
|
@ -0,0 +1,320 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\TypedData\Validation\ExecutionContext.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\TypedData\Validation;
|
||||
|
||||
use Symfony\Component\Translation\TranslatorInterface;
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
use Symfony\Component\Validator\ConstraintViolation;
|
||||
use Symfony\Component\Validator\ConstraintViolationList;
|
||||
use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
||||
use Symfony\Component\Validator\Mapping\MetadataInterface;
|
||||
use Symfony\Component\Validator\Util\PropertyPath;
|
||||
use Symfony\Component\Validator\Validator\ValidatorInterface;
|
||||
|
||||
/**
|
||||
* Defines an execution context class.
|
||||
*
|
||||
* We do not use the context provided by Symfony as it is marked internal, so
|
||||
* this class is pretty much the same, but has some code style changes as well
|
||||
* as exceptions for methods we don't support.
|
||||
*/
|
||||
class ExecutionContext implements ExecutionContextInterface {
|
||||
|
||||
/**
|
||||
* @var \Symfony\Component\Validator\ValidatorInterface
|
||||
*/
|
||||
protected $validator;
|
||||
|
||||
/**
|
||||
* The root value of the validated object graph.
|
||||
*
|
||||
* @var mixed
|
||||
*/
|
||||
protected $root;
|
||||
|
||||
/**
|
||||
* @var \Symfony\Component\Translation\TranslatorInterface
|
||||
*/
|
||||
protected $translator;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $translationDomain;
|
||||
|
||||
/**
|
||||
* The violations generated in the current context.
|
||||
*
|
||||
* @var \Symfony\Component\Validator\ConstraintViolationList
|
||||
*/
|
||||
protected $violations;
|
||||
|
||||
/**
|
||||
* The currently validated value.
|
||||
*
|
||||
* @var mixed
|
||||
*/
|
||||
protected $value;
|
||||
|
||||
/**
|
||||
* The currently validated typed data object.
|
||||
*
|
||||
* @var \Drupal\Core\TypedData\TypedDataInterface
|
||||
*/
|
||||
protected $data;
|
||||
|
||||
/**
|
||||
* The property path leading to the current value.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $propertyPath = '';
|
||||
|
||||
/**
|
||||
* The current validation metadata.
|
||||
*
|
||||
* @var \Symfony\Component\Validator\Mapping\MetadataInterface|null
|
||||
*/
|
||||
protected $metadata;
|
||||
|
||||
/**
|
||||
* The currently validated group.
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
protected $group;
|
||||
|
||||
/**
|
||||
* The currently validated constraint.
|
||||
*
|
||||
* @var \Symfony\Component\Validator\Constraint|null
|
||||
*/
|
||||
protected $constraint;
|
||||
|
||||
/**
|
||||
* Stores which objects have been validated in which group.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $validatedObjects = array();
|
||||
|
||||
/**
|
||||
* Stores which class constraint has been validated for which object.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $validatedConstraints = array();
|
||||
|
||||
/**
|
||||
* Creates a new ExecutionContext.
|
||||
*
|
||||
* @param \Symfony\Component\Validator\Validator\ValidatorInterface $validator
|
||||
* The validator.
|
||||
* @param mixed $root
|
||||
* The root.
|
||||
* @param \Symfony\Component\Translation\TranslatorInterface $translator
|
||||
* The translator.
|
||||
* @param string $translationDomain
|
||||
* (optional) The translation domain.
|
||||
*
|
||||
* @internal Called by \Drupal\Core\TypedData\Validation\ExecutionContextFactory.
|
||||
* Should not be used in user code.
|
||||
*/
|
||||
public function __construct(ValidatorInterface $validator, $root, TranslatorInterface $translator, $translationDomain = NULL) {
|
||||
$this->validator = $validator;
|
||||
$this->root = $root;
|
||||
$this->translator = $translator;
|
||||
$this->translationDomain = $translationDomain;
|
||||
$this->violations = new ConstraintViolationList();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setNode($value, $object, MetadataInterface $metadata = NULL, $propertyPath) {
|
||||
$this->value = $value;
|
||||
$this->data = $object;
|
||||
$this->metadata = $metadata;
|
||||
$this->propertyPath = (string) $propertyPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setGroup($group) {
|
||||
$this->group = $group;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setConstraint(Constraint $constraint) {
|
||||
$this->constraint = $constraint;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addViolation($message, array $parameters = array(), $invalidValue = NULL, $plural = NULL, $code = NULL) {
|
||||
// The parameters $invalidValue and following are ignored by the new
|
||||
// API, as they are not present in the new interface anymore.
|
||||
// You should use buildViolation() instead.
|
||||
if (func_num_args() > 2) {
|
||||
throw new \LogicException('Legacy validator API is unsupported.');
|
||||
}
|
||||
|
||||
$this->violations->add(new ConstraintViolation($this->translator->trans($message, $parameters, $this->translationDomain), $message, $parameters, $this->root, $this->propertyPath, $this->value, NULL, NULL, $this->constraint));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildViolation($message, array $parameters = array()) {
|
||||
return new ConstraintViolationBuilder($this->violations, $this->constraint, $message, $parameters, $this->root, $this->propertyPath, $this->value, $this->translator, $this->translationDomain);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getViolations() {
|
||||
return $this->violations;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getValidator() {
|
||||
return $this->validator;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getRoot() {
|
||||
return $this->root;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getValue() {
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getObject() {
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getMetadata() {
|
||||
return $this->metadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getGroup() {
|
||||
return Constraint::DEFAULT_GROUP;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getClassName() {
|
||||
return get_class($this->data);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getPropertyName() {
|
||||
return $this->data->getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getPropertyPath($sub_path = '') {
|
||||
return PropertyPath::append($this->propertyPath, $sub_path);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addViolationAt($subPath, $message, array $parameters = array(), $invalidValue = NULL, $plural = NULL, $code = NULL) {
|
||||
throw new \LogicException('Legacy validator API is unsupported.');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validate($value, $subPath = '', $groups = NULL, $traverse = FALSE, $deep = FALSE) {
|
||||
throw new \LogicException('Legacy validator API is unsupported.');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function markConstraintAsValidated($cache_key, $constraint_hash) {
|
||||
$this->validatedConstraints[$cache_key . ':' . $constraint_hash] = TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isConstraintValidated($cache_key, $constraint_hash) {
|
||||
return isset($this->validatedConstraints[$cache_key . ':' . $constraint_hash]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validateValue($value, $constraints, $subPath = '', $groups = NULL) {
|
||||
throw new \LogicException('Legacy validator API is unsupported.');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function markGroupAsValidated($cache_key, $group_hash) {
|
||||
$this->validatedObjects[$cache_key][$group_hash] = TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isGroupValidated($cache_key, $group_hash) {
|
||||
return isset($this->validatedObjects[$cache_key][$group_hash]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function markObjectAsInitialized($cache_key) {
|
||||
// Not supported, so nothing todo.
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isObjectInitialized($cache_key) {
|
||||
// Not supported, so nothing todo.
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getMetadataFactory() {
|
||||
throw new \LogicException('Legacy validator API is unsupported.');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\TypedData\Validation\ExecutionContextFactory.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\TypedData\Validation;
|
||||
|
||||
use Symfony\Component\Translation\TranslatorInterface;
|
||||
use Symfony\Component\Validator\Context\ExecutionContextFactoryInterface;
|
||||
use Symfony\Component\Validator\Validator\ValidatorInterface;
|
||||
|
||||
/**
|
||||
* Defines an execution factory for the Typed Data validator.
|
||||
*
|
||||
* We do not use the factory provided by Symfony as it is marked internal.
|
||||
*
|
||||
* @codingStandardsIgnoreStart
|
||||
*/
|
||||
class ExecutionContextFactory implements ExecutionContextFactoryInterface {
|
||||
|
||||
/**
|
||||
* @var TranslatorInterface
|
||||
*/
|
||||
protected $translator;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
protected $translationDomain;
|
||||
|
||||
/**
|
||||
* Constructs a new ExecutionContextFactory instance.
|
||||
*
|
||||
* @param \Symfony\Component\Translation\TranslatorInterface $translator
|
||||
* The translator instance.
|
||||
* @param string $translationDomain
|
||||
* (optional) The translation domain.
|
||||
*/
|
||||
public function __construct(TranslatorInterface $translator, $translationDomain = null)
|
||||
{
|
||||
$this->translator = $translator;
|
||||
$this->translationDomain = $translationDomain;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function createContext(ValidatorInterface $validator, $root)
|
||||
{
|
||||
return new ExecutionContext(
|
||||
$validator,
|
||||
$root,
|
||||
$this->translator,
|
||||
$this->translationDomain
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,115 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\TypedData\Validation\Metadata.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\TypedData\Validation;
|
||||
|
||||
use Drupal\Core\TypedData\TypedDataInterface;
|
||||
use Drupal\Core\TypedData\TypedDataManager;
|
||||
use Symfony\Component\Validator\ValidationVisitorInterface;
|
||||
use Symfony\Component\Validator\PropertyMetadataInterface;
|
||||
|
||||
/**
|
||||
* Typed data implementation of the validator MetadataInterface.
|
||||
*/
|
||||
class Metadata implements PropertyMetadataInterface {
|
||||
|
||||
/**
|
||||
* The name of the property, or empty if this is the root.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name;
|
||||
|
||||
/**
|
||||
* The typed data object the metadata is about.
|
||||
*
|
||||
* @var \Drupal\Core\TypedData\TypedDataInterface
|
||||
*/
|
||||
protected $typedData;
|
||||
|
||||
/**
|
||||
* The metadata factory used.
|
||||
*
|
||||
* @var \Drupal\Core\TypedData\Validation\MetadataFactory
|
||||
*/
|
||||
protected $factory;
|
||||
|
||||
/**
|
||||
* The typed data manager.
|
||||
*
|
||||
* @var \Drupal\Core\TypedData\TypedDataManager
|
||||
*/
|
||||
protected $typedDataManager;
|
||||
|
||||
/**
|
||||
* Constructs the object.
|
||||
*
|
||||
* @param \Drupal\Core\TypedData\TypedDataInterface $typed_data
|
||||
* The typed data object the metadata is about.
|
||||
* @param $name
|
||||
* The name of the property to get metadata for. Leave empty, if
|
||||
* the data is the root of the typed data tree.
|
||||
* @param \Drupal\Core\TypedData\Validation\MetadataFactory $factory
|
||||
* The factory to use for instantiating property metadata.
|
||||
* @param \Drupal\Core\TypedData\TypedDataManager $typed_data_manager
|
||||
* The typed data manager.
|
||||
*/
|
||||
public function __construct(TypedDataInterface $typed_data, $name = '', MetadataFactory $factory, TypedDataManager $typed_data_manager) {
|
||||
$this->typedData = $typed_data;
|
||||
$this->name = $name;
|
||||
$this->factory = $factory;
|
||||
$this->typedDataManager = $typed_data_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements MetadataInterface::accept().
|
||||
*/
|
||||
public function accept(ValidationVisitorInterface $visitor, $typed_data, $group, $propertyPath) {
|
||||
|
||||
// @todo: Do we have to care about groups? Symfony class metadata has
|
||||
// $propagatedGroup.
|
||||
|
||||
$visitor->visit($this, $this->typedDataManager->getCanonicalRepresentation($typed_data), $group, $propertyPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements MetadataInterface::findConstraints().
|
||||
*/
|
||||
public function findConstraints($group) {
|
||||
return $this->typedData->getConstraints();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the property.
|
||||
*
|
||||
* @return string The property name.
|
||||
*/
|
||||
public function getPropertyName() {
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the value of the property from the given container.
|
||||
*
|
||||
* @param mixed $container The container to extract the property value from.
|
||||
*
|
||||
* @return mixed The value of the property.
|
||||
*/
|
||||
public function getPropertyValue($container) {
|
||||
return $this->typedDataManager->getCanonicalRepresentation($this->typedData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the typed data object.
|
||||
*
|
||||
* @return \Drupal\Core\TypedData\TypedDataInterface
|
||||
* The typed data object.
|
||||
*/
|
||||
public function getTypedData() {
|
||||
return $this->typedData;
|
||||
}
|
||||
}
|
|
@ -1,62 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\TypedData\Validation\MetadataFactory.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\TypedData\Validation;
|
||||
|
||||
use Drupal\Core\TypedData\ComplexDataInterface;
|
||||
use Drupal\Core\TypedData\ListInterface;
|
||||
use Drupal\Core\TypedData\TypedDataInterface;
|
||||
use Drupal\Core\TypedData\TypedDataManager;
|
||||
use Symfony\Component\Validator\MetadataFactoryInterface;
|
||||
|
||||
/**
|
||||
* Typed data implementation of the validator MetadataFactoryInterface.
|
||||
*/
|
||||
class MetadataFactory implements MetadataFactoryInterface {
|
||||
|
||||
/**
|
||||
* The typed data manager.
|
||||
*
|
||||
* @var \Drupal\Core\TypedData\TypedDataManager
|
||||
*/
|
||||
protected $typedDataManager;
|
||||
|
||||
/**
|
||||
* Constructs the object.
|
||||
*
|
||||
* @param \Drupal\Core\TypedData\TypedDataManager $typed_data_manager
|
||||
* The typed data manager.
|
||||
*/
|
||||
public function __construct(TypedDataManager $typed_data_manager) {
|
||||
$this->typedDataManager = $typed_data_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @param \Drupal\Core\TypedData\TypedDataInterface $typed_data
|
||||
* Some typed data object containing the value to validate.
|
||||
* @param $name
|
||||
* (optional) The name of the property to get metadata for. Leave empty, if
|
||||
* the data is the root of the typed data tree.
|
||||
*/
|
||||
public function getMetadataFor($typed_data, $name = '') {
|
||||
if (!$typed_data instanceof TypedDataInterface) {
|
||||
throw new \InvalidArgumentException('The passed value must be a typed data object.');
|
||||
}
|
||||
$is_container = $typed_data instanceof ComplexDataInterface || $typed_data instanceof ListInterface;
|
||||
$class = '\Drupal\Core\TypedData\Validation\\' . ($is_container ? 'PropertyContainerMetadata' : 'Metadata');
|
||||
return new $class($typed_data, $name, $this, $this->typedDataManager);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements MetadataFactoryInterface::hasMetadataFor().
|
||||
*/
|
||||
public function hasMetadataFor($value) {
|
||||
return $value instanceof TypedDataInterface;
|
||||
}
|
||||
}
|
|
@ -1,72 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\TypedData\Validation\PropertyContainerMetadata.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\TypedData\Validation;
|
||||
|
||||
use Drupal\Core\TypedData\ComplexDataInterface;
|
||||
use Drupal\Core\TypedData\ListInterface;
|
||||
use Symfony\Component\Validator\PropertyMetadataContainerInterface;
|
||||
use Symfony\Component\Validator\ValidationVisitorInterface;
|
||||
|
||||
/**
|
||||
* Typed data implementation of the validator MetadataInterface.
|
||||
*/
|
||||
class PropertyContainerMetadata extends Metadata implements PropertyMetadataContainerInterface {
|
||||
|
||||
/**
|
||||
* Overrides Metadata::accept().
|
||||
*/
|
||||
public function accept(ValidationVisitorInterface $visitor, $typed_data, $group, $propertyPath) {
|
||||
// To let all constraints properly handle empty structures, pass on NULL
|
||||
// if the data structure is empty. That way existing NotNull or NotBlank
|
||||
// constraints work as expected.
|
||||
if ($typed_data->isEmpty()) {
|
||||
$data = NULL;
|
||||
}
|
||||
else {
|
||||
$data = $this->typedDataManager->getCanonicalRepresentation($typed_data);
|
||||
}
|
||||
$visitor->visit($this, $data, $group, $propertyPath);
|
||||
$pathPrefix = isset($propertyPath) && $propertyPath !== '' ? $propertyPath . '.' : '';
|
||||
|
||||
// Only continue validating if the data is not empty.
|
||||
if ($data) {
|
||||
foreach ($typed_data as $name => $data) {
|
||||
$metadata = $this->factory->getMetadataFor($data, $name);
|
||||
$metadata->accept($visitor, $data, $group, $pathPrefix . $name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements PropertyMetadataContainerInterface::hasPropertyMetadata().
|
||||
*/
|
||||
public function hasPropertyMetadata($property_name) {
|
||||
try {
|
||||
$exists = (bool)$this->getPropertyMetadata($property_name);
|
||||
}
|
||||
catch (\LogicException $e) {
|
||||
$exists = FALSE;
|
||||
}
|
||||
return $exists;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements PropertyMetadataContainerInterface::getPropertyMetadata().
|
||||
*/
|
||||
public function getPropertyMetadata($property_name) {
|
||||
if ($this->typedData instanceof ListInterface) {
|
||||
return array(new Metadata($this->typedData[$property_name], $property_name, $this->factory, $this->typedDataManager));
|
||||
}
|
||||
elseif ($this->typedData instanceof ComplexDataInterface) {
|
||||
return array(new Metadata($this->typedData->get($property_name), $property_name, $this->factory, $this->typedDataManager));
|
||||
}
|
||||
else {
|
||||
throw new \LogicException("There are no known properties.");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,239 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\TypedData\Validation\PropertyContainerPropertyMetadata.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\TypedData\Validation;
|
||||
|
||||
use Drupal\Core\TypedData\ComplexDataInterface;
|
||||
use Drupal\Core\TypedData\ListInterface;
|
||||
use Drupal\Core\TypedData\TypedDataInterface;
|
||||
use Drupal\Core\TypedData\TypedDataManager;
|
||||
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;
|
||||
|
||||
/**
|
||||
* Defines a recursive contextual validator for Typed Data.
|
||||
*
|
||||
* For both list and complex data it call recursively out to the properties /
|
||||
* elements of the list.
|
||||
*
|
||||
* This class calls out to some methods on the execution context marked as
|
||||
* internal. These methods are internal to the validator (which is implemented
|
||||
* by this class) but should not be called by users.
|
||||
* See http://symfony.com/doc/current/contributing/code/bc.html for more
|
||||
* information about @internal.
|
||||
*
|
||||
* @see \Drupal\Core\TypedData\Validation\RecursiveValidator::startContext()
|
||||
* @see \Drupal\Core\TypedData\Validation\RecursiveValidator::inContext()
|
||||
*/
|
||||
class RecursiveContextualValidator implements ContextualValidatorInterface {
|
||||
|
||||
/**
|
||||
* The execution context.
|
||||
*
|
||||
* @var \Symfony\Component\Validator\Context\ExecutionContextInterface
|
||||
*/
|
||||
protected $context;
|
||||
|
||||
/**
|
||||
* The metadata factory.
|
||||
*
|
||||
* @var \Symfony\Component\Validator\Mapping\Factory\MetadataFactoryInterface
|
||||
*/
|
||||
protected $metadataFactory;
|
||||
|
||||
/**
|
||||
* The constraint validator factory.
|
||||
*
|
||||
* @var \Symfony\Component\Validator\ConstraintValidatorFactoryInterface
|
||||
*/
|
||||
protected $constraintValidatorFactory;
|
||||
|
||||
/**
|
||||
* Creates a validator for the given context.
|
||||
*
|
||||
* @param \Symfony\Component\Validator\Context\ExecutionContextInterface $context
|
||||
* The factory for creating new contexts.
|
||||
* @param \Symfony\Component\Validator\Mapping\Factory\MetadataFactoryInterface $metadata_factory
|
||||
* The metadata factory.
|
||||
* @param \Symfony\Component\Validator\ConstraintValidatorFactoryInterface $validator_factory
|
||||
* The constraint validator factory.
|
||||
* @param \Drupal\Core\TypedData\TypedDataManager $typed_data_manager
|
||||
* The typed data manager.
|
||||
*/
|
||||
public function __construct(ExecutionContextInterface $context, MetadataFactoryInterface $metadata_factory, ConstraintValidatorFactoryInterface $validator_factory, TypedDataManager $typed_data_manager) {
|
||||
$this->context = $context;
|
||||
$this->metadataFactory = $metadata_factory;
|
||||
$this->constraintValidatorFactory = $validator_factory;
|
||||
$this->typedDataManager = $typed_data_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function atPath($path) {
|
||||
// @todo This method is not used at the moment, see
|
||||
// https://www.drupal.org/node/2482527
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
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.');
|
||||
}
|
||||
|
||||
// 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 (isset($constraints) && !is_array($constraints)) {
|
||||
$constraints = array($constraints);
|
||||
}
|
||||
|
||||
$this->validateNode($data, $constraints, $is_root_call);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates a node's value against all constraints in the given group.
|
||||
*
|
||||
* @param mixed $value
|
||||
* The validated value.
|
||||
* @param string $cache_key
|
||||
* The cache key used internally to ensure we don't validate the same
|
||||
* constraint twice.
|
||||
* @param \Symfony\Component\Validator\Constraint[] $constraints
|
||||
* The constraints which should be ensured for the given value.
|
||||
*/
|
||||
protected function validateConstraints($value, $cache_key, $constraints) {
|
||||
foreach ($constraints as $constraint) {
|
||||
// Prevent duplicate validation of constraints, in the case
|
||||
// that constraints belong to multiple validated groups
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getViolations() {
|
||||
return $this->context->getViolations();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,121 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\TypedData\Validation\RecursiveValidator.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\TypedData\Validation;
|
||||
|
||||
use Drupal\Core\TypedData\TypedDataInterface;
|
||||
use Drupal\Core\TypedData\TypedDataManager;
|
||||
use Symfony\Component\Validator\ConstraintValidatorFactoryInterface;
|
||||
use Symfony\Component\Validator\Context\ExecutionContextFactoryInterface;
|
||||
use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
||||
use Symfony\Component\Validator\Validator\ValidatorInterface;
|
||||
|
||||
/**
|
||||
* Defines a recursive validator for Typed Data.
|
||||
*
|
||||
* The difference to \Symfony\Component\Validator\Validator\RecursiveValidator
|
||||
* is that we just allow to validate typed data objects.
|
||||
*/
|
||||
class RecursiveValidator implements ValidatorInterface {
|
||||
|
||||
/**
|
||||
* @var \Symfony\Component\Validator\Context\ExecutionContextFactoryInterface
|
||||
*/
|
||||
protected $contextFactory;
|
||||
|
||||
/**
|
||||
* @var \Symfony\Component\Validator\ConstraintValidatorFactoryInterface
|
||||
*/
|
||||
protected $constraintValidatorFactory;
|
||||
|
||||
/**
|
||||
* @var \Drupal\Core\TypedData\TypedDataManager
|
||||
*/
|
||||
protected $typedDataManager;
|
||||
|
||||
/**
|
||||
* Creates a new validator.
|
||||
*
|
||||
* @param \Symfony\Component\Validator\Context\ExecutionContextFactoryInterface $context_factory
|
||||
* The factory for creating new contexts.
|
||||
* @param \Symfony\Component\Validator\ConstraintValidatorFactoryInterface $validator_factory
|
||||
* The constraint validator factory.
|
||||
* @param \Drupal\Core\TypedData\TypedDataManager $typed_data_manager
|
||||
* The typed data manager.
|
||||
*/
|
||||
public function __construct(ExecutionContextFactoryInterface $context_factory, ConstraintValidatorFactoryInterface $validator_factory, TypedDataManager $typed_data_manager) {
|
||||
$this->contextFactory = $context_factory;
|
||||
$this->constraintValidatorFactory = $validator_factory;
|
||||
$this->typedDataManager = $typed_data_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function startContext($root = NULL) {
|
||||
return new RecursiveContextualValidator($this->contextFactory->createContext($this, $root), $this, $this->constraintValidatorFactory, $this->typedDataManager);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function inContext(ExecutionContextInterface $context) {
|
||||
return new RecursiveContextualValidator($context, $this, $this->constraintValidatorFactory, $this->typedDataManager);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @param \Drupal\Core\TypedData\TypedDataInterface $typed_data
|
||||
* A typed data object containing the value to validate.
|
||||
*/
|
||||
public function getMetadataFor($typed_data) {
|
||||
if (!$typed_data instanceof TypedDataInterface) {
|
||||
throw new \InvalidArgumentException('The passed value must be a typed data object.');
|
||||
}
|
||||
return new TypedDataMetadata($typed_data);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function hasMetadataFor($value) {
|
||||
return $value instanceof TypedDataInterface;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validate($value, $constraints = null, $groups = null) {
|
||||
return $this->startContext($value)
|
||||
->validate($value, $constraints, $groups)
|
||||
->getViolations();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validateProperty($object, $propertyName, $groups = NULL) {
|
||||
return $this->startContext($object)
|
||||
->validateProperty($object, $propertyName, $groups)
|
||||
->getViolations();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validatePropertyValue($objectOrClass, $propertyName, $value, $groups = NULL) {
|
||||
// Just passing a class name is not supported.
|
||||
if (!is_object($objectOrClass)) {
|
||||
throw new \LogicException('Typed data validation does not support passing the class name only.');
|
||||
}
|
||||
return $this->startContext($objectOrClass)
|
||||
->validatePropertyValue($objectOrClass, $propertyName, $value, $groups)
|
||||
->getViolations();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\TypedData\Validation\TypedDataAwareValidatorTrait.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\TypedData\Validation;
|
||||
|
||||
use Drupal\Core\TypedData\TypedDataInterface;
|
||||
|
||||
/**
|
||||
* Defines a trait to access the typed data object of a validated value.
|
||||
*
|
||||
* The trait assumes to be used on classes extending
|
||||
* \Symfony\Component\Validator\ConstraintValidator.
|
||||
*/
|
||||
trait TypedDataAwareValidatorTrait {
|
||||
|
||||
/**
|
||||
* Gets the typed data object for the validated value.
|
||||
*
|
||||
* @return \Drupal\Core\TypedData\TypedDataInterface
|
||||
* The typed data object.
|
||||
*/
|
||||
public function getTypedData() {
|
||||
$context = $this->context;
|
||||
/** @var \Symfony\Component\Validator\Context\ExecutionContextInterface $context */
|
||||
$data = $context->getObject();
|
||||
if (!$data instanceof TypedDataInterface) {
|
||||
throw new \LogicException("There is no Typed Data object available.");
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\TypedData\Validation\MetadataBase.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\TypedData\Validation;
|
||||
|
||||
use Drupal\Core\TypedData\TypedDataInterface;
|
||||
use Symfony\Component\Validator\Exception\BadMethodCallException;
|
||||
use Symfony\Component\Validator\Mapping\CascadingStrategy;
|
||||
use Symfony\Component\Validator\Mapping\MetadataInterface;
|
||||
use Symfony\Component\Validator\Mapping\TraversalStrategy;
|
||||
use Symfony\Component\Validator\ValidationVisitorInterface;
|
||||
|
||||
/**
|
||||
* Validator metadata for typed data objects.
|
||||
*
|
||||
* @see \Drupal\Core\TypedData\Validation\RecursiveValidator::getMetadataFor()
|
||||
*/
|
||||
class TypedDataMetadata implements MetadataInterface {
|
||||
|
||||
/**
|
||||
* The typed data object the metadata is about.
|
||||
*
|
||||
* @var \Drupal\Core\TypedData\TypedDataInterface
|
||||
*/
|
||||
protected $typedData;
|
||||
|
||||
/**
|
||||
* Constructs the object.
|
||||
*
|
||||
* @param \Drupal\Core\TypedData\TypedDataInterface $typed_data
|
||||
* The typed data object the metadata is about.
|
||||
*/
|
||||
public function __construct(TypedDataInterface $typed_data) {
|
||||
$this->typedData = $typed_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function accept(ValidationVisitorInterface $visitor, $typed_data, $group, $propertyPath) {
|
||||
throw new BadMethodCallException('Not supported.');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function findConstraints($group) {
|
||||
return $this->getConstraints();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getConstraints() {
|
||||
return $this->typedData->getConstraints();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTraversalStrategy() {
|
||||
return TraversalStrategy::NONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCascadingStrategy() {
|
||||
// By default, never cascade into validating referenced data structures.
|
||||
return CascadingStrategy::NONE;
|
||||
}
|
||||
|
||||
}
|
|
@ -79,14 +79,9 @@ class ConstraintManager extends DefaultPluginManager {
|
|||
* @see ConstraintManager::__construct()
|
||||
*/
|
||||
public function registerDefinitions() {
|
||||
$this->discovery->setDefinition('Null', array(
|
||||
'label' => new TranslationWrapper('Null'),
|
||||
'class' => '\Symfony\Component\Validator\Constraints\Null',
|
||||
'type' => FALSE,
|
||||
));
|
||||
$this->discovery->setDefinition('NotNull', array(
|
||||
'label' => new TranslationWrapper('Not null'),
|
||||
'class' => '\Symfony\Component\Validator\Constraints\NotNull',
|
||||
$this->discovery->setDefinition('Callback', array(
|
||||
'label' => new TranslationWrapper('Callback'),
|
||||
'class' => '\Symfony\Component\Validator\Constraints\Callback',
|
||||
'type' => FALSE,
|
||||
));
|
||||
$this->discovery->setDefinition('Blank', array(
|
||||
|
|
|
@ -7,25 +7,53 @@
|
|||
|
||||
namespace Drupal\Core\Validation\Plugin\Validation\Constraint;
|
||||
|
||||
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Drupal\Core\TypedData\OptionsProviderInterface;
|
||||
use Drupal\Core\TypedData\ComplexDataInterface;
|
||||
use Drupal\Core\TypedData\Validation\TypedDataAwareValidatorTrait;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
use Symfony\Component\Validator\Constraints\ChoiceValidator;
|
||||
|
||||
/**
|
||||
* Validates the AllowedValues constraint.
|
||||
*/
|
||||
class AllowedValuesConstraintValidator extends ChoiceValidator {
|
||||
class AllowedValuesConstraintValidator extends ChoiceValidator implements ContainerInjectionInterface {
|
||||
|
||||
use TypedDataAwareValidatorTrait;
|
||||
|
||||
/**
|
||||
* The current user.
|
||||
*
|
||||
* @var \Drupal\Core\Session\AccountInterface
|
||||
*/
|
||||
protected $currentUser;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static($container->get('current_user'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new AllowedValuesConstraintValidator.
|
||||
*
|
||||
* @param \Drupal\Core\Session\AccountInterface $current_user
|
||||
* The current user.
|
||||
*/
|
||||
public function __construct(AccountInterface $current_user) {
|
||||
$this->currentUser = $current_user;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validate($value, Constraint $constraint) {
|
||||
$typed_data = $this->context->getMetadata()->getTypedData();
|
||||
|
||||
$typed_data = $this->getTypedData();
|
||||
if ($typed_data instanceof OptionsProviderInterface) {
|
||||
$account = \Drupal::currentUser();
|
||||
$allowed_values = $typed_data->getSettableValues($account);
|
||||
$allowed_values = $typed_data->getSettableValues($this->currentUser);
|
||||
$constraint->choices = $allowed_values;
|
||||
|
||||
// If the data is complex, we have to validate its main property.
|
||||
|
|
|
@ -9,6 +9,7 @@ namespace Drupal\Core\Validation\Plugin\Validation\Constraint;
|
|||
|
||||
use Drupal\Core\TypedData\ComplexDataInterface;
|
||||
use Drupal\Core\TypedData\TypedDataInterface;
|
||||
use Drupal\Core\TypedData\Validation\TypedDataAwareValidatorTrait;
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
use Symfony\Component\Validator\ConstraintValidator;
|
||||
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
|
||||
|
@ -18,35 +19,28 @@ use Symfony\Component\Validator\Exception\UnexpectedTypeException;
|
|||
*/
|
||||
class ComplexDataConstraintValidator extends ConstraintValidator {
|
||||
|
||||
use TypedDataAwareValidatorTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validate($value, Constraint $constraint) {
|
||||
if (!isset($value)) {
|
||||
return;
|
||||
}
|
||||
public function validate($data, Constraint $constraint) {
|
||||
|
||||
// If un-wrapped data has been passed, fetch the typed data object first.
|
||||
if (!$value instanceof TypedDataInterface) {
|
||||
$value = $this->context->getMetadata()->getTypedData();
|
||||
if (!$data instanceof TypedDataInterface) {
|
||||
$data = $this->getTypedData();
|
||||
}
|
||||
if (!$value instanceof ComplexDataInterface) {
|
||||
throw new UnexpectedTypeException($value, 'ComplexData');
|
||||
if (!$data instanceof ComplexDataInterface) {
|
||||
throw new UnexpectedTypeException($data, 'ComplexData');
|
||||
}
|
||||
|
||||
$group = $this->context->getGroup();
|
||||
|
||||
foreach ($constraint->properties as $name => $constraints) {
|
||||
$property = $value->get($name);
|
||||
$is_container = $property instanceof ComplexDataInterface || $property instanceof ListInterface;
|
||||
if (!$is_container) {
|
||||
$property = $property->getValue();
|
||||
}
|
||||
elseif ($property->isEmpty()) {
|
||||
// @see \Drupal\Core\TypedData\Validation\PropertyContainerMetadata::accept();
|
||||
$property = NULL;
|
||||
}
|
||||
$this->context->validateValue($property, $constraints, $name, $group);
|
||||
$this->context->getValidator()
|
||||
->inContext($this->context)
|
||||
// Specifically pass along FALSE as $root_call, as we validate the data
|
||||
// as part of the typed data tree.
|
||||
->validate($data->get($name), $constraints, NULL, FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Validation\Plugin\Validation\Constraint\NotNullConstraint.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Validation\Plugin\Validation\Constraint;
|
||||
|
||||
use Symfony\Component\Validator\Constraints\NotNull;
|
||||
|
||||
/**
|
||||
* NotNull constraint.
|
||||
*
|
||||
* Overrides the symfony constraint to handle empty Typed Data structures.
|
||||
*
|
||||
* @Plugin(
|
||||
* id = "NotNull",
|
||||
* label = @Translation("NotNull", context = "Validation"),
|
||||
* type = false
|
||||
* )
|
||||
*/
|
||||
class NotNullConstraint extends NotNull { }
|
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Validation\Plugin\Validation\Constraint\NotNullConstraintValidator.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Validation\Plugin\Validation\Constraint;
|
||||
|
||||
use Drupal\Core\TypedData\ComplexDataInterface;
|
||||
use Drupal\Core\TypedData\ListInterface;
|
||||
use Drupal\Core\TypedData\Validation\TypedDataAwareValidatorTrait;
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
use Symfony\Component\Validator\Constraints\NotNullValidator;
|
||||
|
||||
/**
|
||||
* NotNull constraint validator.
|
||||
*
|
||||
* Overrides the symfony validator to handle empty Typed Data structures.
|
||||
*/
|
||||
class NotNullConstraintValidator extends NotNullValidator {
|
||||
|
||||
use TypedDataAwareValidatorTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validate($value, Constraint $constraint) {
|
||||
$typed_data = $this->getTypedData();
|
||||
if (($typed_data instanceof ListInterface || $typed_data instanceof ComplexDataInterface) && $typed_data->isEmpty()) {
|
||||
$value = NULL;
|
||||
}
|
||||
parent::validate($value, $constraint);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Validation\Plugin\Validation\Constraint\NullConstraint.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Validation\Plugin\Validation\Constraint;
|
||||
|
||||
use Symfony\Component\Validator\Constraints\Null;
|
||||
|
||||
/**
|
||||
* Null constraint.
|
||||
*
|
||||
* Overrides the symfony constraint to handle empty Typed Data structures.
|
||||
*
|
||||
* @Plugin(
|
||||
* id = "Null",
|
||||
* label = @Translation("Null", context = "Validation"),
|
||||
* type = false
|
||||
* )
|
||||
*/
|
||||
class NullConstraint extends Null { }
|
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Validation\Plugin\Validation\Constraint\NullConstraintValidator.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Validation\Plugin\Validation\Constraint;
|
||||
|
||||
use Drupal\Core\TypedData\ComplexDataInterface;
|
||||
use Drupal\Core\TypedData\ListInterface;
|
||||
use Drupal\Core\TypedData\Validation\TypedDataAwareValidatorTrait;
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
use Symfony\Component\Validator\Constraints\NullValidator;
|
||||
|
||||
/**
|
||||
* Null constraint validator.
|
||||
*
|
||||
* Overrides the symfony validator to handle empty Typed Data structures.
|
||||
*/
|
||||
class NullConstraintValidator extends NullValidator {
|
||||
|
||||
use TypedDataAwareValidatorTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validate($value, Constraint $constraint) {
|
||||
$typed_data = $this->getTypedData();
|
||||
if (($typed_data instanceof ListInterface || $typed_data instanceof ComplexDataInterface) && $typed_data->isEmpty()) {
|
||||
$value = NULL;
|
||||
}
|
||||
parent::validate($value, $constraint);
|
||||
}
|
||||
|
||||
}
|
|
@ -15,6 +15,7 @@ use Drupal\Core\TypedData\Type\FloatInterface;
|
|||
use Drupal\Core\TypedData\Type\IntegerInterface;
|
||||
use Drupal\Core\TypedData\Type\StringInterface;
|
||||
use Drupal\Core\TypedData\Type\UriInterface;
|
||||
use Drupal\Core\TypedData\Validation\TypedDataAwareValidatorTrait;
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
use Symfony\Component\Validator\ConstraintValidator;
|
||||
|
||||
|
@ -23,6 +24,8 @@ use Symfony\Component\Validator\ConstraintValidator;
|
|||
*/
|
||||
class PrimitiveTypeConstraintValidator extends ConstraintValidator {
|
||||
|
||||
use TypedDataAwareValidatorTrait;
|
||||
|
||||
/**
|
||||
* Implements \Symfony\Component\Validator\ConstraintValidatorInterface::validate().
|
||||
*/
|
||||
|
@ -32,7 +35,7 @@ class PrimitiveTypeConstraintValidator extends ConstraintValidator {
|
|||
return;
|
||||
}
|
||||
|
||||
$typed_data = $this->context->getMetadata()->getTypedData();
|
||||
$typed_data = $this->getTypedData();
|
||||
$valid = TRUE;
|
||||
if ($typed_data instanceof BinaryInterface && !is_resource($value)) {
|
||||
$valid = FALSE;
|
||||
|
|
|
@ -19,7 +19,7 @@ class UniqueFieldValueValidator extends ConstraintValidator {
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
public function validate($items, Constraint $constraint) {
|
||||
if (!isset($items)) {
|
||||
if (!$item = $items->first()) {
|
||||
return;
|
||||
}
|
||||
$field_name = $items->getFieldDefinition()->getName();
|
||||
|
@ -31,13 +31,13 @@ class UniqueFieldValueValidator extends ConstraintValidator {
|
|||
$value_taken = (bool) \Drupal::entityQuery($entity_type_id)
|
||||
// The id could be NULL, so we cast it to 0 in that case.
|
||||
->condition($id_key, (int) $items->getEntity()->id(), '<>')
|
||||
->condition($field_name, $items->first()->value)
|
||||
->condition($field_name, $item->value)
|
||||
->range(0, 1)
|
||||
->count()
|
||||
->execute();
|
||||
|
||||
if ($value_taken) {
|
||||
$this->context->addViolation($constraint->message, array("%value" => $items->value));
|
||||
$this->context->addViolation($constraint->message, array("%value" => $item->value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,10 +20,10 @@ class ForumLeafConstraintValidator extends ConstraintValidator {
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
public function validate($items, Constraint $constraint) {
|
||||
if (!isset($items)) {
|
||||
return;
|
||||
}
|
||||
$item = $items->first();
|
||||
if (!isset($item)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Verify that a term has been selected.
|
||||
if (!$item->entity) {
|
||||
|
|
|
@ -17,7 +17,7 @@ use Drupal\Core\Render\Element;
|
|||
use Drupal\Core\Entity\Entity\EntityFormDisplay;
|
||||
use Drupal\user\PrivateTempStoreFactory;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\Validator\ValidatorInterface;
|
||||
use Symfony\Component\Validator\Validator\ValidatorInterface;
|
||||
|
||||
/**
|
||||
* Builds and process a form for editing a single entity field.
|
||||
|
@ -48,7 +48,7 @@ class QuickEditFieldForm extends FormBase {
|
|||
/**
|
||||
* The typed data validator.
|
||||
*
|
||||
* @var \Symfony\Component\Validator\ValidatorInterface
|
||||
* @var \Symfony\Component\Validator\Validator\ValidatorInterface
|
||||
*/
|
||||
protected $validator;
|
||||
|
||||
|
@ -61,7 +61,7 @@ class QuickEditFieldForm extends FormBase {
|
|||
* The module handler.
|
||||
* @param \Drupal\Core\Entity\EntityStorageInterface $node_type_storage
|
||||
* The node type storage.
|
||||
* @param \Symfony\Component\Validator\ValidatorInterface $validator
|
||||
* @param \Symfony\Component\Validator\Validator\ValidatorInterface $validator
|
||||
* The typed data validator service.
|
||||
*/
|
||||
public function __construct(PrivateTempStoreFactory $temp_store_factory, ModuleHandlerInterface $module_handler, EntityStorageInterface $node_type_storage, ValidatorInterface $validator) {
|
||||
|
@ -165,7 +165,7 @@ class QuickEditFieldForm extends FormBase {
|
|||
// @todo: Improve this in https://www.drupal.org/node/2395831.
|
||||
$typed_entity = $entity->getTypedData();
|
||||
$violations = $this->validator
|
||||
->validateValue($entity, $typed_entity->getConstraints());
|
||||
->validate($typed_entity, $typed_entity->getConstraints());
|
||||
|
||||
foreach ($violations as $violation) {
|
||||
$form_state->setErrorByName($violation->getPropertyPath(), $violation->getMessage());
|
||||
|
|
|
@ -58,7 +58,7 @@ class UserMailRequired extends Constraint implements ConstraintValidatorInterfac
|
|||
public function validate($items, Constraint $constraint) {
|
||||
/** @var \Drupal\Core\Field\FieldItemListInterface $items */
|
||||
/** @var \Drupal\user\UserInterface $account */
|
||||
$account = $this->context->getMetadata()->getTypedData()->getEntity();
|
||||
$account = $items->getEntity();
|
||||
$existing_value = NULL;
|
||||
if ($account->id()) {
|
||||
$account_unchanged = \Drupal::entityManager()
|
||||
|
|
|
@ -0,0 +1,349 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Tests\Core\TypedData\RecursiveContextualValidatorTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\Tests\Core\TypedData;
|
||||
|
||||
use Drupal\Core\Cache\NullBackend;
|
||||
use Drupal\Core\DependencyInjection\ContainerBuilder;
|
||||
use Drupal\Core\TypedData\DataDefinition;
|
||||
use Drupal\Core\TypedData\MapDataDefinition;
|
||||
use Drupal\Core\TypedData\TypedDataManager;
|
||||
use Drupal\Core\TypedData\Validation\ExecutionContextFactory;
|
||||
use Drupal\Core\TypedData\Validation\RecursiveValidator;
|
||||
use Drupal\Core\Validation\ConstraintManager;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
use Symfony\Component\Validator\ConstraintValidatorFactory;
|
||||
use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
||||
use Symfony\Component\Validator\DefaultTranslator;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\Core\TypedData\Validation\RecursiveContextualValidator
|
||||
* @group typedData
|
||||
*/
|
||||
class RecursiveContextualValidatorTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* The type data manager.
|
||||
*
|
||||
* @var \Drupal\Core\TypedData\TypedDataManager
|
||||
*/
|
||||
protected $typedDataManager;
|
||||
|
||||
/**
|
||||
* The recursive validator.
|
||||
*
|
||||
* @var \Drupal\Core\TypedData\Validation\RecursiveValidator
|
||||
*/
|
||||
protected $recursiveValidator;
|
||||
|
||||
/**
|
||||
* The validator factory.
|
||||
*
|
||||
* @var \Symfony\Component\Validator\ConstraintValidatorFactoryInterface
|
||||
*/
|
||||
protected $validatorFactory;
|
||||
|
||||
/**
|
||||
* The execution context factory.
|
||||
*
|
||||
* @var \Drupal\Core\TypedData\Validation\ExecutionContextFactory
|
||||
*/
|
||||
protected $contextFactory;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$cache_backend = new NullBackend('cache');
|
||||
$namespaces = new \ArrayObject([
|
||||
'Drupal\\Core\\TypedData' => $this->root . '/core/lib/Drupal/Core/TypedData',
|
||||
'Drupal\\Core\\Validation' => $this->root . '/core/lib/Drupal/Core/Validation',
|
||||
]);
|
||||
$module_handler = $this->getMockBuilder('Drupal\Core\Extension\ModuleHandlerInterface')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$class_resolver = $this->getMockBuilder('Drupal\Core\DependencyInjection\ClassResolverInterface')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
|
||||
$this->typedDataManager = new TypedDataManager($namespaces, $cache_backend, $module_handler, $class_resolver);
|
||||
$this->typedDataManager->setValidationConstraintManager(
|
||||
new ConstraintManager($namespaces, $cache_backend, $module_handler)
|
||||
);
|
||||
// Typed data definitions access the manager in the container.
|
||||
$container = new ContainerBuilder();
|
||||
$container->set('typed_data_manager', $this->typedDataManager);
|
||||
\Drupal::setContainer($container);
|
||||
|
||||
$translator = new DefaultTranslator();
|
||||
$this->contextFactory = new ExecutionContextFactory($translator);
|
||||
$this->validatorFactory = new ConstraintValidatorFactory();
|
||||
$this->recursiveValidator = new RecursiveValidator($this->contextFactory, $this->validatorFactory, $this->typedDataManager);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that passing an explicit group is not supported.
|
||||
*
|
||||
* @covers ::validate
|
||||
*
|
||||
* @expectedException \LogicException
|
||||
*/
|
||||
public function testValidateWithGroups() {
|
||||
$this->recursiveValidator->validate('test', NULL, 'test group');
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that passing a non typed data value is not supported.
|
||||
*
|
||||
* @covers ::validate
|
||||
*
|
||||
* @expectedException \InvalidArgumentException
|
||||
*/
|
||||
public function testValidateWithoutTypedData() {
|
||||
$this->recursiveValidator->validate('test');
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::validate
|
||||
*/
|
||||
public function testBasicValidateWithoutConstraints() {
|
||||
$typed_data = $this->typedDataManager->create(DataDefinition::create('string'));
|
||||
$violations = $this->recursiveValidator->validate($typed_data);
|
||||
$this->assertCount(0, $violations);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::validate
|
||||
*/
|
||||
public function testBasicValidateWithConstraint() {
|
||||
$typed_data = $this->typedDataManager->create(
|
||||
DataDefinition::create('string')
|
||||
->addConstraint('Callback', [
|
||||
'callback' => function ($value, ExecutionContextInterface $context) {
|
||||
$context->addViolation('test violation: ' . $value);
|
||||
}
|
||||
])
|
||||
);
|
||||
$typed_data->setValue('foo');
|
||||
|
||||
$violations = $this->recursiveValidator->validate($typed_data);
|
||||
$this->assertCount(1, $violations);
|
||||
// Ensure that the right value is passed into the validator.
|
||||
$this->assertEquals('test violation: foo', $violations->get(0)->getMessage());
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::validate
|
||||
*/
|
||||
public function testBasicValidateWithMultipleConstraints() {
|
||||
$options = [
|
||||
'callback' => function ($value, ExecutionContextInterface $context) {
|
||||
$context->addViolation('test violation');
|
||||
}
|
||||
];
|
||||
$typed_data = $this->typedDataManager->create(
|
||||
DataDefinition::create('string')
|
||||
->addConstraint('Callback', $options)
|
||||
->addConstraint('NotNull')
|
||||
);
|
||||
$violations = $this->recursiveValidator->validate($typed_data);
|
||||
$this->assertCount(2, $violations);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::validate
|
||||
*/
|
||||
public function testPropertiesValidateWithMultipleLevels() {
|
||||
|
||||
$typed_data = $this->buildExampleTypedDataWithProperties();
|
||||
|
||||
$violations = $this->recursiveValidator->validate($typed_data);
|
||||
$this->assertCount(6, $violations);
|
||||
|
||||
$this->assertEquals('violation: 3', $violations->get(0)->getMessage());
|
||||
$this->assertEquals('violation: value1', $violations->get(1)->getMessage());
|
||||
$this->assertEquals('violation: value2', $violations->get(2)->getMessage());
|
||||
$this->assertEquals('violation: 2', $violations->get(3)->getMessage());
|
||||
$this->assertEquals('violation: subvalue1', $violations->get(4)->getMessage());
|
||||
$this->assertEquals('violation: subvalue2', $violations->get(5)->getMessage());
|
||||
|
||||
$this->assertEquals('', $violations->get(0)->getPropertyPath());
|
||||
$this->assertEquals('key1', $violations->get(1)->getPropertyPath());
|
||||
$this->assertEquals('key2', $violations->get(2)->getPropertyPath());
|
||||
$this->assertEquals('key_with_properties', $violations->get(3)->getPropertyPath());
|
||||
$this->assertEquals('key_with_properties.subkey1', $violations->get(4)->getPropertyPath());
|
||||
$this->assertEquals('key_with_properties.subkey2', $violations->get(5)->getPropertyPath());
|
||||
}
|
||||
|
||||
/**
|
||||
* Setups a typed data object used for test purposes.
|
||||
*
|
||||
* @param array $tree
|
||||
* An array of value, constraints and properties.
|
||||
*
|
||||
* @return \Drupal\Core\TypedData\TypedDataInterface|\PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
protected function setupTypedData(array $tree, $name = '') {
|
||||
$callback = function ($value, ExecutionContextInterface $context) {
|
||||
$context->addViolation('violation: ' . (is_array($value) ? count($value) : $value));
|
||||
};
|
||||
|
||||
$tree += ['constraints' => []];
|
||||
|
||||
if (isset($tree['properties'])) {
|
||||
$map_data_definition = MapDataDefinition::create();
|
||||
$map_data_definition->addConstraint('Callback', ['callback' => $callback]);
|
||||
foreach ($tree['properties'] as $property_name => $property) {
|
||||
$sub_typed_data = $this->setupTypedData($property, $property_name);
|
||||
$map_data_definition->setPropertyDefinition($property_name, $sub_typed_data->getDataDefinition());
|
||||
}
|
||||
$typed_data = $this->typedDataManager->create(
|
||||
$map_data_definition,
|
||||
$tree['value'],
|
||||
$name
|
||||
);
|
||||
}
|
||||
else {
|
||||
/** @var \Drupal\Core\TypedData\TypedDataInterface $typed_data */
|
||||
$typed_data = $this->typedDataManager->create(
|
||||
DataDefinition::create('string')
|
||||
->addConstraint('Callback', ['callback' => $callback]),
|
||||
$tree['value'],
|
||||
$name
|
||||
);
|
||||
}
|
||||
|
||||
return $typed_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::validateProperty
|
||||
*
|
||||
* @expectedException \LogicException
|
||||
*/
|
||||
public function testValidatePropertyWithCustomGroup() {
|
||||
$tree = [
|
||||
'value' => [],
|
||||
'properties' => [
|
||||
'key1' => ['value' => 'value1'],
|
||||
],
|
||||
];
|
||||
$typed_data = $this->setupTypedData($tree, 'test_name');
|
||||
$this->recursiveValidator->validateProperty($typed_data, 'key1', 'test group');
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::validateProperty
|
||||
*
|
||||
* @dataProvider providerTestValidatePropertyWithInvalidObjects
|
||||
*
|
||||
* @expectedException \InvalidArgumentException
|
||||
*/
|
||||
public function testValidatePropertyWithInvalidObjects($object) {
|
||||
$this->recursiveValidator->validateProperty($object, 'key1', NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides data for testValidatePropertyWithInvalidObjects.
|
||||
* @return array
|
||||
*/
|
||||
public function providerTestValidatePropertyWithInvalidObjects() {
|
||||
$data = [];
|
||||
$data[] = [new \stdClass()];
|
||||
$data[] = [new TestClass()];
|
||||
|
||||
$data[] = [$this->getMock('Drupal\Core\TypedData\TypedDataInterface')];
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::validateProperty
|
||||
*/
|
||||
public function testValidateProperty() {
|
||||
$typed_data = $this->buildExampleTypedDataWithProperties();
|
||||
|
||||
$violations = $this->recursiveValidator->validateProperty($typed_data, 'key_with_properties');
|
||||
$this->assertCount(3, $violations);
|
||||
|
||||
$this->assertEquals('violation: 2', $violations->get(0)->getMessage());
|
||||
$this->assertEquals('violation: subvalue1', $violations->get(1)->getMessage());
|
||||
$this->assertEquals('violation: subvalue2', $violations->get(2)->getMessage());
|
||||
|
||||
$this->assertEquals('', $violations->get(0)->getPropertyPath());
|
||||
$this->assertEquals('subkey1', $violations->get(1)->getPropertyPath());
|
||||
$this->assertEquals('subkey2', $violations->get(2)->getPropertyPath());
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::validatePropertyValue
|
||||
*
|
||||
* @dataProvider providerTestValidatePropertyWithInvalidObjects
|
||||
*
|
||||
* @expectedException \InvalidArgumentException
|
||||
*/
|
||||
public function testValidatePropertyValueWithInvalidObjects($object) {
|
||||
$this->recursiveValidator->validatePropertyValue($object, 'key1', [], NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::validatePropertyValue
|
||||
*/
|
||||
public function testValidatePropertyValue() {
|
||||
$typed_data = $this->buildExampleTypedDataWithProperties(['subkey1' => 'subvalue11', 'subkey2' => 'subvalue22']);
|
||||
|
||||
$violations = $this->recursiveValidator->validatePropertyValue($typed_data, 'key_with_properties', $typed_data->get('key_with_properties'));
|
||||
$this->assertCount(3, $violations);
|
||||
|
||||
$this->assertEquals('violation: 2', $violations->get(0)->getMessage());
|
||||
$this->assertEquals('violation: subvalue11', $violations->get(1)->getMessage());
|
||||
$this->assertEquals('violation: subvalue22', $violations->get(2)->getMessage());
|
||||
|
||||
$this->assertEquals('', $violations->get(0)->getPropertyPath());
|
||||
$this->assertEquals('subkey1', $violations->get(1)->getPropertyPath());
|
||||
$this->assertEquals('subkey2', $violations->get(2)->getPropertyPath());
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Builds some example type data object.
|
||||
*
|
||||
* @return \Drupal\Core\TypedData\TypedDataInterface|\PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
protected function buildExampleTypedDataWithProperties($subkey_value = NULL) {
|
||||
$subkey_value = $subkey_value ?: ['subkey1' => 'subvalue1', 'subkey2' => 'subvalue2'];
|
||||
$tree = [
|
||||
'value' => [
|
||||
'key1' => 'value1',
|
||||
'key2' => 'value2',
|
||||
'key_with_properties' => $subkey_value
|
||||
],
|
||||
];
|
||||
$tree['properties'] = [
|
||||
'key1' => [
|
||||
'value' => 'value1',
|
||||
],
|
||||
'key2' => [
|
||||
'value' => 'value2',
|
||||
],
|
||||
'key_with_properties' => [
|
||||
'value' => $subkey_value ?: ['subkey1' => 'subvalue1', 'subkey2' => 'subvalue2'],
|
||||
],
|
||||
];
|
||||
$tree['properties']['key_with_properties']['properties']['subkey1'] = ['value' => $tree['properties']['key_with_properties']['value']['subkey1']];
|
||||
$tree['properties']['key_with_properties']['properties']['subkey2'] = ['value' => $tree['properties']['key_with_properties']['value']['subkey2']];
|
||||
|
||||
return $this->setupTypedData($tree, 'test_name');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class TestClass {
|
||||
|
||||
}
|
|
@ -7,6 +7,12 @@
|
|||
|
||||
namespace Drupal\Tests\Core\Validation\Plugin\Validation\Constraint;
|
||||
|
||||
use Drupal\Core\TypedData\DataDefinition;
|
||||
use Drupal\Core\TypedData\Plugin\DataType\BooleanData;
|
||||
use Drupal\Core\TypedData\Plugin\DataType\FloatData;
|
||||
use Drupal\Core\TypedData\Plugin\DataType\IntegerData;
|
||||
use Drupal\Core\TypedData\Plugin\DataType\StringData;
|
||||
use Drupal\Core\TypedData\Plugin\DataType\Uri;
|
||||
use Drupal\Core\TypedData\PrimitiveInterface;
|
||||
use Drupal\Core\Validation\Plugin\Validation\Constraint\PrimitiveTypeConstraint;
|
||||
use Drupal\Core\Validation\Plugin\Validation\Constraint\PrimitiveTypeConstraintValidator;
|
||||
|
@ -24,17 +30,10 @@ class PrimitiveTypeConstraintValidatorTest extends UnitTestCase {
|
|||
* @dataProvider provideTestValidate
|
||||
*/
|
||||
public function testValidate(PrimitiveInterface $typed_data, $value, $valid) {
|
||||
$metadata = $this->getMockBuilder('Drupal\Core\TypedData\Validation\Metadata')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$metadata->expects($this->any())
|
||||
->method('getTypedData')
|
||||
->willReturn($typed_data);
|
||||
|
||||
$context = $this->getMock('Symfony\Component\Validator\ExecutionContextInterface');
|
||||
$context = $this->getMock('\Symfony\Component\Validator\Context\ExecutionContextInterface');
|
||||
$context->expects($this->any())
|
||||
->method('getMetadata')
|
||||
->willReturn($metadata);
|
||||
->method('getObject')
|
||||
->willReturn($typed_data);
|
||||
|
||||
if ($valid) {
|
||||
$context->expects($this->never())
|
||||
|
@ -54,31 +53,31 @@ class PrimitiveTypeConstraintValidatorTest extends UnitTestCase {
|
|||
|
||||
public function provideTestValidate() {
|
||||
$data = [];
|
||||
$data[] = [$this->getMock('Drupal\Core\TypedData\Type\BooleanInterface'), NULL, TRUE];
|
||||
$data[] = [new BooleanData(DataDefinition::create('boolean')), NULL, TRUE];
|
||||
|
||||
$data[] = [$this->getMock('Drupal\Core\TypedData\Type\BooleanInterface'), 1, TRUE];
|
||||
$data[] = [$this->getMock('Drupal\Core\TypedData\Type\BooleanInterface'), 'test', FALSE];
|
||||
$data[] = [$this->getMock('Drupal\Core\TypedData\Type\FloatInterface'), 1.5, TRUE];
|
||||
$data[] = [$this->getMock('Drupal\Core\TypedData\Type\FloatInterface'), 'test', FALSE];
|
||||
$data[] = [$this->getMock('Drupal\Core\TypedData\Type\IntegerInterface'), 1, TRUE];
|
||||
$data[] = [$this->getMock('Drupal\Core\TypedData\Type\IntegerInterface'), 1.5, FALSE];
|
||||
$data[] = [$this->getMock('Drupal\Core\TypedData\Type\IntegerInterface'), 'test', FALSE];
|
||||
$data[] = [$this->getMock('Drupal\Core\TypedData\Type\StringInterface'), 'test', TRUE];
|
||||
$data[] = [new BooleanData(DataDefinition::create('boolean')), 1, TRUE];
|
||||
$data[] = [new BooleanData(DataDefinition::create('boolean')), 'test', FALSE];
|
||||
$data[] = [new FloatData(DataDefinition::create('float')), 1.5, TRUE];
|
||||
$data[] = [new FloatData(DataDefinition::create('float')), 'test', FALSE];
|
||||
$data[] = [new IntegerData(DataDefinition::create('integer')), 1, TRUE];
|
||||
$data[] = [new IntegerData(DataDefinition::create('integer')), 1.5, FALSE];
|
||||
$data[] = [new IntegerData(DataDefinition::create('integer')), 'test', FALSE];
|
||||
$data[] = [new StringData(DataDefinition::create('string')), 'test', TRUE];
|
||||
// It is odd that 1 is a valid string.
|
||||
// $data[] = [$this->getMock('Drupal\Core\TypedData\Type\StringInterface'), 1, FALSE];
|
||||
$data[] = [$this->getMock('Drupal\Core\TypedData\Type\StringInterface'), [], FALSE];
|
||||
$data[] = [$this->getMock('Drupal\Core\TypedData\Type\UriInterface'), 'http://www.drupal.org', TRUE];
|
||||
$data[] = [$this->getMock('Drupal\Core\TypedData\Type\UriInterface'), 'https://www.drupal.org', TRUE];
|
||||
$data[] = [$this->getMock('Drupal\Core\TypedData\Type\UriInterface'), 'Invalid', FALSE];
|
||||
$data[] = [$this->getMock('Drupal\Core\TypedData\Type\UriInterface'), 'entity:node/1', TRUE];
|
||||
$data[] = [$this->getMock('Drupal\Core\TypedData\Type\UriInterface'), 'base:', TRUE];
|
||||
$data[] = [$this->getMock('Drupal\Core\TypedData\Type\UriInterface'), 'base:node', TRUE];
|
||||
$data[] = [$this->getMock('Drupal\Core\TypedData\Type\UriInterface'), 'internal:', TRUE];
|
||||
$data[] = [$this->getMock('Drupal\Core\TypedData\Type\UriInterface'), 'public://', FALSE];
|
||||
$data[] = [$this->getMock('Drupal\Core\TypedData\Type\UriInterface'), 'public://foo.png', TRUE];
|
||||
$data[] = [$this->getMock('Drupal\Core\TypedData\Type\UriInterface'), 'private://', FALSE];
|
||||
$data[] = [$this->getMock('Drupal\Core\TypedData\Type\UriInterface'), 'private://foo.png', TRUE];
|
||||
$data[] = [$this->getMock('Drupal\Core\TypedData\Type\UriInterface'), 'drupal.org', FALSE];
|
||||
$data[] = [new StringData(DataDefinition::create('string')), [], FALSE];
|
||||
$data[] = [new Uri(DataDefinition::create('uri')), 'http://www.drupal.org', TRUE];
|
||||
$data[] = [new Uri(DataDefinition::create('uri')), 'https://www.drupal.org', TRUE];
|
||||
$data[] = [new Uri(DataDefinition::create('uri')), 'Invalid', FALSE];
|
||||
$data[] = [new Uri(DataDefinition::create('uri')), 'entity:node/1', TRUE];
|
||||
$data[] = [new Uri(DataDefinition::create('uri')), 'base:', TRUE];
|
||||
$data[] = [new Uri(DataDefinition::create('uri')), 'base:node', TRUE];
|
||||
$data[] = [new Uri(DataDefinition::create('uri')), 'internal:', TRUE];
|
||||
$data[] = [new Uri(DataDefinition::create('uri')), 'public://', FALSE];
|
||||
$data[] = [new Uri(DataDefinition::create('uri')), 'public://foo.png', TRUE];
|
||||
$data[] = [new Uri(DataDefinition::create('uri')), 'private://', FALSE];
|
||||
$data[] = [new Uri(DataDefinition::create('uri')), 'private://foo.png', TRUE];
|
||||
$data[] = [new Uri(DataDefinition::create('uri')), 'drupal.org', FALSE];
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
|
|
@ -351,7 +351,7 @@ class ClassLoader
|
|||
foreach ($this->prefixLengthsPsr4[$first] as $prefix => $length) {
|
||||
if (0 === strpos($class, $prefix)) {
|
||||
foreach ($this->prefixDirsPsr4[$prefix] as $dir) {
|
||||
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) {
|
||||
if (is_file($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
|
@ -361,7 +361,7 @@ class ClassLoader
|
|||
|
||||
// PSR-4 fallback dirs
|
||||
foreach ($this->fallbackDirsPsr4 as $dir) {
|
||||
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
|
||||
if (is_file($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
|
@ -380,7 +380,7 @@ class ClassLoader
|
|||
foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
|
||||
if (0 === strpos($class, $prefix)) {
|
||||
foreach ($dirs as $dir) {
|
||||
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
|
||||
if (is_file($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
|
@ -390,7 +390,7 @@ class ClassLoader
|
|||
|
||||
// PSR-0 fallback dirs
|
||||
foreach ($this->fallbackDirsPsr0 as $dir) {
|
||||
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
|
||||
if (is_file($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://github.com/doctrine/lexer/archive/v1.0.zip",
|
||||
"url": "https://api.github.com/repos/doctrine/lexer/zipball/2f708a85bb3aab5d99dab8be435abd73e0b18acb",
|
||||
"reference": "v1.0",
|
||||
"shasum": ""
|
||||
},
|
||||
|
@ -64,7 +64,7 @@
|
|||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://github.com/doctrine/inflector/archive/v1.0.zip",
|
||||
"url": "https://api.github.com/repos/doctrine/inflector/zipball/54b8333d2a5682afdc690060c1cf384ba9f47f08",
|
||||
"reference": "v1.0",
|
||||
"shasum": ""
|
||||
},
|
||||
|
@ -625,12 +625,12 @@
|
|||
"version_normalized": "1.0.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/php-fig/log",
|
||||
"url": "https://github.com/php-fig/log.git",
|
||||
"reference": "1.0.0"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://github.com/php-fig/log/archive/1.0.0.zip",
|
||||
"url": "https://api.github.com/repos/php-fig/log/zipball/fe0936ee26643249e916849d48e3a51d5f5e278b",
|
||||
"reference": "1.0.0",
|
||||
"shasum": ""
|
||||
},
|
||||
|
@ -2080,80 +2080,6 @@
|
|||
"description": "Symfony Translation Component",
|
||||
"homepage": "http://symfony.com"
|
||||
},
|
||||
{
|
||||
"name": "symfony/validator",
|
||||
"version": "v2.6.6",
|
||||
"version_normalized": "2.6.6.0",
|
||||
"target-dir": "Symfony/Component/Validator",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/Validator.git",
|
||||
"reference": "85d9b42fe71bf88e7a1e5dec2094605dc9fbff28"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/Validator/zipball/85d9b42fe71bf88e7a1e5dec2094605dc9fbff28",
|
||||
"reference": "85d9b42fe71bf88e7a1e5dec2094605dc9fbff28",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3.3",
|
||||
"symfony/translation": "~2.0,>=2.0.5"
|
||||
},
|
||||
"require-dev": {
|
||||
"doctrine/annotations": "~1.0",
|
||||
"doctrine/cache": "~1.0",
|
||||
"doctrine/common": "~2.3",
|
||||
"egulias/email-validator": "~1.2,>=1.2.1",
|
||||
"symfony/config": "~2.2",
|
||||
"symfony/expression-language": "~2.4",
|
||||
"symfony/http-foundation": "~2.1",
|
||||
"symfony/intl": "~2.3",
|
||||
"symfony/phpunit-bridge": "~2.7",
|
||||
"symfony/property-access": "~2.3",
|
||||
"symfony/yaml": "~2.0,>=2.0.5"
|
||||
},
|
||||
"suggest": {
|
||||
"doctrine/annotations": "For using the annotation mapping. You will also need doctrine/cache.",
|
||||
"doctrine/cache": "For using the default cached annotation reader and metadata cache.",
|
||||
"egulias/email-validator": "Strict (RFC compliant) email validation",
|
||||
"symfony/config": "",
|
||||
"symfony/expression-language": "For using the 2.4 Expression validator",
|
||||
"symfony/http-foundation": "",
|
||||
"symfony/intl": "",
|
||||
"symfony/property-access": "For using the 2.4 Validator API",
|
||||
"symfony/yaml": ""
|
||||
},
|
||||
"time": "2015-03-30 15:54:10",
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.6-dev"
|
||||
}
|
||||
},
|
||||
"installation-source": "dist",
|
||||
"autoload": {
|
||||
"psr-0": {
|
||||
"Symfony\\Component\\Validator\\": ""
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "http://symfony.com/contributors"
|
||||
},
|
||||
{
|
||||
"name": "Fabien Potencier",
|
||||
"email": "fabien@symfony.com"
|
||||
}
|
||||
],
|
||||
"description": "Symfony Validator Component",
|
||||
"homepage": "http://symfony.com"
|
||||
},
|
||||
{
|
||||
"name": "symfony/process",
|
||||
"version": "v2.6.6",
|
||||
|
@ -3325,5 +3251,79 @@
|
|||
"testing",
|
||||
"xunit"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "symfony/validator",
|
||||
"version": "2.6.x-dev",
|
||||
"version_normalized": "2.6.9999999.9999999-dev",
|
||||
"target-dir": "Symfony/Component/Validator",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/Validator.git",
|
||||
"reference": "6bb1b474d25cb80617d8da6cb14c955ba914e495"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/Validator/zipball/6bb1b474d25cb80617d8da6cb14c955ba914e495",
|
||||
"reference": "6bb1b474d25cb80617d8da6cb14c955ba914e495",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3.3",
|
||||
"symfony/translation": "~2.0,>=2.0.5"
|
||||
},
|
||||
"require-dev": {
|
||||
"doctrine/annotations": "~1.0",
|
||||
"doctrine/cache": "~1.0",
|
||||
"doctrine/common": "~2.3",
|
||||
"egulias/email-validator": "~1.2,>=1.2.1",
|
||||
"symfony/config": "~2.2",
|
||||
"symfony/expression-language": "~2.4",
|
||||
"symfony/http-foundation": "~2.1",
|
||||
"symfony/intl": "~2.3",
|
||||
"symfony/phpunit-bridge": "~2.7",
|
||||
"symfony/property-access": "~2.3",
|
||||
"symfony/yaml": "~2.0,>=2.0.5"
|
||||
},
|
||||
"suggest": {
|
||||
"doctrine/annotations": "For using the annotation mapping. You will also need doctrine/cache.",
|
||||
"doctrine/cache": "For using the default cached annotation reader and metadata cache.",
|
||||
"egulias/email-validator": "Strict (RFC compliant) email validation",
|
||||
"symfony/config": "",
|
||||
"symfony/expression-language": "For using the 2.4 Expression validator",
|
||||
"symfony/http-foundation": "",
|
||||
"symfony/intl": "",
|
||||
"symfony/property-access": "For using the 2.4 Validator API",
|
||||
"symfony/yaml": ""
|
||||
},
|
||||
"time": "2015-05-05 01:29:27",
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.6-dev"
|
||||
}
|
||||
},
|
||||
"installation-source": "dist",
|
||||
"autoload": {
|
||||
"psr-0": {
|
||||
"Symfony\\Component\\Validator\\": ""
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Fabien Potencier",
|
||||
"email": "fabien@symfony.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"description": "Symfony Validator Component",
|
||||
"homepage": "https://symfony.com"
|
||||
}
|
||||
]
|
||||
|
|
|
@ -232,7 +232,7 @@ abstract class Constraint
|
|||
*/
|
||||
public function addImplicitGroupName($group)
|
||||
{
|
||||
if (in_array(Constraint::DEFAULT_GROUP, $this->groups) && !in_array($group, $this->groups)) {
|
||||
if (in_array(self::DEFAULT_GROUP, $this->groups) && !in_array($group, $this->groups)) {
|
||||
$this->groups[] = $group;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,7 +36,7 @@ class ChoiceValidator extends ConstraintValidator
|
|||
throw new UnexpectedTypeException($constraint, __NAMESPACE__.'\Choice');
|
||||
}
|
||||
|
||||
if (!$constraint->choices && !$constraint->callback) {
|
||||
if (!is_array($constraint->choices) && !$constraint->callback) {
|
||||
throw new ConstraintDefinitionException('Either "choices" or "callback" must be specified on constraint Choice');
|
||||
}
|
||||
|
||||
|
|
|
@ -88,7 +88,7 @@ class DefaultTranslator implements TranslatorInterface
|
|||
* have the same expressiveness. While Translator supports intervals in
|
||||
* message translations, which are needed for languages other than English,
|
||||
* this translator does not. You should use Translator or a custom
|
||||
* implementation of {@link TranslatorInterface} if you need this or similar
|
||||
* implementation of {@link \Symfony\Component\Translation\TranslatorInterface} if you need this or similar
|
||||
* functionality.
|
||||
*
|
||||
* Example usage:
|
||||
|
|
|
@ -14,6 +14,8 @@ namespace Symfony\Component\Validator\Mapping\Factory;
|
|||
use Symfony\Component\Validator\Exception\NoSuchMetadataException;
|
||||
use Symfony\Component\Validator\Mapping\Cache\CacheInterface;
|
||||
use Symfony\Component\Validator\Mapping\ClassMetadata;
|
||||
use Symfony\Component\Validator\Mapping\ClassMetadataInterface;
|
||||
use Symfony\Component\Validator\Mapping\Loader\LoaderChain;
|
||||
use Symfony\Component\Validator\Mapping\Loader\LoaderInterface;
|
||||
|
||||
/**
|
||||
|
@ -28,7 +30,7 @@ use Symfony\Component\Validator\Mapping\Loader\LoaderInterface;
|
|||
* Whenever a new metadata instance is created, it is passed to the loader,
|
||||
* which can configure the metadata based on configuration loaded from the
|
||||
* filesystem or a database. If you want to use multiple loaders, wrap them in a
|
||||
* {@link Loader\LoaderChain}.
|
||||
* {@link LoaderChain}.
|
||||
*
|
||||
* You can also optionally pass a {@link CacheInterface} instance to the
|
||||
* constructor. This cache will be used for persisting the generated metadata
|
||||
|
|
|
@ -14,7 +14,7 @@ namespace Symfony\Component\Validator\Mapping\Factory;
|
|||
use Symfony\Component\Validator\MetadataFactoryInterface as LegacyMetadataFactoryInterface;
|
||||
|
||||
/**
|
||||
* Returns {@link MetadataInterface} instances for values.
|
||||
* Returns {@link \Symfony\Component\Validator\Mapping\MetadataInterface} instances for values.
|
||||
*
|
||||
* @since 2.5
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
|
|
|
@ -58,7 +58,7 @@ class AnnotationLoader implements LoaderInterface
|
|||
}
|
||||
|
||||
foreach ($reflClass->getProperties() as $property) {
|
||||
if ($property->getDeclaringClass()->name == $className) {
|
||||
if ($property->getDeclaringClass()->name === $className) {
|
||||
foreach ($this->reader->getPropertyAnnotations($property) as $constraint) {
|
||||
if ($constraint instanceof Constraint) {
|
||||
$metadata->addPropertyConstraint($property->name, $constraint);
|
||||
|
@ -70,7 +70,7 @@ class AnnotationLoader implements LoaderInterface
|
|||
}
|
||||
|
||||
foreach ($reflClass->getMethods() as $method) {
|
||||
if ($method->getDeclaringClass()->name == $className) {
|
||||
if ($method->getDeclaringClass()->name === $className) {
|
||||
foreach ($this->reader->getMethodAnnotations($method) as $constraint) {
|
||||
if ($constraint instanceof Callback) {
|
||||
$constraint->callback = $method->getName();
|
||||
|
|
|
@ -85,7 +85,7 @@ class YamlFileLoader extends FileLoader
|
|||
$values = array();
|
||||
|
||||
foreach ($nodes as $name => $childNodes) {
|
||||
if (is_numeric($name) && is_array($childNodes) && count($childNodes) == 1) {
|
||||
if (is_numeric($name) && is_array($childNodes) && 1 === count($childNodes)) {
|
||||
$options = current($childNodes);
|
||||
|
||||
if (is_array($options)) {
|
||||
|
|
|
@ -113,7 +113,7 @@ https://github.com/fabpot/Silex/blob/master/src/Silex/Provider/ValidatorServiceP
|
|||
|
||||
Documentation:
|
||||
|
||||
http://symfony.com/doc/2.6/book/validation.html
|
||||
https://symfony.com/doc/2.6/book/validation.html
|
||||
|
||||
JSR-303 Specification:
|
||||
|
||||
|
|
|
@ -40,7 +40,7 @@ EOF;
|
|||
'42 cannot be used here',
|
||||
'this is the message template',
|
||||
array(),
|
||||
array('some_value' => 42),
|
||||
array('some_value' => 42),
|
||||
'some_value',
|
||||
null
|
||||
);
|
||||
|
|
|
@ -150,6 +150,23 @@ class ChoiceValidatorTest extends AbstractConstraintValidatorTest
|
|||
->assertRaised();
|
||||
}
|
||||
|
||||
public function testInvalidChoiceEmptyChoices()
|
||||
{
|
||||
$constraint = new Choice(array(
|
||||
// May happen when the choices are provided dynamically, e.g. from
|
||||
// the DB or the model
|
||||
'choices' => array(),
|
||||
'message' => 'myMessage',
|
||||
));
|
||||
|
||||
$this->validator->validate('baz', $constraint);
|
||||
|
||||
$this->buildViolation('myMessage')
|
||||
->setParameter('{{ value }}', '"baz"')
|
||||
->setCode(Choice::NO_SUCH_CHOICE_ERROR)
|
||||
->assertRaised();
|
||||
}
|
||||
|
||||
public function testInvalidChoiceMultiple()
|
||||
{
|
||||
$constraint = new Choice(array(
|
||||
|
|
|
@ -30,6 +30,7 @@ class PropertyPathTest extends \PHPUnit_Framework_TestCase
|
|||
array('', 'bar', 'bar', 'It returns the subPath if basePath is empty'),
|
||||
array('foo', 'bar', 'foo.bar', 'It append the subPath to the basePath'),
|
||||
array('foo', '[bar]', 'foo[bar]', 'It does not include the dot separator if subPath uses the array notation'),
|
||||
array('0', 'bar', '0.bar', 'Leading zeros are kept.'),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ namespace Symfony\Component\Validator\Util;
|
|||
* For more extensive functionality, use Symfony's PropertyAccess component.
|
||||
*
|
||||
* @since 2.5
|
||||
*
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
class PropertyPath
|
||||
|
@ -42,7 +43,7 @@ class PropertyPath
|
|||
return $basePath.$subPath;
|
||||
}
|
||||
|
||||
return $basePath ? $basePath.'.'.$subPath : $subPath;
|
||||
return '' !== (string) $basePath ? $basePath.'.'.$subPath : $subPath;
|
||||
}
|
||||
|
||||
return $basePath;
|
||||
|
|
|
@ -34,6 +34,7 @@ use Symfony\Component\Validator\Util\PropertyPath;
|
|||
* Recursive implementation of {@link ContextualValidatorInterface}.
|
||||
*
|
||||
* @since 2.5
|
||||
*
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
class RecursiveContextualValidator implements ContextualValidatorInterface
|
||||
|
@ -526,7 +527,7 @@ class RecursiveContextualValidator implements ContextualValidatorInterface
|
|||
} elseif ($metadata->isGroupSequenceProvider()) {
|
||||
// The group sequence is dynamically obtained from the validated
|
||||
// object
|
||||
/** @var \Symfony\Component\Validator\GroupSequenceProviderInterface $object */
|
||||
/* @var \Symfony\Component\Validator\GroupSequenceProviderInterface $object */
|
||||
$group = $object->getGroupSequence();
|
||||
$defaultOverridden = true;
|
||||
|
||||
|
@ -590,9 +591,7 @@ class RecursiveContextualValidator implements ContextualValidatorInterface
|
|||
$object,
|
||||
$cacheKey.':'.$propertyName,
|
||||
$propertyMetadata,
|
||||
$propertyPath
|
||||
? $propertyPath.'.'.$propertyName
|
||||
: $propertyName,
|
||||
PropertyPath::append($propertyPath, $propertyName),
|
||||
$groups,
|
||||
$cascadedGroups,
|
||||
TraversalStrategy::IMPLICIT,
|
||||
|
|
|
@ -19,7 +19,7 @@ namespace Symfony\Component\Validator;
|
|||
* @api
|
||||
*
|
||||
* @deprecated Deprecated since version 2.5, to be removed in Symfony 3.0.
|
||||
* Use {@link Validator\ValidatorInterface} instead.
|
||||
* Use {@link \Symfony\Component\Validator\Validator\ValidatorInterface} instead.
|
||||
*/
|
||||
interface ValidatorInterface
|
||||
{
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
"type": "library",
|
||||
"description": "Symfony Validator Component",
|
||||
"keywords": [],
|
||||
"homepage": "http://symfony.com",
|
||||
"homepage": "https://symfony.com",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
|
@ -12,7 +12,7 @@
|
|||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "http://symfony.com/contributors"
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
|
|
Loading…
Reference in New Issue