Commit 1d8a302a authored by catch's avatar catch

Issue #1211864 by Xano, Taz, Dave Reid: Add caching to \Drupal\Core\Utility\Token::getInfo().

parent 5cc27d94
......@@ -652,7 +652,7 @@ services:
arguments: ['@module_handler']
token:
class: Drupal\Core\Utility\Token
arguments: ['@module_handler']
arguments: ['@module_handler', '@cache.discovery', '@language_manager']
batch.storage:
class: Drupal\Core\Batch\BatchStorage
arguments: ['@database']
......
......@@ -7,7 +7,10 @@
namespace Drupal\Core\Utility;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Language\Language;
use Drupal\Core\Language\LanguageManagerInterface;
/**
* Drupal placeholder/token replacement system.
......@@ -54,10 +57,29 @@
*/
class Token {
/**
* The tag to cache token info with.
*/
const TOKEN_INFO_CACHE_TAG = 'token_info';
/**
* The token cache.
*
* @var \Drupal\Core\Cache\CacheBackendInterface
*/
protected $cache;
/**
* The language manager.
*
* @var \Drupal\Core\Language\LanguageManagerInterface
*/
protected $languageManager;
/**
* Token definitions.
*
* @var array|null
* @var array[]|null
* An array of token definitions, or NULL when the definitions are not set.
*
* @see self::setInfo()
......@@ -74,11 +96,18 @@ class Token {
protected $moduleHandler;
/**
* Constructor.
* Constructs a new class instance.
*
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
* The module handler.
* @param \Drupal\Core\Cache\CacheBackendInterface $cache
* The token cache.
* @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
* The language manager.
*/
public function __construct(ModuleHandlerInterface $module_handler) {
public function __construct(ModuleHandlerInterface $module_handler, CacheBackendInterface $cache, LanguageManagerInterface $language_manager) {
$this->cache = $cache;
$this->languageManager = $language_manager;
$this->moduleHandler = $module_handler;
}
......@@ -282,8 +311,18 @@ public function findWithPrefix(array $tokens, $prefix, $delimiter = ':') {
*/
public function getInfo() {
if (is_null($this->tokenInfo)) {
$this->tokenInfo = $this->moduleHandler->invokeAll('token_info');
$this->moduleHandler->alter('token_info', $this->tokenInfo);
$cache_id = 'token_info:' . $this->languageManager->getCurrentLanguage(Language::TYPE_CONTENT)->id;
$cache = $this->cache->get($cache_id);
if ($cache) {
$this->tokenInfo = $cache->data;
}
else {
$this->tokenInfo = $this->moduleHandler->invokeAll('token_info');
$this->moduleHandler->alter('token_info', $this->tokenInfo);
$this->cache->set($cache_id, $this->tokenInfo, CacheBackendInterface::CACHE_PERMANENT, array(
static::TOKEN_INFO_CACHE_TAG => TRUE,
));
}
}
return $this->tokenInfo;
......@@ -307,5 +346,8 @@ public function setInfo(array $tokens) {
*/
public function resetInfo() {
$this->tokenInfo = NULL;
$this->cache->deleteTags(array(
static::TOKEN_INFO_CACHE_TAG => TRUE,
));
}
}
<?php
/**
* @file
* Contains \Drupal\Tests\Core\Utility\TokenUnitTest.
*/
namespace Drupal\Tests\Core\Utility;
use Drupal\Core\Language\Language;
use Drupal\Core\Utility\Token;
use Drupal\Tests\UnitTestCase;
/**
* @coversDefaultClass \Drupal\Tests\Core\Utility\Token
*/
class TokenUnitTest extends UnitTestCase {
/**
* The cache used for testing.
*
* @var \Drupal\Core\Cache\CacheBackendInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $cache;
/**
* The language manager used for testing.
*
* @var \Drupal\Core\Language\LanguageManager|\PHPUnit_Framework_MockObject_MockObject
*/
protected $languageManager;
/**
* The module handler service used for testing.
*
* @var \Drupal\Core\Extension\ModuleHandlerInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $moduleHandler;
/**
* The token service under test.
*
* @var \Drupal\Core\Utility\Token|\PHPUnit_Framework_MockObject_MockObject
*/
protected $token;
/**
* {@inheritdoc}
*/
public static function getInfo() {
return array(
'description' => '',
'name' => '\Drupal\Tests\Core\Utility\Token unit test',
'group' => 'System',
);
}
/**
* {@inheritdoc}
*/
public function setUp() {
$this->cache = $this->getMock('\Drupal\Core\Cache\CacheBackendInterface');
$this->languageManager = $this->getMockBuilder('\Drupal\Core\Language\LanguageManager')
->disableOriginalConstructor()
->getMock();
$this->moduleHandler = $this->getMock('\Drupal\Core\Extension\ModuleHandlerInterface');
$this->token = new Token($this->moduleHandler, $this->cache, $this->languageManager);
}
/**
* @covers getInfo
*/
public function testGetInfo() {
$token_info = array(
'types' => array(
'foo' => array(
'name' => $this->randomName(),
),
),
);
$language = $this->getMock('\Drupal\Core\Language\Language');
$language->id = $this->randomName();
$this->languageManager->expects($this->once())
->method('getCurrentLanguage')
->with(Language::TYPE_CONTENT)
->will($this->returnValue($language));
// The persistent cache must only be hit once, after which the info is
// cached statically.
$this->cache->expects($this->once())
->method('get');
$this->cache->expects($this->once())
->method('set')
->with('token_info:' . $language->id, $token_info);
$this->moduleHandler->expects($this->once())
->method('invokeAll')
->with('token_info')
->will($this->returnValue($token_info));
$this->moduleHandler->expects($this->once())
->method('alter')
->with('token_info', $token_info);
// Get the information for the first time. The cache should be checked, the
// hooks invoked, and the info should be set to the cache should.
$this->token->getInfo();
// Get the information for the second time. The data must be returned from
// the static cache, so the persistent cache must not be accessed and the
// hooks must not be invoked.
$this->token->getInfo();
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment