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