8000 feature #17446 [Serializer] Add PSR-6 adapter (dunglas) · symfony/symfony@cb16bff · GitHub
[go: up one dir, main page]

Skip to content

Commit cb16bff

Browse files
committed
feature #17446 [Serializer] Add PSR-6 adapter (dunglas)
This PR was squashed before being merged into the 3.1-dev branch (closes #17446). Discussion ---------- [Serializer] Add PSR-6 adapter | Q | A | ------------- | --- | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? | yes | Tests pass? | yes | Fixed tickets | n/a | License | MIT | Doc PR | todo - [x] Add tests Commits ------- 4bf9d05 [Serializer] Add PSR-6 adapter
2 parents 127538e + 4bf9d05 commit cb16bff

File tree

6 files changed

+209
-35
lines changed

6 files changed

+209
-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(sprintf('Passing a Doctrine Cache instance as 2nd parameter of the "%s" constructor is deprecated. This parameter will be removed in Symfony 4.0. Use the "%s" class instead.', __CLASS__, CacheClassMetadataFactory::class), 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: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
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+
* @internal
20+
*
21+
* @author Kévin Dunglas <dunglas@gmail.com>
22+
*/
23+
trait ClassResolverTrait
24+
{
25+
/**
26+
* Gets a class name for a given class or instance.
27+
*
28+
* @param mixed $value
29+
*
30+
* @return string
31+
*
32+
* @throws InvalidArgumentException If the class does not exists
33+
*/
34+
private function getClass($value)
35+
{
36+
if (is_string($value)) {
37+
if (!class_exists($value) && !interface_exists($value)) {
38+
throw new InvalidArgumentException(sprintf('The class or interface "%s" does not exist.', $value));
39+
}
40+
41+
return ltrim($value, '\\');
42+
}
43+
44+
if (!is_object($value)) {
45+
throw new InvalidArgumentException(sprintf('Cannot create metadata for non-objects. Got: "%s"', gettype($value)));
46+
}
47+
48+
return get_class($value);
49+
}
50+
}
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+
"psr/cache-implementation": "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)
0