Skip to content

Commit 355f48c

Browse files
committed
[Serializer] Add a PSR-6 adapter
1 parent c5a7886 commit 355f48c

File tree

6 files changed

+207
-35
lines changed

6 files changed

+207
-35
lines changed
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Serializer\Mapping\Factory;
13+
14+
use Psr\Cache\CacheItemPoolInterface;
15+
16+
/**
17+
* Caches metadata using a PSR-6 implementation.
18+
*
19+
* @author Kévin Dunglas <dunglas@gmail.com>
20+
*/
21+
class CacheClassMetadataFactory implements ClassMetadataFactoryInterface
22+
{
23+
use ClassResolverTrait;
24+
25+
/**
26+
* @var ClassMetadataFactoryInterface
27+
*/
28+
private $decorated;
29+
30+
/**
31+
* @var CacheItemPoolInterface
32+
*/
33+
private $cacheItemPool;
34+
35+
public function __construct(ClassMetadataFactoryInterface $decorated, CacheItemPoolInterface $cacheItemPool)
36+
{
37+
$this->decorated = $decorated;
38+
$this->cacheItemPool = $cacheItemPool;
39+
}
40+
41+
/**
42+
* {@inheritdoc}
43+
*/
44+
public function getMetadataFor($value)
45+
{
46+
$class = $this->getClass($value);
47+
// Key cannot contain backslashes according to PSR-6
48+
$key = strtr($class, '\\', '_');
49+
50+
$item = $this->cacheItemPool->getItem($key);
51+
if ($item->isHit()) {
52+
return $item->get();
53+
}
54+
55+
$metadata = $this->decorated->getMetadataFor($value);
56+
$this->cacheItemPool->save($item->set($metadata));
57+
58+
return $metadata;
59+
}
60+
61+
/**
62+
* {@inheritdoc}
63+
*/
64+
public function hasMetadataFor($value)
65+
{
66+
return $this->decorated->hasMetadataFor($value);
67+
}
68+
}

src/Symfony/Component/Serializer/Mapping/Factory/ClassMetadataFactory.php

Lines changed: 12 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
*/
2424
class ClassMetadataFactory implements ClassMetadataFactoryInterface
2525
{
26+
use ClassResolverTrait;
27+
2628
/**
2729
* @var LoaderInterface
2830
*/
@@ -44,6 +46,10 @@ public function __construct(LoaderInterface $loader, Cache $cache = null)
4446
{
4547
$this->loader = $loader;
4648
$this->cache = $cache;
49+
50+
if (null !== $cache) {
51+
@trigger_error('Passing a Doctrine Cache instance as 2nd parameter of the "Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface" is deprecated. This parameter will be removed in Symfony 4.0. Use the "Symfony\Component\Serializer\Mapping\Factory\CacheMetadataFactory" class instead.', E_USER_DEPRECATED);
52+
}
4753
}
4854

4955
/**
@@ -52,9 +58,6 @@ public function __construct(LoaderInterface $loader, Cache $cache = null)
5258
public function getMetadataFor($value)
5359
{
5460
$class = $this->getClass($value);
55-
if (!$class) {
56-
throw new InvalidArgumentException(sprintf('Cannot create metadata for non-objects. Got: "%s"', gettype($value)));
57-
}
5861

5962
if (isset($this->loadedClasses[$class])) {
6063
return $this->loadedClasses[$class];
@@ -64,10 +67,6 @@ public function getMetadataFor($value)
6467
return $this->loadedClasses[$class];
6568
}
6669

67-
if (!class_exists($class) && !interface_exists($class)) {
68-
throw new InvalidArgumentException(sprintf('The class or interface "%s" does not exist.', $class));
69-
}
70-
7170
$classMetadata = new ClassMetadata($class);
7271
$this->loader->loadClassMetadata($classMetadata);
7372

@@ -95,24 +94,14 @@ public function getMetadataFor($value)
9594
*/
9695
public function hasMetadataFor($value)
9796
{
98-
$class = $this->getClass($value);
99-
100-
return class_exists($class) || interface_exists($class);
101-
}
97+
try {
98+
$this->getClass($value);
10299

103-
/**
104-
* Gets a class name for a given class or instance.
105-
*
106-
* @param mixed $value
107-
*
108-
* @return string|bool
109-
*/
110-
private function getClass($value)
111-
{
112-
if (!is_object($value) && !is_string($value)) {
113-
return false;
100+
return true;
101+
} catch (InvalidArgumentException $invalidArgumentException) {
102+
// Return false in case of exception
114103
}
115104

116-
return ltrim(is_object($value) ? get_class($value) : $value, '\\');
105+
return false;
117106
}
118107
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Serializer\Mapping\Factory;
13+
14+
use Symfony\Component\Serializer\Exception\InvalidArgumentException;
15+
16+
/**
17+
* Resolves a class name.
18+
*
19+
* @author Kévin Dunglas <dunglas@gmail.com>
20+
*/
21+
trait ClassResolverTrait
22+
{
23+
/**
24+
* Gets a class name for a given class or instance.
25+
*
26+
* @param mixed $value
27+
*
28+
* @return string
29+
*
30+
* @throws InvalidArgumentException If the class does not exists
31+
*/
32+
private function getClass($value)
33+
{
34+
if (is_string($value)) {
35+
if (!class_exists($value) && !interface_exists($value)) {
36+
throw new InvalidArgumentException(sprintf('The class or interface "%s" does not exist.', $value));
37+
}
38+
39+
return ltrim($value, '\\');
40+
}
41+
42+
if (!is_object($value)) {
43+
throw new InvalidArgumentException(sprintf('Cannot create metadata for non-objects. Got: "%s"', gettype($value)));
44+
}
45+
46+
return get_class($value);
47+
}
48+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Serializer\Tests\Mapping\Factory;
13+
14+
use Symfony\Component\Cache\Adapter\ArrayAdapter;
15+
use Symfony\Component\Serializer\Mapping\ClassMetadata;
16+
use Symfony\Component\Serializer\Mapping\Factory\CacheClassMetadataFactory;
17+
18+
/**
19+
* @author Kévin Dunglas <dunglas@gmail.com>
20+
*/
21+
class CacheMetadataFactoryTest extends \PHPUnit_Framework_TestCase
22+
{
23+
public function testGetMetadataFor()
24+
{
25+
$metadata = new ClassMetadata('Symfony\Component\Serializer\Tests\Fixtures\Dummy');
26+
27+
$decorated = $this->getMock('Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface');
28+
$decorated
29+
->expects($this->once())
30+
->method('getMetadataFor')
31+
->will($this->returnValue($metadata))
32+
;
33+
34+
$factory = new CacheClassMetadataFactory($decorated, new ArrayAdapter());
35+
36+
$this->assertEquals($metadata, $factory->getMetadataFor('Symfony\Component\Serializer\Tests\Fixtures\Dummy'));
37+
// The second call should retrieve the value from the cache
38+
$this->assertEquals($metadata, $factory->getMetadataFor('Symfony\Component\Serializer\Tests\Fixtures\Dummy'));
39+
}
40+
41+
public function testHasMetadataFor()
42+
{
43+
$decorated = $this->getMock('Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface');
44+
$decorated
45+
->expects($this->once())
46+
->method('hasMetadataFor')
47+
->will($this->returnValue(true))
48+
;
49+
50+
$factory = new CacheClassMetadataFactory($decorated, new ArrayAdapter());
51+
52+
$this->assertTrue($factory->hasMetadataFor('Symfony\Component\Serializer\Tests\Fixtures\Dummy'));
53+
}
54+
55+
/**
56+
* @expectedException \Symfony\Component\Serializer\Exception\InvalidArgumentException
57+
*/
58+
public function testInvalidClassThrowsException()
59+
{
60+
$decorated = $this->getMock('Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface');
61+
$factory = new CacheClassMetadataFactory($decorated, new ArrayAdapter());
62+
63+
$factory->getMetadataFor('Not\Exist');
64+
}
65+
}

src/Symfony/Component/Serializer/Tests/Mapping/Factory/ClassMetadataFactoryTest.php

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ class ClassMetadataFactoryTest extends \PHPUnit_Framework_TestCase
2525
public function testInterface()
2626
{
2727
$classMetadata = new ClassMetadataFactory(new LoaderChain(array()));
28-
$this->assertInstanceOf('Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactory', $classMetadata);
28+
$this->assertInstanceOf('Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface', $classMetadata);
2929
}
3030

3131
public function testGetMetadataFor()
@@ -45,6 +45,9 @@ public function testHasMetadataFor()
4545
$this->assertFalse($factory->hasMetadataFor('Dunglas\Entity'));
4646
}
4747

48+
/**
49+
* @group legacy
50+
*/
4851
public function testCacheExists()
4952
{
5053
$cache = $this->getMock('Doctrine\Common\Cache\Cache');
@@ -58,17 +61,14 @@ public function testCacheExists()
5861
$this->assertEquals('foo', $factory->getMetadataFor('Symfony\Component\Serializer\Tests\Fixtures\GroupDummy'));
5962
}
6063

64+
/**
65+
* @group legacy
66+
*/
6167
public function testCacheNotExists()
6268
{
6369
$cache = $this->getMock('Doctrine\Common\Cache\Cache');
64-
$cache
65-
->method('fetch')
66-
->will($this->returnValue(false))
67-
;
68-
69-
$cache
70-
->method('save')
71-
;
70+
$cache->method('fetch')->will($this->returnValue(false));
71+
$cache->method('save');
7272

7373
$factory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()), $cache);
7474
$metadata = $factory->getMetadataFor('Symfony\Component\Serializer\Tests\Fixtures\GroupDummy');

src/Symfony/Component/Serializer/composer.json

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,16 +23,18 @@
2323
"symfony/config": "~2.8|~3.0",
2424
"symfony/property-access": "~2.8|~3.0",
2525
"symfony/http-foundation": "~2.8|~3.0",
26+
"symfony/cache": "~3.1",
2627
"doctrine/annotations": "~1.0",
2728
"doctrine/cache": "~1.0"
2829
},
2930
"suggest": {
30-
"doctrine/annotations": "For using the annotation mapping. You will also need doctrine/cache.",
31-
"doctrine/cache": "For using the default cached annotation reader and metadata cache.",
31+
"symfony/cache": "For using the metadata cache.",
3232
"symfony/yaml": "For using the default YAML mapping loader.",
3333
"symfony/config": "For using the XML mapping loader.",
3434
"symfony/property-access": "For using the ObjectNormalizer.",
35-
"symfony/http-foundation": "To use the DataUriNormalizer."
35+
"symfony/http-foundation": "To use the DataUriNormalizer.",
36+
"doctrine/annotations": "For using the annotation mapping. You will also need doctrine/cache.",
37+
"doctrine/cache": "For using the default cached annotation reader and metadata cache."
3638
},
3739
"autoload": {
3840
"psr-4": { "Symfony\\Component\\Serializer\\": "" },

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy