Code Coverage |
||||||||||
Classes and Traits |
Functions and Methods |
Lines |
||||||||
Total | |
0.00% |
0 / 1 |
|
54.55% |
6 / 11 |
CRAP | |
50.00% |
29 / 58 |
UpdateRegistry | |
0.00% |
0 / 1 |
|
54.55% |
6 / 11 |
64.12 | |
50.00% |
29 / 58 |
__construct | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 6 |
|||
getAvailableUpdateFunctions | |
0.00% |
0 / 1 |
20 | |
0.00% |
0 / 9 |
|||
getPendingUpdateFunctions | |
100.00% |
1 / 1 |
1 | |
100.00% |
5 / 5 |
|||
loadUpdateFiles | |
0.00% |
0 / 1 |
12 | |
0.00% |
0 / 4 |
|||
loadUpdateFile | |
0.00% |
0 / 1 |
6 | |
0.00% |
0 / 4 |
|||
getPendingUpdateInformation | |
100.00% |
1 / 1 |
3 | |
100.00% |
10 / 10 |
|||
registerInvokedUpdates | |
100.00% |
1 / 1 |
1 | |
100.00% |
4 / 4 |
|||
getModuleUpdateFunctions | |
100.00% |
1 / 1 |
1 | |
100.00% |
5 / 5 |
|||
anonymous function | |
100.00% |
1 / 1 |
1 | |
100.00% |
2 / 2 |
|||
scanExtensionsAndLoadUpdateFiles | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 6 |
|||
filterOutInvokedUpdatesByModule | |
100.00% |
1 / 1 |
1 | |
100.00% |
1 / 1 |
<?php | |
/** | |
* @file | |
* Contains \Drupal\Core\Update\UpdateRegistry. | |
*/ | |
namespace Drupal\Core\Update; | |
use Drupal\Core\Extension\Extension; | |
use Drupal\Core\Extension\ExtensionDiscovery; | |
use Drupal\Core\KeyValueStore\KeyValueStoreInterface; | |
/** | |
* Provides all and missing update implementations. | |
* | |
* Note: This registry is specific to a type of updates, like 'post_update' as | |
* example. | |
* | |
* It therefore scans for functions named like the type of updates, so it looks | |
* like MODULE_UPDATETYPE_NAME() with NAME being a machine name. | |
*/ | |
class UpdateRegistry { | |
/** | |
* The used update name. | |
* | |
* @var string | |
*/ | |
protected $updateType = 'post_update'; | |
/** | |
* The app root. | |
* | |
* @var string | |
*/ | |
protected $root; | |
/** | |
* The filename of the log file. | |
* | |
* @var string | |
*/ | |
protected $logFilename; | |
/** | |
* @var string[] | |
*/ | |
protected $enabledModules; | |
/** | |
* The key value storage. | |
* | |
* @var \Drupal\Core\KeyValueStore\KeyValueStoreInterface | |
*/ | |
protected $keyValue; | |
/** | |
* Should we respect update functions in tests. | |
* | |
* @var bool|null | |
*/ | |
protected $includeTests = NULL; | |
/** | |
* The site path. | |
* | |
* @var string | |
*/ | |
protected $sitePath; | |
/** | |
* Constructs a new UpdateRegistry. | |
* | |
* @param string $root | |
* The app root. | |
* @param string $site_path | |
* The site path. | |
* @param string[] $enabled_modules | |
* A list of enabled modules. | |
* @param \Drupal\Core\KeyValueStore\KeyValueStoreInterface $key_value | |
* The key value store. | |
* @param bool|NULL $include_tests | |
* (optional) A flag whether to include tests in the scanning of modules. | |
*/ | |
public function __construct($root, $site_path, array $enabled_modules, KeyValueStoreInterface $key_value, $include_tests = NULL) { | |
$this->root = $root; | |
$this->sitePath = $site_path; | |
$this->enabledModules = $enabled_modules; | |
$this->keyValue = $key_value; | |
$this->includeTests = $include_tests; | |
} | |
/** | |
* Gets all available update functions. | |
* | |
* @return callable[] | |
* A list of update functions. | |
*/ | |
protected function getAvailableUpdateFunctions() { | |
$regexp = '/^(?<module>.+)_' . $this->updateType . '_(?<name>.+)$/'; | |
$functions = get_defined_functions(); | |
$updates = []; | |
foreach (preg_grep('/_' . $this->updateType . '_/', $functions['user']) as $function) { | |
// If this function is a module update function, add it to the list of | |
// module updates. | |
if (preg_match($regexp, $function, $matches)) { | |
if (in_array($matches['module'], $this->enabledModules)) { | |
$updates[] = $matches['module'] . '_' . $this->updateType . '_' . $matches['name']; | |
} | |
} | |
} | |
// Ensure that the update order is deterministic. | |
sort($updates); | |
return $updates; | |
} | |
/** | |
* Find all update functions that haven't been executed. | |
* | |
* @return callable[] | |
* A list of update functions. | |
*/ | |
public function getPendingUpdateFunctions() { | |
// We need a) the list of active modules (we get that from the config | |
// bootstrap factory) and b) the path to the modules, we use the extension | |
// discovery for that. | |
$this->scanExtensionsAndLoadUpdateFiles(); | |
// First figure out which hook_{$this->updateType}_NAME got executed | |
// already. | |
$existing_update_functions = $this->keyValue->get('existing_updates', []); | |
$available_update_functions = $this->getAvailableUpdateFunctions(); | |
$not_executed_update_functions = array_diff($available_update_functions, $existing_update_functions); | |
return $not_executed_update_functions; | |
} | |
/** | |
* Loads all update files for a given list of extension. | |
* | |
* @param \Drupal\Core\Extension\Extension[] $module_extensions | |
* The extensions used for loading. | |
*/ | |
protected function loadUpdateFiles(array $module_extensions) { | |
// Load all the {$this->updateType}.php files. | |
foreach ($this->enabledModules as $module) { | |
if (isset($module_extensions[$module])) { | |
$this->loadUpdateFile($module_extensions[$module]); | |
} | |
} | |
} | |
/** | |
* Loads the {$this->updateType}.php file for a given extension. | |
* | |
* @param \Drupal\Core\Extension\Extension $module | |
* The extension of the module to load its file. | |
*/ | |
protected function loadUpdateFile(Extension $module) { | |
$filename = $this->root . '/' . $module->getPath() . '/' . $module->getName() . ".{$this->updateType}.php"; | |
if (file_exists($filename)) { | |
include_once $filename; | |
} | |
} | |
/** | |
* Returns a list of all the pending updates. | |
* | |
* @return array[] | |
* An associative array keyed by module name which contains all information | |
* about database updates that need to be run, and any updates that are not | |
* going to proceed due to missing requirements. | |
* | |
* The subarray for each module can contain the following keys: | |
* - start: The starting update that is to be processed. If this does not | |
* exist then do not process any updates for this module as there are | |
* other requirements that need to be resolved. | |
* - pending: An array of all the pending updates for the module including | |
* the description from source code comment for each update function. | |
* This array is keyed by the update name. | |
*/ | |
public function getPendingUpdateInformation() { | |
$functions = $this->getPendingUpdateFunctions(); | |
$ret = []; | |
foreach ($functions as $function) { | |
list($module, $update) = explode("_{$this->updateType}_", $function); | |
// The description for an update comes from its Doxygen. | |
$func = new \ReflectionFunction($function); | |
$description = trim(str_replace(array("\n", '*', '/'), '', $func->getDocComment()), ' '); | |
$ret[$module]['pending'][$update] = $description; | |
if (!isset($ret[$module]['start'])) { | |
$ret[$module]['start'] = $update; | |
} | |
} | |
return $ret; | |
} | |
/** | |
* Registers that update fucntions got executed. | |
* | |
* @param string[] $function_names | |
* The executed update functions. | |
* | |
* @return $this | |
*/ | |
public function registerInvokedUpdates(array $function_names) { | |
$executed_updates = $this->keyValue->get('existing_updates', []); | |
$executed_updates = array_merge($executed_updates, $function_names); | |
$this->keyValue->set('existing_updates', $executed_updates); | |
return $this; | |
} | |
/** | |
* Returns all available updates for a given module. | |
* | |
* @param string $module_name | |
* The module name. | |
* | |
* @return callable[] | |
* A list of update functions. | |
*/ | |
public function getModuleUpdateFunctions($module_name) { | |
$this->scanExtensionsAndLoadUpdateFiles(); | |
$all_functions = $this->getAvailableUpdateFunctions(); | |
return array_filter($all_functions, function($function_name) use ($module_name) { | |
list($function_module_name, ) = explode("_{$this->updateType}_", $function_name); | |
return $function_module_name === $module_name; | |
}); | |
} | |
/** | |
* Scans all module + profile extensions and load the update files. | |
*/ | |
protected function scanExtensionsAndLoadUpdateFiles() { | |
// Scan the module list. | |
$extension_discovery = new ExtensionDiscovery($this->root, FALSE, [], $this->sitePath); | |
$module_extensions = $extension_discovery->scan('module'); | |
$profile_extensions = $extension_discovery->scan('profile'); | |
$extensions = array_merge($module_extensions, $profile_extensions); | |
$this->loadUpdateFiles($extensions); | |
} | |
/** | |
* Filters out already executed update functions by module. | |
* | |
* @param string $module | |
* The module name. | |
*/ | |
public function filterOutInvokedUpdatesByModule($module) { | |
$existing_update_functions = $this->keyValue->get('existing_updates', []); | |
$remaining_update_functions = array_filter($existing_update_functions, function($function_name) use ($module) { | |
return strpos($function_name, "{$module}_{$this->updateType}_") !== 0; | |
}); | |
$this->keyValue->set('existing_updates', array_values($remaining_update_functions)); | |
} | |
} |