2010-06-24 09:40:05 +01:00
|
|
|
<?php
|
|
|
|
|
2010-10-02 11:42:31 +01:00
|
|
|
/*
|
2011-01-15 13:29:43 +00:00
|
|
|
* This file is part of the Symfony package.
|
2010-10-02 11:42:31 +01:00
|
|
|
*
|
2011-03-06 11:40:06 +00:00
|
|
|
* (c) Fabien Potencier <fabien@symfony.com>
|
2010-10-02 11:42:31 +01:00
|
|
|
*
|
2011-01-15 13:29:43 +00:00
|
|
|
* For the full copyright and license information, please view the LICENSE
|
|
|
|
* file that was distributed with this source code.
|
2010-10-02 11:42:31 +01:00
|
|
|
*/
|
|
|
|
|
2011-01-15 13:29:43 +00:00
|
|
|
namespace Symfony\Component\Validator;
|
|
|
|
|
2010-08-20 22:09:55 +01:00
|
|
|
use Symfony\Component\Validator\ConstraintValidatorFactoryInterface;
|
|
|
|
use Symfony\Component\Validator\Constraint;
|
|
|
|
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
|
|
|
|
use Symfony\Component\Validator\Mapping\ClassMetadataFactoryInterface;
|
|
|
|
use Symfony\Component\Validator\Mapping\ClassMetadata;
|
|
|
|
use Symfony\Component\Validator\Mapping\MemberMetadata;
|
2010-06-24 09:40:05 +01:00
|
|
|
|
2011-01-16 04:41:23 +00:00
|
|
|
/**
|
|
|
|
* Responsible for walking over and initializing validation on different
|
|
|
|
* types of items.
|
|
|
|
*
|
2011-03-06 11:40:06 +00:00
|
|
|
* @author Fabien Potencier <fabien@symfony.com>
|
|
|
|
* @author Bernhard Schussek <bernhard.schussek@symfony.com>
|
2011-01-16 04:41:23 +00:00
|
|
|
*/
|
2010-06-24 09:40:05 +01:00
|
|
|
class GraphWalker
|
|
|
|
{
|
2012-01-31 20:35:48 +00:00
|
|
|
private $globalContext;
|
|
|
|
private $validatorFactory;
|
|
|
|
private $metadataFactory;
|
|
|
|
private $validatorInitializers = array();
|
|
|
|
private $validatedObjects = array();
|
2010-06-24 10:24:08 +01:00
|
|
|
|
2011-07-06 11:34:41 +01:00
|
|
|
public function __construct($root, ClassMetadataFactoryInterface $metadataFactory, ConstraintValidatorFactoryInterface $factory, array $validatorInitializers = array())
|
2010-06-24 09:40:05 +01:00
|
|
|
{
|
2012-01-31 20:35:48 +00:00
|
|
|
$this->globalContext = new GlobalExecutionContext($root, $this, $metadataFactory);
|
2010-06-24 10:24:08 +01:00
|
|
|
$this->validatorFactory = $factory;
|
|
|
|
$this->metadataFactory = $metadataFactory;
|
2011-07-06 11:34:41 +01:00
|
|
|
$this->validatorInitializers = $validatorInitializers;
|
2010-06-24 09:40:05 +01:00
|
|
|
}
|
|
|
|
|
2011-01-16 04:41:23 +00:00
|
|
|
/**
|
|
|
|
* @return ConstraintViolationList
|
|
|
|
*/
|
2010-06-24 10:24:08 +01:00
|
|
|
public function getViolations()
|
2010-06-24 09:40:05 +01:00
|
|
|
{
|
2012-01-31 20:35:48 +00:00
|
|
|
return $this->globalContext->getViolations();
|
2010-06-24 09:40:05 +01:00
|
|
|
}
|
|
|
|
|
2011-01-16 04:41:23 +00:00
|
|
|
/**
|
|
|
|
* Initialize validation on the given object using the given metadata
|
|
|
|
* instance and validation group.
|
|
|
|
*
|
|
|
|
* @param ClassMetadata $metadata
|
2011-04-23 16:05:44 +01:00
|
|
|
* @param object $object The object to validate
|
|
|
|
* @param string $group The validator group to use for validation
|
|
|
|
* @param string $propertyPath
|
2011-01-16 04:41:23 +00:00
|
|
|
*/
|
2011-01-19 14:57:40 +00:00
|
|
|
public function walkObject(ClassMetadata $metadata, $object, $group, $propertyPath)
|
2010-06-24 09:40:05 +01:00
|
|
|
{
|
2011-07-06 11:34:41 +01:00
|
|
|
foreach ($this->validatorInitializers as $initializer) {
|
|
|
|
if (!$initializer instanceof ObjectInitializerInterface) {
|
|
|
|
throw new \LogicException('Validator initializers must implement ObjectInitializerInterface.');
|
|
|
|
}
|
|
|
|
$initializer->initialize($object);
|
|
|
|
}
|
|
|
|
|
2012-02-02 14:59:37 +00:00
|
|
|
if ($group === Constraint::DEFAULT_GROUP && ($metadata->hasGroupSequence() || $metadata->isGroupSequenceProvider())) {
|
2012-01-28 21:10:33 +00:00
|
|
|
if ($metadata->hasGroupSequence()) {
|
|
|
|
$groups = $metadata->getGroupSequence();
|
|
|
|
} else {
|
2012-02-02 19:27:06 +00:00
|
|
|
$groups = $object->getGroupSequence();
|
2012-01-27 01:33:45 +00:00
|
|
|
}
|
|
|
|
|
2010-11-16 23:51:45 +00:00
|
|
|
foreach ($groups as $group) {
|
2011-01-19 14:57:40 +00:00
|
|
|
$this->walkObjectForGroup($metadata, $object, $group, $propertyPath, Constraint::DEFAULT_GROUP);
|
2010-11-16 23:51:45 +00:00
|
|
|
|
|
|
|
if (count($this->getViolations()) > 0) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
2011-01-19 14:57:40 +00:00
|
|
|
$this->walkObjectForGroup($metadata, $object, $group, $propertyPath);
|
2010-11-16 23:51:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-01-19 14:57:40 +00:00
|
|
|
protected function walkObjectForGroup(ClassMetadata $metadata, $object, $group, $propertyPath, $propagatedGroup = null)
|
2010-11-16 23:51:45 +00:00
|
|
|
{
|
2011-01-19 14:57:40 +00:00
|
|
|
$hash = spl_object_hash($object);
|
|
|
|
|
|
|
|
// Exit, if the object is already validated for the current group
|
|
|
|
if (isset($this->validatedObjects[$hash])) {
|
|
|
|
if (isset($this->validatedObjects[$hash][$group])) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
$this->validatedObjects[$hash] = array();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Remember validating this object before starting and possibly
|
|
|
|
// traversing the object graph
|
|
|
|
$this->validatedObjects[$hash][$group] = true;
|
|
|
|
|
2012-01-31 20:35:48 +00:00
|
|
|
$currentClass = $metadata->getClassName();
|
|
|
|
|
2010-06-24 10:24:08 +01:00
|
|
|
foreach ($metadata->findConstraints($group) as $constraint) {
|
2012-01-31 20:35:48 +00:00
|
|
|
$this->walkConstraint($constraint, $object, $group, $propertyPath, $currentClass);
|
2010-06-24 10:24:08 +01:00
|
|
|
}
|
|
|
|
|
2010-12-21 02:59:17 +00:00
|
|
|
if (null !== $object) {
|
2010-06-24 10:24:08 +01:00
|
|
|
foreach ($metadata->getConstrainedProperties() as $property) {
|
|
|
|
$localPropertyPath = empty($propertyPath) ? $property : $propertyPath.'.'.$property;
|
|
|
|
|
2010-11-16 23:51:45 +00:00
|
|
|
$this->walkProperty($metadata, $property, $object, $group, $localPropertyPath, $propagatedGroup);
|
2010-06-24 10:24:08 +01:00
|
|
|
}
|
|
|
|
}
|
2010-06-24 09:40:05 +01:00
|
|
|
}
|
|
|
|
|
2010-11-16 23:51:45 +00:00
|
|
|
public function walkProperty(ClassMetadata $metadata, $property, $object, $group, $propertyPath, $propagatedGroup = null)
|
2010-06-24 09:40:05 +01:00
|
|
|
{
|
2010-06-24 10:24:08 +01:00
|
|
|
foreach ($metadata->getMemberMetadatas($property) as $member) {
|
2010-11-16 23:51:45 +00:00
|
|
|
$this->walkMember($member, $member->getValue($object), $group, $propertyPath, $propagatedGroup);
|
2010-06-24 10:24:08 +01:00
|
|
|
}
|
2010-06-24 09:40:05 +01:00
|
|
|
}
|
|
|
|
|
2010-06-24 10:24:08 +01:00
|
|
|
public function walkPropertyValue(ClassMetadata $metadata, $property, $value, $group, $propertyPath)
|
|
|
|
{
|
|
|
|
foreach ($metadata->getMemberMetadatas($property) as $member) {
|
|
|
|
$this->walkMember($member, $value, $group, $propertyPath);
|
|
|
|
}
|
|
|
|
}
|
2010-06-24 09:40:05 +01:00
|
|
|
|
2010-11-16 23:51:45 +00:00
|
|
|
protected function walkMember(MemberMetadata $metadata, $value, $group, $propertyPath, $propagatedGroup = null)
|
2010-06-24 09:40:05 +01:00
|
|
|
{
|
2012-01-31 20:35:48 +00:00
|
|
|
$currentClass = $metadata->getClassName();
|
|
|
|
$currentProperty = $metadata->getPropertyName();
|
2010-06-24 10:24:08 +01:00
|
|
|
|
|
|
|
foreach ($metadata->findConstraints($group) as $constraint) {
|
2012-01-31 20:35:48 +00:00
|
|
|
$this->walkConstraint($constraint, $value, $group, $propertyPath, $currentClass, $currentProperty);
|
2010-06-24 10:24:08 +01:00
|
|
|
}
|
2010-11-16 22:41:46 +00:00
|
|
|
|
|
|
|
if ($metadata->isCascaded()) {
|
2011-01-27 15:25:25 +00:00
|
|
|
$this->walkReference($value, $propagatedGroup ?: $group, $propertyPath, $metadata->isCollectionCascaded());
|
2010-11-16 22:41:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-02-03 09:53:30 +00:00
|
|
|
public function walkReference($value, $group, $propertyPath, $traverse)
|
2010-11-16 22:41:46 +00:00
|
|
|
{
|
|
|
|
if (null !== $value) {
|
2011-01-27 15:25:25 +00:00
|
|
|
if (!is_object($value) && !is_array($value)) {
|
|
|
|
throw new UnexpectedTypeException($value, 'object or array');
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($traverse && (is_array($value) || $value instanceof \Traversable)) {
|
2010-11-16 22:41:46 +00:00
|
|
|
foreach ($value as $key => $element) {
|
2011-02-03 12:21:37 +00:00
|
|
|
// Ignore any scalar values in the collection
|
|
|
|
if (is_object($element) || is_array($element)) {
|
|
|
|
$this->walkReference($element, $group, $propertyPath.'['.$key.']', $traverse);
|
|
|
|
}
|
2010-11-16 22:41:46 +00:00
|
|
|
}
|
2011-01-27 15:25:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (is_object($value)) {
|
2010-11-16 22:41:46 +00:00
|
|
|
$metadata = $this->metadataFactory->getClassMetadata(get_class($value));
|
2011-01-19 14:57:40 +00:00
|
|
|
$this->walkObject($metadata, $value, $group, $propertyPath);
|
2010-11-16 22:41:46 +00:00
|
|
|
}
|
|
|
|
}
|
2010-06-24 09:40:05 +01:00
|
|
|
}
|
|
|
|
|
2012-01-31 20:35:48 +00:00
|
|
|
public function walkConstraint(Constraint $constraint, $value, $group, $propertyPath, $currentClass = null, $currentProperty = null)
|
2010-06-24 10:24:08 +01:00
|
|
|
{
|
|
|
|
$validator = $this->validatorFactory->getInstance($constraint);
|
2010-06-24 09:40:05 +01:00
|
|
|
|
2012-01-31 20:35:48 +00:00
|
|
|
$localContext = new ExecutionContext(
|
|
|
|
$this->globalContext,
|
|
|
|
$value,
|
|
|
|
$propertyPath,
|
|
|
|
$group,
|
|
|
|
$currentClass,
|
|
|
|
$currentProperty
|
|
|
|
);
|
2010-06-24 09:40:05 +01:00
|
|
|
|
2012-01-31 20:35:48 +00:00
|
|
|
$validator->initialize($localContext);
|
2012-02-01 12:53:45 +00:00
|
|
|
$validator->isValid($value, $constraint);
|
2010-06-24 09:40:05 +01:00
|
|
|
}
|
2011-06-08 11:16:48 +01:00
|
|
|
}
|