Commit 49699d5b authored by webchick's avatar webchick

#533586 by dww and chx: Refactor dependency checks into sharable helper functions.

parent 5327ed26
......@@ -4917,3 +4917,83 @@ function _drupal_flush_css_js() {
}
variable_set('css_js_query_string', $new_character . substr($string_history, 0, 19));
}
/**
* Parse a dependency for comparison by drupal_check_incompatibility().
*
* @param $dependency
* A dependency string, for example 'foo (>=7.x-4.5-beta5, 3.x)'.
* @return
* An associative array with three keys:
* - 'name' includes the name of the thing to depend on (e.g. 'foo').
* - 'original_version' contains the original version string (which can be
* used in the UI for reporting incompatibilities).
* - 'versions' is a list of associative arrays, each containing the keys
* 'op' and 'version'. 'op' can be one of: '=', '==', '!=', '<>', '<',
* '<=', '>', or '>='. 'version' is one piece like '4.5-beta3'.
* Callers should pass this structure to drupal_check_incompatibility().
*
* @see drupal_check_incompatibility()
*/
function drupal_parse_dependency($dependency) {
// We use named subpatterns and support every op that version_compare
// supports. Also, op is optional and defaults to equals.
$p_op = '(?P<operation>!=|==|=|<|<=|>|>=|<>)?';
// Core version is always optional: 7.x-2.x and 2.x is treated the same.
$p_core = '(?:' . preg_quote(DRUPAL_CORE_COMPATIBILITY) . '-)?';
$p_major = '(?P<major>\d+)';
// By setting the minor version to x, branches can be matched.
$p_minor = '(?P<minor>(?:\d+|x)(?:-[A-Za-z]+\d+)?)';
$value = array();
$parts = explode('(', $dependency, 2);
$value['name'] = trim($parts[0]);
if (isset($parts[1])) {
$value['original_version'] = ' (' . $parts[1];
foreach (explode(',', $parts[1]) as $version) {
if (preg_match("/^\s*$p_op\s*$p_core$p_major\.$p_minor/", $version, $matches)) {
$op = !empty($matches['operation']) ? $matches['operation'] : '=';
if ($matches['minor'] == 'x') {
// Drupal considers "2.x" to mean any version that begins with
// "2" (e.g. 2.0, 2.9 are all "2.x"). PHP's version_compare(),
// on the other hand, treats "x" as a string; so to
// version_compare(), "2.x" is considered less than 2.0. This
// means that >=2.x and <2.x are handled by version_compare()
// as we need, but > and <= are not.
if ($op == '>' || $op == '<=') {
$matches['major']++;
}
// Equivalence can be checked by adding two restrictions.
if ($op == '=' || $op == '==') {
$value['versions'][] = array('op' => '<', 'version' => ($matches['major'] + 1) . '.x');
$op = '>=';
}
}
$value['versions'][] = array('op' => $op, 'version' => $matches['major'] . '.' . $matches['minor']);
}
}
}
return $value;
}
/**
* Check whether a version is compatible with a given dependency.
*
* @param $v
* The parsed dependency structure from drupal_parse_dependency().
* @param $current_version
* The version to check against (like 4.2).
* @return
* NULL if compatible, otherwise the original dependency version string that
* caused the incompatiblity.
*
* @see drupal_parse_dependency()
*/
function drupal_check_incompatibility($v, $current_version) {
if (!empty($v['versions'])) {
foreach ($v['versions'] as $required_version) {
if ((isset($required_version['op']) && !version_compare($current_version, $required_version['version'], $required_version['op']))) {
return $v['original_version'];
}
}
}
}
......@@ -95,50 +95,13 @@ function module_list($refresh = FALSE, $sort = FALSE, $fixed_list = NULL) {
function _module_build_dependencies($files) {
require_once DRUPAL_ROOT . '/includes/graph.inc';
$roots = $files;
// We use named subpatterns and support every op that version_compare
// supports. Also, op is optional and defaults to equals.
$p_op = '(?P<operation>!=|==|=|<|<=|>|>=|<>)?';
// Core version is always optional: 7.x-2.x and 2.x is treated the same.
$p_core = '(?:' . preg_quote(DRUPAL_CORE_COMPATIBILITY) . '-)?';
$p_major = '(?P<major>\d+)';
// By setting the minor version to x, branches can be matched.
$p_minor = '(?P<minor>(?:\d+|x)(?:-[A-Za-z]+\d+)?)';
foreach ($files as $filename => $file) {
$graph[$file->name]['edges'] = array();
if (isset($file->info['dependencies']) && is_array($file->info['dependencies'])) {
foreach ($file->info['dependencies'] as $dependency_name) {
$value = array();
$parts = explode('(', $dependency_name, 2);
$dependency_name = trim($parts[0]);
if (isset($parts[1])) {
$value['original_version'] = ' (' . $parts[1];
foreach (explode(',', $parts[1]) as $version) {
if (preg_match("/^\s*$p_op\s*$p_core$p_major\.$p_minor/", $version, $matches)) {
$op = !empty($matches['operation']) ? $matches['operation'] : '=';
if ($matches['minor'] == 'x') {
// Drupal considers "2.x" to mean any version that begins with
// "2" (e.g. 2.0, 2.9 are all "2.x"). PHP's version_compare(),
// on the other hand, treats "x" as a string; so to
// version_compare(), "2.x" is considered less than 2.0. This
// means that >=2.x and <2.x are handled by version_compare()
// as we need, but > and <= are not.
if ($op == '>' || $op == '<=') {
$matches['major']++;
}
// Equivalence is checked by preg.
if ($op == '=' || $op == '==') {
$value['versions'][] = array('preg' => '/^' . $matches['major'] . '\./');
$op = '';
}
}
if ($op) {
$value['versions'][] = array('op' => $op, 'version' => $matches['major'] . '.' . $matches['minor']);
}
}
}
}
$graph[$file->name]['edges'][$dependency_name] = $value;
unset($roots[$dependency_name]);
foreach ($file->info['dependencies'] as $dependency) {
$dependency_data = drupal_parse_dependency($dependency);
$graph[$file->name]['edges'][$dependency_data['name']] = $dependency_data;
unset($roots[$dependency_data['name']]);
}
}
}
......
......@@ -660,27 +660,18 @@ function system_modules($form_state = array()) {
}
else {
$requires_name = $files[$requires]->info['name'];
if ($v) {
$requires_name .= $v['original_version'];
$current_version = str_replace(DRUPAL_CORE_COMPATIBILITY . '-', '', $files[$requires]->info['version']);
foreach ($v['versions'] as $required_version) {
if ((isset($required_version['op']) && !version_compare($current_version, $required_version['version'], $required_version['op'])) ||
(isset($required_version['preg']) && !preg_match($required_version['preg'], $current_version))) {
$extra['requires'][$requires] = t('@module (<span class="admin-missing">incompatible with</span> version @version)', array(
'@module' => $requires_name,
'@version' => $files[$requires]->info['version'],
));
$extra['disabled'] = TRUE;
}
}
if ($incompatible_version = drupal_check_incompatibility($v, str_replace(DRUPAL_CORE_COMPATIBILITY . '-', '', $files[$requires]->info['version']))) {
$extra['requires'][$requires] = t('@module (<span class="admin-missing">incompatible with</span> version @version)', array(
'@module' => $requires_name . $incompatible_version,
'@version' => $files[$requires]->info['version'],
));
$extra['disabled'] = TRUE;
}
if (!isset($extra['requires'][$requires])) {
if ($files[$requires]->status) {
$extra['requires'][$requires] = t('@module (<span class="admin-enabled">enabled</span>)', array('@module' => $requires_name));
}
else {
$extra['requires'][$requires] = t('@module (<span class="admin-disabled">disabled</span>)', array('@module' => $requires_name));
}
elseif ($files[$requires]->status) {
$extra['requires'][$requires] = t('@module (<span class="admin-enabled">enabled</span>)', array('@module' => $requires_name));
}
else {
$extra['requires'][$requires] = t('@module (<span class="admin-disabled">disabled</span>)', array('@module' => $requires_name));
}
}
}
......
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