[Validator] Prototype of the traverser implementation
This commit is contained in:
parent
25cdc68d36
commit
a6ed4cae5d
|
@ -11,6 +11,8 @@
|
|||
|
||||
namespace Symfony\Component\Validator\Constraints;
|
||||
|
||||
use Traversable;
|
||||
|
||||
/**
|
||||
* Annotation for group sequences
|
||||
*
|
||||
|
@ -28,6 +30,22 @@ class GroupSequence implements \ArrayAccess, \IteratorAggregate, \Countable
|
|||
*/
|
||||
public $groups;
|
||||
|
||||
/**
|
||||
* The group under which cascaded objects are validated when validating
|
||||
* this sequence.
|
||||
*
|
||||
* By default, cascaded objects are validated in each of the groups of
|
||||
* the sequence.
|
||||
*
|
||||
* If a class has a group sequence attached, that sequence replaces the
|
||||
* "Default" group. When validating that class in the "Default" group, the
|
||||
* group sequence is used instead, but still the "Default" group should be
|
||||
* cascaded to other objects.
|
||||
*
|
||||
* @var string|GroupSequence
|
||||
*/
|
||||
public $cascadedGroup;
|
||||
|
||||
public function __construct(array $groups)
|
||||
{
|
||||
// Support for Doctrine annotations
|
||||
|
|
|
@ -0,0 +1,174 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Validator\Context;
|
||||
|
||||
use Symfony\Component\Validator\ClassBasedInterface;
|
||||
use Symfony\Component\Validator\ConstraintViolationList;
|
||||
use Symfony\Component\Validator\Group\GroupManagerInterface;
|
||||
use Symfony\Component\Validator\Mapping\PropertyMetadataInterface;
|
||||
use Symfony\Component\Validator\MetadataFactoryInterface;
|
||||
use Symfony\Component\Validator\Node\Node;
|
||||
use Symfony\Component\Validator\Validator\ValidatorInterface;
|
||||
|
||||
/**
|
||||
* @since %%NextVersion%%
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
class ExecutionContext implements ExecutionContextInterface
|
||||
{
|
||||
private $root;
|
||||
|
||||
private $violations;
|
||||
|
||||
/**
|
||||
* @var Node
|
||||
*/
|
||||
private $node;
|
||||
|
||||
/**
|
||||
* @var \SplStack
|
||||
*/
|
||||
private $nodeStack;
|
||||
|
||||
/**
|
||||
* @var MetadataFactoryInterface
|
||||
*/
|
||||
private $metadataFactory;
|
||||
|
||||
/**
|
||||
* @var ValidatorInterface
|
||||
*/
|
||||
private $validator;
|
||||
|
||||
/**
|
||||
* @var GroupManagerInterface
|
||||
*/
|
||||
private $groupManager;
|
||||
|
||||
public function __construct(MetadataFactoryInterface $metadataFactory, ValidatorInterface $validator, GroupManagerInterface $groupManager)
|
||||
{
|
||||
$this->metadataFactory = $metadataFactory;
|
||||
$this->validator = $validator;
|
||||
$this->groupManager = $groupManager;
|
||||
$this->violations = new ConstraintViolationList();
|
||||
}
|
||||
|
||||
public function pushNode(Node $node)
|
||||
{
|
||||
if (null === $this->node) {
|
||||
$this->root = $node->value;
|
||||
} else {
|
||||
$this->nodeStack->push($this->node);
|
||||
}
|
||||
|
||||
$this->node = $node;
|
||||
}
|
||||
|
||||
public function popNode()
|
||||
{
|
||||
$poppedNode = $this->node;
|
||||
|
||||
if (0 === count($this->nodeStack)) {
|
||||
$this->node = null;
|
||||
|
||||
return $poppedNode;
|
||||
}
|
||||
|
||||
if (1 === count($this->nodeStack)) {
|
||||
$this->nodeStack->pop();
|
||||
$this->node = null;
|
||||
|
||||
return $poppedNode;
|
||||
}
|
||||
|
||||
$this->nodeStack->pop();
|
||||
$this->node = $this->nodeStack->top();
|
||||
|
||||
return $poppedNode;
|
||||
}
|
||||
|
||||
public function addViolation($message, array $params = array(), $invalidValue = null, $pluralization = null, $code = null)
|
||||
{
|
||||
}
|
||||
|
||||
public function buildViolation($message)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public function getMetadataFor($object)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public function getViolations()
|
||||
{
|
||||
return $this->violations;
|
||||
}
|
||||
|
||||
public function getRoot()
|
||||
{
|
||||
return $this->root;
|
||||
}
|
||||
|
||||
public function getValue()
|
||||
{
|
||||
return $this->node ? $this->node->value : null;
|
||||
}
|
||||
|
||||
public function getMetadata()
|
||||
{
|
||||
return $this->node ? $this->node->metadata : null;
|
||||
}
|
||||
|
||||
public function getGroup()
|
||||
{
|
||||
return $this->groupManager->getCurrentGroup();
|
||||
}
|
||||
|
||||
public function getClassName()
|
||||
{
|
||||
$metadata = $this->getMetadata();
|
||||
|
||||
return $metadata instanceof ClassBasedInterface ? $metadata->getClassName() : null;
|
||||
}
|
||||
|
||||
public function getPropertyName()
|
||||
{
|
||||
$metadata = $this->getMetadata();
|
||||
|
||||
return $metadata instanceof PropertyMetadataInterface ? $metadata->getPropertyName() : null;
|
||||
}
|
||||
|
||||
public function getPropertyPath($subPath = '')
|
||||
{
|
||||
$propertyPath = $this->node ? $this->node->propertyPath : '';
|
||||
|
||||
if (strlen($subPath) > 0) {
|
||||
if ('[' === $subPath{1}) {
|
||||
return $propertyPath.$subPath;
|
||||
}
|
||||
|
||||
return $propertyPath ? $propertyPath.'.'.$subPath : $subPath;
|
||||
}
|
||||
|
||||
return $propertyPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ValidatorInterface
|
||||
*/
|
||||
public function getValidator()
|
||||
{
|
||||
return $this->validator;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,154 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Validator\Context;
|
||||
|
||||
use Symfony\Component\Validator\ConstraintViolationListInterface;
|
||||
use Symfony\Component\Validator\Mapping\MetadataInterface;
|
||||
use Symfony\Component\Validator\Validator\ValidatorInterface;
|
||||
|
||||
/**
|
||||
* @since %%NextVersion%%
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
interface ExecutionContextInterface
|
||||
{
|
||||
/**
|
||||
* @return ValidatorInterface
|
||||
*/
|
||||
public function getValidator();
|
||||
|
||||
/**
|
||||
* Adds a violation at the current node of the validation graph.
|
||||
*
|
||||
* @param string $message The error message.
|
||||
* @param array $params The parameters substituted in the error message.
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function addViolation($message, array $params = array());
|
||||
|
||||
public function buildViolation($message);
|
||||
|
||||
/**
|
||||
* Returns the violations generated by the validator so far.
|
||||
*
|
||||
* @return ConstraintViolationListInterface The constraint violation list.
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function getViolations();
|
||||
|
||||
/**
|
||||
* Returns the value at which validation was started in the object graph.
|
||||
*
|
||||
* The validator, when given an object, traverses the properties and
|
||||
* related objects and their properties. The root of the validation is the
|
||||
* object from which the traversal started.
|
||||
*
|
||||
* The current value is returned by {@link getValue}.
|
||||
*
|
||||
* @return mixed The root value of the validation.
|
||||
*/
|
||||
public function getRoot();
|
||||
|
||||
/**
|
||||
* Returns the value that the validator is currently validating.
|
||||
*
|
||||
* If you want to retrieve the object that was originally passed to the
|
||||
* validator, use {@link getRoot}.
|
||||
*
|
||||
* @return mixed The currently validated value.
|
||||
*/
|
||||
public function getValue();
|
||||
|
||||
/**
|
||||
* Returns the metadata for the currently validated value.
|
||||
*
|
||||
* With the core implementation, this method returns a
|
||||
* {@link Mapping\ClassMetadata} instance if the current value is an object,
|
||||
* a {@link Mapping\PropertyMetadata} instance if the current value is
|
||||
* the value of a property and a {@link Mapping\GetterMetadata} instance if
|
||||
* the validated value is the result of a getter method.
|
||||
*
|
||||
* If the validated value is neither of these, for example if the validator
|
||||
* has been called with a plain value and constraint, this method returns
|
||||
* null.
|
||||
*
|
||||
* @return MetadataInterface|null The metadata of the currently validated
|
||||
* value.
|
||||
*/
|
||||
public function getMetadata();
|
||||
|
||||
public function getMetadataFor($object);
|
||||
|
||||
/**
|
||||
* Returns the validation group that is currently being validated.
|
||||
*
|
||||
* @return string The current validation group.
|
||||
*/
|
||||
public function getGroup();
|
||||
|
||||
/**
|
||||
* Returns the class name of the current node.
|
||||
*
|
||||
* If the metadata of the current node does not implement
|
||||
* {@link ClassBasedInterface} or if no metadata is available for the
|
||||
* current node, this method returns null.
|
||||
*
|
||||
* @return string|null The class name or null, if no class name could be found.
|
||||
*/
|
||||
public function getClassName();
|
||||
|
||||
/**
|
||||
* Returns the property name of the current node.
|
||||
*
|
||||
* If the metadata of the current node does not implement
|
||||
* {@link PropertyMetadataInterface} or if no metadata is available for the
|
||||
* current node, this method returns null.
|
||||
*
|
||||
* @return string|null The property name or null, if no property name could be found.
|
||||
*/
|
||||
public function getPropertyName();
|
||||
|
||||
/**
|
||||
* Returns the property path to the value that the validator is currently
|
||||
* validating.
|
||||
*
|
||||
* For example, take the following object graph:
|
||||
*
|
||||
* <pre>
|
||||
* (Person)---($address: Address)---($street: string)
|
||||
* </pre>
|
||||
*
|
||||
* When the <tt>Person</tt> instance is passed to the validator, the
|
||||
* property path is initially empty. When the <tt>$address</tt> property
|
||||
* of that person is validated, the property path is "address". When
|
||||
* the <tt>$street</tt> property of the related <tt>Address</tt> instance
|
||||
* is validated, the property path is "address.street".
|
||||
*
|
||||
* Properties of objects are prefixed with a dot in the property path.
|
||||
* Indices of arrays or objects implementing the {@link \ArrayAccess}
|
||||
* interface are enclosed in brackets. For example, if the property in
|
||||
* the previous example is <tt>$addresses</tt> and contains an array
|
||||
* of <tt>Address</tt> instance, the property path generated for the
|
||||
* <tt>$street</tt> property of one of these addresses is for example
|
||||
* "addresses[0].street".
|
||||
*
|
||||
* @param string $subPath Optional. The suffix appended to the current
|
||||
* property path.
|
||||
*
|
||||
* @return string The current property path. The result may be an empty
|
||||
* string if the validator is currently validating the
|
||||
* root value of the validation graph.
|
||||
*/
|
||||
public function getPropertyPath($subPath = '');
|
||||
}
|
|
@ -0,0 +1,130 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Validator\Context;
|
||||
|
||||
use Symfony\Component\Validator\Group\GroupManagerInterface;
|
||||
use Symfony\Component\Validator\MetadataFactoryInterface;
|
||||
use Symfony\Component\Validator\Node\Node;
|
||||
use Symfony\Component\Validator\NodeTraverser\AbstractVisitor;
|
||||
use Symfony\Component\Validator\Validator\ValidatorInterface;
|
||||
|
||||
/**
|
||||
* @since %%NextVersion%%
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
class ExecutionContextManager extends AbstractVisitor implements ExecutionContextManagerInterface
|
||||
{
|
||||
/**
|
||||
* @var MetadataFactoryInterface
|
||||
*/
|
||||
private $metadataFactory;
|
||||
|
||||
/**
|
||||
* @var GroupManagerInterface
|
||||
*/
|
||||
private $groupManager;
|
||||
|
||||
/**
|
||||
* @var ValidatorInterface
|
||||
*/
|
||||
private $validator;
|
||||
|
||||
/**
|
||||
* @var ExecutionContext
|
||||
*/
|
||||
private $currentContext;
|
||||
|
||||
/**
|
||||
* @var \SplStack|ExecutionContext[]
|
||||
*/
|
||||
private $contextStack;
|
||||
|
||||
public function __construct(MetadataFactoryInterface $metadataFactory, GroupManagerInterface $groupManager)
|
||||
{
|
||||
$this->metadataFactory = $metadataFactory;
|
||||
$this->groupManager = $groupManager;
|
||||
|
||||
$this->reset();
|
||||
}
|
||||
|
||||
public function initialize(ValidatorInterface $validator)
|
||||
{
|
||||
$this->validator = $validator;
|
||||
}
|
||||
|
||||
public function startContext()
|
||||
{
|
||||
if (null !== $this->currentContext) {
|
||||
$this->contextStack->push($this->currentContext);
|
||||
}
|
||||
|
||||
$this->currentContext = new ExecutionContext($this->metadataFactory, $this->validator, $this->groupManager);
|
||||
|
||||
return $this->currentContext;
|
||||
}
|
||||
|
||||
public function stopContext()
|
||||
{
|
||||
$stoppedContext = $this->currentContext;
|
||||
|
||||
if (0 === count($this->contextStack)) {
|
||||
$this->currentContext = null;
|
||||
|
||||
return $stoppedContext;
|
||||
}
|
||||
|
||||
if (1 === count($this->contextStack)) {
|
||||
$this->contextStack->pop();
|
||||
$this->currentContext = null;
|
||||
|
||||
return $stoppedContext;
|
||||
}
|
||||
|
||||
$this->contextStack->pop();
|
||||
$this->currentContext = $this->contextStack->top();
|
||||
|
||||
return $stoppedContext;
|
||||
}
|
||||
|
||||
public function getCurrentContext()
|
||||
{
|
||||
return $this->currentContext;
|
||||
}
|
||||
|
||||
public function afterTraversal(array $nodes)
|
||||
{
|
||||
$this->reset();
|
||||
}
|
||||
|
||||
public function enterNode(Node $node)
|
||||
{
|
||||
if (null === $this->currentContext) {
|
||||
// error no context started
|
||||
}
|
||||
|
||||
$this->currentContext->pushNode($node);
|
||||
}
|
||||
|
||||
public function leaveNode(Node $node)
|
||||
{
|
||||
if (null === $this->currentContext) {
|
||||
// error no context started
|
||||
}
|
||||
|
||||
$this->currentContext->popNode();
|
||||
}
|
||||
|
||||
private function reset()
|
||||
{
|
||||
$this->contextStack = new \SplStack();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Validator\Context;
|
||||
|
||||
/**
|
||||
* @since %%NextVersion%%
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
interface ExecutionContextManagerInterface
|
||||
{
|
||||
/**
|
||||
* @return ExecutionContextInterface The started context
|
||||
*/
|
||||
public function startContext();
|
||||
|
||||
/**
|
||||
* @return ExecutionContextInterface The stopped context
|
||||
*/
|
||||
public function stopContext();
|
||||
|
||||
/**
|
||||
* @return ExecutionContextInterface The current context
|
||||
*/
|
||||
public function getCurrentContext();
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Validator\Context;
|
||||
|
||||
use Symfony\Component\Validator\ExecutionContextInterface as LegacyExecutionContextInterface;
|
||||
|
||||
/**
|
||||
* @since %%NextVersion%%
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
class LegacyExecutionContext extends ExecutionContext implements LegacyExecutionContextInterface
|
||||
{
|
||||
public function addViolationAt($subPath, $message, array $params = array(), $invalidValue = null, $pluralization = null, $code = null)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public function validate($value, $subPath = '', $groups = null, $traverse = false, $deep = false)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public function validateValue($value, $constraints, $subPath = '', $groups = null)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public function getMetadataFactory()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Validator\Group;
|
||||
|
||||
/**
|
||||
* @since %%NextVersion%%
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
interface GroupManagerInterface
|
||||
{
|
||||
public function getCurrentGroup();
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Validator\Mapping;
|
||||
|
||||
use Symfony\Component\Validator\ClassBasedInterface;
|
||||
|
||||
/**
|
||||
* @since %%NextVersion%%
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
interface ClassMetadataInterface extends MetadataInterface, ClassBasedInterface
|
||||
{
|
||||
public function getConstrainedProperties();
|
||||
|
||||
public function hasPropertyMetadata($property);
|
||||
|
||||
/**
|
||||
* Returns all metadata instances for the given named property.
|
||||
*
|
||||
* If your implementation does not support properties, simply throw an
|
||||
* exception in this method (for example a <tt>BadMethodCallException</tt>).
|
||||
*
|
||||
* @param string $property The property name.
|
||||
*
|
||||
* @return PropertyMetadataInterface[] A list of metadata instances. Empty if
|
||||
* no metadata exists for the property.
|
||||
*/
|
||||
public function getPropertyMetadata($property);
|
||||
|
||||
public function hasGroupSequence();
|
||||
|
||||
public function getGroupSequence();
|
||||
|
||||
public function isGroupSequenceProvider();
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Validator\Mapping;
|
||||
|
||||
/**
|
||||
* A container for validation metadata.
|
||||
*
|
||||
* The container contains constraints that may belong to different validation
|
||||
* groups. Constraints for a specific group can be fetched by calling
|
||||
* {@link findConstraints}.
|
||||
*
|
||||
* Implement this interface to add validation metadata to your own metadata
|
||||
* layer. Each metadata may have named properties. Each property can be
|
||||
* represented by one or more {@link PropertyMetadataInterface} instances that
|
||||
* are returned by {@link getPropertyMetadata}. Since
|
||||
* <tt>PropertyMetadataInterface</tt> inherits from <tt>MetadataInterface</tt>,
|
||||
* each property may be divided into further properties.
|
||||
*
|
||||
* The {@link accept} method of each metadata implements the Visitor pattern.
|
||||
* The method should forward the call to the visitor's
|
||||
* {@link ValidationVisitorInterface::visit} method and additionally call
|
||||
* <tt>accept()</tt> on all structurally related metadata instances.
|
||||
*
|
||||
* For example, to store constraints for PHP classes and their properties,
|
||||
* create a class <tt>ClassMetadata</tt> (implementing <tt>MetadataInterface</tt>)
|
||||
* and a class <tt>PropertyMetadata</tt> (implementing <tt>PropertyMetadataInterface</tt>).
|
||||
* <tt>ClassMetadata::getPropertyMetadata($property)</tt> returns all
|
||||
* <tt>PropertyMetadata</tt> instances for a property of that class. Its
|
||||
* <tt>accept()</tt>-method simply forwards to <tt>ValidationVisitorInterface::visit()</tt>
|
||||
* and calls <tt>accept()</tt> on all contained <tt>PropertyMetadata</tt>
|
||||
* instances, which themselves call <tt>ValidationVisitorInterface::visit()</tt>
|
||||
* again.
|
||||
*
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
interface MetadataInterface
|
||||
{
|
||||
/**
|
||||
* Returns all constraints for a given validation group.
|
||||
*
|
||||
* @param string $group The validation group.
|
||||
*
|
||||
* @return \Symfony\Component\Validator\Constraint[] A list of constraint instances.
|
||||
*/
|
||||
public function findConstraints($group);
|
||||
|
||||
public function supportsCascading();
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Validator\Mapping;
|
||||
|
||||
use Symfony\Component\Validator\ClassBasedInterface;
|
||||
|
||||
/**
|
||||
* A container for validation metadata of a property.
|
||||
*
|
||||
* What exactly you define as "property" is up to you. The validator expects
|
||||
* implementations of {@link MetadataInterface} that contain constraints and
|
||||
* optionally a list of named properties that also have constraints (and may
|
||||
* have further sub properties). Such properties are mapped by implementations
|
||||
* of this interface.
|
||||
*
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*
|
||||
* @see MetadataInterface
|
||||
*/
|
||||
interface PropertyMetadataInterface extends MetadataInterface, ClassBasedInterface
|
||||
{
|
||||
/**
|
||||
* Returns the name of the property.
|
||||
*
|
||||
* @return string The property name.
|
||||
*/
|
||||
public function getPropertyName();
|
||||
|
||||
/**
|
||||
* Extracts the value of the property from the given object.
|
||||
*
|
||||
* @param mixed $object The object to extract the property value from.
|
||||
*
|
||||
* @return mixed The value of the property.
|
||||
*/
|
||||
public function getPropertyValue($object);
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Validator\Mapping;
|
||||
|
||||
/**
|
||||
* @since %%NextVersion%%
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
class ValueMetadata implements MetadataInterface
|
||||
{
|
||||
/**
|
||||
* Returns all constraints for a given validation group.
|
||||
*
|
||||
* @param string $group The validation group.
|
||||
*
|
||||
* @return \Symfony\Component\Validator\Constraint[] A list of constraint instances.
|
||||
*/
|
||||
public function findConstraints($group)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public function supportsCascading()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public function supportsIteration()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public function supportsRecursiveIteration()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Validator\Node;
|
||||
|
||||
use Symfony\Component\Validator\Mapping\ClassMetadataInterface;
|
||||
|
||||
/**
|
||||
* @since %%NextVersion%%
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
class ClassNode extends Node
|
||||
{
|
||||
/**
|
||||
* @var ClassMetadataInterface
|
||||
*/
|
||||
public $metadata;
|
||||
|
||||
public function __construct($value, ClassMetadataInterface $metadata, $propertyPath, array $groups)
|
||||
{
|
||||
if (!is_object($value)) {
|
||||
// error
|
||||
}
|
||||
|
||||
parent::__construct(
|
||||
$value,
|
||||
$metadata,
|
||||
$propertyPath,
|
||||
$groups
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Validator\Node;
|
||||
|
||||
use Symfony\Component\Validator\Mapping\MetadataInterface;
|
||||
|
||||
/**
|
||||
* @since %%NextVersion%%
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
abstract class Node
|
||||
{
|
||||
public $value;
|
||||
|
||||
public $metadata;
|
||||
|
||||
public $propertyPath;
|
||||
|
||||
public $groups;
|
||||
|
||||
public function __construct($value, MetadataInterface $metadata, $propertyPath, array $groups)
|
||||
{
|
||||
$this->value = $value;
|
||||
$this->metadata = $metadata;
|
||||
$this->propertyPath = $propertyPath;
|
||||
$this->groups = $groups;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Validator\Node;
|
||||
|
||||
use Symfony\Component\Validator\Mapping\PropertyMetadataInterface;
|
||||
|
||||
/**
|
||||
* @since %%NextVersion%%
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
class PropertyNode extends Node
|
||||
{
|
||||
/**
|
||||
* @var PropertyMetadataInterface
|
||||
*/
|
||||
public $metadata;
|
||||
|
||||
public function __construct($value, PropertyMetadataInterface $metadata, $propertyPath, array $groups)
|
||||
{
|
||||
parent::__construct(
|
||||
$value,
|
||||
$metadata,
|
||||
$propertyPath,
|
||||
$groups
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Validator\Node;
|
||||
|
||||
/**
|
||||
* @since %%NextVersion%%
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
class ValueNode extends Node
|
||||
{
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Validator\NodeTraverser;
|
||||
|
||||
use Symfony\Component\Validator\Node\Node;
|
||||
|
||||
/**
|
||||
* @since %%NextVersion%%
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
abstract class AbstractVisitor implements NodeVisitorInterface
|
||||
{
|
||||
public function beforeTraversal(array $nodes)
|
||||
{
|
||||
}
|
||||
|
||||
public function afterTraversal(array $nodes)
|
||||
{
|
||||
}
|
||||
|
||||
public function enterNode(Node $node)
|
||||
{
|
||||
}
|
||||
|
||||
public function leaveNode(Node $node)
|
||||
{
|
||||
}
|
||||
}
|
|
@ -0,0 +1,166 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Validator\NodeTraverser;
|
||||
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
use Symfony\Component\Validator\Context\ExecutionContextManagerInterface;
|
||||
use Symfony\Component\Validator\MetadataFactoryInterface;
|
||||
|
||||
/**
|
||||
* @since %%NextVersion%%
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
class NodeTraverser implements NodeTraverserInterface
|
||||
{
|
||||
/**
|
||||
* @var NodeVisitorInterface[]
|
||||
*/
|
||||
private $visitors;
|
||||
|
||||
/**
|
||||
* @var MetadataFactoryInterface
|
||||
*/
|
||||
private $metadataFactory;
|
||||
|
||||
private $traversalStarted = false;
|
||||
|
||||
public function __construct(MetadataFactoryInterface $metadataFactory)
|
||||
{
|
||||
$this->visitors = new \SplObjectStorage();
|
||||
$this->metadataFactory = $metadataFactory;
|
||||
}
|
||||
|
||||
public function addVisitor(NodeVisitorInterface $visitor)
|
||||
{
|
||||
$this->visitors->attach($visitor);
|
||||
}
|
||||
|
||||
public function removeVisitor(NodeVisitorInterface $visitor)
|
||||
{
|
||||
$this->visitors->detach($visitor);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function traverse(array $nodes)
|
||||
{
|
||||
$isTopLevelCall = !$this->traversalStarted;
|
||||
|
||||
if ($isTopLevelCall) {
|
||||
$this->traversalStarted = true;
|
||||
|
||||
foreach ($this->visitors as $visitor) {
|
||||
/** @var NodeVisitorInterface $visitor */
|
||||
$visitor->beforeTraversal($nodes);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($nodes as $node) {
|
||||
if ($node instanceof ClassNode) {
|
||||
$this->traverseClassNode($node);
|
||||
} else {
|
||||
$this->traverseNode($node);
|
||||
}
|
||||
}
|
||||
|
||||
if ($isTopLevelCall) {
|
||||
$this->traversalStarted = false;
|
||||
|
||||
foreach ($this->visitors as $visitor) {
|
||||
/** @var NodeVisitorInterface $visitor */
|
||||
$visitor->afterTraversal($nodes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function traverseNode(Node $node)
|
||||
{
|
||||
$stopTraversal = false;
|
||||
|
||||
foreach ($this->visitors as $visitor) {
|
||||
if (false === $visitor->enterNode($node)) {
|
||||
$stopTraversal = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Stop the traversal, but execute the leaveNode() methods anyway to
|
||||
// perform possible cleanups
|
||||
if (!$stopTraversal && is_object($node->value) && $node->metadata->supportsCascading()) {
|
||||
$classMetadata = $this->metadataFactory->getMetadataFor($node->value);
|
||||
|
||||
$this->traverseClassNode(new ClassNode(
|
||||
$node->value,
|
||||
$classMetadata,
|
||||
$node->propertyPath,
|
||||
$node->groups
|
||||
));
|
||||
}
|
||||
|
||||
foreach ($this->visitors as $visitor) {
|
||||
$visitor->leaveNode($node);
|
||||
}
|
||||
}
|
||||
|
||||
private function traverseClassNode(ClassNode $node)
|
||||
{
|
||||
// Replace "Default" group by the group sequence attached to the class
|
||||
// (if any)
|
||||
foreach ($node->groups as $key => $group) {
|
||||
if (Constraint::DEFAULT_GROUP !== $group) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($node->metadata->hasGroupSequence()) {
|
||||
$node->groups[$key] = $node->metadata->getGroupSequence();
|
||||
} elseif ($node->metadata->isGroupSequenceProvider()) {
|
||||
/** @var \Symfony\Component\Validator\GroupSequenceProviderInterface $value */
|
||||
$node->groups[$key] = $value->getGroupSequence();
|
||||
}
|
||||
|
||||
// Cascade the "Default" group when validating the sequence
|
||||
$node->groups[$key]->cascadedGroup = Constraint::DEFAULT_GROUP;
|
||||
|
||||
// "Default" group found, abort
|
||||
break;
|
||||
}
|
||||
|
||||
$stopTraversal = false;
|
||||
|
||||
foreach ($this->visitors as $visitor) {
|
||||
if (false === $visitor->enterNode($node)) {
|
||||
$stopTraversal = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Stop the traversal, but execute the leaveNode() methods anyway to
|
||||
// perform possible cleanups
|
||||
if (!$stopTraversal) {
|
||||
foreach ($node->metadata->getConstrainedProperties() as $propertyName) {
|
||||
foreach ($node->metadata->getPropertyMetadata($propertyName) as $propertyMetadata) {
|
||||
$this->traverseNode(new PropertyNode(
|
||||
$propertyMetadata->getPropertyValue($node->value),
|
||||
$propertyMetadata,
|
||||
$node->propertyPath
|
||||
? $node->propertyPath.'.'.$propertyName
|
||||
: $propertyName,
|
||||
$node->groups
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($this->visitors as $visitor) {
|
||||
$visitor->leaveNode($node);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Validator\NodeTraverser;
|
||||
|
||||
use Symfony\Component\Validator\Node\Node;
|
||||
|
||||
/**
|
||||
* @since %%NextVersion%%
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
interface NodeTraverserInterface
|
||||
{
|
||||
public function addVisitor(NodeVisitorInterface $visitor);
|
||||
|
||||
public function removeVisitor(NodeVisitorInterface $visitor);
|
||||
|
||||
/**
|
||||
* @param Node[] $nodes
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function traverse(array $nodes);
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Validator\NodeTraverser;
|
||||
|
||||
use Symfony\Component\Validator\Node\Node;
|
||||
|
||||
/**
|
||||
* @since %%NextVersion%%
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
interface NodeVisitorInterface
|
||||
{
|
||||
public function beforeTraversal(array $nodes);
|
||||
|
||||
public function afterTraversal(array $nodes);
|
||||
|
||||
public function enterNode(Node $node);
|
||||
|
||||
public function leaveNode(Node $node);
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Validator\Tests\Validator;
|
||||
|
||||
use Symfony\Component\Validator\Context\ExecutionContextManager;
|
||||
use Symfony\Component\Validator\MetadataFactoryInterface;
|
||||
use Symfony\Component\Validator\Tests\AbstractValidatorTest;
|
||||
use Symfony\Component\Validator\NodeTraverser\NodeTraverser;
|
||||
use Symfony\Component\Validator\NodeTraverser\NodeVisitor\NodeValidator;
|
||||
use Symfony\Component\Validator\DefaultTranslator;
|
||||
use Symfony\Component\Validator\ConstraintValidatorFactory;
|
||||
use Symfony\Component\Validator\Validator\Validator;
|
||||
|
||||
class TraversingValidatorTest extends AbstractValidatorTest
|
||||
{
|
||||
protected function createValidator(MetadataFactoryInterface $metadataFactory)
|
||||
{
|
||||
$validatorFactory = new ConstraintValidatorFactory();
|
||||
$nodeTraverser = new NodeTraverser($metadataFactory);
|
||||
$nodeValidator = new NodeValidator($validatorFactory, $nodeTraverser);
|
||||
$contextManager = new ExecutionContextManager($metadataFactory, $nodeValidator, new DefaultTranslator());
|
||||
$validator = new Validator($nodeTraverser, $metadataFactory, $contextManager);
|
||||
|
||||
$contextManager->initialize($validator);
|
||||
$nodeValidator->setContextManager($contextManager);
|
||||
|
||||
$nodeTraverser->addVisitor($contextManager);
|
||||
$nodeTraverser->addVisitor($nodeValidator);
|
||||
|
||||
return $validator;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,155 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Validator\Validator;
|
||||
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
||||
use Symfony\Component\Validator\Mapping\ClassMetadataInterface;
|
||||
use Symfony\Component\Validator\Mapping\ValueMetadata;
|
||||
use Symfony\Component\Validator\MetadataFactoryInterface;
|
||||
use Symfony\Component\Validator\NodeTraverser\ClassNode;
|
||||
use Symfony\Component\Validator\NodeTraverser\NodeTraverserInterface;
|
||||
use Symfony\Component\Validator\NodeTraverser\PropertyNode;
|
||||
use Symfony\Component\Validator\NodeTraverser\ValueNode;
|
||||
|
||||
/**
|
||||
* @since %%NextVersion%%
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
abstract class AbstractValidator implements ValidatorInterface
|
||||
{
|
||||
/**
|
||||
* @var NodeTraverserInterface
|
||||
*/
|
||||
protected $nodeTraverser;
|
||||
|
||||
/**
|
||||
* @var MetadataFactoryInterface
|
||||
*/
|
||||
protected $metadataFactory;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $defaultPropertyPath = '';
|
||||
|
||||
protected $defaultGroups = array(Constraint::DEFAULT_GROUP);
|
||||
|
||||
public function __construct(NodeTraverserInterface $nodeTraverser, MetadataFactoryInterface $metadataFactory)
|
||||
{
|
||||
$this->nodeTraverser = $nodeTraverser;
|
||||
$this->metadataFactory = $metadataFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ExecutionContextInterface $context
|
||||
*
|
||||
* @return ContextualValidatorInterface
|
||||
*/
|
||||
public function inContext(ExecutionContextInterface $context)
|
||||
{
|
||||
return new ContextualValidator($this->nodeTraverser, $this->metadataFactory, $context);
|
||||
}
|
||||
|
||||
public function getMetadataFactory()
|
||||
{
|
||||
return $this->metadataFactory;
|
||||
}
|
||||
|
||||
protected function traverseObject($object, $groups = null)
|
||||
{
|
||||
$classMetadata = $this->metadataFactory->getMetadataFor($object);
|
||||
|
||||
if (!$classMetadata instanceof ClassMetadataInterface) {
|
||||
// error
|
||||
}
|
||||
|
||||
$this->nodeTraverser->traverse(array(new ClassNode(
|
||||
$object,
|
||||
$classMetadata,
|
||||
$this->defaultPropertyPath,
|
||||
// TODO use cascade group here
|
||||
$groups ? $this->normalizeGroups($groups) : $this->defaultGroups
|
||||
)));
|
||||
}
|
||||
|
||||
protected function traverseProperty($object, $propertyName, $groups = null)
|
||||
{
|
||||
$classMetadata = $this->metadataFactory->getMetadataFor($object);
|
||||
|
||||
if (!$classMetadata instanceof ClassMetadataInterface) {
|
||||
// error
|
||||
}
|
||||
|
||||
$propertyMetadatas = $classMetadata->getPropertyMetadata($propertyName);
|
||||
$groups = $groups ? $this->normalizeGroups($groups) : $this->defaultGroups;
|
||||
$nodes = array();
|
||||
|
||||
foreach ($propertyMetadatas as $propertyMetadata) {
|
||||
$propertyValue = $propertyMetadata->getPropertyValue($object);
|
||||
|
||||
$nodes[] = new PropertyNode(
|
||||
$propertyValue,
|
||||
$propertyMetadata,
|
||||
$this->defaultPropertyPath,
|
||||
$groups
|
||||
);
|
||||
}
|
||||
|
||||
$this->nodeTraverser->traverse($nodes);
|
||||
}
|
||||
|
||||
protected function traversePropertyValue($object, $propertyName, $value, $groups = null)
|
||||
{
|
||||
$classMetadata = $this->metadataFactory->getMetadataFor($object);
|
||||
|
||||
if (!$classMetadata instanceof ClassMetadataInterface) {
|
||||
// error
|
||||
}
|
||||
|
||||
$propertyMetadatas = $classMetadata->getPropertyMetadata($propertyName);
|
||||
$groups = $groups ? $this->normalizeGroups($groups) : $this->defaultGroups;
|
||||
$nodes = array();
|
||||
|
||||
foreach ($propertyMetadatas as $propertyMetadata) {
|
||||
$nodes[] = new PropertyNode(
|
||||
$value,
|
||||
$propertyMetadata,
|
||||
$this->defaultPropertyPath,
|
||||
$groups
|
||||
);
|
||||
}
|
||||
|
||||
$this->nodeTraverser->traverse($nodes);
|
||||
}
|
||||
|
||||
protected function traverseValue($value, $constraints, $groups = null)
|
||||
{
|
||||
$metadata = new ValueMetadata($constraints);
|
||||
|
||||
$this->nodeTraverser->traverse(array(new ValueNode(
|
||||
$value,
|
||||
$metadata,
|
||||
$this->defaultPropertyPath,
|
||||
$groups ? $this->normalizeGroups($groups) : $this->defaultGroups
|
||||
)));
|
||||
}
|
||||
|
||||
protected function normalizeGroups($groups)
|
||||
{
|
||||
if (is_array($groups)) {
|
||||
return $groups;
|
||||
}
|
||||
|
||||
return array($groups);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,123 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Validator\Validator;
|
||||
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
use Symfony\Component\Validator\ConstraintViolationListInterface;
|
||||
use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
||||
use Symfony\Component\Validator\Context\ExecutionContextManagerInterface;
|
||||
use Symfony\Component\Validator\MetadataFactoryInterface;
|
||||
use Symfony\Component\Validator\NodeTraverser\NodeTraverserInterface;
|
||||
|
||||
/**
|
||||
* @since %%NextVersion%%
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
class ContextualValidator extends AbstractValidator implements ContextualValidatorInterface
|
||||
{
|
||||
/**
|
||||
* @var ExecutionContextManagerInterface
|
||||
*/
|
||||
private $context;
|
||||
|
||||
public function __construct(NodeTraverserInterface $nodeTraverser, MetadataFactoryInterface $metadataFactory, ExecutionContextInterface $context)
|
||||
{
|
||||
parent::__construct($nodeTraverser, $metadataFactory);
|
||||
|
||||
$this->context = $context;
|
||||
$this->defaultPropertyPath = $context->getPropertyPath();
|
||||
$this->defaultGroups = array($context->getGroup());
|
||||
}
|
||||
|
||||
public function atPath($subPath)
|
||||
{
|
||||
$this->defaultPropertyPath = $this->context->getPropertyPath($subPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates a value.
|
||||
*
|
||||
* The accepted values depend on the {@link MetadataFactoryInterface}
|
||||
* implementation.
|
||||
*
|
||||
* @param mixed $object The value to validate
|
||||
* @param array|null $groups The validation groups to validate.
|
||||
*
|
||||
* @return ConstraintViolationListInterface A list of constraint violations. If the
|
||||
* list is empty, validation succeeded.
|
||||
*/
|
||||
public function validateObject($object, $groups = null)
|
||||
{
|
||||
$this->traverseObject($object, $groups);
|
||||
|
||||
return $this->context->getViolations();
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates a property of a value against its current value.
|
||||
*
|
||||
* The accepted values depend on the {@link MetadataFactoryInterface}
|
||||
* implementation.
|
||||
*
|
||||
* @param mixed $object The value containing the property.
|
||||
* @param string $propertyName The name of the property to validate.
|
||||
* @param array|null $groups The validation groups to validate.
|
||||
*
|
||||
* @return ConstraintViolationListInterface A list of constraint violations. If the
|
||||
* list is empty, validation succeeded.
|
||||
*/
|
||||
public function validateProperty($object, $propertyName, $groups = null)
|
||||
{
|
||||
$this->traverseProperty($object, $propertyName, $groups);
|
||||
|
||||
return $this->context->getViolations();
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate a property of a value against a potential value.
|
||||
*
|
||||
* The accepted values depend on the {@link MetadataFactoryInterface}
|
||||
* implementation.
|
||||
*
|
||||
* @param string $object The value containing the property.
|
||||
* @param string $propertyName The name of the property to validate
|
||||
* @param string $value The value to validate against the
|
||||
* constraints of the property.
|
||||
* @param array|null $groups The validation groups to validate.
|
||||
*
|
||||
* @return ConstraintViolationListInterface A list of constraint violations. If the
|
||||
* list is empty, validation succeeded.
|
||||
*/
|
||||
public function validatePropertyValue($object, $propertyName, $value, $groups = null)
|
||||
{
|
||||
$this->traversePropertyValue($object, $propertyName, $value, $groups);
|
||||
|
||||
return $this->context->getViolations();
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates a value against a constraint or a list of constraints.
|
||||
*
|
||||
* @param mixed $value The value to validate.
|
||||
* @param Constraint|Constraint[] $constraints The constraint(s) to validate against.
|
||||
* @param array|null $groups The validation groups to validate.
|
||||
*
|
||||
* @return ConstraintViolationListInterface A list of constraint violations. If the
|
||||
* list is empty, validation succeeded.
|
||||
*/
|
||||
public function validateValue($value, $constraints, $groups = null)
|
||||
{
|
||||
$this->traverseValue($value, $constraints, $groups);
|
||||
|
||||
return $this->context->getViolations();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Validator\Validator;
|
||||
|
||||
/**
|
||||
* @since %%NextVersion%%
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
interface ContextualValidatorInterface extends ValidatorInterface
|
||||
{
|
||||
/**
|
||||
* @param $subPath
|
||||
*
|
||||
* @return ContextualValidatorInterface
|
||||
*/
|
||||
public function atPath($subPath);
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Validator\Validator;
|
||||
|
||||
use Symfony\Component\Validator\ValidatorInterface as LegacyValidatorInterface;
|
||||
|
||||
/**
|
||||
* @since %%NextVersion%%
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
class LegacyValidator extends Validator implements LegacyValidatorInterface
|
||||
{
|
||||
public function validate($value, $groups = null, $traverse = false, $deep = false)
|
||||
{
|
||||
// TODO what about $traverse and $deep?
|
||||
return $this->validateObject($value, $groups);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,155 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Validator\Validator;
|
||||
|
||||
use Symfony\Component\Validator\Constraints\GroupSequence;
|
||||
use Symfony\Component\Validator\ConstraintValidatorFactoryInterface;
|
||||
use Symfony\Component\Validator\Context\ExecutionContextManagerInterface;
|
||||
use Symfony\Component\Validator\Group\GroupManagerInterface;
|
||||
use Symfony\Component\Validator\Node\ClassNode;
|
||||
use Symfony\Component\Validator\Node\Node;
|
||||
use Symfony\Component\Validator\NodeTraverser\AbstractVisitor;
|
||||
use Symfony\Component\Validator\NodeTraverser\NodeTraverserInterface;
|
||||
|
||||
/**
|
||||
* @since %%NextVersion%%
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
class NodeValidator extends AbstractVisitor implements GroupManagerInterface
|
||||
{
|
||||
private $validatedNodes = array();
|
||||
|
||||
/**
|
||||
* @var ConstraintValidatorFactoryInterface
|
||||
*/
|
||||
private $validatorFactory;
|
||||
|
||||
/**
|
||||
* @var ExecutionContextManagerInterface
|
||||
*/
|
||||
private $contextManager;
|
||||
|
||||
/**
|
||||
* @var NodeTraverserInterface
|
||||
*/
|
||||
private $nodeTraverser;
|
||||
|
||||
private $currentGroup;
|
||||
|
||||
public function __construct(ConstraintValidatorFactoryInterface $validatorFactory, NodeTraverserInterface $nodeTraverser)
|
||||
{
|
||||
$this->validatorFactory = $validatorFactory;
|
||||
$this->nodeTraverser = $nodeTraverser;
|
||||
}
|
||||
|
||||
public function setContextManager(ExecutionContextManagerInterface $contextManager)
|
||||
{
|
||||
$this->contextManager = $contextManager;
|
||||
}
|
||||
|
||||
public function afterTraversal(array $nodes)
|
||||
{
|
||||
$this->validatedNodes = array();
|
||||
}
|
||||
|
||||
public function enterNode(Node $node)
|
||||
{
|
||||
$cacheKey = $node instanceof ClassNode
|
||||
? spl_object_hash($node->value)
|
||||
: null;
|
||||
|
||||
// if group (=[<G1,G2>,G3,G4]) contains group sequence (=<G1,G2>)
|
||||
// then call traverse() with each entry of the group sequence and abort
|
||||
// if necessary (G1, G2)
|
||||
// finally call traverse() with remaining entries ([G3,G4]) or
|
||||
// simply continue traversal (if possible)
|
||||
|
||||
foreach ($node->groups as $group) {
|
||||
// Validate object nodes only once per group
|
||||
if (null !== $cacheKey) {
|
||||
// Use the object hash for group sequences
|
||||
$groupKey = is_object($group) ? spl_object_hash($group) : $group;
|
||||
|
||||
// Exit, if the object is already validated for the current group
|
||||
if (isset($this->validatedNodes[$cacheKey][$groupKey])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Remember validating this object before starting and possibly
|
||||
// traversing the object graph
|
||||
$this->validatedNodes[$cacheKey][$groupKey] = true;
|
||||
}
|
||||
|
||||
// Validate group sequence until a violation is generated
|
||||
if ($group instanceof GroupSequence) {
|
||||
// Rename for clarity
|
||||
$groupSequence = $group;
|
||||
|
||||
// Only evaluate group sequences at class, not at property level
|
||||
if (!$node instanceof ClassNode) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$context = $this->contextManager->getCurrentContext();
|
||||
$violationCount = count($context->getViolations());
|
||||
|
||||
foreach ($groupSequence->groups as $groupInSequence) {
|
||||
$this->nodeTraverser->traverse(array(new ClassNode(
|
||||
$node->value,
|
||||
$node->metadata,
|
||||
$node->propertyPath,
|
||||
array($groupInSequence),
|
||||
array($groupSequence->cascadedGroup ?: $groupInSequence)
|
||||
)));
|
||||
|
||||
// Abort sequence validation if a violation was generated
|
||||
if (count($context->getViolations()) > $violationCount) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Optimization: If the groups only contain the group sequence,
|
||||
// we can skip the traversal for the properties of the object
|
||||
if (1 === count($node->groups)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// We're done for the current loop execution.
|
||||
continue;
|
||||
}
|
||||
|
||||
// Validate normal group (non group sequences)
|
||||
try {
|
||||
$this->currentGroup = $group;
|
||||
|
||||
foreach ($node->metadata->findConstraints($group) as $constraint) {
|
||||
$validator = $this->validatorFactory->getInstance($constraint);
|
||||
$validator->initialize($this->contextManager->getCurrentContext());
|
||||
$validator->validate($node->value, $constraint);
|
||||
}
|
||||
|
||||
$this->currentGroup = null;
|
||||
} catch (\Exception $e) {
|
||||
$this->currentGroup = null;
|
||||
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getCurrentGroup()
|
||||
{
|
||||
return $this->currentGroup;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Validator\Validator;
|
||||
|
||||
use Symfony\Component\Validator\Context\ExecutionContextManagerInterface;
|
||||
use Symfony\Component\Validator\NodeTraverser\NodeTraverserInterface;
|
||||
use Symfony\Component\Validator\MetadataFactoryInterface;
|
||||
|
||||
/**
|
||||
* @since %%NextVersion%%
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
class Validator extends AbstractValidator
|
||||
{
|
||||
/**
|
||||
* @var ExecutionContextManagerInterface
|
||||
*/
|
||||
private $contextManager;
|
||||
|
||||
public function __construct(NodeTraverserInterface $nodeTraverser, MetadataFactoryInterface $metadataFactory, ExecutionContextManagerInterface $contextManager)
|
||||
{
|
||||
parent::__construct($nodeTraverser, $metadataFactory);
|
||||
|
||||
$this->contextManager = $contextManager;
|
||||
}
|
||||
|
||||
public function validateObject($object, $groups = null)
|
||||
{
|
||||
$this->contextManager->startContext();
|
||||
|
||||
$this->traverseObject($object, $groups);
|
||||
|
||||
return $this->contextManager->stopContext()->getViolations();
|
||||
}
|
||||
|
||||
public function validateProperty($object, $propertyName, $groups = null)
|
||||
{
|
||||
$this->contextManager->startContext();
|
||||
|
||||
$this->traverseProperty($object, $propertyName, $groups);
|
||||
|
||||
return $this->contextManager->stopContext()->getViolations();
|
||||
}
|
||||
|
||||
public function validatePropertyValue($object, $propertyName, $value, $groups = null)
|
||||
{
|
||||
$this->contextManager->startContext();
|
||||
|
||||
$this->traversePropertyValue($object, $propertyName, $value, $groups);
|
||||
|
||||
return $this->contextManager->stopContext()->getViolations();
|
||||
}
|
||||
|
||||
public function validateValue($value, $constraints, $groups = null)
|
||||
{
|
||||
$this->contextManager->startContext();
|
||||
|
||||
$this->traverseValue($value, $constraints, $groups);
|
||||
|
||||
return $this->contextManager->stopContext()->getViolations();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Validator\Validator;
|
||||
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
use Symfony\Component\Validator\ConstraintViolationListInterface;
|
||||
use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
||||
|
||||
/**
|
||||
* @since %%NextVersion%%
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
interface ValidatorInterface
|
||||
{
|
||||
/**
|
||||
* Validates a value.
|
||||
*
|
||||
* The accepted values depend on the {@link MetadataFactoryInterface}
|
||||
* implementation.
|
||||
*
|
||||
* @param mixed $object The value to validate
|
||||
* @param array|null $groups The validation groups to validate.
|
||||
*
|
||||
* @return ConstraintViolationListInterface A list of constraint violations. If the
|
||||
* list is empty, validation succeeded.
|
||||
*/
|
||||
public function validateObject($object, $groups = null);
|
||||
|
||||
/**
|
||||
* Validates a property of a value against its current value.
|
||||
*
|
||||
* The accepted values depend on the {@link MetadataFactoryInterface}
|
||||
* implementation.
|
||||
*
|
||||
* @param mixed $object The value containing the property.
|
||||
* @param string $propertyName The name of the property to validate.
|
||||
* @param array|null $groups The validation groups to validate.
|
||||
*
|
||||
* @return ConstraintViolationListInterface A list of constraint violations. If the
|
||||
* list is empty, validation succeeded.
|
||||
*/
|
||||
public function validateProperty($object, $propertyName, $groups = null);
|
||||
|
||||
/**
|
||||
* Validate a property of a value against a potential value.
|
||||
*
|
||||
* The accepted values depend on the {@link MetadataFactoryInterface}
|
||||
* implementation.
|
||||
*
|
||||
* @param string $object The value containing the property.
|
||||
* @param string $propertyName The name of the property to validate
|
||||
* @param string $value The value to validate against the
|
||||
* constraints of the property.
|
||||
* @param array|null $groups The validation groups to validate.
|
||||
*
|
||||
* @return ConstraintViolationListInterface A list of constraint violations. If the
|
||||
* list is empty, validation succeeded.
|
||||
*/
|
||||
public function validatePropertyValue($object, $propertyName, $value, $groups = null);
|
||||
|
||||
/**
|
||||
* Validates a value against a constraint or a list of constraints.
|
||||
*
|
||||
* @param mixed $value The value to validate.
|
||||
* @param Constraint|Constraint[] $constraints The constraint(s) to validate against.
|
||||
* @param array|null $groups The validation groups to validate.
|
||||
*
|
||||
* @return ConstraintViolationListInterface A list of constraint violations. If the
|
||||
* list is empty, validation succeeded.
|
||||
*/
|
||||
public function validateValue($value, $constraints, $groups = null);
|
||||
|
||||
/**
|
||||
* @param ExecutionContextInterface $context
|
||||
*
|
||||
* @return ContextualValidatorInterface
|
||||
*/
|
||||
public function inContext(ExecutionContextInterface $context);
|
||||
}
|
Reference in New Issue