Skip to content

Commit d66cfc3

Browse files
committed
[Validator] Allow load mappings from attributes without doctrine/annotations.
1 parent e8c253a commit d66cfc3

File tree

5 files changed

+115
-15
lines changed

5 files changed

+115
-15
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ CHANGELOG
3434
* added support for UUIDv6 in `Uuid` constraint
3535
* enabled the validator to load constraints from PHP attributes
3636
* deprecated the `NumberConstraintTrait` trait
37+
* deprecated setting or creating a Doctrine annotation reader via `ValidatorBuilder::enableAnnotationMapping()`, pass `true` as first parameter and additionally call `setDoctrineAnnotationReader()` or `addDefaultDoctrineAnnotationReader()` to set up the annotation reader
3738

3839
5.1.0
3940
-----

Tests/Constraints/ValidValidatorTest.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ class ValidValidatorTest extends TestCase
1212
public function testPropertyPathsArePassedToNestedContexts()
1313
{
1414
$validatorBuilder = new ValidatorBuilder();
15-
$validator = $validatorBuilder->enableAnnotationMapping()->getValidator();
15+
$validator = $validatorBuilder->enableAnnotationMapping(true)->addDefaultDoctrineAnnotationReader()->getValidator();
1616

1717
$violations = $validator->validate(new Foo(), null, ['nested']);
1818

@@ -23,7 +23,7 @@ public function testPropertyPathsArePassedToNestedContexts()
2323
public function testNullValues()
2424
{
2525
$validatorBuilder = new ValidatorBuilder();
26-
$validator = $validatorBuilder->enableAnnotationMapping()->getValidator();
26+
$validator = $validatorBuilder->enableAnnotationMapping(true)->addDefaultDoctrineAnnotationReader()->getValidator();
2727

2828
$foo = new Foo();
2929
$foo->fooBar = null;

Tests/Mapping/Loader/PropertyInfoLoaderTest.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,8 @@ public function testLoadClassMetadata()
8989
$propertyInfoLoader = new PropertyInfoLoader($propertyInfoStub, $propertyInfoStub, $propertyInfoStub, '{.*}');
9090

9191
$validator = Validation::createValidatorBuilder()
92-
->enableAnnotationMapping()
92+
->enableAnnotationMapping(true)
93+
->addDefaultDoctrineAnnotationReader()
9394
->addLoader($propertyInfoLoader)
9495
->getValidator()
9596
;
@@ -220,7 +221,8 @@ public function testClassNoAutoMapping()
220221

221222
$propertyInfoLoader = new PropertyInfoLoader($propertyInfoStub, $propertyInfoStub, $propertyInfoStub, '{.*}');
222223
$validator = Validation::createValidatorBuilder()
223-
->enableAnnotationMapping()
224+
->enableAnnotationMapping(true)
225+
->addDefaultDoctrineAnnotationReader()
224226
->addLoader($propertyInfoLoader)
225227
->getValidator()
226228
;

Tests/ValidatorBuilderTest.php

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,18 @@
1111

1212
namespace Symfony\Component\Validator\Tests;
1313

14+
use Doctrine\Common\Annotations\CachedReader;
15+
use Doctrine\Common\Annotations\Reader;
1416
use PHPUnit\Framework\TestCase;
1517
use Psr\Cache\CacheItemPoolInterface;
18+
use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait;
19+
use Symfony\Component\Validator\Mapping\Loader\AnnotationLoader;
1620
use Symfony\Component\Validator\ValidatorBuilder;
1721

1822
class ValidatorBuilderTest extends TestCase
1923
{
24+
use ExpectDeprecationTrait;
25+
2026
/**
2127
* @var ValidatorBuilder
2228
*/
@@ -74,9 +80,74 @@ public function testAddMethodMappings()
7480
$this->assertSame($this->builder, $this->builder->addMethodMappings([]));
7581
}
7682

83+
/**
84+
* @group legacy
85+
*/
7786
public function testEnableAnnotationMapping()
7887
{
88+
$this->expectDeprecation('Since symfony/validator 5.2: Not passing true as first argument to "Symfony\Component\Validator\ValidatorBuilder::enableAnnotationMapping" is deprecated. Pass true and call "addDefaultDoctrineAnnotationReader()" if you want to enable annotation mapping with Doctrine Annotations.');
7989
$this->assertSame($this->builder, $this->builder->enableAnnotationMapping());
90+
91+
$loaders = $this->builder->getLoaders();
92+
$this->assertCount(1, $loaders);
93+
$this->assertInstanceOf(AnnotationLoader::class, $loaders[0]);
94+
95+
$r = new \ReflectionProperty(AnnotationLoader::class, 'reader');
96+
$r->setAccessible(true);
97+
98+
$this->assertInstanceOf(CachedReader::class, $r->getValue($loaders[0]));
99+
}
100+
101+
public function testEnableAnnotationMappingWithDefaultDoctrineAnnotationReader()
102+
{
103+
$this->assertSame($this->builder, $this->builder->enableAnnotationMapping(true));
104+
$this->assertSame($this->builder, $this->builder->addDefaultDoctrineAnnotationReader());
105+
106+
$loaders = $this->builder->getLoaders();
107+
$this->assertCount(1, $loaders);
108+
$this->assertInstanceOf(AnnotationLoader::class, $loaders[0]);
109+
110+
$r = new \ReflectionProperty(AnnotationLoader::class, 'reader');
111+
$r->setAccessible(true);
112+
113+
$this->assertInstanceOf(CachedReader::class, $r->getValue($loaders[0]));
114+
}
115+
116+
/**
117+
* @group legacy
118+
*/
119+
public function testEnableAnnotationMappingWithCustomDoctrineAnnotationReaderLegacy()
120+
{
121+
$reader = $this->createMock(Reader::class);
122+
123+
$this->expectDeprecation('Since symfony/validator 5.2: Passing an instance of "'.\get_class($reader).'" as first argument to "Symfony\Component\Validator\ValidatorBuilder::enableAnnotationMapping" is deprecated. Pass true instead and call setDoctrineAnnotationReader() if you want to enable annotation mapping with Doctrine Annotations.');
124+
$this->assertSame($this->builder, $this->builder->enableAnnotationMapping($reader));
125+
126+
$loaders = $this->builder->getLoaders();
127+
$this->assertCount(1, $loaders);
128+
$this->assertInstanceOf(AnnotationLoader::class, $loaders[0]);
129+
130+
$r = new \ReflectionProperty(AnnotationLoader::class, 'reader');
131+
$r->setAccessible(true);
132+
133+
$this->assertSame($reader, $r->getValue($loaders[0]));
134+
}
135+
136+
public function testEnableAnnotationMappingWithCustomDoctrineAnnotationReader()
137+
{
138+
$reader = $this->createMock(Reader::class);
139+
140+
$this->assertSame($this->builder, $this->builder->enableAnnotationMapping(true));
141+
$this->assertSame($this->builder, $this->builder->setDoctrineAnnotationReader($reader));
142+
143+
$loaders = $this->builder->getLoaders();
144+
$this->assertCount(1, $loaders);
145+
$this->assertInstanceOf(AnnotationLoader::class, $loaders[0]);
146+
147+
$r = new \ReflectionProperty(AnnotationLoader::class, 'reader');
148+
$r->setAccessible(true);
149+
150+
$this->assertSame($reader, $r->getValue($loaders[0]));
80151
}
81152

82153
public function testDisableAnnotationMapping()

ValidatorBuilder.php

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
use Doctrine\Common\Cache\ArrayCache;
1818
use Psr\Cache\CacheItemPoolInterface;
1919
use Symfony\Component\Validator\Context\ExecutionContextFactory;
20-
use Symfony\Component\Validator\Exception\LogicException;
2120
use Symfony\Component\Validator\Exception\ValidatorException;
2221
use Symfony\Component\Validator\Mapping\Factory\LazyLoadingMetadataFactory;
2322
use Symfony\Component\Validator\Mapping\Factory\MetadataFactoryInterface;
@@ -53,6 +52,7 @@ class ValidatorBuilder
5352
* @var Reader|null
5453
*/
5554
private $annotationReader;
55+
private $enableAnnotationMapping = false;
5656

5757
/**
5858
* @var MetadataFactoryInterface|null
@@ -212,23 +212,28 @@ public function addMethodMappings(array $methodNames)
212212
/**
213213
* Enables annotation based constraint mapping.
214214
*
215+
* @param bool $skipDoctrineAnnotations
216+
*
215217
* @return $this
216218
*/
217-
public function enableAnnotationMapping(Reader $annotationReader = null)
219+
public function enableAnnotationMapping(/* bool $skipDoctrineAnnotations = true */)
218220
{
219221
if (null !== $this->metadataFactory) {
220222
throw new ValidatorException('You cannot enable annotation mapping after setting a custom metadata factory. Configure your metadata factory instead.');
221223
}
222224

223-
if (null === $annotationReader) {
224-
if (!class_exists(AnnotationReader::class) || !class_exists(ArrayCache::class)) {
225-
throw new LogicException('Enabling annotation based constraint mapping requires the packages doctrine/annotations and doctrine/cache to be installed.');
226-
}
227-
228-
$annotationReader = new CachedReader(new AnnotationReader(), new ArrayCache());
225+
$skipDoctrineAnnotations = 1 > \func_num_args() ? false : func_get_arg(0);
226+
if (false === $skipDoctrineAnnotations || null === $skipDoctrineAnnotations) {
227+
trigger_deprecation('symfony/validator', '5.2', 'Not passing true as first argument to "%s" is deprecated. Pass true and call "addDefaultDoctrineAnnotationReader()" if you want to enable annotation mapping with Doctrine Annotations.', __METHOD__);
228+
$this->addDefaultDoctrineAnnotationReader();
229+
} elseif ($skipDoctrineAnnotations instanceof Reader) {
230+
trigger_deprecation('symfony/validator', '5.2', 'Passing an instance of "%s" as first argument to "%s" is deprecated. Pass true instead and call setDoctrineAnnotationReader() if you want to enable annotation mapping with Doctrine Annotations.', get_debug_type($skipDoctrineAnnotations), __METHOD__);
231+
$this->setDoctrineAnnotationReader($skipDoctrineAnnotations);
232+
} elseif (true !== $skipDoctrineAnnotations) {
233+
throw new \TypeError(sprintf('"%s": Argument 1 is expected to be a boolean, "%s" given.', __METHOD__, get_debug_type($skipDoctrineAnnotations)));
229234
}
230235

231-
$this->annotationReader = $annotationReader;
236+
$this->enableAnnotationMapping = true;
232237

233238
return $this;
234239
}
@@ -240,19 +245,40 @@ public function enableAnnotationMapping(Reader $annotationReader = null)
240245
*/
241246
public function disableAnnotationMapping()
242247
{
248+
$this->enableAnnotationMapping = false;
243249
$this->annotationReader = null;
244250

245251
return $this;
246252
}
247253

254+
/**
255+
* @return $this
256+
*/
257+
public function setDoctrineAnnotationReader(?Reader $reader): self
258+
{
259+
$this->annotationReader = $reader;
260+
261+
return $this;
262+
}
263+
264+
/**
265+
* @return $this
266+
*/
267+
public function addDefaultDoctrineAnnotationReader(): self
268+
{
269+
$this->annotationReader = new CachedReader(new AnnotationReader(), new ArrayCache());
270+
271+
return $this;
272+
}
273+
248274
/**
249275
* Sets the class metadata factory used by the validator.
250276
*
251277
* @return $this
252278
*/
253279
public function setMetadataFactory(MetadataFactoryInterface $metadataFactory)
254280
{
255-
if (\count($this->xmlMappings) > 0 || \count($this->yamlMappings) > 0 || \count($this->methodMappings) > 0 || null !== $this->annotationReader) {
281+
if (\count($this->xmlMappings) > 0 || \count($this->yamlMappings) > 0 || \count($this->methodMappings) > 0 || $this->enableAnnotationMapping) {
256282
throw new ValidatorException('You cannot set a custom metadata factory after adding custom mappings. You should do either of both.');
257283
}
258284

@@ -346,7 +372,7 @@ public function getLoaders()
346372
$loaders[] = new StaticMethodLoader($methodName);
347373
}
348374

349-
if ($this->annotationReader) {
375+
if ($this->enableAnnotationMapping) {
350376
$loaders[] = new AnnotationLoader($this->annotationReader);
351377
}
352378

0 commit comments

Comments
 (0)