Skip to content

[PropertyInfo] Cache support #16917

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

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
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
Next Next commit
[PropertyInfo] Cache support
  • Loading branch information
dunglas committed Jan 26, 2016
commit adb4493886fadec4f98f6b20c0d0d93580b22ae4
34 changes: 32 additions & 2 deletions src/Symfony/Component/PropertyInfo/PropertyInfoExtractor.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@

namespace Symfony\Component\PropertyInfo;

use Doctrine\Common\Cache\Cache;

/**
* Default {@see PropertyInfoExtractorInterface} implementation.
*
Expand Down Expand Up @@ -38,18 +40,30 @@ class PropertyInfoExtractor implements PropertyInfoExtractorInterface
*/
private $accessExtractors;

/**
* @var Cache
*/
private $cache;

/**
* @var array
*/
private $arrayCache = array();

/**
* @param PropertyListExtractorInterface[] $listExtractors
* @param PropertyTypeExtractorInterface[] $typeExtractors
* @param PropertyDescriptionExtractorInterface[] $descriptionExtractors
* @param PropertyAccessExtractorInterface[] $accessExtractors
* @param Cache|null $cache
*/
public function __construct(array $listExtractors = array(), array $typeExtractors = array(), array $descriptionExtractors = array(), array $accessExtractors = array())
public function __construct(array $listExtractors = array(), array $typeExtractors = array(), array $descriptionExtractors = array(), array $accessExtractors = array(), Cache $cache = null)
{
$this->listExtractors = $listExtractors;
$this->typeExtractors = $typeExtractors;
$this->descriptionExtractors = $descriptionExtractors;
$this->accessExtractors = $accessExtractors;
$this->cache = $cache;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

instead of passing the cache here I would prefer using composition instead which decorate the default PropertyInfoExtractor something like https://gist.github.com/aitboudad/12419c2db6d19320c5fa

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same thing for #16838

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking the same thing initially, but finally I'm not sure that it's a good idea:

  • Performance (and cache) is not an "optional" feature. It should be enable for all implementations in prod and IMO it's why it belongs to this class.
  • In the context of the Standard Edition it adds a lot of complexity and an overhead: it requires to use the service decorator feature to switch the class depending of the environment (the cache must be enabled in prod, but not in dev)
  • In the context of the component, it makes it harder to enable the cache (one more class to instantiate)
  • all other components using cache doesn't use composition for that (it is considered as a builtin feature)

When the PSR-6 will be ready, the argument can even be made mandatory (it's better to always use a cache) and a NullCache or something like that can be added for the dev environment (like with the Logger).

What do you think @symfony/deciders?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 Composition is much cleaner and should be used unless it is not possible, like with the property accessor cache.

}

/**
Expand Down Expand Up @@ -111,11 +125,27 @@ public function isWritable($class, $property, array $context = array())
*/
private function extract(array $extractors, $method, array $arguments)
{
$key = $method.serialize($arguments);

if (isset($this->arrayCache[$key])) {
return $this->arrayCache[$key];
}

if ($this->cache && $value = $this->cache->fetch($key)) {
return $this->arrayCache[$key] = $value;
}

foreach ($extractors as $extractor) {
$value = call_user_func_array(array($extractor, $method), $arguments);
if (null !== $value) {
return $value;
break;
}
}

if ($this->cache) {
$this->cache->save($key, $value);
}

return $this->arrayCache[$key] = $value;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

namespace Symfony\Component\PropertyInfo\Tests;

use Doctrine\Common\Cache\ArrayCache;
use Symfony\Component\PropertyInfo\PropertyInfoExtractor;
use Symfony\Component\PropertyInfo\Tests\Fixtures\DummyExtractor;
use Symfony\Component\PropertyInfo\Tests\Fixtures\NullExtractor;
Expand Down Expand Up @@ -69,4 +70,13 @@ public function testGetProperties()
{
$this->assertEquals(array('a', 'b'), $this->propertyInfo->getProperties('Foo'));
}

public function testCache()
{
$extractors = array(new NullExtractor(), new DummyExtractor());
$this->propertyInfo = new PropertyInfoExtractor($extractors, $extractors, $extractors, $extractors, new ArrayCache());

$this->assertSame('short', $this->propertyInfo->getShortDescription('Foo', 'bar', array()));
$this->assertSame('short', $this->propertyInfo->getShortDescription('Foo', 'bar', array()));
}
}
4 changes: 3 additions & 1 deletion src/Symfony/Component/PropertyInfo/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,14 @@
"require-dev": {
"symfony/serializer": "~2.8|~3.0",
"phpdocumentor/reflection": "^1.0.7",
"doctrine/annotations": "~1.0"
"doctrine/annotations": "~1.0",
"doctrine/cache": "~1.0"
},
"conflict": {
"phpdocumentor/reflection": "<1.0.7"
},
"suggest": {
"doctrine/cache": "To cache results",
"symfony/doctrine-bridge": "To use Doctrine metadata",
"phpdocumentor/reflection": "To use the PHPDoc",
"symfony/serializer": "To use Serializer metadata"
Expand Down