Skip to content

Commit

Permalink
[Validator] Improved performance of *ContextualValidator::validate()
Browse files Browse the repository at this point in the history
  • Loading branch information
webmozart committed Mar 30, 2014
1 parent 021cac2 commit 48fe07a
Show file tree
Hide file tree
Showing 7 changed files with 121 additions and 39 deletions.
2 changes: 1 addition & 1 deletion Exception/ValidatorException.php
Expand Up @@ -11,6 +11,6 @@

namespace Symfony\Component\Validator\Exception;

class ValidatorException extends \RuntimeException
class ValidatorException extends RuntimeException
{
}
3 changes: 0 additions & 3 deletions Tests/Fixtures/FakeClassMetadata.php
Expand Up @@ -11,10 +11,7 @@

namespace Symfony\Component\Validator\Tests\Fixtures;

use Symfony\Component\Validator\ClassBasedInterface;
use Symfony\Component\Validator\Mapping\ClassMetadata;
use Symfony\Component\Validator\MetadataInterface;
use Symfony\Component\Validator\PropertyMetadataContainerInterface;

class FakeClassMetadata extends ClassMetadata
{
Expand Down
2 changes: 1 addition & 1 deletion Tests/Fixtures/FakeMetadataFactory.php
Expand Up @@ -49,8 +49,8 @@ public function hasMetadataFor($class)
$hash = null;

if (is_object($class)) {
$hash = spl_object_hash($class);
$class = get_class($class);
$hash = spl_object_hash($hash);
}

if (!is_string($class)) {
Expand Down
8 changes: 8 additions & 0 deletions Tests/Validator/Abstract2Dot5ApiTest.php
Expand Up @@ -650,4 +650,12 @@ public function testNoDuplicateValidationIfPropertyConstraintInMultipleGroups()
/** @var ConstraintViolationInterface[] $violations */
$this->assertCount(1, $violations);
}

/**
* @expectedException \Symfony\Component\Validator\Exception\RuntimeException
*/
public function testValidateFailsIfNoConstraintsAndNoObjectOrArray()
{
$this->validate('Foobar');
}
}
4 changes: 4 additions & 0 deletions Tests/Validator/AbstractValidatorTest.php
Expand Up @@ -837,6 +837,8 @@ public function testValidateProperty()
}

/**
* Cannot be UnsupportedMetadataException for BC with Symfony < 2.5.
*
* @expectedException \Symfony\Component\Validator\Exception\ValidatorException
*/
public function testValidatePropertyFailsIfPropertiesNotSupported()
Expand Down Expand Up @@ -903,6 +905,8 @@ public function testValidatePropertyValue()
}

/**
* Cannot be UnsupportedMetadataException for BC with Symfony < 2.5.
*
* @expectedException \Symfony\Component\Validator\Exception\ValidatorException
*/
public function testValidatePropertyValueFailsIfPropertiesNotSupported()
Expand Down
73 changes: 54 additions & 19 deletions Validator/RecursiveContextualValidator.php
Expand Up @@ -13,11 +13,11 @@

use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\Constraints\GroupSequence;
use Symfony\Component\Validator\Constraints\Valid;
use Symfony\Component\Validator\ConstraintValidatorFactoryInterface;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
use Symfony\Component\Validator\Exception\NoSuchMetadataException;
use Symfony\Component\Validator\Exception\RuntimeException;
use Symfony\Component\Validator\Exception\UnsupportedMetadataException;
use Symfony\Component\Validator\Exception\ValidatorException;
use Symfony\Component\Validator\Mapping\CascadingStrategy;
Expand Down Expand Up @@ -90,28 +90,59 @@ public function atPath($path)
*/
public function validate($value, $constraints = null, $groups = null)
{
if (null === $constraints) {
$constraints = array(new Valid());
} elseif (!is_array($constraints)) {
$constraints = array($constraints);
$groups = $groups ? $this->normalizeGroups($groups) : $this->defaultGroups;

if (null !== $constraints) {
if (!is_array($constraints)) {
$constraints = array($constraints);
}

$metadata = new GenericMetadata();
$metadata->addConstraints($constraints);

$this->traverseGenericNode(
$value,
null,
$metadata,
$this->defaultPropertyPath,
$groups,
null,
TraversalStrategy::IMPLICIT,
$this->context
);

return $this;
}

$metadata = new GenericMetadata();
$metadata->addConstraints($constraints);
$groups = $groups ? $this->normalizeGroups($groups) : $this->defaultGroups;
if (is_object($value)) {
$this->cascadeObject(
$value,
$this->defaultPropertyPath,
$groups,
TraversalStrategy::IMPLICIT,
$this->context
);

$this->traverseGenericNode(
$value,
null,
$metadata,
$this->defaultPropertyPath,
$groups,
null,
TraversalStrategy::IMPLICIT,
$this->context
);
return $this;
}

return $this;
if (is_array($value)) {
$this->cascadeCollection(
$value,
$this->defaultPropertyPath,
$groups,
TraversalStrategy::IMPLICIT,
$this->context
);

return $this;
}

throw new RuntimeException(sprintf(
'Cannot validate values of type "%s" automatically. Please '.
'provide a constraint.',
gettype($value)
));
}

/**
Expand All @@ -122,6 +153,8 @@ public function validateProperty($object, $propertyName, $groups = null)
$classMetadata = $this->metadataFactory->getMetadataFor($object);

if (!$classMetadata instanceof ClassMetadataInterface) {
// Cannot be UnsupportedMetadataException because of BC with
// Symfony < 2.5
throw new ValidatorException(sprintf(
'The metadata factory should return instances of '.
'"\Symfony\Component\Validator\Mapping\ClassMetadataInterface", '.
Expand Down Expand Up @@ -159,6 +192,8 @@ public function validatePropertyValue($object, $propertyName, $value, $groups =
$classMetadata = $this->metadataFactory->getMetadataFor($object);

if (!$classMetadata instanceof ClassMetadataInterface) {
// Cannot be UnsupportedMetadataException because of BC with
// Symfony < 2.5
throw new ValidatorException(sprintf(
'The metadata factory should return instances of '.
'"\Symfony\Component\Validator\Mapping\ClassMetadataInterface", '.
Expand Down
68 changes: 53 additions & 15 deletions Validator/TraversingContextualValidator.php
Expand Up @@ -12,12 +12,15 @@
namespace Symfony\Component\Validator\Validator;

use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\Constraints\Valid;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
use Symfony\Component\Validator\Exception\RuntimeException;
use Symfony\Component\Validator\Exception\UnsupportedMetadataException;
use Symfony\Component\Validator\Exception\ValidatorException;
use Symfony\Component\Validator\Mapping\ClassMetadataInterface;
use Symfony\Component\Validator\Mapping\GenericMetadata;
use Symfony\Component\Validator\MetadataFactoryInterface;
use Symfony\Component\Validator\Node\ClassNode;
use Symfony\Component\Validator\Node\CollectionNode;
use Symfony\Component\Validator\Node\GenericNode;
use Symfony\Component\Validator\Node\PropertyNode;
use Symfony\Component\Validator\NodeTraverser\NodeTraverserInterface;
Expand Down Expand Up @@ -79,22 +82,53 @@ public function atPath($path)
*/
public function validate($value, $constraints = null, $groups = null)
{
if (null === $constraints) {
$constraints = array(new Valid());
} elseif (!is_array($constraints)) {
$constraints = array($constraints);
}

$metadata = new GenericMetadata();
$metadata->addConstraints($constraints);
$groups = $groups ? $this->normalizeGroups($groups) : $this->defaultGroups;

$node = new GenericNode(
$value,
$metadata,
$this->defaultPropertyPath,
$groups
);
if (null !== $constraints) {
if (!is_array($constraints)) {
$constraints = array($constraints);
}

$metadata = new GenericMetadata();
$metadata->addConstraints($constraints);

$node = new GenericNode(
$value,
$metadata,
$this->defaultPropertyPath,
$groups
);
} elseif (is_array($value) || $value instanceof \Traversable && !$this->metadataFactory->hasMetadataFor($value)) {
$node = new CollectionNode(
$value,
$this->defaultPropertyPath,
$groups
);
} elseif (is_object($value)) {
$metadata = $this->metadataFactory->getMetadataFor($value);

if (!$metadata instanceof ClassMetadataInterface) {
throw new UnsupportedMetadataException(sprintf(
'The metadata factory should return instances of '.
'"\Symfony\Component\Validator\Mapping\ClassMetadataInterface", '.
'got: "%s".',
is_object($metadata) ? get_class($metadata) : gettype($metadata)
));
}

$node = new ClassNode(
$value,
$metadata,
$this->defaultPropertyPath,
$groups
);
} else {
throw new RuntimeException(sprintf(
'Cannot validate values of type "%s" automatically. Please '.
'provide a constraint.',
gettype($value)
));
}

$this->nodeTraverser->traverse(array($node), $this->context);

Expand All @@ -109,6 +143,8 @@ public function validateProperty($object, $propertyName, $groups = null)
$classMetadata = $this->metadataFactory->getMetadataFor($object);

if (!$classMetadata instanceof ClassMetadataInterface) {
// Cannot be UnsupportedMetadataException because of BC with
// Symfony < 2.5
throw new ValidatorException(sprintf(
'The metadata factory should return instances of '.
'"\Symfony\Component\Validator\Mapping\ClassMetadataInterface", '.
Expand Down Expand Up @@ -146,6 +182,8 @@ public function validatePropertyValue($object, $propertyName, $value, $groups =
$classMetadata = $this->metadataFactory->getMetadataFor($object);

if (!$classMetadata instanceof ClassMetadataInterface) {
// Cannot be UnsupportedMetadataException because of BC with
// Symfony < 2.5
throw new ValidatorException(sprintf(
'The metadata factory should return instances of '.
'"\Symfony\Component\Validator\Mapping\ClassMetadataInterface", '.
Expand Down

0 comments on commit 48fe07a

Please sign in to comment.