Skip to content

[Validator] Deprecated CacheInterface in favor of PSR-6 #33459

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions UPGRADE-4.4.md
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,8 @@ Validator
Set it to `true` to keep the current behavior and `false` to reject empty strings.
In 5.0, it'll become optional and will default to `false`.
* Overriding the methods `ConstraintValidatorTestCase::setUp()` and `ConstraintValidatorTestCase::tearDown()` without the `void` return-type is deprecated.
* deprecated `Symfony\Component\Validator\Mapping\Cache\CacheInterface` and all implementations in favor of PSR-6.
* deprecated `ValidatorBuilder::setMetadataCache`, use `ValidatorBuilder::setMappingCache` instead.

WebProfilerBundle
-----------------
Expand Down
2 changes: 2 additions & 0 deletions UPGRADE-5.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -567,6 +567,8 @@ Validator
* The `symfony/expression-language` component is now required for using the `Expression` constraint
* Changed the default value of `Length::$allowEmptyString` to `false` and made it optional
* Added support for PHPUnit 8. A `void` return-type was added to the `ConstraintValidatorTestCase::setUp()` and `ConstraintValidatorTestCase::tearDown()` methods.
* The `Symfony\Component\Validator\Mapping\Cache\CacheInterface` and all its implementations have been removed.
* The `ValidatorBuilder::setMetadataCache` has been removed, use `ValidatorBuilder::setMappingCache` instead.

WebProfilerBundle
-----------------
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
use Psr\Cache\CacheItemPoolInterface;
use Symfony\Component\Cache\Adapter\ArrayAdapter;
use Symfony\Component\Cache\Adapter\PhpArrayAdapter;
use Symfony\Component\Validator\Mapping\Cache\Psr6Cache;
use Symfony\Component\Validator\Mapping\Factory\LazyLoadingMetadataFactory;
use Symfony\Component\Validator\Mapping\Loader\LoaderChain;
use Symfony\Component\Validator\Mapping\Loader\LoaderInterface;
Expand Down Expand Up @@ -59,7 +58,7 @@ protected function doWarmUp($cacheDir, ArrayAdapter $arrayAdapter)
}

$loaders = $this->validatorBuilder->getLoaders();
$metadataFactory = new LazyLoadingMetadataFactory(new LoaderChain($loaders), new Psr6Cache($arrayAdapter));
$metadataFactory = new LazyLoadingMetadataFactory(new LoaderChain($loaders), $arrayAdapter);

foreach ($this->extractSupportedLoaders($loaders) as $loader) {
foreach ($loader->getMappedClasses() as $mappedClass) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1288,7 +1288,7 @@ private function registerValidationConfiguration(array $config, ContainerBuilder
}

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

$container->setParameter('validator.auto_mapping', $config['auto_mapping']);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,14 @@
</service>

<service id="validator.mapping.cache.symfony" class="Symfony\Component\Validator\Mapping\Cache\Psr6Cache">
<argument type="service">
<service class="Symfony\Component\Cache\Adapter\PhpArrayAdapter">
<factory class="Symfony\Component\Cache\Adapter\PhpArrayAdapter" method="create" />
<argument>%validator.mapping.cache.file%</argument>
<argument type="service" id="cache.validator" />
</service>
</argument>
<argument type="service" id="validator.mapping.cache.adapter" />
<deprecated>The "%service_id%" service is deprecated since Symfony 4.4. Use validator.mapping.cache.adapter instead.</deprecated>
</service>

<service id="validator.mapping.cache.adapter" class="Symfony\Component\Cache\Adapter\PhpArrayAdapter">
<factory class="Symfony\Component\Cache\Adapter\PhpArrayAdapter" method="create" />
<argument>%validator.mapping.cache.file%</argument>
<argument type="service" id="cache.validator" />
</service>

<service id="validator.validator_factory" class="Symfony\Component\Validator\ContainerConstraintValidatorFactory">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -908,8 +908,8 @@ public function testValidation()
}
$this->assertSame('addMethodMapping', $calls[++$i][0]);
$this->assertSame(['loadValidatorMetadata'], $calls[$i][1]);
$this->assertSame('setMetadataCache', $calls[++$i][0]);
$this->assertEquals([new Reference('validator.mapping.cache.symfony')], $calls[$i][1]);
$this->assertSame('setMappingCache', $calls[++$i][0]);
$this->assertEquals([new Reference('validator.mapping.cache.adapter')], $calls[$i][1]);
}

public function testValidationService()
Expand Down Expand Up @@ -951,8 +951,8 @@ public function testValidationAnnotations()
$this->assertEquals([new Reference('annotation_reader')], $calls[4][1]);
$this->assertSame('addMethodMapping', $calls[5][0]);
$this->assertSame(['loadValidatorMetadata'], $calls[5][1]);
$this->assertSame('setMetadataCache', $calls[6][0]);
$this->assertEquals([new Reference('validator.mapping.cache.symfony')], $calls[6][1]);
$this->assertSame('setMappingCache', $calls[6][0]);
$this->assertEquals([new Reference('validator.mapping.cache.adapter')], $calls[6][1]);
// no cache this time
}

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

$xmlMappings = $calls[3][1][0];
$this->assertCount(3, $xmlMappings);
Expand Down Expand Up @@ -1033,8 +1033,8 @@ public function testValidationNoStaticMethod()
if ($annotations) {
$this->assertSame('enableAnnotationMapping', $calls[++$i][0]);
}
$this->assertSame('setMetadataCache', $calls[++$i][0]);
$this->assertEquals([new Reference('validator.mapping.cache.symfony')], $calls[$i][1]);
$this->assertSame('setMappingCache', $calls[++$i][0]);
$this->assertEquals([new Reference('validator.mapping.cache.adapter')], $calls[$i][1]);
// no cache, no annotations, no static methods
}

Expand Down
4 changes: 2 additions & 2 deletions src/Symfony/Bundle/FrameworkBundle/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@
"symfony/translation": "^4.3|^5.0",
"symfony/templating": "^3.4|^4.0|^5.0",
"symfony/twig-bundle": "^4.4|^5.0",
"symfony/validator": "^4.1|^5.0",
"symfony/validator": "^4.4|^5.0",
"symfony/var-dumper": "^4.3|^5.0",
"symfony/workflow": "^4.3|^5.0",
"symfony/yaml": "^3.4|^4.0|^5.0",
Expand Down Expand Up @@ -83,7 +83,7 @@
"symfony/translation": "<4.3",
"symfony/twig-bridge": "<4.1.1",
"symfony/twig-bundle": "<4.4",
"symfony/validator": "<4.1",
"symfony/validator": "<4.4",
"symfony/workflow": "<4.3"
},
"suggest": {
Expand Down
2 changes: 2 additions & 0 deletions src/Symfony/Component/Validator/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ CHANGELOG
be used in the violation builder when both `min` and `max` are not null
* added ability to use stringable objects as violation messages
* Overriding the methods `ConstraintValidatorTestCase::setUp()` and `ConstraintValidatorTestCase::tearDown()` without the `void` return-type is deprecated.
* deprecated `Symfony\Component\Validator\Mapping\Cache\CacheInterface` in favor of PSR-6.
* deprecated `ValidatorBuilder::setMetadataCache`, use `ValidatorBuilder::setMappingCache` instead.

4.3.0
-----
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,14 @@

use Symfony\Component\Validator\Mapping\ClassMetadata;

@trigger_error(sprintf('The "%s" interface is deprecated since Symfony 4.4.', CacheInterface::class), E_USER_DEPRECATED);

/**
* Persists ClassMetadata instances in a cache.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @deprecated since Symfony 4.4.
*/
interface CacheInterface
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,14 @@
use Doctrine\Common\Cache\Cache;
use Symfony\Component\Validator\Mapping\ClassMetadata;

@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.4.', DoctrineCache::class), E_USER_DEPRECATED);

/**
* Adapts a Doctrine cache to a CacheInterface.
*
* @author Florian Voutzinos <florian@voutzinos.com>
*
* @deprecated since Symfony 4.4.
*/
final class DoctrineCache implements CacheInterface
{
Expand Down
4 changes: 4 additions & 0 deletions src/Symfony/Component/Validator/Mapping/Cache/Psr6Cache.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,14 @@
use Psr\Cache\CacheItemPoolInterface;
use Symfony\Component\Validator\Mapping\ClassMetadata;

@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.4.', Psr6Cache::class), E_USER_DEPRECATED);

/**
* PSR-6 adapter.
*
* @author Kévin Dunglas <dunglas@gmail.com>
*
* @deprecated since Symfony 4.4.
*/
class Psr6Cache implements CacheInterface
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

namespace Symfony\Component\Validator\Mapping\Factory;

use Psr\Cache\CacheItemPoolInterface;
use Symfony\Component\Validator\Exception\NoSuchMetadataException;
use Symfony\Component\Validator\Mapping\Cache\CacheInterface;
use Symfony\Component\Validator\Mapping\ClassMetadata;
Expand Down Expand Up @@ -51,12 +52,17 @@ class LazyLoadingMetadataFactory implements MetadataFactoryInterface
/**
* Creates a new metadata factory.
*
* @param LoaderInterface|null $loader The loader for configuring new metadata
* @param CacheInterface|null $cache The cache for persisting metadata
* between multiple PHP requests
* @param CacheItemPoolInterface|null $cache The cache for persisting metadata
* between multiple PHP requests
*/
public function __construct(LoaderInterface $loader = null, CacheInterface $cache = null)
public function __construct(LoaderInterface $loader = null, $cache = null)
{
if ($cache instanceof CacheInterface) {
@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);
} elseif (!$cache instanceof CacheItemPoolInterface && null !== $cache) {
throw new \TypeError(sprintf('Expected an instance of %s, got %s.', CacheItemPoolInterface::class, \is_object($cache) ? \get_class($cache) : \gettype($cache)));
}

$this->loader = $loader;
$this->cache = $cache;
}
Expand Down Expand Up @@ -92,11 +98,24 @@ public function getMetadataFor($value)
throw new NoSuchMetadataException(sprintf('The class or interface "%s" does not exist.', $class));
}

if (null !== $this->cache && false !== ($metadata = $this->cache->read($class))) {
// Include constraints from the parent class
$this->mergeConstraints($metadata);
$cacheItem = null;
if ($this->cache instanceof CacheInterface) {
if ($metadata = $this->cache->read($class)) {
// Include constraints from the parent class
$this->mergeConstraints($metadata);

return $this->loadedClasses[$class] = $metadata;
}
} elseif (null !== $this->cache) {
$cacheItem = $this->cache->getItem($this->escapeClassName($class));
if ($cacheItem->isHit()) {
$metadata = $cacheItem->get();

return $this->loadedClasses[$class] = $metadata;
// Include constraints from the parent class
$this->mergeConstraints($metadata);

return $this->loadedClasses[$class] = $metadata;
}
}

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

if (null !== $this->cache) {
if ($this->cache instanceof CacheInterface) {
$this->cache->write($metadata);
} elseif (null !== $cacheItem) {
$this->cache->save($cacheItem->set($metadata));
}

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

return class_exists($class) || interface_exists($class, false);
}

/**
* Replaces backslashes by dots in a class name.
*/
private function escapeClassName(string $class): string
{
if (false !== strpos($class, '@')) {
// anonymous class: replace all PSR6-reserved characters
return str_replace(["\0", '\\', '/', '@', ':', '{', '}', '(', ')'], '.', $class);
}

return str_replace('\\', '.', $class);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@
use Doctrine\Common\Cache\ArrayCache;
use Symfony\Component\Validator\Mapping\Cache\DoctrineCache;

/**
* @group legacy
*/
class DoctrineCacheTest extends AbstractCacheTest
{
protected function setUp(): void
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

/**
* @author Kévin Dunglas <dunglas@gmail.com>
*
* @group legacy
*/
class Psr6CacheTest extends AbstractCacheTest
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
namespace Symfony\Component\Validator\Tests\Mapping\Factory;

use PHPUnit\Framework\TestCase;
use Psr\Cache\CacheItemPoolInterface;
use Symfony\Component\Cache\Adapter\ArrayAdapter;
use Symfony\Component\Validator\Constraints\Callback;
use Symfony\Component\Validator\Mapping\ClassMetadata;
use Symfony\Component\Validator\Mapping\Factory\LazyLoadingMetadataFactory;
Expand Down Expand Up @@ -76,7 +78,36 @@ public function testMergeParentConstraints()
$this->assertEquals($constraints, $metadata->getConstraints());
}

public function testWriteMetadataToCache()
public function testCachedMetadata()
{
$cache = new ArrayAdapter();
$factory = new LazyLoadingMetadataFactory(new TestLoader(), $cache);

$expectedConstraints = [
new ConstraintA(['groups' => ['Default', 'EntityParent']]),
new ConstraintA(['groups' => ['Default', 'EntityInterfaceA', 'EntityParent']]),
];

$metadata = $factory->getMetadataFor(self::PARENT_CLASS);

$this->assertEquals(self::PARENT_CLASS, $metadata->getClassName());
$this->assertEquals($expectedConstraints, $metadata->getConstraints());

$loader = $this->createMock(LoaderInterface::class);
$loader->expects($this->never())->method('loadClassMetadata');

$factory = new LazyLoadingMetadataFactory($loader, $cache);

$metadata = $factory->getMetadataFor(self::PARENT_CLASS);

$this->assertEquals(self::PARENT_CLASS, $metadata->getClassName());
$this->assertEquals($expectedConstraints, $metadata->getConstraints());
}

/**
* @group legacy
*/
public function testWriteMetadataToLegacyCache()
{
$cache = $this->getMockBuilder('Symfony\Component\Validator\Mapping\Cache\CacheInterface')->getMock();
$factory = new LazyLoadingMetadataFactory(new TestLoader(), $cache);
Expand Down Expand Up @@ -115,7 +146,10 @@ public function testWriteMetadataToCache()
$this->assertEquals($parentClassConstraints, $metadata->getConstraints());
}

public function testReadMetadataFromCache()
/**
* @group legacy
*/
public function testReadMetadataFromLegacyCache()
{
$loader = $this->getMockBuilder('Symfony\Component\Validator\Mapping\Loader\LoaderInterface')->getMock();
$cache = $this->getMockBuilder('Symfony\Component\Validator\Mapping\Cache\CacheInterface')->getMock();
Expand Down Expand Up @@ -154,29 +188,19 @@ public function testNonClassNameStringValues()
$this->expectException('Symfony\Component\Validator\Exception\NoSuchMetadataException');
$testedValue = 'error@example.com';
$loader = $this->getMockBuilder('Symfony\Component\Validator\Mapping\Loader\LoaderInterface')->getMock();
$cache = $this->getMockBuilder('Symfony\Component\Validator\Mapping\Cache\CacheInterface')->getMock();
$cache = $this->createMock(CacheItemPoolInterface::class);
$factory = new LazyLoadingMetadataFactory($loader, $cache);
$cache
->expects($this->never())
->method('read');
->method('getItem');
$factory->getMetadataFor($testedValue);
}

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

$cache
->expects($this->any())
->method('write')
->willReturnCallback(function ($metadata) { serialize($metadata); })
;

$cache->expects($this->any())
->method('read')
->willReturn(false);

$metadata = $factory->getMetadataFor(self::PARENT_CLASS);
$metadata->addConstraint(new Callback(function () {}));

Expand Down
Loading