Issue #2231419 by sun, jibran, Rajendar Reddy, Jalandhar, damiankloip: Merge obsolete CachedModuleHandler into ModuleHandler.
parent
b4dba2ba6f
commit
c8f6aceba4
|
@ -191,8 +191,8 @@ services:
|
||||||
abstract: true
|
abstract: true
|
||||||
arguments: ['@container.namespaces', '@cache.discovery', '@language_manager', '@module_handler']
|
arguments: ['@container.namespaces', '@cache.discovery', '@language_manager', '@module_handler']
|
||||||
module_handler:
|
module_handler:
|
||||||
class: Drupal\Core\Extension\CachedModuleHandler
|
class: Drupal\Core\Extension\ModuleHandler
|
||||||
arguments: ['%container.modules%', '@state', '@cache.bootstrap']
|
arguments: ['%container.modules%', '@cache.bootstrap']
|
||||||
theme_handler:
|
theme_handler:
|
||||||
class: Drupal\Core\Extension\ThemeHandler
|
class: Drupal\Core\Extension\ThemeHandler
|
||||||
arguments: ['@config.factory', '@module_handler', '@cache.default', '@info_parser', '@config.installer', '@router.builder']
|
arguments: ['@config.factory', '@module_handler', '@cache.default', '@info_parser', '@config.installer', '@router.builder']
|
||||||
|
|
|
@ -8,7 +8,6 @@
|
||||||
namespace Drupal\Core\EventSubscriber;
|
namespace Drupal\Core\EventSubscriber;
|
||||||
|
|
||||||
use Drupal\Core\Extension\ModuleHandlerInterface;
|
use Drupal\Core\Extension\ModuleHandlerInterface;
|
||||||
use Drupal\Core\Extension\CachedModuleHandlerInterface;
|
|
||||||
use Symfony\Component\HttpKernel\KernelEvents;
|
use Symfony\Component\HttpKernel\KernelEvents;
|
||||||
use Symfony\Component\HttpKernel\Event\PostResponseEvent;
|
use Symfony\Component\HttpKernel\Event\PostResponseEvent;
|
||||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||||
|
@ -42,10 +41,8 @@ class RequestCloseSubscriber implements EventSubscriberInterface {
|
||||||
* The Event to process.
|
* The Event to process.
|
||||||
*/
|
*/
|
||||||
public function onTerminate(PostResponseEvent $event) {
|
public function onTerminate(PostResponseEvent $event) {
|
||||||
if ($this->moduleHandler instanceof CachedModuleHandlerInterface) {
|
|
||||||
$this->moduleHandler->writeCache();
|
$this->moduleHandler->writeCache();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registers the methods in this class that should be listeners.
|
* Registers the methods in this class that should be listeners.
|
||||||
|
|
|
@ -1,149 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @file
|
|
||||||
* Contains Drupal\Core\Extension\CachedModuleHandler.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Drupal\Core\Extension;
|
|
||||||
|
|
||||||
use Drupal\Core\Cache\CacheBackendInterface;
|
|
||||||
use Drupal\Core\State\StateInterface;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class that manages enabled modules in a Drupal installation.
|
|
||||||
*/
|
|
||||||
class CachedModuleHandler extends ModuleHandler implements CachedModuleHandlerInterface {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* State key/value store.
|
|
||||||
*
|
|
||||||
* @var \Drupal\Core\State\StateInterface
|
|
||||||
*/
|
|
||||||
protected $state;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Cache backend for storing enabled modules.
|
|
||||||
*
|
|
||||||
* @var \Drupal\Core\Cache\CacheBackendInterface
|
|
||||||
*/
|
|
||||||
protected $bootstrapCache;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether the cache needs to be written.
|
|
||||||
*
|
|
||||||
* @var boolean
|
|
||||||
*/
|
|
||||||
protected $cacheNeedsWriting = FALSE;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs a new CachedModuleHandler object.
|
|
||||||
*/
|
|
||||||
public function __construct(array $module_list = array(), StateInterface $state, CacheBackendInterface $bootstrap_cache) {
|
|
||||||
parent::__construct($module_list);
|
|
||||||
$this->state = $state;
|
|
||||||
$this->bootstrapCache = $bootstrap_cache;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Overrides \Drupal\Core\Extension\ModuleHandler::getHookInfo().
|
|
||||||
*/
|
|
||||||
public function getHookInfo() {
|
|
||||||
// When this function is indirectly invoked from bootstrap_invoke_all() prior
|
|
||||||
// to all modules being loaded, we do not want to cache an incomplete
|
|
||||||
// hook_hookInfo() result, so instead return an empty array. This requires
|
|
||||||
// bootstrap hook implementations to reside in the .module file, which is
|
|
||||||
// optimal for performance anyway.
|
|
||||||
if (!$this->loaded) {
|
|
||||||
return array();
|
|
||||||
}
|
|
||||||
if (!isset($this->hookInfo)) {
|
|
||||||
if ($cache = $this->bootstrapCache->get('hook_info')) {
|
|
||||||
$this->hookInfo = $cache->data;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$this->hookInfo = parent::getHookInfo();
|
|
||||||
$this->bootstrapCache->set('hook_info', $this->hookInfo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $this->hookInfo;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Implements \Drupal\Core\Extension\ModuleHandlerInterface::resetImplementations().
|
|
||||||
*/
|
|
||||||
public function resetImplementations() {
|
|
||||||
// We maintain a persistent cache of hook implementations in addition to the
|
|
||||||
// static cache to avoid looping through every module and every hook on each
|
|
||||||
// request. Benchmarks show that the benefit of this caching outweighs the
|
|
||||||
// additional database hit even when using the default database caching
|
|
||||||
// backend and only a small number of modules are enabled. The cost of the
|
|
||||||
// $this->bootstrapCache->get() is more or less constant and reduced further when
|
|
||||||
// non-database caching backends are used, so there will be more significant
|
|
||||||
// gains when a large number of modules are installed or hooks invoked, since
|
|
||||||
// this can quickly lead to \Drupal::moduleHandler()->implementsHook() being
|
|
||||||
// called several thousand times per request.
|
|
||||||
parent::resetImplementations();
|
|
||||||
$this->bootstrapCache->set('module_implements', array());
|
|
||||||
$this->bootstrapCache->delete('hook_info');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Implements \Drupal\Core\Extension\CachedModuleHandlerInterface::writeCache().
|
|
||||||
*/
|
|
||||||
public function writeCache() {
|
|
||||||
if ($this->cacheNeedsWriting) {
|
|
||||||
$this->bootstrapCache->set('module_implements', $this->implementations);
|
|
||||||
$this->cacheNeedsWriting = FALSE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Overrides \Drupal\Core\Extension\ModuleHandler::getImplementationInfo().
|
|
||||||
*/
|
|
||||||
protected function getImplementationInfo($hook) {
|
|
||||||
if (!isset($this->implementations)) {
|
|
||||||
$this->implementations = $this->getCachedImplementationInfo();
|
|
||||||
}
|
|
||||||
if (!isset($this->implementations[$hook])) {
|
|
||||||
// The hook is not cached, so ensure that whether or not it has
|
|
||||||
// implementations, the cache is updated at the end of the request.
|
|
||||||
$this->cacheNeedsWriting = TRUE;
|
|
||||||
$this->implementations[$hook] = parent::getImplementationInfo($hook);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
foreach ($this->implementations[$hook] as $module => $group) {
|
|
||||||
// If this hook implementation is stored in a lazy-loaded file, include
|
|
||||||
// that file first.
|
|
||||||
if ($group) {
|
|
||||||
$this->loadInclude($module, 'inc', "$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 \Drupal::moduleHandler()->implementsHook() 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($this->implementations[$hook][$module]);
|
|
||||||
$this->cacheNeedsWriting = TRUE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $this->implementations[$hook];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves hook implementation info from the cache.
|
|
||||||
*/
|
|
||||||
protected function getCachedImplementationInfo() {
|
|
||||||
if ($cache = $this->bootstrapCache->get('module_implements')) {
|
|
||||||
return $cache->data;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return array();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,20 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @file
|
|
||||||
* Contains Drupal\Core\Extension\CachedModuleHandlerInterface.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Drupal\Core\Extension;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Interface for cacheable module handlers.
|
|
||||||
*/
|
|
||||||
interface CachedModuleHandlerInterface extends ModuleHandlerInterface {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Write the hook implementation info to the cache.
|
|
||||||
*/
|
|
||||||
public function writeCache();
|
|
||||||
|
|
||||||
}
|
|
|
@ -55,6 +55,20 @@ class ModuleHandler implements ModuleHandlerInterface {
|
||||||
*/
|
*/
|
||||||
protected $hookInfo;
|
protected $hookInfo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cache backend for storing module hook implementation information.
|
||||||
|
*
|
||||||
|
* @var \Drupal\Core\Cache\CacheBackendInterface
|
||||||
|
*/
|
||||||
|
protected $cacheBackend;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the cache needs to be written.
|
||||||
|
*
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
protected $cacheNeedsWriting = FALSE;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List of alter hook implementations keyed by hook name(s).
|
* List of alter hook implementations keyed by hook name(s).
|
||||||
*
|
*
|
||||||
|
@ -69,19 +83,22 @@ class ModuleHandler implements ModuleHandlerInterface {
|
||||||
* An associative array whose keys are the names of installed modules and
|
* An associative array whose keys are the names of installed modules and
|
||||||
* whose values are Extension class parameters. This is normally the
|
* whose values are Extension class parameters. This is normally the
|
||||||
* %container.modules% parameter being set up by DrupalKernel.
|
* %container.modules% parameter being set up by DrupalKernel.
|
||||||
|
* @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
|
||||||
|
* Cache backend for storing module hook implementation information.
|
||||||
*
|
*
|
||||||
* @see \Drupal\Core\DrupalKernel
|
* @see \Drupal\Core\DrupalKernel
|
||||||
* @see \Drupal\Core\CoreServiceProvider
|
* @see \Drupal\Core\CoreServiceProvider
|
||||||
*/
|
*/
|
||||||
public function __construct(array $module_list = array()) {
|
public function __construct(array $module_list = array(), CacheBackendInterface $cache_backend) {
|
||||||
$this->moduleList = array();
|
$this->moduleList = array();
|
||||||
foreach ($module_list as $name => $module) {
|
foreach ($module_list as $name => $module) {
|
||||||
$this->moduleList[$name] = new Extension($module['type'], $module['pathname'], $module['filename']);
|
$this->moduleList[$name] = new Extension($module['type'], $module['pathname'], $module['filename']);
|
||||||
}
|
}
|
||||||
|
$this->cacheBackend = $cache_backend;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implements \Drupal\Core\Extension\ModuleHandlerInterface::load().
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public function load($name) {
|
public function load($name) {
|
||||||
if (isset($this->loadedFiles[$name])) {
|
if (isset($this->loadedFiles[$name])) {
|
||||||
|
@ -97,7 +114,7 @@ class ModuleHandler implements ModuleHandlerInterface {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implements \Drupal\Core\Extension\ModuleHandlerInterface::loadAll().
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public function loadAll() {
|
public function loadAll() {
|
||||||
if (!$this->loaded) {
|
if (!$this->loaded) {
|
||||||
|
@ -109,7 +126,7 @@ class ModuleHandler implements ModuleHandlerInterface {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implements \Drupal\Core\Extension\ModuleHandlerInterface::reload().
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public function reload() {
|
public function reload() {
|
||||||
$this->loaded = FALSE;
|
$this->loaded = FALSE;
|
||||||
|
@ -117,21 +134,21 @@ class ModuleHandler implements ModuleHandlerInterface {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implements \Drupal\Core\Extension\ModuleHandlerInterface::isLoaded().
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public function isLoaded() {
|
public function isLoaded() {
|
||||||
return $this->loaded;
|
return $this->loaded;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implements \Drupal\Core\Extension\ModuleHandlerInterface::getModuleList().
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public function getModuleList() {
|
public function getModuleList() {
|
||||||
return $this->moduleList;
|
return $this->moduleList;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implements \Drupal\Core\Extension\ModuleHandlerInterface::setModuleList().
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public function setModuleList(array $module_list = array()) {
|
public function setModuleList(array $module_list = array()) {
|
||||||
$this->moduleList = $module_list;
|
$this->moduleList = $module_list;
|
||||||
|
@ -172,7 +189,7 @@ class ModuleHandler implements ModuleHandlerInterface {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implements \Drupal\Core\Extension\ModuleHandlerInterface::buildModuleDependencies().
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public function buildModuleDependencies(array $modules) {
|
public function buildModuleDependencies(array $modules) {
|
||||||
foreach ($modules as $module) {
|
foreach ($modules as $module) {
|
||||||
|
@ -195,14 +212,14 @@ class ModuleHandler implements ModuleHandlerInterface {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implements \Drupal\Core\Extension\ModuleHandlerInterface::moduleExists().
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public function moduleExists($module) {
|
public function moduleExists($module) {
|
||||||
return isset($this->moduleList[$module]);
|
return isset($this->moduleList[$module]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implements \Drupal\Core\Extension\ModuleHandlerInterface::loadAllIncludes().
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public function loadAllIncludes($type, $name = NULL) {
|
public function loadAllIncludes($type, $name = NULL) {
|
||||||
foreach ($this->moduleList as $module => $filename) {
|
foreach ($this->moduleList as $module => $filename) {
|
||||||
|
@ -211,7 +228,7 @@ class ModuleHandler implements ModuleHandlerInterface {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implements \Drupal\Core\Extension\ModuleHandlerInterface::loadInclude().
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public function loadInclude($module, $type, $name = NULL) {
|
public function loadInclude($module, $type, $name = NULL) {
|
||||||
if ($type == 'install') {
|
if ($type == 'install') {
|
||||||
|
@ -232,17 +249,31 @@ class ModuleHandler implements ModuleHandlerInterface {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implements \Drupal\Core\Extension\ModuleHandlerInterface::getHookInfo().
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public function getHookInfo() {
|
public function getHookInfo() {
|
||||||
if (isset($this->hookInfo)) {
|
if (!isset($this->hookInfo)) {
|
||||||
|
if ($cache = $this->cacheBackend->get('hook_info')) {
|
||||||
|
$this->hookInfo = $cache->data;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$this->buildHookInfo();
|
||||||
|
$this->cacheBackend->set('hook_info', $this->hookInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
return $this->hookInfo;
|
return $this->hookInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds hook_hook_info() information.
|
||||||
|
*
|
||||||
|
* @see \Drupal\Core\Extension\ModuleHandler::getHookInfo()
|
||||||
|
*/
|
||||||
|
protected function buildHookInfo() {
|
||||||
$this->hookInfo = array();
|
$this->hookInfo = array();
|
||||||
// We can't use $this->invokeAll() here or it would cause an infinite
|
|
||||||
// loop.
|
|
||||||
// Make sure that the modules are loaded before checking.
|
// Make sure that the modules are loaded before checking.
|
||||||
$this->reload();
|
$this->reload();
|
||||||
|
// $this->invokeAll() would cause an infinite recursion.
|
||||||
foreach ($this->moduleList as $module => $filename) {
|
foreach ($this->moduleList as $module => $filename) {
|
||||||
$function = $module . '_hook_info';
|
$function = $module . '_hook_info';
|
||||||
if (function_exists($function)) {
|
if (function_exists($function)) {
|
||||||
|
@ -252,11 +283,10 @@ class ModuleHandler implements ModuleHandlerInterface {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return $this->hookInfo;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implements \Drupal\Core\Extension\ModuleHandlerInterface::getImplementations().
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public function getImplementations($hook) {
|
public function getImplementations($hook) {
|
||||||
$implementations = $this->getImplementationInfo($hook);
|
$implementations = $this->getImplementationInfo($hook);
|
||||||
|
@ -264,16 +294,39 @@ class ModuleHandler implements ModuleHandlerInterface {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implements \Drupal\Core\Extension\ModuleHandlerInterface::resetImplementations().
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function writeCache() {
|
||||||
|
if ($this->cacheNeedsWriting) {
|
||||||
|
$this->cacheBackend->set('module_implements', $this->implementations);
|
||||||
|
$this->cacheNeedsWriting = FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public function resetImplementations() {
|
public function resetImplementations() {
|
||||||
$this->implementations = NULL;
|
$this->implementations = NULL;
|
||||||
$this->hookInfo = NULL;
|
$this->hookInfo = NULL;
|
||||||
$this->alterFunctions = NULL;
|
$this->alterFunctions = NULL;
|
||||||
|
// We maintain a persistent cache of hook implementations in addition to the
|
||||||
|
// static cache to avoid looping through every module and every hook on each
|
||||||
|
// request. Benchmarks show that the benefit of this caching outweighs the
|
||||||
|
// additional database hit even when using the default database caching
|
||||||
|
// backend and only a small number of modules are enabled. The cost of the
|
||||||
|
// $this->cacheBackend->get() is more or less constant and reduced further
|
||||||
|
// when non-database caching backends are used, so there will be more
|
||||||
|
// significant gains when a large number of modules are installed or hooks
|
||||||
|
// invoked, since this can quickly lead to
|
||||||
|
// \Drupal::moduleHandler()->implementsHook() being called several thousand
|
||||||
|
// times per request.
|
||||||
|
$this->cacheBackend->set('module_implements', array());
|
||||||
|
$this->cacheBackend->delete('hook_info');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implements \Drupal\Core\Extension\ModuleHandlerInterface::implementsHook().
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public function implementsHook($module, $hook) {
|
public function implementsHook($module, $hook) {
|
||||||
$function = $module . '_' . $hook;
|
$function = $module . '_' . $hook;
|
||||||
|
@ -293,7 +346,7 @@ class ModuleHandler implements ModuleHandlerInterface {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implements \Drupal\Core\Extension\ModuleHandlerInterface::invoke().
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public function invoke($module, $hook, array $args = array()) {
|
public function invoke($module, $hook, array $args = array()) {
|
||||||
if (!$this->implementsHook($module, $hook)) {
|
if (!$this->implementsHook($module, $hook)) {
|
||||||
|
@ -304,7 +357,7 @@ class ModuleHandler implements ModuleHandlerInterface {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implements \Drupal\Core\Extension\ModuleHandlerInterface::invokeAll().
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public function invokeAll($hook, array $args = array()) {
|
public function invokeAll($hook, array $args = array()) {
|
||||||
$return = array();
|
$return = array();
|
||||||
|
@ -326,7 +379,7 @@ class ModuleHandler implements ModuleHandlerInterface {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implements \Drupal\Core\Extension\ModuleHandlerInterface::alter().
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public function alter($type, &$data, &$context1 = NULL, &$context2 = NULL) {
|
public function alter($type, &$data, &$context1 = NULL, &$context2 = NULL) {
|
||||||
// Most of the time, $type is passed as a string, so for performance,
|
// Most of the time, $type is passed as a string, so for performance,
|
||||||
|
@ -338,8 +391,8 @@ class ModuleHandler implements ModuleHandlerInterface {
|
||||||
$extra_types = $type;
|
$extra_types = $type;
|
||||||
$type = array_shift($extra_types);
|
$type = array_shift($extra_types);
|
||||||
// Allow if statements in this function to use the faster isset() rather
|
// Allow if statements in this function to use the faster isset() rather
|
||||||
// than !empty() both when $type is passed as a string, or as an array with
|
// than !empty() both when $type is passed as a string, or as an array
|
||||||
// one item.
|
// with one item.
|
||||||
if (empty($extra_types)) {
|
if (empty($extra_types)) {
|
||||||
unset($extra_types);
|
unset($extra_types);
|
||||||
}
|
}
|
||||||
|
@ -357,8 +410,8 @@ class ModuleHandler implements ModuleHandlerInterface {
|
||||||
$modules = $this->getImplementations($hook);
|
$modules = $this->getImplementations($hook);
|
||||||
if (!isset($extra_types)) {
|
if (!isset($extra_types)) {
|
||||||
// For the more common case of a single hook, we do not need to call
|
// For the more common case of a single hook, we do not need to call
|
||||||
// function_exists(), since $this->getImplementations() returns only modules with
|
// function_exists(), since $this->getImplementations() returns only
|
||||||
// implementations.
|
// modules with implementations.
|
||||||
foreach ($modules as $module) {
|
foreach ($modules as $module) {
|
||||||
$this->alterFunctions[$cid][] = $module . '_' . $hook;
|
$this->alterFunctions[$cid][] = $module . '_' . $hook;
|
||||||
}
|
}
|
||||||
|
@ -372,22 +425,24 @@ class ModuleHandler implements ModuleHandlerInterface {
|
||||||
}
|
}
|
||||||
// If any modules implement one of the extra hooks that do not implement
|
// If any modules implement one of the extra hooks that do not implement
|
||||||
// the primary hook, we need to add them to the $modules array in their
|
// the primary hook, we need to add them to the $modules array in their
|
||||||
// appropriate order. $this->getImplementations() can only return ordered
|
// appropriate order. $this->getImplementations() can only return
|
||||||
// implementations of a single hook. To get the ordered implementations
|
// ordered implementations of a single hook. To get the ordered
|
||||||
// of multiple hooks, we mimic the $this->getImplementations() logic of first
|
// implementations of multiple hooks, we mimic the
|
||||||
// ordering by $this->getModuleList(), and then calling
|
// $this->getImplementations() logic of first ordering by
|
||||||
|
// $this->getModuleList(), and then calling
|
||||||
// $this->alter('module_implements').
|
// $this->alter('module_implements').
|
||||||
if (array_diff($extra_modules, $modules)) {
|
if (array_diff($extra_modules, $modules)) {
|
||||||
// Merge the arrays and order by getModuleList().
|
// Merge the arrays and order by getModuleList().
|
||||||
$modules = array_intersect(array_keys($this->moduleList), array_merge($modules, $extra_modules));
|
$modules = array_intersect(array_keys($this->moduleList), array_merge($modules, $extra_modules));
|
||||||
// Since $this->getImplementations() already took care of loading the necessary
|
// Since $this->getImplementations() already took care of loading the
|
||||||
// include files, we can safely pass FALSE for the array values.
|
// necessary include files, we can safely pass FALSE for the array
|
||||||
|
// values.
|
||||||
$implementations = array_fill_keys($modules, FALSE);
|
$implementations = array_fill_keys($modules, FALSE);
|
||||||
// Let modules adjust the order solely based on the primary hook. This
|
// Let modules adjust the order solely based on the primary hook. This
|
||||||
// ensures the same module order regardless of whether this if block
|
// ensures the same module order regardless of whether this if block
|
||||||
// runs. Calling $this->alter() recursively in this way does not result
|
// runs. Calling $this->alter() recursively in this way does not
|
||||||
// in an infinite loop, because this call is for a single $type, so we
|
// result in an infinite loop, because this call is for a single
|
||||||
// won't end up in this code block again.
|
// $type, so we won't end up in this code block again.
|
||||||
$this->alter('module_implements', $implementations, $hook);
|
$this->alter('module_implements', $implementations, $hook);
|
||||||
$modules = array_keys($implementations);
|
$modules = array_keys($implementations);
|
||||||
}
|
}
|
||||||
|
@ -450,9 +505,55 @@ class ModuleHandler implements ModuleHandlerInterface {
|
||||||
* hook_hook_info() or FALSE if the implementation is in the module file.
|
* hook_hook_info() or FALSE if the implementation is in the module file.
|
||||||
*/
|
*/
|
||||||
protected function getImplementationInfo($hook) {
|
protected function getImplementationInfo($hook) {
|
||||||
if (isset($this->implementations[$hook])) {
|
if (!isset($this->implementations)) {
|
||||||
|
$this->implementations = array();
|
||||||
|
if ($cache = $this->cacheBackend->get('module_implements')) {
|
||||||
|
$this->implementations = $cache->data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!isset($this->implementations[$hook])) {
|
||||||
|
// The hook is not cached, so ensure that whether or not it has
|
||||||
|
// implementations, the cache is updated at the end of the request.
|
||||||
|
$this->cacheNeedsWriting = TRUE;
|
||||||
|
$this->implementations[$hook] = $this->buildImplementationInfo($hook);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
foreach ($this->implementations[$hook] as $module => $group) {
|
||||||
|
// If this hook implementation is stored in a lazy-loaded file, include
|
||||||
|
// that file first.
|
||||||
|
if ($group) {
|
||||||
|
$this->loadInclude($module, 'inc', "$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 ModuleHandler::implementsHook() 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($this->implementations[$hook][$module]);
|
||||||
|
$this->cacheNeedsWriting = TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return $this->implementations[$hook];
|
return $this->implementations[$hook];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds hook implementation information for a given hook name.
|
||||||
|
*
|
||||||
|
* @param string $hook
|
||||||
|
* The name of the hook (e.g. "help" or "menu").
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
* An array whose keys are the names of the modules which are implementing
|
||||||
|
* this hook and whose values are either an array of information from
|
||||||
|
* hook_hook_info() or FALSE if the implementation is in the module file.
|
||||||
|
*
|
||||||
|
* @see \Drupal\Core\Extension\ModuleHandler::getImplementationInfo()
|
||||||
|
*/
|
||||||
|
protected function buildImplementationInfo($hook) {
|
||||||
$this->implementations[$hook] = array();
|
$this->implementations[$hook] = array();
|
||||||
$hook_info = $this->getHookInfo();
|
$hook_info = $this->getHookInfo();
|
||||||
foreach ($this->moduleList as $module => $filename) {
|
foreach ($this->moduleList as $module => $filename) {
|
||||||
|
@ -552,8 +653,8 @@ class ModuleHandler implements ModuleHandlerInterface {
|
||||||
|
|
||||||
// Conditionally add the dependencies to the list of modules.
|
// Conditionally add the dependencies to the list of modules.
|
||||||
if ($enable_dependencies) {
|
if ($enable_dependencies) {
|
||||||
// Add dependencies to the list. The new modules will be processed as the
|
// Add dependencies to the list. The new modules will be processed as
|
||||||
// while loop continues.
|
// the while loop continues.
|
||||||
while (list($module) = each($module_list)) {
|
while (list($module) = each($module_list)) {
|
||||||
foreach (array_keys($module_data[$module]->requires) as $dependency) {
|
foreach (array_keys($module_data[$module]->requires) as $dependency) {
|
||||||
if (!isset($module_data[$dependency])) {
|
if (!isset($module_data[$dependency])) {
|
||||||
|
@ -606,14 +707,15 @@ class ModuleHandler implements ModuleHandlerInterface {
|
||||||
->save();
|
->save();
|
||||||
|
|
||||||
// Prepare the new module list, sorted by weight, including filenames.
|
// Prepare the new module list, sorted by weight, including filenames.
|
||||||
// This list is used for both the ModuleHandler and DrupalKernel. It needs
|
// This list is used for both the ModuleHandler and DrupalKernel. It
|
||||||
// to be kept in sync between both. A DrupalKernel reboot or rebuild will
|
// needs to be kept in sync between both. A DrupalKernel reboot or
|
||||||
// automatically re-instantiate a new ModuleHandler that uses the new
|
// rebuild will automatically re-instantiate a new ModuleHandler that
|
||||||
// module list of the kernel. However, DrupalKernel does not cause any
|
// uses the new module list of the kernel. However, DrupalKernel does
|
||||||
// modules to be loaded.
|
// not cause any modules to be loaded.
|
||||||
// Furthermore, the currently active (fixed) module list can be different
|
// Furthermore, the currently active (fixed) module list can be
|
||||||
// from the configured list of enabled modules. For all active modules not
|
// different from the configured list of enabled modules. For all active
|
||||||
// contained in the configured enabled modules, we assume a weight of 0.
|
// modules not contained in the configured enabled modules, we assume a
|
||||||
|
// weight of 0.
|
||||||
$current_module_filenames = $this->getModuleList();
|
$current_module_filenames = $this->getModuleList();
|
||||||
$current_modules = array_fill_keys(array_keys($current_module_filenames), 0);
|
$current_modules = array_fill_keys(array_keys($current_module_filenames), 0);
|
||||||
$current_modules = module_config_sort(array_merge($current_modules, $extension_config->get('module')));
|
$current_modules = module_config_sort(array_merge($current_modules, $extension_config->get('module')));
|
||||||
|
@ -631,10 +733,10 @@ class ModuleHandler implements ModuleHandlerInterface {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the module handler in order to load the module's code.
|
// Update the module handler in order to load the module's code.
|
||||||
// This allows the module to participate in hooks and its existence to be
|
// This allows the module to participate in hooks and its existence to
|
||||||
// discovered by other modules.
|
// be discovered by other modules.
|
||||||
// The current ModuleHandler instance is obsolete with the kernel rebuild
|
// The current ModuleHandler instance is obsolete with the kernel
|
||||||
// below.
|
// rebuild below.
|
||||||
$this->setModuleList($module_filenames);
|
$this->setModuleList($module_filenames);
|
||||||
$this->load($module);
|
$this->load($module);
|
||||||
module_load_install($module);
|
module_load_install($module);
|
||||||
|
@ -647,8 +749,8 @@ class ModuleHandler implements ModuleHandlerInterface {
|
||||||
// Update the kernel to include it.
|
// Update the kernel to include it.
|
||||||
// This reboots the kernel to register the module's bundle and its
|
// This reboots the kernel to register the module's bundle and its
|
||||||
// services in the service container. The $module_filenames argument is
|
// services in the service container. The $module_filenames argument is
|
||||||
// taken over as %container.modules% parameter, which is passed to a fresh
|
// taken over as %container.modules% parameter, which is passed to a
|
||||||
// ModuleHandler instance upon first retrieval.
|
// fresh ModuleHandler instance upon first retrieval.
|
||||||
// @todo install_begin_request() creates a container without a kernel.
|
// @todo install_begin_request() creates a container without a kernel.
|
||||||
if ($kernel = \Drupal::service('kernel', ContainerInterface::NULL_ON_INVALID_REFERENCE)) {
|
if ($kernel = \Drupal::service('kernel', ContainerInterface::NULL_ON_INVALID_REFERENCE)) {
|
||||||
$kernel->updateModules($module_filenames, $module_filenames);
|
$kernel->updateModules($module_filenames, $module_filenames);
|
||||||
|
|
|
@ -174,6 +174,11 @@ interface ModuleHandlerInterface {
|
||||||
*/
|
*/
|
||||||
public function getImplementations($hook);
|
public function getImplementations($hook);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write the hook implementation info to the cache.
|
||||||
|
*/
|
||||||
|
public function writeCache();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resets the cached list of hook implementations.
|
* Resets the cached list of hook implementations.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -11,6 +11,7 @@ use Drupal\simpletest\UnitTestBase;
|
||||||
use Drupal\plugin_test\Plugin\TestPluginManager;
|
use Drupal\plugin_test\Plugin\TestPluginManager;
|
||||||
use Drupal\plugin_test\Plugin\MockBlockManager;
|
use Drupal\plugin_test\Plugin\MockBlockManager;
|
||||||
use Drupal\plugin_test\Plugin\DefaultsTestPluginManager;
|
use Drupal\plugin_test\Plugin\DefaultsTestPluginManager;
|
||||||
|
use Drupal\Core\Cache\MemoryBackend;
|
||||||
use Drupal\Core\Extension\ModuleHandler;
|
use Drupal\Core\Extension\ModuleHandler;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -37,7 +38,7 @@ abstract class PluginTestBase extends UnitTestBase {
|
||||||
// as derivatives and ReflectionFactory.
|
// as derivatives and ReflectionFactory.
|
||||||
$this->testPluginManager = new TestPluginManager();
|
$this->testPluginManager = new TestPluginManager();
|
||||||
$this->mockBlockManager = new MockBlockManager();
|
$this->mockBlockManager = new MockBlockManager();
|
||||||
$module_handler = new ModuleHandler();
|
$module_handler = new ModuleHandler(array(), new MemoryBackend('plugin'));
|
||||||
$this->defaultsTestPluginManager = new DefaultsTestPluginManager($module_handler);
|
$this->defaultsTestPluginManager = new DefaultsTestPluginManager($module_handler);
|
||||||
|
|
||||||
// The expected plugin definitions within each manager. Several tests assert
|
// The expected plugin definitions within each manager. Several tests assert
|
||||||
|
|
|
@ -1,42 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @file
|
|
||||||
* Contains \Drupal\Core\Extension\ModuleHanderUnitTest.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Drupal\Tests\Core\Extension;
|
|
||||||
|
|
||||||
use Drupal\Core\Extension\ModuleHandler;
|
|
||||||
use Drupal\Tests\UnitTestCase;
|
|
||||||
use PHPUnit_Framework_Error_Notice;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests the ModuleHandler class.
|
|
||||||
*
|
|
||||||
* @group System
|
|
||||||
*/
|
|
||||||
class ModuleHandlerUnitTest extends UnitTestCase {
|
|
||||||
|
|
||||||
public static function getInfo() {
|
|
||||||
return array(
|
|
||||||
'name' => 'ModuleHandler functionality',
|
|
||||||
'description' => 'Tests the ModuleHandler class.',
|
|
||||||
'group' => 'System',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function setUp() {
|
|
||||||
parent::setUp();
|
|
||||||
$this->moduleHandler = new ModuleHandler;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests loading of an include from a nonexistent module.
|
|
||||||
*/
|
|
||||||
public function testLoadInclude() {
|
|
||||||
// Attepmting to load a file from a non-existent module should return FALSE.
|
|
||||||
$this->assertFalse($this->moduleHandler->loadInclude('foo', 'inc'));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -105,7 +105,9 @@ class DefaultPluginManagerTest extends UnitTestCase {
|
||||||
* Tests the plugin manager with no cache and altering.
|
* Tests the plugin manager with no cache and altering.
|
||||||
*/
|
*/
|
||||||
public function testDefaultPluginManagerWithAlter() {
|
public function testDefaultPluginManagerWithAlter() {
|
||||||
$module_handler = $this->getMock('Drupal\Core\Extension\ModuleHandler');
|
$module_handler = $this->getMockBuilder('Drupal\Core\Extension\ModuleHandler')
|
||||||
|
->disableOriginalConstructor()
|
||||||
|
->getMock();
|
||||||
|
|
||||||
// Configure the stub.
|
// Configure the stub.
|
||||||
$alter_hook_name = $this->randomName();
|
$alter_hook_name = $this->randomName();
|
||||||
|
|
Loading…
Reference in New Issue