Skip to content

Commit

Permalink
[Validator] Simplified usage of the Callback constraint
Browse files Browse the repository at this point in the history
  • Loading branch information
webmozart committed Sep 26, 2013
1 parent 98c0d38 commit cccb1db
Show file tree
Hide file tree
Showing 12 changed files with 393 additions and 31 deletions.
58 changes: 58 additions & 0 deletions UPGRADE-3.0.md
Expand Up @@ -425,6 +425,64 @@ UPGRADE FROM 2.x to 3.0
private $property;
```

* The option "methods" of the `Callback` constraint was removed. You should
use the option "callback" instead. If you have multiple callbacks, add
multiple callback constraints instead.

Before (YAML):

```
constraints:
- Callback: [firstCallback, secondCallback]
```

After (YAML):

```
constraints:
- Callback: firstCallback
- Callback: secondCallback
```

When using annotations, you can now put the Callback constraint directly on
the method that should be executed.

Before (Annotations):

```
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Validator\ExecutionContextInterface;
/**
* @Assert\Callback({"callback"})
*/
class MyClass
{
public function callback(ExecutionContextInterface $context)
{
// ...
}
}
```

After (Annotations):

```
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Validator\ExecutionContextInterface;
class MyClass
{
/**
* @Assert\Callback
*/
public function callback(ExecutionContextInterface $context)
{
// ...
}
}
```

### Yaml

* The ability to pass file names to `Yaml::parse()` has been removed.
Expand Down
38 changes: 32 additions & 6 deletions src/Symfony/Component/Validator/Constraints/Callback.php
Expand Up @@ -22,26 +22,52 @@
*/
class Callback extends Constraint
{
/**
* @var string|callable
*
* @since 2.4
*/
public $callback;

/**
* @var array
*
* @deprecated Deprecated since version 2.4, to be removed in Symfony 3.0.
*/
public $methods;

/**
* {@inheritDoc}
* {@inheritdoc}
*/
public function getRequiredOptions()
public function __construct($options = null)
{
return array('methods');
// Invocation through annotations with an array parameter only
if (is_array($options) && 1 === count($options) && isset($options['value'])) {
$options = $options['value'];
}

if (is_array($options) && !isset($options['callback']) && !isset($options['methods']) && !isset($options['groups'])) {
if (is_callable($options)) {
$options = array('callback' => $options);
} else {
// BC with Symfony < 2.4
$options = array('methods' => $options);
}
}

parent::__construct($options);
}

/**
* {@inheritDoc}
* {@inheritdoc}
*/
public function getDefaultOption()
{
return 'methods';
return 'callback';
}

/**
* {@inheritDoc}
* {@inheritdoc}
*/
public function getTargets()
{
Expand Down
19 changes: 16 additions & 3 deletions src/Symfony/Component/Validator/Constraints/CallbackValidator.php
Expand Up @@ -34,13 +34,20 @@ public function validate($object, Constraint $constraint)
return;
}

if (null !== $constraint->callback && null !== $constraint->methods) {
throw new ConstraintDefinitionException(
'The Callback constraint supports either the option "callback" ' .
'or "methods", but not both at the same time.'
);
}

// has to be an array so that we can differentiate between callables
// and method names
if (!is_array($constraint->methods)) {
if (null !== $constraint->methods && !is_array($constraint->methods)) {
throw new UnexpectedTypeException($constraint->methods, 'array');
}

$methods = $constraint->methods;
$methods = $constraint->methods ?: array($constraint->callback);

foreach ($methods as $method) {
if (is_array($method) || $method instanceof \Closure) {
Expand All @@ -54,7 +61,13 @@ public function validate($object, Constraint $constraint)
throw new ConstraintDefinitionException(sprintf('Method "%s" targeted by Callback constraint does not exist', $method));
}

$object->$method($this->context);
$reflMethod = new \ReflectionMethod($object, $method);

if ($reflMethod->isStatic()) {
$reflMethod->invoke(null, $object, $this->context);
} else {
$reflMethod->invoke($object, $this->context);
}
}
}
}
Expand Down
Expand Up @@ -12,6 +12,7 @@
namespace Symfony\Component\Validator\Mapping\Loader;

use Doctrine\Common\Annotations\Reader;
use Symfony\Component\Validator\Constraints\Callback;
use Symfony\Component\Validator\Exception\MappingException;
use Symfony\Component\Validator\Mapping\ClassMetadata;
use Symfony\Component\Validator\Constraints\GroupSequence;
Expand Down Expand Up @@ -63,7 +64,12 @@ public function loadClassMetadata(ClassMetadata $metadata)
foreach ($reflClass->getMethods() as $method) {
if ($method->getDeclaringClass()->name == $className) {
foreach ($this->reader->getMethodAnnotations($method) as $constraint) {
if ($constraint instanceof Constraint) {
if ($constraint instanceof Callback) {
$constraint->callback = $method->getName();
$constraint->methods = null;

$metadata->addConstraint($constraint);
} elseif ($constraint instanceof Constraint) {
if (preg_match('/^(get|is)(.+)$/i', $method->name, $matches)) {
$metadata->addGetterConstraint(lcfirst($matches[2]), $constraint);
} else {
Expand Down

0 comments on commit cccb1db

Please sign in to comment.