Skip to content

Commit 4330ae5

Browse files
committed
merged branch bschussek/issue2480 (PR symfony#9133)
This PR was merged into the master branch. Discussion ---------- [Validator] Simplified usage of the Callback constraint | Q | A | ------------- | --- | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | fixes symfony#2480, replaces symfony#3939 | License | MIT | Doc PR | symfony/symfony-docs#3012 Commits ------- cccb1db [Validator] Simplified usage of the Callback constraint
2 parents 9dffa6f + cccb1db commit 4330ae5

File tree

12 files changed

+393
-31
lines changed

12 files changed

+393
-31
lines changed

UPGRADE-3.0.md

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -425,6 +425,64 @@ UPGRADE FROM 2.x to 3.0
425425
private $property;
426426
```
427427

428+
* The option "methods" of the `Callback` constraint was removed. You should
429+
use the option "callback" instead. If you have multiple callbacks, add
430+
multiple callback constraints instead.
431+
432+
Before (YAML):
433+
434+
```
435+
constraints:
436+
- Callback: [firstCallback, secondCallback]
437+
```
438+
439+
After (YAML):
440+
441+
```
442+
constraints:
443+
- Callback: firstCallback
444+
- Callback: secondCallback
445+
```
446+
447+
When using annotations, you can now put the Callback constraint directly on
448+
the method that should be executed.
449+
450+
Before (Annotations):
451+
452+
```
453+
use Symfony\Component\Validator\Constraints as Assert;
454+
use Symfony\Component\Validator\ExecutionContextInterface;
455+
456+
/**
457+
* @Assert\Callback({"callback"})
458+
*/
459+
class MyClass
460+
{
461+
public function callback(ExecutionContextInterface $context)
462+
{
463+
// ...
464+
}
465+
}
466+
```
467+
468+
After (Annotations):
469+
470+
```
471+
use Symfony\Component\Validator\Constraints as Assert;
472+
use Symfony\Component\Validator\ExecutionContextInterface;
473+
474+
class MyClass
475+
{
476+
/**
477+
* @Assert\Callback
478+
*/
479+
public function callback(ExecutionContextInterface $context)
480+
{
481+
// ...
482+
}
483+
}
484+
```
485+
428486
### Yaml
429487

430488
* The ability to pass file names to `Yaml::parse()` has been removed.

src/Symfony/Component/Validator/Constraints/Callback.php

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,26 +22,52 @@
2222
*/
2323
class Callback extends Constraint
2424
{
25+
/**
26+
* @var string|callable
27+
*
28+
* @since 2.4
29+
*/
30+
public $callback;
31+
32+
/**
33+
* @var array
34+
*
35+
* @deprecated Deprecated since version 2.4, to be removed in Symfony 3.0.
36+
*/
2537
public $methods;
2638

2739
/**
28-
* {@inheritDoc}
40+
* {@inheritdoc}
2941
*/
30-
public function getRequiredOptions()
42+
public function __construct($options = null)
3143
{
32-
return array('methods');
44+
// Invocation through annotations with an array parameter only
45+
if (is_array($options) && 1 === count($options) && isset($options['value'])) {
46+
$options = $options['value'];
47+
}
48+
49+
if (is_array($options) && !isset($options['callback']) && !isset($options['methods']) && !isset($options['groups'])) {
50+
if (is_callable($options)) {
51+
$options = array('callback' => $options);
52+
} else {
53+
// BC with Symfony < 2.4
54+
$options = array('methods' => $options);
55+
}
56+
}
57+
58+
parent::__construct($options);
3359
}
3460

3561
/**
36-
* {@inheritDoc}
62+
* {@inheritdoc}
3763
*/
3864
public function getDefaultOption()
3965
{
40-
return 'methods';
66+
return 'callback';
4167
}
4268

4369
/**
44-
* {@inheritDoc}
70+
* {@inheritdoc}
4571
*/
4672
public function getTargets()
4773
{

src/Symfony/Component/Validator/Constraints/CallbackValidator.php

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,20 @@ public function validate($object, Constraint $constraint)
3434
return;
3535
}
3636

37+
if (null !== $constraint->callback && null !== $constraint->methods) {
38+
throw new ConstraintDefinitionException(
39+
'The Callback constraint supports either the option "callback" ' .
40+
'or "methods", but not both at the same time.'
41+
);
42+
}
43+
3744
// has to be an array so that we can differentiate between callables
3845
// and method names
39-
if (!is_array($constraint->methods)) {
46+
if (null !== $constraint->methods && !is_array($constraint->methods)) {
4047
throw new UnexpectedTypeException($constraint->methods, 'array');
4148
}
4249

43-
$methods = $constraint->methods;
50+
$methods = $constraint->methods ?: array($constraint->callback);
4451

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

57-
$object->$method($this->context);
64+
$reflMethod = new \ReflectionMethod($object, $method);
65+
66+
if ($reflMethod->isStatic()) {
67+
$reflMethod->invoke(null, $object, $this->context);
68+
} else {
69+
$reflMethod->invoke($object, $this->context);
70+
}
5871
}
5972
}
6073
}

src/Symfony/Component/Validator/Mapping/Loader/AnnotationLoader.php

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
namespace Symfony\Component\Validator\Mapping\Loader;
1313

1414
use Doctrine\Common\Annotations\Reader;
15+
use Symfony\Component\Validator\Constraints\Callback;
1516
use Symfony\Component\Validator\Exception\MappingException;
1617
use Symfony\Component\Validator\Mapping\ClassMetadata;
1718
use Symfony\Component\Validator\Constraints\GroupSequence;
@@ -63,7 +64,12 @@ public function loadClassMetadata(ClassMetadata $metadata)
6364
foreach ($reflClass->getMethods() as $method) {
6465
if ($method->getDeclaringClass()->name == $className) {
6566
foreach ($this->reader->getMethodAnnotations($method) as $constraint) {
66-
if ($constraint instanceof Constraint) {
67+
if ($constraint instanceof Callback) {
68+
$constraint->callback = $method->getName();
69+
$constraint->methods = null;
70+
71+
$metadata->addConstraint($constraint);
72+
} elseif ($constraint instanceof Constraint) {
6773
if (preg_match('/^(get|is)(.+)$/i', $method->name, $matches)) {
6874
$metadata->addGetterConstraint(lcfirst($matches[2]), $constraint);
6975
} else {

0 commit comments

Comments
 (0)