views_natural_sort.inc 5.21 KB
Newer Older
1 2 3
<?php

/**
4 5 6 7 8 9 10 11 12 13 14 15
 * @file
 * The Views Natural Sort module include file.
 */

/**
 * Remove all the configured words from the beginning of the string only.
 *
 * @param string $string
 *   The string we wish to transform.
 *
 * @return string
 *   The transformed string.
16 17 18 19 20 21 22 23
 */
function views_natural_sort_remove_beginning_words($string) {
  $beginning_words = [
    t('The'),
    t('A'),
    t('An'),
    t('La'),
    t('Le'),
24
    t('Il'),
25 26 27 28 29 30 31 32 33 34 35 36 37
  ];
  if (empty($beginning_words)) {
    return $string;
  }

  array_walk($beginning_words, 'preg_quote');
  return preg_replace(
    '/^(' . implode('|', $beginning_words) . ')\s+/i',
    '',
    $string
  );
}

38 39 40 41 42 43 44 45 46
/**
 * Remove all the configured words from the string.
 *
 * @param string $string
 *   The string we wish to transform.
 *
 * @return string
 *   The transformed string.
 */
47 48 49 50 51 52 53 54 55 56 57 58
function views_natural_sort_remove_words($string) {
  $words = [
    t('and'),
    t('or'),
    t('of'),
  ];
  if (empty($words)) {
    return $string;
  }

  array_walk($words, 'preg_quote');
  return preg_replace(
59
    [
60 61
      '/\s(' . implode('|', $words) . ')\s+/i',
      '/^(' . implode('|', $words) . ')\s+/i',
62 63
    ],
    [
64
      ' ',
65
      '',
66
    ],
67 68 69 70
    $string
  );
}

71 72 73 74 75 76 77 78 79
/**
 * Remove all the configured symbols from the string.
 *
 * @param string $string
 *   The string we wish to transform.
 *
 * @return string
 *   The transformed string.
 */
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
function views_natural_sort_remove_symbols($string) {
  $symbols = "#\"'\\()[]";
  if (strlen($symbols) == 0) {
    return $string;
  }
  return preg_replace(
    '/[' . preg_quote($symbols) . ']/',
    '',
    $string
  );
}

/**
 * Transform numbers in a string into a natural sortable string.
 *
 * Rules are as follows:
96
 *  - Embedded numbers will sort in numerical order. The following possibilities
97 98 99 100 101 102 103 104
 *    are supported
 *    - A leading dash indicates a negative number, unless it is preceded by a
 *      non-whitespace character, which case it is considered just a dash.
 *    - Leading zeros are properly ignored so as to not influence sort order
 *    - Decimal numbers are supported using a period as the decimal character
 *    - Thousands separates are ignored, using the comma as the thous. character
 *    - Numbers may be up to 99 digits before the decimal, up to the precision
 *      of the processor.
105 106 107
 *
 * @param string $string
 *   The string we wish to transform.
108 109 110 111
 */
function views_natural_sort_numbers($string) {
  // Find an optional leading dash (either preceded by whitespace or the first
  // character) followed by either:
112 113 114
  // - an optional series of digits (with optional embedded commas), then a
  //   period, then an optional series of digits
  // - a series of digits (with optional embedded commas)
115 116 117 118 119 120 121 122
  return preg_replace_callback(
    '/(\s-|^-)?(?:(\d[\d,]*)?\.(\d+)|(\d[\d,]*))/',
    '_views_natural_sort_number_transform_match_callback',
    $string
  );
}

/**
123 124 125 126
 * Transforms a string representing numbers into a special format.
 *
 * This special format can be sorted as if it was a number but in reality is
 *   being sorted alphanumerically.
127 128
 *
 * @param array $match
129
 *   Array of matches passed from preg_replace_callback
130 131 132 133
 *   $match[0] is the entire matching string
 *   $match[1] if present, is the optional dash, preceded by optional whitespace
 *   $match[2] if present, is whole number portion of the decimal number
 *   $match[3] if present, is the fractional portion of the decimal number
134
 *   $match[4] if present, is the integer (when no fraction is matched).
135 136 137 138 139
 *
 * @return string
 *   String representing a numerical value that will sort numerically in an
 *   alphanumeric search.
 */
140
function _views_natural_sort_number_transform_match_callback(array $match) {
141

142 143 144 145 146 147 148
  // Remove commas and leading zeros from whole number.
  $whole = (string) (int) str_replace(',', '', (isset($match[4]) && strlen($match[4]) > 0) ? $match[4] : $match[2]);
  // Remove traililng 0's from fraction, then add the decimal and one trailing
  // 0 and a space. The space serves as a way to always sort shorter decimal
  // numbers that match exactly as less than longer ones.
  // Ex: 3.05 and 3.05011.
  $fraction = trim('.' . $match[3], '0') . '0 ';
149 150
  $encode = sprintf('%02u', strlen($whole)) . $whole . $fraction;
  if (strlen($match[1])) {
151 152 153 154 155 156 157 158 159 160
    // Negative number. Make 10's complement. Put back any leading white space
    // and the dash requires intermediate to avoid double-replacing the same
    // digit. str_replace() seems to work by copying the source to the result,
    // then successively replacing within it, rather than replacing from the
    // source to the result.
    // In this case since rules are reverced we also have to use a character
    // that would be sorted higher than a space when a number is being compared
    // against a longer one that is identical in negative numbers. This is so
    // that longer numbers are always LESS than sorter numbers that have
    // identical beginnings. Ex: -3.05 and -3.05011.
161 162 163
    $digits       = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ' '];
    $intermediate = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k'];
    $rev_digits   = ['9', '8', '7', '6', '5', '4', '3', '2', '1', '0', ':'];
164
    $encode       = $match[1] . str_replace($intermediate, $rev_digits, str_replace($digits, $intermediate, $encode));
165 166 167
  }
  return $encode;
}