198 lines
5.5 KiB
PHP
Raw Normal View History

2021-01-08 11:55:00 +01:00
<?php
namespace Drupal\xautoload\Tests\VirtualDrupal;
class ModuleImplements {
/**
* @var Cache
*/
private $cache;
/**
* @var ModuleList
*/
private $moduleList;
/**
* @var HookSystem
*/
private $hookSystem;
/**
* @var array[]
*/
private $drupalStaticFast;
/**
* Replacement for $implementations['#write_cache'].
*
* @var bool
*/
private $writeCache = FALSE;
/**
* @var DrupalStatic
*/
private $drupalStatic;
/**
* @param DrupalStatic $drupalStatic
* @param Cache $cache
* @param ModuleList $moduleList
* @param HookSystem $hookSystem
*/
function __construct(DrupalStatic $drupalStatic, Cache $cache, ModuleList $moduleList, HookSystem $hookSystem) {
$this->drupalStatic = $drupalStatic;
$this->cache = $cache;
$this->moduleList = $moduleList;
$this->hookSystem = $hookSystem;
}
/**
* Replicates module_implements(*, *, TRUE)
*
* @see module_implements()
*
* @return null
*/
function reset() {
// Use the advanced drupal_static() pattern, since this is called very often.
if (!isset($this->drupalStaticFast)) {
$this->drupalStaticFast['implementations'] = &$this->drupalStatic->get('module_implements');
}
$implementations = &$this->drupalStaticFast['implementations'];
$implementations = array();
$this->cache->cacheSet('module_implements', array(), 'cache_bootstrap');
$this->drupalStatic->resetKey('module_hook_info');
$this->drupalStatic->resetKey('drupal_alter');
$this->cache->cacheClearAll('hook_info', 'cache_bootstrap');
return NULL;
}
/**
* @see module_implements()
*
* @param string $hook
* @param bool $sort
*
* @return array
*/
function moduleImplements($hook, $sort = FALSE) {
// Use the advanced drupal_static() pattern, since this is called very often.
if (!isset($this->drupalStaticFast)) {
$this->drupalStaticFast['implementations'] = &$this->drupalStatic->get('module_implements');
}
$implementations = &$this->drupalStaticFast['implementations'];
// Fetch implementations from cache.
if (empty($implementations)) {
$cache = $this->cache->cacheGet('module_implements', 'cache_bootstrap');
if (FALSE === $cache) {
$implementations = array();
}
else {
$implementations = $cache->data;
}
}
if (!isset($implementations[$hook])) {
$implementations[$hook] = $this->discoverImplementations($hook, $sort);
}
else {
// @todo Change this when https://drupal.org/node/2263365 has landed in Drupal core.
$this->filterImplementations($implementations[$hook], $hook);
}
return array_keys($implementations[$hook]);
}
/**
* @param string $hook
* @param bool $sort
*
* @return array
*/
private function discoverImplementations($hook, $sort) {
# StaticCallLog::addCall();
// The hook is not cached, so ensure that whether or not it has
// implementations, that the cache is updated at the end of the request.
$this->writeCache = TRUE;
$hook_info = $this->moduleHookInfo();
$implementations = array();
$list = $this->moduleList->moduleList(FALSE, FALSE, $sort);
if ('modules_enabled' === $hook) {
# HackyLog::logx($list);
}
foreach ($list as $module) {
$include_file = isset($hook_info[$hook]['group'])
&& module_load_include('inc', $module, $module . '.' . $hook_info[$hook]['group']);
// Since module_hook() may needlessly try to load the include file again,
// function_exists() is used directly here.
if (function_exists($module . '_' . $hook)) {
$implementations[$module] = $include_file
? $hook_info[$hook]['group']
: FALSE;
}
}
// Allow modules to change the weight of specific implementations but avoid
// an infinite loop.
if ($hook != 'module_implements_alter') {
$this->hookSystem->drupalAlter('module_implements', $implementations, $hook);
}
return $implementations;
}
/**
* @param array &$implementations
* @param string $hook
*/
private function filterImplementations(&$implementations, $hook) {
foreach ($implementations as $module => $group) {
// If this hook implementation is stored in a lazy-loaded file, so include
// that file first.
if ($group) {
module_load_include('inc', $module, "$module.$group");
}
// It is possible that a module removed a hook implementation without the
// implementations cache being rebuilt yet, so we check whether the
// function exists on each request to avoid undefined function errors.
// Since module_hook() may needlessly try to load the include file again,
// function_exists() is used directly here.
if (!function_exists($module . '_' . $hook)) {
// Clear out the stale implementation from the cache and force a cache
// refresh to forget about no longer existing hook implementations.
unset($implementations[$module]);
$this->writeCache = TRUE;
}
}
}
/**
* Replicates module_hook_info() for some known hooks.
*
* @return array
* An associative array whose keys are hook names and whose values are an
* associative array containing a group name. The structure of the array
* is the same as the return value of hook_hook_info().
*
* @see hook_hook_info()
*/
private function moduleHookInfo() {
// No core modules implement hook_hook_info().
return array();
}
}