Skip to content

Commit bfe8ccc

Browse files
committed
[Validator] Deprecated CacheInterface in favor of PSR-6.
1 parent 3e8a726 commit bfe8ccc

18 files changed

+163
-57
lines changed

UPGRADE-4.4.md

+2
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,8 @@ Validator
296296
Set it to `true` to keep the current behavior and `false` to reject empty strings.
297297
In 5.0, it'll become optional and will default to `false`.
298298
* Overriding the methods `ConstraintValidatorTestCase::setUp()` and `ConstraintValidatorTestCase::tearDown()` without the `void` return-type is deprecated.
299+
* deprecated `Symfony\Component\Validator\Mapping\Cache\CacheInterface` and all implementations in favor of PSR-6.
300+
* deprecated `ValidatorBuilder::setMetadataCache`, use `ValidatorBuilder::setMappingCache` instead.
299301

300302
WebProfilerBundle
301303
-----------------

UPGRADE-5.0.md

+2
Original file line numberDiff line numberDiff line change
@@ -567,6 +567,8 @@ Validator
567567
* The `symfony/expression-language` component is now required for using the `Expression` constraint
568568
* Changed the default value of `Length::$allowEmptyString` to `false` and made it optional
569569
* Added support for PHPUnit 8. A `void` return-type was added to the `ConstraintValidatorTestCase::setUp()` and `ConstraintValidatorTestCase::tearDown()` methods.
570+
* The `Symfony\Component\Validator\Mapping\Cache\CacheInterface` and all its implementations have been removed.
571+
* The `ValidatorBuilder::setMetadataCache` has been removed, use `ValidatorBuilder::setMappingCache` instead.
570572

571573
WebProfilerBundle
572574
-----------------

src/Symfony/Bundle/FrameworkBundle/CacheWarmer/ValidatorCacheWarmer.php

+1-2
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
use Psr\Cache\CacheItemPoolInterface;
1616
use Symfony\Component\Cache\Adapter\ArrayAdapter;
1717
use Symfony\Component\Cache\Adapter\PhpArrayAdapter;
18-
use Symfony\Component\Validator\Mapping\Cache\Psr6Cache;
1918
use Symfony\Component\Validator\Mapping\Factory\LazyLoadingMetadataFactory;
2019
use Symfony\Component\Validator\Mapping\Loader\LoaderChain;
2120
use Symfony\Component\Validator\Mapping\Loader\LoaderInterface;
@@ -59,7 +58,7 @@ protected function doWarmUp($cacheDir, ArrayAdapter $arrayAdapter)
5958
}
6059

6160
$loaders = $this->validatorBuilder->getLoaders();
62-
$metadataFactory = new LazyLoadingMetadataFactory(new LoaderChain($loaders), new Psr6Cache($arrayAdapter));
61+
$metadataFactory = new LazyLoadingMetadataFactory(new LoaderChain($loaders), $arrayAdapter);
6362

6463
foreach ($this->extractSupportedLoaders($loaders) as $loader) {
6564
foreach ($loader->getMappedClasses() as $mappedClass) {

src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -1287,7 +1287,7 @@ private function registerValidationConfiguration(array $config, ContainerBuilder
12871287
}
12881288

12891289
if (!$container->getParameter('kernel.debug')) {
1290-
$validatorBuilder->addMethodCall('setMetadataCache', [new Reference('validator.mapping.cache.symfony')]);
1290+
$validatorBuilder->addMethodCall('setMappingCache', [new Reference('validator.mapping.cache.adapter')]);
12911291
}
12921292

12931293
$container->setParameter('validator.auto_mapping', $config['auto_mapping']);

src/Symfony/Bundle/FrameworkBundle/Resources/config/validator.xml

+8-7
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,14 @@
3939
</service>
4040

4141
<service id="validator.mapping.cache.symfony" class="Symfony\Component\Validator\Mapping\Cache\Psr6Cache">
42-
<argument type="service">
43-
<service class="Symfony\Component\Cache\Adapter\PhpArrayAdapter">
44-
<factory class="Symfony\Component\Cache\Adapter\PhpArrayAdapter" method="create" />
45-
<argument>%validator.mapping.cache.file%</argument>
46-
<argument type="service" id="cache.validator" />
47-
</service>
48-
</argument>
42+
<argument type="service" id="validator.mapping.cache.adapter" />
43+
<deprecated>The "%service_id%" service is deprecated since Symfony 4.4. Use validator.mapping.cache.adapter instead.</deprecated>
44+
</service>
45+
46+
<service id="validator.mapping.cache.adapter" class="Symfony\Component\Cache\Adapter\PhpArrayAdapter">
47+
<factory class="Symfony\Component\Cache\Adapter\PhpArrayAdapter" method="create" />
48+
<argument>%validator.mapping.cache.file%</argument>
49+
<argument type="service" id="cache.validator" />
4950
</service>
5051

5152
<service id="validator.validator_factory" class="Symfony\Component\Validator\ContainerConstraintValidatorFactory">

src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php

+8-8
Original file line numberDiff line numberDiff line change
@@ -908,8 +908,8 @@ public function testValidation()
908908
}
909909
$this->assertSame('addMethodMapping', $calls[++$i][0]);
910910
$this->assertSame(['loadValidatorMetadata'], $calls[$i][1]);
911-
$this->assertSame('setMetadataCache', $calls[++$i][0]);
912-
$this->assertEquals([new Reference('validator.mapping.cache.symfony')], $calls[$i][1]);
911+
$this->assertSame('setMappingCache', $calls[++$i][0]);
912+
$this->assertEquals([new Reference('validator.mapping.cache.adapter')], $calls[$i][1]);
913913
}
914914

915915
public function testValidationService()
@@ -951,8 +951,8 @@ public function testValidationAnnotations()
951951
$this->assertEquals([new Reference('annotation_reader')], $calls[4][1]);
952952
$this->assertSame('addMethodMapping', $calls[5][0]);
953953
$this->assertSame(['loadValidatorMetadata'], $calls[5][1]);
954-
$this->assertSame('setMetadataCache', $calls[6][0]);
955-
$this->assertEquals([new Reference('validator.mapping.cache.symfony')], $calls[6][1]);
954+
$this->assertSame('setMappingCache', $calls[6][0]);
955+
$this->assertEquals([new Reference('validator.mapping.cache.adapter')], $calls[6][1]);
956956
// no cache this time
957957
}
958958

@@ -973,8 +973,8 @@ public function testValidationPaths()
973973
$this->assertSame('enableAnnotationMapping', $calls[5][0]);
974974
$this->assertSame('addMethodMapping', $calls[6][0]);
975975
$this->assertSame(['loadValidatorMetadata'], $calls[6][1]);
976-
$this->assertSame('setMetadataCache', $calls[7][0]);
977-
$this->assertEquals([new Reference('validator.mapping.cache.symfony')], $calls[7][1]);
976+
$this->assertSame('setMappingCache', $calls[7][0]);
977+
$this->assertEquals([new Reference('validator.mapping.cache.adapter')], $calls[7][1]);
978978

979979
$xmlMappings = $calls[3][1][0];
980980
$this->assertCount(3, $xmlMappings);
@@ -1033,8 +1033,8 @@ public function testValidationNoStaticMethod()
10331033
if ($annotations) {
10341034
$this->assertSame('enableAnnotationMapping', $calls[++$i][0]);
10351035
}
1036-
$this->assertSame('setMetadataCache', $calls[++$i][0]);
1037-
$this->assertEquals([new Reference('validator.mapping.cache.symfony')], $calls[$i][1]);
1036+
$this->assertSame('setMappingCache', $calls[++$i][0]);
1037+
$this->assertEquals([new Reference('validator.mapping.cache.adapter')], $calls[$i][1]);
10381038
// no cache, no annotations, no static methods
10391039
}
10401040

src/Symfony/Bundle/FrameworkBundle/composer.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@
5353
"symfony/translation": "^4.3|^5.0",
5454
"symfony/templating": "^3.4|^4.0|^5.0",
5555
"symfony/twig-bundle": "^4.4|^5.0",
56-
"symfony/validator": "^4.1|^5.0",
56+
"symfony/validator": "^4.4|^5.0",
5757
"symfony/var-dumper": "^4.3|^5.0",
5858
"symfony/workflow": "^4.3|^5.0",
5959
"symfony/yaml": "^3.4|^4.0|^5.0",
@@ -83,7 +83,7 @@
8383
"symfony/translation": "<4.3",
8484
"symfony/twig-bridge": "<4.1.1",
8585
"symfony/twig-bundle": "<4.4",
86-
"symfony/validator": "<4.1",
86+
"symfony/validator": "<4.4",
8787
"symfony/workflow": "<4.3"
8888
},
8989
"suggest": {

src/Symfony/Component/Validator/CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ CHANGELOG
2222
be used in the violation builder when both `min` and `max` are not null
2323
* added ability to use stringable objects as violation messages
2424
* Overriding the methods `ConstraintValidatorTestCase::setUp()` and `ConstraintValidatorTestCase::tearDown()` without the `void` return-type is deprecated.
25+
* deprecated `Symfony\Component\Validator\Mapping\Cache\CacheInterface` in favor of PSR-6.
26+
* deprecated `ValidatorBuilder::setMetadataCache`, use `ValidatorBuilder::setMappingCache` instead.
2527

2628
4.3.0
2729
-----

src/Symfony/Component/Validator/Mapping/Cache/CacheInterface.php

+4
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,14 @@
1313

1414
use Symfony\Component\Validator\Mapping\ClassMetadata;
1515

16+
@trigger_error(sprintf('The "%s" interface is deprecated since Symfony 4.4.', CacheInterface::class), E_USER_DEPRECATED);
17+
1618
/**
1719
* Persists ClassMetadata instances in a cache.
1820
*
1921
* @author Bernhard Schussek <bschussek@gmail.com>
22+
*
23+
* @deprecated since Symfony 4.4.
2024
*/
2125
interface CacheInterface
2226
{

src/Symfony/Component/Validator/Mapping/Cache/DoctrineCache.php

+4
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,14 @@
1414
use Doctrine\Common\Cache\Cache;
1515
use Symfony\Component\Validator\Mapping\ClassMetadata;
1616

17+
@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.4.', DoctrineCache::class), E_USER_DEPRECATED);
18+
1719
/**
1820
* Adapts a Doctrine cache to a CacheInterface.
1921
*
2022
* @author Florian Voutzinos <florian@voutzinos.com>
23+
*
24+
* @deprecated since Symfony 4.4.
2125
*/
2226
final class DoctrineCache implements CacheInterface
2327
{

src/Symfony/Component/Validator/Mapping/Cache/Psr6Cache.php

+4
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,14 @@
1414
use Psr\Cache\CacheItemPoolInterface;
1515
use Symfony\Component\Validator\Mapping\ClassMetadata;
1616

17+
@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.4.', Psr6Cache::class), E_USER_DEPRECATED);
18+
1719
/**
1820
* PSR-6 adapter.
1921
*
2022
* @author Kévin Dunglas <dunglas@gmail.com>
23+
*
24+
* @deprecated since Symfony 4.4.
2125
*/
2226
class Psr6Cache implements CacheInterface
2327
{

src/Symfony/Component/Validator/Mapping/Factory/LazyLoadingMetadataFactory.php

+43-9
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
namespace Symfony\Component\Validator\Mapping\Factory;
1313

14+
use Psr\Cache\CacheItemPoolInterface;
1415
use Symfony\Component\Validator\Exception\NoSuchMetadataException;
1516
use Symfony\Component\Validator\Mapping\Cache\CacheInterface;
1617
use Symfony\Component\Validator\Mapping\ClassMetadata;
@@ -51,12 +52,17 @@ class LazyLoadingMetadataFactory implements MetadataFactoryInterface
5152
/**
5253
* Creates a new metadata factory.
5354
*
54-
* @param LoaderInterface|null $loader The loader for configuring new metadata
55-
* @param CacheInterface|null $cache The cache for persisting metadata
56-
* between multiple PHP requests
55+
* @param CacheItemPoolInterface|null $cache The cache for persisting metadata
56+
* between multiple PHP requests
5757
*/
58-
public function __construct(LoaderInterface $loader = null, CacheInterface $cache = null)
58+
public function __construct(LoaderInterface $loader = null, $cache = null)
5959
{
60+
if ($cache instanceof CacheInterface) {
61+
@trigger_error(sprintf('Passing a "%s" to "%s" is deprecated in Symfony 4.4 and will trigger a TypeError in 5.0. Please pass an implementation of "%s" instead.', \get_class($cache), __METHOD__, CacheItemPoolInterface::class), E_USER_DEPRECATED);
62+
} elseif (!$cache instanceof CacheItemPoolInterface && null !== $cache) {
63+
throw new \TypeError(sprintf('Expected an instance of %s, got %s.', CacheItemPoolInterface::class, \is_object($cache) ? \get_class($cache) : \gettype($cache)));
64+
}
65+
6066
$this->loader = $loader;
6167
$this->cache = $cache;
6268
}
@@ -92,11 +98,24 @@ public function getMetadataFor($value)
9298
throw new NoSuchMetadataException(sprintf('The class or interface "%s" does not exist.', $class));
9399
}
94100

95-
if (null !== $this->cache && false !== ($metadata = $this->cache->read($class))) {
96-
// Include constraints from the parent class
97-
$this->mergeConstraints($metadata);
101+
$cacheItem = null;
102+
if ($this->cache instanceof CacheInterface) {
103+
if ($metadata = $this->cache->read($class)) {
104+
// Include constraints from the parent class
105+
$this->mergeConstraints($metadata);
106+
107+
return $this->loadedClasses[$class] = $metadata;
108+
}
109+
} elseif (null !== $this->cache) {
110+
$cacheItem = $this->cache->getItem($this->escapeClassName($class));
111+
if ($cacheItem->isHit()) {
112+
$metadata = $cacheItem->get();
98113

99-
return $this->loadedClasses[$class] = $metadata;
114+
// Include constraints from the parent class
115+
$this->mergeConstraints($metadata);
116+
117+
return $this->loadedClasses[$class] = $metadata;
118+
}
100119
}
101120

102121
$metadata = new ClassMetadata($class);
@@ -105,8 +124,10 @@ public function getMetadataFor($value)
105124
$this->loader->loadClassMetadata($metadata);
106125
}
107126

108-
if (null !== $this->cache) {
127+
if ($this->cache instanceof CacheInterface) {
109128
$this->cache->write($metadata);
129+
} elseif (null !== $cacheItem) {
130+
$this->cache->save($cacheItem->set($metadata));
110131
}
111132

112133
// Include constraints from the parent class
@@ -162,4 +183,17 @@ public function hasMetadataFor($value)
162183

163184
return class_exists($class) || interface_exists($class, false);
164185
}
186+
187+
/**
188+
* Replaces backslashes by dots in a class name.
189+
*/
190+
private function escapeClassName(string $class): string
191+
{
192+
if (false !== strpos($class, '@')) {
193+
// anonymous class: replace all PSR6-reserved characters
194+
return str_replace(["\0", '\\', '/', '@', ':', '{', '}', '(', ')'], '.', $class);
195+
}
196+
197+
return str_replace('\\', '.', $class);
198+
}
165199
}

src/Symfony/Component/Validator/Tests/Mapping/Cache/DoctrineCacheTest.php

+3
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@
1414
use Doctrine\Common\Cache\ArrayCache;
1515
use Symfony\Component\Validator\Mapping\Cache\DoctrineCache;
1616

17+
/**
18+
* @group legacy
19+
*/
1720
class DoctrineCacheTest extends AbstractCacheTest
1821
{
1922
protected function setUp(): void

src/Symfony/Component/Validator/Tests/Mapping/Cache/Psr6CacheTest.php

+2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88

99
/**
1010
* @author Kévin Dunglas <dunglas@gmail.com>
11+
*
12+
* @group legacy
1113
*/
1214
class Psr6CacheTest extends AbstractCacheTest
1315
{

src/Symfony/Component/Validator/Tests/Mapping/Factory/LazyLoadingMetadataFactoryTest.php

+39-15
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
namespace Symfony\Component\Validator\Tests\Mapping\Factory;
1313

1414
use PHPUnit\Framework\TestCase;
15+
use Psr\Cache\CacheItemPoolInterface;
16+
use Symfony\Component\Cache\Adapter\ArrayAdapter;
1517
use Symfony\Component\Validator\Constraints\Callback;
1618
use Symfony\Component\Validator\Mapping\ClassMetadata;
1719
use Symfony\Component\Validator\Mapping\Factory\LazyLoadingMetadataFactory;
@@ -76,7 +78,36 @@ public function testMergeParentConstraints()
7678
$this->assertEquals($constraints, $metadata->getConstraints());
7779
}
7880

79-
public function testWriteMetadataToCache()
81+
public function testCachedMetadata()
82+
{
83+
$cache = new ArrayAdapter();
84+
$factory = new LazyLoadingMetadataFactory(new TestLoader(), $cache);
85+
86+
$expectedConstraints = [
87+
new ConstraintA(['groups' => ['Default', 'EntityParent']]),
88+
new ConstraintA(['groups' => ['Default', 'EntityInterfaceA', 'EntityParent']]),
89+
];
90+
91+
$metadata = $factory->getMetadataFor(self::PARENT_CLASS);
92+
93+
$this->assertEquals(self::PARENT_CLASS, $metadata->getClassName());
94+
$this->assertEquals($expectedConstraints, $metadata->getConstraints());
95+
96+
$loader = $this->createMock(LoaderInterface::class);
97+
$loader->expects($this->never())->method('loadClassMetadata');
98+
99+
$factory = new LazyLoadingMetadataFactory($loader, $cache);
100+
101+
$metadata = $factory->getMetadataFor(self::PARENT_CLASS);
102+
103+
$this->assertEquals(self::PARENT_CLASS, $metadata->getClassName());
104+
$this->assertEquals($expectedConstraints, $metadata->getConstraints());
105+
}
106+
107+
/**
108+
* @group legacy
109+
*/
110+
public function testWriteMetadataToLegacyCache()
80111
{
81112
$cache = $this->getMockBuilder('Symfony\Component\Validator\Mapping\Cache\CacheInterface')->getMock();
82113
$factory = new LazyLoadingMetadataFactory(new TestLoader(), $cache);
@@ -115,7 +146,10 @@ public function testWriteMetadataToCache()
115146
$this->assertEquals($parentClassConstraints, $metadata->getConstraints());
116147
}
117148

118-
public function testReadMetadataFromCache()
149+
/**
150+
* @group legacy
151+
*/
152+
public function testReadMetadataFromLegacyCache()
119153
{
120154
$loader = $this->getMockBuilder('Symfony\Component\Validator\Mapping\Loader\LoaderInterface')->getMock();
121155
$cache = $this->getMockBuilder('Symfony\Component\Validator\Mapping\Cache\CacheInterface')->getMock();
@@ -154,29 +188,19 @@ public function testNonClassNameStringValues()
154188
$this->expectException('Symfony\Component\Validator\Exception\NoSuchMetadataException');
155189
$testedValue = 'error@example.com';
156190
$loader = $this->getMockBuilder('Symfony\Component\Validator\Mapping\Loader\LoaderInterface')->getMock();
157-
$cache = $this->getMockBuilder('Symfony\Component\Validator\Mapping\Cache\CacheInterface')->getMock();
191+
$cache = $this->createMock(CacheItemPoolInterface::class);
158192
$factory = new LazyLoadingMetadataFactory($loader, $cache);
159193
$cache
160194
->expects($this->never())
161-
->method('read');
195+
->method('getItem');
162196
$factory->getMetadataFor($testedValue);
163197
}
164198

165199
public function testMetadataCacheWithRuntimeConstraint()
166200
{
167-
$cache = $this->getMockBuilder('Symfony\Component\Validator\Mapping\Cache\CacheInterface')->getMock();
201+
$cache = new ArrayAdapter();
168202
$factory = new LazyLoadingMetadataFactory(new TestLoader(), $cache);
169203

170-
$cache
171-
->expects($this->any())
172-
->method('write')
173-
->willReturnCallback(function ($metadata) { serialize($metadata); })
174-
;
175-
176-
$cache->expects($this->any())
177-
->method('read')
178-
->willReturn(false);
179-
180204
$metadata = $factory->getMetadataFor(self::PARENT_CLASS);
181205
$metadata->addConstraint(new Callback(function () {}));
182206

0 commit comments

Comments
 (0)