When translating Mollom to Czech language today I ran in a issue that I set the wrong version for translation, because the version order in form is confusing. So I was translating version which I did not want for an hour... :-(

Versions are sorted by basic alphabet sorting, so e.g. version 5.x-1.7 precedes version 5.x-2.1 (which is right) but version 5.x-1.10 precedes 5.x-1.2 which is wrong...

See the attached picture.

Comments

wojtha’s picture

My fist suggestion is to create parser and score function which turn drupal version 7.x-1.0-alpha2 into some score like 7010003000 or so...


function _drupal_version_score($ver) {
  preg_match('/(\d+)\.x-(\d+).(\d+)-?([a-z]+)?(\d+)?/i', $ver, $v);

  /* Now we have in $v something like this (4 and 5 are optional!):
   [0] => 7.x-1.0-alpha1
   [1] => 7
   [2] => 1
   [3] => 10
   [4] => alpha
   [5] => 1
  */

  // if optional version attribute exists convert it to number
  $opt = 0;
  if (isset($v[4])) {
    switch (strtolower($v[4])) {
      case 'alpha':
        $opt = 1000;
        break;
      case 'beta':
        $opt = 2000;
        break;
      case 'rc':
        $opt = 3000;
        break;
      default:
        $opt = 0;
    }
    // optional part of score formula
    $opt = $opt - 5000 + ( isset($v[5]) ? $v[5] : 0);
  }

  // main score formula
  return $v[1] * 100000000 + $v[2] * 1000000 + $v[3] * 10000 + $opt;
}

function _drupal_versions_sort($ver_a, $ver_b) {

  $score_a = _drupal_version_score($ver_a);
  $score_b = _drupal_version_score($ver_b);

  if ($score_a == $score_b) {
    return 0;
  }
  return ($score_a < $score_b) ? -1 : 1;
}

// Array to be sorted
$versions = array(
  '5.x-2.1',
  '5.x-1.7',
  '6.x-1.10',
  '6.x-1.11',
  '6.x-1.9',
  '7.x-1.0',
  '7.x-2.0',
  '7.x-1.0-alpha2',
  '7.x-1.0-alpha1',
  '7.x-1.99',
  '7.x-2.0-alpha',
  '7.x-2.1-RC1',
  '7.x-2.2',
  '7.x-2.1-beta13',
);

// Sort and print the resulting array
uasort($versions, '_drupal_versions_sort');
print '<pre>';
print_r($versions);
print '</pre>';

// give us this niceeee :-) result:
/* Array (
      [1] => 5.x-1.7
      [0] => 5.x-2.1
      [4] => 6.x-1.9
      [2] => 6.x-1.10
      [3] => 6.x-1.11
      [8] => 7.x-1.0-alpha1
      [7] => 7.x-1.0-alpha2
      [5] => 7.x-1.0
      [9] => 7.x-1.99
      [10] => 7.x-2.0-alpha
      [6] => 7.x-2.0
      [13] => 7.x-2.1-beta13
      [11] => 7.x-2.1-RC1
      [12] => 7.x-2.2
  ) */
wojtha’s picture

Faster and cleaner implementation I believe :-)

function _drupal_version_score($ver) {
  if (preg_match('/(\d+)\.x-(\d+).(\d+)-?([a-z]+)?(\d+)?/i', $ver, $v)) {

    /* Now we have in $v something like this (4 and 5 are optional!):
     [0] => 7.x-1.0-alpha1
     [1] => 7  [2] => 1  [3] => 10
     [4] => alpha  [5] => 1
    */

    $v[4] = (isset($v[4])) ? strtolower($v[4]) : 'stable';
    $v[5] = (isset($v[5])) ? $v[5] : 0;

    return sprintf('%02d%02d%03d%.1s%02d', $v[1], $v[2], $v[3], $v[4], $v[5]);
  }

  return $ver;
}

function _drupal_versions_sort($ver_a, $ver_b) {

  $score_a = _drupal_version_score($ver_a);
  $score_b = _drupal_version_score($ver_b);

  return strcmp($score_a, $score_b);
}

Function "_drupal_version_score" converts drupal versions human readable strings which can be easily alphabetically sorted in the right order.

There is only one trick... stable versions are marked with "s" that makes them to be ordered after Alpha, Beta and RC versions.

E.g.:
0502001s00 <-- 5.x-2.1
0601009s00 <-- 6.x-1.9
0601010s00 <-- 6.x-1.10
0701000a01 <-- 7.x-1.0-alpha1
0701000a02 <-- 7.x-1.0-alpha2
0701000s00 <-- 7.x-1.0
0702001b13 <-- 7.x-2.1-beta13
0702001r01 <-- 7.x-2.1-RC1
0702002s00 <-- 7.x-2.2

Gábor let me know if you like it. I will try to turn it into patch...

gábor hojtsy’s picture

How does Drupal.org's project module do this? It has the same problems for the version dropdowns we have :) Also, PHP's version_compare() function has no usefulness here?

wojtha’s picture

Hi Gabor,

Thx for your righteous comments and suggestions. Thing is moving forward!... I make some research of the project module. The sorting is done in Project issue tracking module, but it uses function project_release_get_releases() which resides in Project module. The function is using database alphabetic sorting and it has same wrong output in current implementation...

I found this issue: #767366: Sort version lists with version_compare() ... which is basically same problem and there is a fresh solution too.

Project module has had this prob for years and now there is a patch, which was submitted two weeks ago (salvis - April 10, 2010 - 21:30). Funny!

Salvis is also using version_compare() as you suggested. I don't like using str_replace in this way, but it is undoubtly faster than preg_match.

I made a test and sorting is right, but in opposite order - latest version is on the top. Which is also better I think.

Output from the Salvis's version_compare_function() seems very promising:

    [16] => 7.x-2.2
    [15] => 7.x-2.1-RC1
    [17] => 7.x-2.1-beta13
    [6] => 7.x-2.0
    [14] => 7.x-2.0-alpha
    [13] => 7.x-1.99
    [5] => 7.x-1.0
    [11] => 7.x-1.0-rc10
    [10] => 7.x-1.0-beta10
    [9] => 7.x-1.0-beta2
    [7] => 7.x-1.0-alpha2
    [8] => 7.x-1.0-alpha1
    [12] => 7.x-1.0-dev
    [3] => 6.x-1.11
    [2] => 6.x-1.10
    [4] => 6.x-1.9
    [0] => 5.x-2.1
    [1] => 5.x-1.7
gábor hojtsy’s picture

Status: Active » Needs work

The latest results look good. Should we wait on that patch to resolve itself, or is it good enough that we could just integrate it and maybe keep looking for changes to port here later? (I'm marking this needs work since we have some code here, not just talk :).

wojtha’s picture

Status: Needs work » Needs review
StatusFileSize
new63.43 KB
new1.13 KB

Hi Gabor,

here is a tiny patch which fixes it.

Credits for using version_compare() are going to salvis.

gábor hojtsy’s picture

Status: Needs review » Fixed

Great, thanks, committed.

Status: Fixed » Closed (fixed)

Automatically closed -- issue fixed for 2 weeks with no activity.