mustache_templates-8.x-1.0-beta4/src/MustacheTemplates.php
src/MustacheTemplates.php
<?php
namespace Drupal\mustache;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Cache\CacheTagsInvalidatorInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Extension\ThemeHandlerInterface;
use Drupal\Core\File\FileSystemInterface;
use Drupal\Core\PhpStorage\PhpStorageFactory;
use Drupal\Core\State\StateInterface;
use Drupal\Core\Theme\ActiveTheme;
use Drupal\Core\Theme\ThemeManagerInterface;
use Drupal\Core\Url;
use Drupal\mustache\Exception\MustacheTemplateNotFoundException;
/**
* The finder of Mustache templates.
*/
class MustacheTemplates {
/**
* A list of known, currently used template file paths.
*
* @var string[]
*/
protected $uris = [];
/**
* A collection of already scanned files per extension.
*
* @var array
*/
protected $scanned;
/**
* In-memory hold of the last template content.
*
* @var string[]
*/
protected $cachedContent = [];
/**
* Template locations defined by modules.
*
* @var array
*/
protected $moduleTemplates;
/**
* The cache bin to store template file contents.
*
* @var \Drupal\Core\Cache\CacheBackendInterface
*/
protected $templatesCache;
/**
* The cache bin to store template path information.
*
* @var \Drupal\Core\Cache\CacheBackendInterface
*/
protected $cache;
/**
* The Drupal state.
*
* @var \Drupal\Core\State\StateInterface
*/
protected $state;
/**
* The file system service.
*
* @var \Drupal\Core\File\FileSystemInterface
*/
protected $fileSystem;
/**
* The module handler.
*
* @var \Drupal\Core\Extension\ModuleHandlerInterface
*/
protected $moduleHandler;
/**
* The theme handler.
*
* @var \Drupal\Core\Extension\ThemeHandlerInterface
*/
protected $themeHandler;
/**
* The theme manager.
*
* @var \Drupal\Core\Theme\ThemeManagerInterface
*/
protected $themeManager;
/**
* MustacheTemplates constructor.
*
* @param \Drupal\Core\Cache\CacheBackendInterface $templates_cache
* The cache bin to store template file contents.
* @param \Drupal\Core\Cache\CacheBackendInterface $cache
* The cache bin to store template path information.
* @param \Drupal\Core\State\StateInterface $state
* The Drupal state.
* @param \Drupal\Core\File\FileSystemInterface $file_system
* The file system service.
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
* The module handler.
* @param \Drupal\Core\Extension\ThemeHandlerInterface $theme_handler
* The theme handler.
* @param \Drupal\Core\Theme\ThemeManagerInterface $theme_manager
* The theme manager.
*/
public function __construct(CacheBackendInterface $templates_cache, CacheBackendInterface $cache, StateInterface $state, FileSystemInterface $file_system, ModuleHandlerInterface $module_handler, ThemeHandlerInterface $theme_handler, ThemeManagerInterface $theme_manager) {
$this->templatesCache = $templates_cache;
$this->cache = $cache;
$this->state = $state;
$this->fileSystem = $file_system;
$this->moduleHandler = $module_handler;
$this->themeHandler = $theme_handler;
$this->themeManager = $theme_manager;
}
/**
* Get the file content for the given template name.
*
* @param string $name
* The name of the template, without file ending.
* @param \Drupal\Core\Theme\ActiveTheme|null $theme
* (Optional) When given, the lookup is based on this theme.
* By default, the lookup uses the currently active theme.
*
* @return string|false
* The content of the template, or FALSE if the content could not be read.
*
* @throws \Drupal\mustache\Exception\MustacheTemplateNotFoundException
* In case the template file could not be found.
*/
public function getContent($name, ActiveTheme $theme = NULL) {
$theme = $theme ?: $this->themeManager->getActiveTheme();
$cid = 'mustache:template:' . $theme->getName() . ':' . $name;
if (isset($this->cachedContent[$cid])) {
return $this->cachedContent[$cid];
}
if (count($this->cachedContent) > 3) {
// Just cache a limited set of file contents in-memory.
$this->cachedContent = [];
}
if ($cached = $this->templatesCache->get($cid)) {
$this->cachedContent[$cid] = $cached->data;
}
else {
$content = @file_get_contents($this->find($name, $theme));
if ($content !== FALSE) {
$this->templatesCache->set($cid, $content, CacheBackendInterface::CACHE_PERMANENT, ['mustache:template']);
}
$this->cachedContent[$cid] = $content;
}
return $this->cachedContent[$cid];
}
/**
* Looks up the file location for the given template name.
*
* @param string $name
* The name of the template, without file ending.
* @param \Drupal\Core\Theme\ActiveTheme|null $theme
* (Optional) When given, the lookup is based on this theme.
* By default, the lookup uses the currently active theme.
*
* @return string
* The file path as string if found.
*
* @throws \Drupal\mustache\Exception\MustacheTemplateNotFoundException
* In case the template file could not be found.
*/
public function find($name, ActiveTheme $theme = NULL) {
$theme = $theme ?: $this->themeManager->getActiveTheme();
$cid = 'mustache:uri:' . $theme->getName() . ':' . $name;
if (isset($this->uris[$cid])) {
return $this->uris[$cid];
}
if ($cached = $this->cache->get($cid)) {
$this->uris[$cid] = $cached->data;
return $this->uris[$cid];
}
$template_file = $this->findTemplateInExtension('theme', $theme->getName(), $name);
if (!isset($template_file)) {
foreach ($theme->getBaseThemeExtensions() as $base_theme) {
if ($template_file = $this->findTemplateInExtension('theme', $base_theme->getName(), $name)) {
break;
}
}
}
if (!isset($template_file)) {
$module_templates = $this->getModuleTemplates();
if (isset($module_templates[$name]['file'])) {
$template_file = $module_templates[$name]['file'];
}
}
if (!isset($template_file)) {
throw new MustacheTemplateNotFoundException(t('Mustache template not found for name @name.', ['@name' => $name]));
}
elseif (!file_exists($template_file)) {
throw new MustacheTemplateNotFoundException(t('The registered file @file for the Mustache template @name could not be found.',
['@file' => $template_file, '@name' => $name]));
}
$this->uris[$cid] = $template_file;
$this->cache->set($cid, $template_file, CacheBackendInterface::CACHE_PERMANENT, ['mustache:uri']);
return $template_file;
}
/**
* Collects and returns all possible templates.
*
* For each template name, multiple files might exist.
* If you only want to know which template file is currently
* being used for a given template name, use ::find() instead.
*
* @return array
* An associative array of items, grouped by template name.
* Each item is an array holding the following information
* about the found template file:
* - file: A string as the path of the template file.
* - provider: A string with the value 'module' when the template
* has been registered by a module, or the name of the providing theme.
* - active: A boolean indicating whether the item equals
* the file currently being used, as ::find() would return.
*/
public function findAll() {
$templates = [];
foreach ($this->getModuleTemplates() as $name => $info) {
$template_file = $info['file'];
$templates[$name][] = [
'file' => $template_file,
'provider' => 'module',
'active' => ($template_file === $this->find($name)),
];
}
foreach (array_keys($this->themeHandler->listInfo()) as $theme_name) {
foreach ($this->findAllTemplatesInExtension('theme', $theme_name) as $scanned) {
$name = str_replace('.mustache', '', $scanned->name);
$name = str_replace('-', '_', $name);
$templates[$name][] = [
'file' => $scanned->uri,
'provider' => $theme_name,
'active' => ($scanned->uri === $this->find($name)),
];
}
}
return $templates;
}
/**
* Collects and returns templates defined by modules.
*
* @return array
* The list of collected module templates.
*/
public function getModuleTemplates() {
if (!isset($this->moduleTemplates)) {
$cid = 'mustache:module_templates';
if ($cached = $this->cache->get($cid)) {
$this->moduleTemplates = $cached->data;
}
else {
$this->moduleTemplates = $this->moduleHandler->invokeAll('mustache_templates');
$this->moduleHandler->alter('mustache_templates', $this->moduleTemplates);
if (!isset($this->moduleTemplates)) {
$this->moduleTemplates = [];
}
foreach ($this->moduleTemplates as &$info) {
if (isset($info['default'])) {
// Convert url objects to strings for serialization.
if (isset($info['default']['#data']) && ($info['default']['#data'] instanceof Url)) {
/** @var \Drupal\Core\Url $url */
$url = clone $info['default']['#data'];
$info['default']['#data'] = $url->setAbsolute($url->isExternal())->toString();
}
if (isset($info['default']['#sync']['data']) && ($info['default']['#sync']['data'] instanceof Url)) {
/** @var \Drupal\Core\Url $url */
$url = clone $info['default']['#sync']['data'];
$info['default']['#sync']['data'] = $url->setAbsolute($url->isExternal())->toString();
}
}
}
$this->cache->set($cid, $this->moduleTemplates);
}
}
return $this->moduleTemplates;
}
/**
* Try to find the template file for the name inside the given extension.
*
* @param string $type
* The extension type, either module or theme.
* @param string $extension_name
* The extension name.
* @param string $template_name
* The template name, without file ending.
*
* @return string|null
* The file uri if found, NULL otherwise.
*/
public function findTemplateInExtension($type, $extension_name, $template_name) {
$filename = $template_name . '.mustache';
$filename_hyphen = str_replace('_', '-', $template_name) . '.mustache';
foreach ($this->findAllTemplatesInExtension($type, $extension_name) as $scanned) {
if (($scanned->name == $filename) || ($scanned->name == $filename_hyphen)) {
return $scanned->uri;
}
}
return NULL;
}
/**
* Try to find all template files at the given extension.
*
* @param string $type
* The extension type, either module or theme.
* @param string $extension_name
* The extension name.
*
* @return array
* The list of found template files at the extension.
*/
public function findAllTemplatesInExtension($type, $extension_name) {
if (!isset($this->scanned[$extension_name])) {
$cid = 'mustache:scan:' . $extension_name;
if ($cached = $this->cache->get($cid)) {
$this->scanned[$extension_name] = $cached->data;
}
elseif (isset($this->scanned)) {
$this->scanned += $this->state->get('mustache_scan', []);
}
else {
$this->scanned = $this->state->get('mustache_scan', []);
}
if (!isset($this->scanned[$extension_name])) {
$this->scanned[$extension_name] = $this->fileSystem->scanDirectory(\Drupal::service('extension.path.resolver')->getPath($type, $extension_name) . '/templates', '/\.mustache\.tpl$/');
$this->state->set('mustache_scan', $this->scanned);
}
if (!$cached) {
$this->cache->set($cid, $this->scanned[$extension_name], CacheBackendInterface::CACHE_PERMANENT, ['mustache:scan']);
}
}
return $this->scanned[$extension_name];
}
/**
* Get default values for building the render element.
*
* @param string $name
* The name of the template.
*
* @return array
* The default values for the render element.
*/
public function getElementDefaults($name) {
if (!isset($this->moduleTemplates)) {
$this->getModuleTemplates();
}
if (isset($this->moduleTemplates[$name]['default'])) {
return $this->moduleTemplates[$name]['default'];
}
return [];
}
/**
* Clears all caches used by this service.
*/
public function clearCaches() {
$this->cache->invalidate('mustache:module_templates');
if ($this->templatesCache instanceof CacheTagsInvalidatorInterface) {
$this->templatesCache->invalidateTags(['mustache:template']);
}
else {
$this->templatesCache->invalidateAll();
}
if ($this->cache instanceof CacheTagsInvalidatorInterface) {
$this->cache->invalidateTags(['mustache:scan', 'mustache:uri']);
}
else {
$this->cache->invalidateAll();
}
$this->moduleTemplates = NULL;
$this->cachedContent = [];
$this->uris = [];
$this->scanned = [];
// Delete caches that are stored as state.
$this->state->deleteMultiple(['mustache_cache_prefix', 'mustache_scan']);
// Wipe the Mustache PHP storage cache.
PhpStorageFactory::get('mustache')->deleteAll();
}
}
