Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

[Validator] Fixed object initializers in 2.5 version of the Validator

  • Loading branch information...
commit ce04073b4e9e2cdc6a757e137f0f4f9a3775d0dc 1 parent a9af6be
@webmozart webmozart authored
View
40 src/Symfony/Component/Validator/Context/ExecutionContext.php
@@ -18,6 +18,7 @@
use Symfony\Component\Validator\Exception\BadMethodCallException;
use Symfony\Component\Validator\Mapping\MetadataInterface;
use Symfony\Component\Validator\Mapping\PropertyMetadataInterface;
+use Symfony\Component\Validator\ObjectInitializerInterface;
use Symfony\Component\Validator\Util\PropertyPath;
use Symfony\Component\Validator\Validator\ValidatorInterface;
use Symfony\Component\Validator\Violation\ConstraintViolationBuilder;
@@ -114,6 +115,13 @@ class ExecutionContext implements ExecutionContextInterface
private $validatedConstraints = array();
/**
+ * Stores which objects have been initialized.
+ *
+ * @var array
+ */
+ private $initializedObjects;
+
+ /**
* Creates a new execution context.
*
* @param ValidatorInterface $validator The validator
@@ -360,4 +368,36 @@ public function isConstraintValidated($cacheKey, $constraintHash)
{
return isset($this->validatedConstraints[$cacheKey.':'.$constraintHash]);
}
+
+ /**
+ * Marks that an object was initialized.
+ *
+ * @param string $cacheKey The hash of the object
+ *
+ * @internal Used by the validator engine. Should not be called by user
+ * code.
+ *
+ * @see ObjectInitializerInterface
+ */
+ public function markObjectAsInitialized($cacheKey)
+ {
+ $this->initializedObjects[$cacheKey] = true;
+ }
+
+ /**
+ * Returns whether an object was initialized.
+ *
+ * @param string $cacheKey The hash of the object
+ *
+ * @return bool Whether the object was already initialized
+ *
+ * @internal Used by the validator engine. Should not be called by user
+ * code.
+ *
+ * @see ObjectInitializerInterface
+ */
+ public function isObjectInitialized($cacheKey)
+ {
+ return isset($this->initializedObjects[$cacheKey]);
+ }
}
View
1  src/Symfony/Component/Validator/Tests/Fixtures/Entity.php
@@ -38,6 +38,7 @@ class Entity extends EntityParent implements EntityInterface
public $reference2;
private $internal;
public $data = 'Overridden data';
+ public $initialized = false;
public function __construct($internal = null)
{
View
50 src/Symfony/Component/Validator/Tests/Validator/Abstract2Dot5ApiTest.php
@@ -42,7 +42,7 @@
*
* @return ValidatorInterface
*/
- abstract protected function createValidator(MetadataFactoryInterface $metadataFactory);
+ abstract protected function createValidator(MetadataFactoryInterface $metadataFactory, array $objectInitializers = array());
protected function setUp()
{
@@ -678,4 +678,52 @@ public function testAccessCurrentObject()
$this->assertTrue($called);
}
+
+ public function testInitializeObjectsOnFirstValidation()
+ {
+ $test = $this;
+ $entity = new Entity();
+ $entity->initialized = false;
+
+ // prepare initializers that set "initialized" to true
+ $initializer1 = $this->getMock('Symfony\\Component\\Validator\\ObjectInitializerInterface');
+ $initializer2 = $this->getMock('Symfony\\Component\\Validator\\ObjectInitializerInterface');
+
+ $initializer1->expects($this->once())
+ ->method('initialize')
+ ->with($entity)
+ ->will($this->returnCallback(function ($object) {
+ $object->initialized = true;
+ }));
+
+ $initializer2->expects($this->once())
+ ->method('initialize')
+ ->with($entity);
+
+ $this->validator = $this->createValidator($this->metadataFactory, array(
+ $initializer1,
+ $initializer2
+ ));
+
+ // prepare constraint which
+ // * checks that "initialized" is set to true
+ // * validates the object again
+ $callback = function ($object, ExecutionContextInterface $context) use ($test) {
+ $test->assertTrue($object->initialized);
+
+ // validate again in same group
+ $validator = $context->getValidator()->inContext($context);
+
+ $validator->validate($object);
+
+ // validate again in other group
+ $validator->validate($object, null, 'SomeGroup');
+ };
+
+ $this->metadata->addConstraint(new Callback($callback));
+
+ $this->validate($entity);
+
+ $this->assertTrue($entity->initialized);
+ }
}
View
48 src/Symfony/Component/Validator/Tests/Validator/AbstractLegacyApiTest.php
@@ -38,7 +38,7 @@
*
* @return LegacyValidatorInterface
*/
- abstract protected function createValidator(MetadataFactoryInterface $metadataFactory);
+ abstract protected function createValidator(MetadataFactoryInterface $metadataFactory, array $objectInitializers = array());
protected function setUp()
{
@@ -238,6 +238,52 @@ public function testAddCustomizedViolation()
$this->assertSame('Code', $violations[0]->getCode());
}
+ public function testInitializeObjectsOnFirstValidation()
+ {
+ $test = $this;
+ $entity = new Entity();
+ $entity->initialized = false;
+
+ // prepare initializers that set "initialized" to true
+ $initializer1 = $this->getMock('Symfony\\Component\\Validator\\ObjectInitializerInterface');
+ $initializer2 = $this->getMock('Symfony\\Component\\Validator\\ObjectInitializerInterface');
+
+ $initializer1->expects($this->once())
+ ->method('initialize')
+ ->with($entity)
+ ->will($this->returnCallback(function ($object) {
+ $object->initialized = true;
+ }));
+
+ $initializer2->expects($this->once())
+ ->method('initialize')
+ ->with($entity);
+
+ $this->validator = $this->createValidator($this->metadataFactory, array(
+ $initializer1,
+ $initializer2
+ ));
+
+ // prepare constraint which
+ // * checks that "initialized" is set to true
+ // * validates the object again
+ $callback = function ($object, ExecutionContextInterface $context) use ($test) {
+ $test->assertTrue($object->initialized);
+
+ // validate again in same group
+ $context->validate($object);
+
+ // validate again in other group
+ $context->validate($object, '', 'SomeGroup');
+ };
+
+ $this->metadata->addConstraint(new Callback($callback));
+
+ $this->validate($entity);
+
+ $this->assertTrue($entity->initialized);
+ }
+
public function testGetMetadataFactory()
{
$this->assertSame($this->metadataFactory, $this->validator->getMetadataFactory());
View
4 src/Symfony/Component/Validator/Tests/Validator/LegacyValidator2Dot5ApiTest.php
@@ -28,10 +28,10 @@ protected function setUp()
parent::setUp();
}
- protected function createValidator(MetadataFactoryInterface $metadataFactory)
+ protected function createValidator(MetadataFactoryInterface $metadataFactory, array $objectInitializers = array())
{
$contextFactory = new LegacyExecutionContextFactory($metadataFactory, new DefaultTranslator());
- return new LegacyValidator($contextFactory, $metadataFactory, new ConstraintValidatorFactory());
+ return new LegacyValidator($contextFactory, $metadataFactory, new ConstraintValidatorFactory(), $objectInitializers);
}
}
View
4 src/Symfony/Component/Validator/Tests/Validator/LegacyValidatorLegacyApiTest.php
@@ -28,10 +28,10 @@ protected function setUp()
parent::setUp();
}
- protected function createValidator(MetadataFactoryInterface $metadataFactory)
+ protected function createValidator(MetadataFactoryInterface $metadataFactory, array $objectInitializers = array())
{
$contextFactory = new LegacyExecutionContextFactory($metadataFactory, new DefaultTranslator());
- return new LegacyValidator($contextFactory, $metadataFactory, new ConstraintValidatorFactory());
+ return new LegacyValidator($contextFactory, $metadataFactory, new ConstraintValidatorFactory(), $objectInitializers);
}
}
View
4 src/Symfony/Component/Validator/Tests/Validator/RecursiveValidator2Dot5ApiTest.php
@@ -19,10 +19,10 @@
class RecursiveValidator2Dot5ApiTest extends Abstract2Dot5ApiTest
{
- protected function createValidator(MetadataFactoryInterface $metadataFactory)
+ protected function createValidator(MetadataFactoryInterface $metadataFactory, array $objectInitializers = array())
{
$contextFactory = new ExecutionContextFactory(new DefaultTranslator());
- return new RecursiveValidator($contextFactory, $metadataFactory, new ConstraintValidatorFactory());
+ return new RecursiveValidator($contextFactory, $metadataFactory, new ConstraintValidatorFactory(), $objectInitializers);
}
}
View
4 src/Symfony/Component/Validator/Tests/ValidatorTest.php
@@ -21,9 +21,9 @@
class ValidatorTest extends AbstractLegacyApiTest
{
- protected function createValidator(MetadataFactoryInterface $metadataFactory)
+ protected function createValidator(MetadataFactoryInterface $metadataFactory, array $objectInitializers = array())
{
- return new LegacyValidator($metadataFactory, new ConstraintValidatorFactory(), new DefaultTranslator());
+ return new LegacyValidator($metadataFactory, new ConstraintValidatorFactory(), new DefaultTranslator(), 'validators', $objectInitializers);
}
/**
View
17 src/Symfony/Component/Validator/ValidationVisitor.php
@@ -129,16 +129,19 @@ public function validate($value, $group, $propertyPath, $traverse = false, $deep
return;
}
+ // Initialize if the object wasn't initialized before
+ if (!isset($this->validatedObjects[$hash])) {
+ foreach ($this->objectInitializers as $initializer) {
+ if (!$initializer instanceof ObjectInitializerInterface) {
+ throw new \LogicException('Validator initializers must implement ObjectInitializerInterface.');
+ }
+ $initializer->initialize($value);
+ }
+ }
+
// Remember validating this object before starting and possibly
// traversing the object graph
$this->validatedObjects[$hash][$group] = true;
-
- foreach ($this->objectInitializers as $initializer) {
- if (!$initializer instanceof ObjectInitializerInterface) {
- throw new \LogicException('Validator initializers must implement ObjectInitializerInterface.');
- }
- $initializer->initialize($value);
- }
}
// Validate arrays recursively by default, otherwise every driver needs
View
30 src/Symfony/Component/Validator/Validator/RecursiveContextualValidator.php
@@ -27,6 +27,7 @@
use Symfony\Component\Validator\Mapping\PropertyMetadataInterface;
use Symfony\Component\Validator\Mapping\TraversalStrategy;
use Symfony\Component\Validator\MetadataFactoryInterface;
+use Symfony\Component\Validator\ObjectInitializerInterface;
use Symfony\Component\Validator\Util\PropertyPath;
/**
@@ -53,22 +54,29 @@ class RecursiveContextualValidator implements ContextualValidatorInterface
private $validatorFactory;
/**
+ * @var ObjectInitializerInterface[]
+ */
+ private $objectInitializers;
+
+ /**
* Creates a validator for the given context.
*
- * @param ExecutionContextInterface $context The execution context
- * @param MetadataFactoryInterface $metadataFactory The factory for
- * fetching the metadata
- * of validated objects
- * @param ConstraintValidatorFactoryInterface $validatorFactory The factory for creating
- * constraint validators
+ * @param ExecutionContextInterface $context The execution context
+ * @param MetadataFactoryInterface $metadataFactory The factory for
+ * fetching the metadata
+ * of validated objects
+ * @param ConstraintValidatorFactoryInterface $validatorFactory The factory for creating
+ * constraint validators
+ * @param ObjectInitializerInterface[] $objectInitializers The object initializers
*/
- public function __construct(ExecutionContextInterface $context, MetadataFactoryInterface $metadataFactory, ConstraintValidatorFactoryInterface $validatorFactory)
+ public function __construct(ExecutionContextInterface $context, MetadataFactoryInterface $metadataFactory, ConstraintValidatorFactoryInterface $validatorFactory, array $objectInitializers = array())
{
$this->context = $context;
$this->defaultPropertyPath = $context->getPropertyPath();
$this->defaultGroups = array($context->getGroup() ?: Constraint::DEFAULT_GROUP);
$this->metadataFactory = $metadataFactory;
$this->validatorFactory = $validatorFactory;
+ $this->objectInitializers = $objectInitializers;
}
/**
@@ -432,6 +440,14 @@ private function validateClassNode($object, $cacheKey, ClassMetadataInterface $m
{
$context->setNode($object, $object, $metadata, $propertyPath);
+ if (!$context->isObjectInitialized($cacheKey)) {
+ foreach ($this->objectInitializers as $initializer) {
+ $initializer->initialize($object);
+ }
+
+ $context->markObjectAsInitialized($cacheKey);
+ }
+
foreach ($groups as $key => $group) {
// If the "Default" group is replaced by a group sequence, remember
// to cascade the "Default" group when traversing the group
View
30 src/Symfony/Component/Validator/Validator/RecursiveValidator.php
@@ -15,6 +15,7 @@
use Symfony\Component\Validator\Context\ExecutionContextFactoryInterface;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
use Symfony\Component\Validator\MetadataFactoryInterface;
+use Symfony\Component\Validator\ObjectInitializerInterface;
/**
* Recursive implementation of {@link ValidatorInterface}.
@@ -40,21 +41,28 @@ class RecursiveValidator implements ValidatorInterface
protected $validatorFactory;
/**
+ * @var ObjectInitializerInterface[]
+ */
+ protected $objectInitializers;
+
+ /**
* Creates a new validator.
*
- * @param ExecutionContextFactoryInterface $contextFactory The factory for
- * creating new contexts
- * @param MetadataFactoryInterface $metadataFactory The factory for
- * fetching the metadata
- * of validated objects
- * @param ConstraintValidatorFactoryInterface $validatorFactory The factory for creating
- * constraint validators
+ * @param ExecutionContextFactoryInterface $contextFactory The factory for
+ * creating new contexts
+ * @param MetadataFactoryInterface $metadataFactory The factory for
+ * fetching the metadata
+ * of validated objects
+ * @param ConstraintValidatorFactoryInterface $validatorFactory The factory for creating
+ * constraint validators
+ * @param ObjectInitializerInterface[] $objectInitializers The object initializers
*/
- public function __construct(ExecutionContextFactoryInterface $contextFactory, MetadataFactoryInterface $metadataFactory, ConstraintValidatorFactoryInterface $validatorFactory)
+ public function __construct(ExecutionContextFactoryInterface $contextFactory, MetadataFactoryInterface $metadataFactory, ConstraintValidatorFactoryInterface $validatorFactory, array $objectInitializers = array())
{
$this->contextFactory = $contextFactory;
$this->metadataFactory = $metadataFactory;
$this->validatorFactory = $validatorFactory;
+ $this->objectInitializers = $objectInitializers;
}
/**
@@ -65,7 +73,8 @@ public function startContext($root = null)
return new RecursiveContextualValidator(
$this->contextFactory->createContext($this, $root),
$this->metadataFactory,
- $this->validatorFactory
+ $this->validatorFactory,
+ $this->objectInitializers
);
}
@@ -77,7 +86,8 @@ public function inContext(ExecutionContextInterface $context)
return new RecursiveContextualValidator(
$context,
$this->metadataFactory,
- $this->validatorFactory
+ $this->validatorFactory,
+ $this->objectInitializers
);
}
View
4 src/Symfony/Component/Validator/ValidatorBuilder.php
@@ -411,9 +411,9 @@ public function getValidator()
$contextFactory = new LegacyExecutionContextFactory($metadataFactory, $translator, $this->translationDomain);
if (Validation::API_VERSION_2_5 === $apiVersion) {
- return new RecursiveValidator($contextFactory, $metadataFactory, $validatorFactory);
+ return new RecursiveValidator($contextFactory, $metadataFactory, $validatorFactory, $this->initializers);
}
- return new LegacyValidator($contextFactory, $metadataFactory, $validatorFactory);
+ return new LegacyValidator($contextFactory, $metadataFactory, $validatorFactory, $this->initializers);
}
}
Please sign in to comment.
Something went wrong with that request. Please try again.