Skip to content

Commit

Permalink
fixed validation of Doctrine proxy objects
Browse files Browse the repository at this point in the history
  • Loading branch information
fabpot committed Jul 6, 2011
1 parent 090a51a commit 0824736
Show file tree
Hide file tree
Showing 8 changed files with 125 additions and 3 deletions.
38 changes: 38 additions & 0 deletions src/Symfony/Bridge/Doctrine/Validator/EntityInitializer.php
@@ -0,0 +1,38 @@
<?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\Bridge\Doctrine\Validator;

use Symfony\Bridge\Doctrine\RegistryInterface;
use Symfony\Component\Validator\ObjectInitializerInterface;
use Doctrine\ORM\Proxy\Proxy;

/**
* Automatically loads proxy object before validation.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class EntityInitializer implements ObjectInitializerInterface
{
protected $registry;

public function __construct(RegistryInterface $registry)
{
$this->registry = $registry;
}

public function initialize($object)
{
if ($object instanceof Proxy) {
$this->registry->getEntityManagerForObject($object)->getUnitOfWork()->initializeObject($object);

This comment has been minimized.

Copy link
@stof

stof Jul 6, 2011

Member

The method of the registry can return null if the entity manager is not found. So you should test this case here.

This comment has been minimized.

Copy link
@fabpot

fabpot Jul 6, 2011

Author Member

If it returns null, you have a big problem as we are using a Proxy object generated by Doctrine. So, it should never happen.

This comment has been minimized.

Copy link
@stof

stof Jul 6, 2011

Member

yeah, you have a problem but the PHP fatal error on this line will not tell the user which one :)

}
}
}
6 changes: 6 additions & 0 deletions src/Symfony/Bundle/DoctrineBundle/Resources/config/orm.xml
Expand Up @@ -34,6 +34,7 @@

<!-- validator -->
<parameter key="doctrine.orm.validator.unique.class">Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntityValidator</parameter>
<parameter key="doctrine.orm.validator_initializer.class">Symfony\Bridge\Doctrine\Validator\EntityInitializer</parameter>
</parameters>

<services>
Expand Down Expand Up @@ -66,5 +67,10 @@
<tag name="validator.constraint_validator" alias="doctrine.orm.validator.unique" />
<argument type="service" id="doctrine" />
</service>

<service id="doctrine.orm.validator_initializer" class="%doctrine.orm.validator_initializer.class%">
<tag name="validator.initializer" />
<argument type="service" id="doctrine" />
</service>
</services>
</container>
@@ -0,0 +1,33 @@
<?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\Bundle\FrameworkBundle\DependencyInjection\Compiler;

use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;

class AddValidatorInitializersPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
if (!$container->hasDefinition('validator')) {
return;
}

$initializers = array();
foreach ($container->findTaggedServiceIds('validator.initializer') as $id => $attributes) {
$initializers[] = new Reference($id);
}

$container->getDefinition('validator')->replaceArgument(2, $initializers);
}
}
2 changes: 2 additions & 0 deletions src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php
Expand Up @@ -12,6 +12,7 @@
namespace Symfony\Bundle\FrameworkBundle;

use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddConstraintValidatorsPass;
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddValidatorInitializersPass;
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\FormPass;
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\TemplatingPass;
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\RegisterKernelListenersPass;
Expand Down Expand Up @@ -52,6 +53,7 @@ public function build(ContainerBuilder $container)
$container->addCompilerPass(new RegisterKernelListenersPass());
$container->addCompilerPass(new TemplatingPass());
$container->addCompilerPass(new AddConstraintValidatorsPass());
$container->addCompilerPass(new AddValidatorInitializersPass());
$container->addCompilerPass(new FormPass());
$container->addCompilerPass(new TranslatorPass());
$container->addCompilerPass(new AddCacheWarmerPass());
Expand Down
Expand Up @@ -23,6 +23,7 @@
<service id="validator" class="%validator.class%">
<argument type="service" id="validator.mapping.class_metadata_factory" />
<argument type="service" id="validator.validator_factory" />
<argument type="collection" />
</service>

<service id="validator.mapping.class_metadata_factory" class="%validator.mapping.class_metadata_factory.class%" public="false">
Expand Down
11 changes: 10 additions & 1 deletion src/Symfony/Component/Validator/GraphWalker.php
Expand Up @@ -32,13 +32,15 @@ class GraphWalker
protected $context;
protected $validatorFactory;
protected $metadataFactory;
protected $validatorInitializers = array();
protected $validatedObjects = array();

public function __construct($root, ClassMetadataFactoryInterface $metadataFactory, ConstraintValidatorFactoryInterface $factory)
public function __construct($root, ClassMetadataFactoryInterface $metadataFactory, ConstraintValidatorFactoryInterface $factory, array $validatorInitializers = array())
{
$this->context = new ExecutionContext($root, $this, $metadataFactory);
$this->validatorFactory = $factory;
$this->metadataFactory = $metadataFactory;
$this->validatorInitializers = $validatorInitializers;
}

/**
Expand All @@ -60,6 +62,13 @@ public function getViolations()
*/
public function walkObject(ClassMetadata $metadata, $object, $group, $propertyPath)
{
foreach ($this->validatorInitializers as $initializer) {
if (!$initializer instanceof ObjectInitializerInterface) {
throw new \LogicException('Validator initializers must implement ObjectInitializerInterface.');
}
$initializer->initialize($object);
}

$this->context->setCurrentClass($metadata->getClassName());

if ($group === Constraint::DEFAULT_GROUP && $metadata->hasGroupSequence()) {
Expand Down
30 changes: 30 additions & 0 deletions src/Symfony/Component/Validator/ObjectInitializerInterface.php
@@ -0,0 +1,30 @@
<?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;

/**
* Interface for object initializers.
*
* Concrete implementations of this interface are used by the GraphWalker
* to initialize objects just before validating them/
*
* @author Fabien Potencier <fabien@symfony.com>
*/
interface ObjectInitializerInterface
{
/**
* Initializes an object just before validation.
*
* @param object $object The object to validate
*/
function initialize($object);
}
7 changes: 5 additions & 2 deletions src/Symfony/Component/Validator/Validator.php
Expand Up @@ -28,14 +28,17 @@ class Validator implements ValidatorInterface
{
protected $metadataFactory;
protected $validatorFactory;
protected $validatorInitializers;

public function __construct(
ClassMetadataFactoryInterface $metadataFactory,
ConstraintValidatorFactoryInterface $validatorFactory
ConstraintValidatorFactoryInterface $validatorFactory,
array $validatorInitializers = array()
)
{
$this->metadataFactory = $metadataFactory;
$this->validatorFactory = $validatorFactory;
$this->validatorInitializers = $validatorInitializers;
}

/**
Expand Down Expand Up @@ -102,7 +105,7 @@ public function validateValue($value, Constraint $constraint, $groups = null)

protected function validateGraph($root, \Closure $walk, $groups = null)
{
$walker = new GraphWalker($root, $this->metadataFactory, $this->validatorFactory);
$walker = new GraphWalker($root, $this->metadataFactory, $this->validatorFactory, $this->validatorInitializers);
$groups = $groups ? (array) $groups : array(Constraint::DEFAULT_GROUP);

foreach ($groups as $group) {
Expand Down

2 comments on commit 0824736

@benjamindulau
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This commit introduces an issue for me. Well, i'm not sure if it's an issue or an undocumented BC break, but since these changes i get the following error on a form process which was working just before this push :

Warning: strpos() expects parameter 1 to be string, object given in F:\PHPServer\Zend\Apache2\htdocs\mval\vendor\doctrine\lib\Doctrine\ORM\Mapping\Driver\DriverChain.php line 113

Here is the end of the stack trace : http://awesomescreenshot.com/0d6gb4035

The isTransient() method is called (by the Registry) with an object instead of a string as parameter.

Not sure of what i need to do to fix this so i prefer letting you doing it to avoid unwanted issues :)

@stof
Copy link
Member

@stof stof commented on 0824736 Jul 9, 2011

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is fixed by #1600 which is not merged yet.

Please sign in to comment.