1<?php 2 3namespace dokuwiki\Utf8; 4 5/** 6 * DokuWiki sort functions 7 * 8 * When "intl" extension is available, all sorts are done using a collator. 9 * Otherwise, primitive PHP functions are called. 10 * 11 * The collator is created using the locale given in $conf['lang']. 12 * It always uses case insensitive "natural" ordering in its collation. 13 * The fallback solution uses the primitive PHP functions that return almost the same results 14 * when the input is text with only [A-Za-z0-9] characters. 15 * 16 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 17 * @author Moisés Braga Ribeiro <moisesbr@gmail.com> 18 */ 19class Sort 20{ 21 /** @var \Collator[] language specific collators, usually only one */ 22 protected static $collator; 23 24 /** 25 * Initialization of a collator using $conf['lang'] as the locale. 26 * The initialization is done only once, except when $reload is set to true. 27 * The collation takes "natural ordering" into account, that is, "page 2" is before "page 10". 28 * 29 * @param bool $reload Usually false; true forces collator re-creation 30 * @return \Collator Returns a configured collator or null if the collator cannot be created. 31 * 32 * @author Moisés Braga Ribeiro <moisesbr@gmail.com> 33 */ 34 protected static function getCollator($reload = false) 35 { 36 global $conf; 37 $lc = $conf['lang']; 38 39 // check if intl extension is available 40 if (!class_exists('\Collator')) { 41 return null; 42 } 43 44 // load collator if not available yet 45 if ($reload || !isset(self::$collator[$lc])) { 46 $collator = \Collator::create($lc); 47 if (!isset($collator)) return null; 48 $collator->setAttribute(\Collator::NUMERIC_COLLATION, \Collator::ON); 49 self::$collator[$lc] = $collator; 50 } 51 52 return self::$collator[$lc]; 53 } 54 55 /** 56 * Drop-in replacement for strcmp(), strcasecmp(), strnatcmp() and strnatcasecmp(). 57 * It uses a collator-based comparison, or strnatcasecmp() as a fallback. 58 * 59 * @param string $str1 The first string. 60 * @param string $str2 The second string. 61 * @return int Returns < 0 if $str1 is less than $str2; > 0 if $str1 is greater than $str2, and 0 if they are equal. 62 * 63 * @author Moisés Braga Ribeiro <moisesbr@gmail.com> 64 */ 65 public static function strcmp($str1, $str2) 66 { 67 $collator = self::getCollator(); 68 if (isset($collator)) { 69 return $collator->compare($str1, $str2); 70 } else { 71 return strnatcasecmp($str1, $str2); 72 } 73 } 74 75 /** 76 * Drop-in replacement for sort(). 77 * It uses a collator-based sort, or sort() with flags SORT_NATURAL and SORT_FLAG_CASE as a fallback. 78 * 79 * @param array $array The input array. 80 * @return bool Returns true on success or false on failure. 81 * 82 * @author Moisés Braga Ribeiro <moisesbr@gmail.com> 83 */ 84 public static function sort(&$array) 85 { 86 $collator = self::getCollator(); 87 if (isset($collator)) { 88 return $collator->sort($array); 89 } else { 90 return sort($array, SORT_NATURAL | SORT_FLAG_CASE); 91 } 92 } 93 94 /** 95 * Drop-in replacement for ksort(). 96 * It uses a collator-based sort, or ksort() with flags SORT_NATURAL and SORT_FLAG_CASE as a fallback. 97 * 98 * @param array $array The input array. 99 * @return bool Returns true on success or false on failure. 100 * 101 * @author Moisés Braga Ribeiro <moisesbr@gmail.com> 102 */ 103 public static function ksort(&$array) 104 { 105 $collator = self::getCollator(); 106 if (isset($collator)) { 107 return uksort($array, array($collator, 'compare')); 108 } else { 109 return ksort($array, SORT_NATURAL | SORT_FLAG_CASE); 110 } 111 } 112 113 /** 114 * Drop-in replacement for asort(), natsort() and natcasesort(). 115 * It uses a collator-based sort, or asort() with flags SORT_NATURAL and SORT_FLAG_CASE as a fallback. 116 * 117 * @param array $array The input array. 118 * @return bool Returns true on success or false on failure. 119 * 120 * @author Moisés Braga Ribeiro <moisesbr@gmail.com> 121 */ 122 public static function asort(&$array) 123 { 124 $collator = self::getCollator(); 125 if (isset($collator)) { 126 return $collator->asort($array); 127 } else { 128 return asort($array, SORT_NATURAL | SORT_FLAG_CASE); 129 } 130 } 131 132 /** 133 * Drop-in replacement for asort(), natsort() and natcasesort() when the parameter is an array of filenames. 134 * Filenames may not be equal to page names, depending on the setting in $conf['fnencode'], 135 * so the correct behavior is to sort page names and reflect this sorting in the filename array. 136 * 137 * @param array $array The input array. 138 * @return bool Returns true on success or false on failure. 139 * 140 * @author Moisés Braga Ribeiro <moisesbr@gmail.com> 141 */ 142 public static function asortFN(&$array) 143 { 144 $collator = self::getCollator(); 145 return uasort($array, function ($fn1, $fn2) use ($collator) { 146 if (isset($collator)) { 147 return $collator->compare(utf8_decodeFN($fn1), utf8_decodeFN($fn2)); 148 } else { 149 return strnatcasecmp(utf8_decodeFN($fn1), utf8_decodeFN($fn2)); 150 } 151 }); 152 } 153 154} 155