Failed Conditions
Push — pr/3115 ( f9aa34 )
by Andreas
07:32 queued 04:11
created

Sort::getCollator()   B

Complexity

Conditions 6
Paths 4

Size

Total Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
nc 4
nop 1
dl 0
loc 20
rs 8.9777
c 0
b 0
f 0
1
<?php
2
3
namespace 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 <[email protected]>
18
 */
19
class Sort
20
{
21
    /** @var \Collator[] language specific collators, usually only one */
22
    protected static $collator = [];
23
24
    /** @var bool should the intl extension be used if available? For testing only */
25
    protected static $useIntl = true;
26
27
    /**
28
     * Initialization of a collator using $conf['lang'] as the locale.
29
     * The initialization is done only once, except when $reload is set to true.
30
     * The collation takes "natural ordering" into account, that is, "page 2" is before "page 10".
31
     *
32
     * @param bool $reload Usually false; true forces collator re-creation
33
     * @return \Collator Returns a configured collator or null if the collator cannot be created.
34
     *
35
     * @author Moisés Braga Ribeiro <[email protected]>
36
     */
37
    protected static function getCollator($reload = false)
38
    {
39
        global $conf;
40
        $lc = $conf['lang'];
41
42
        // check if intl extension is available
43
        if (!self::$useIntl || !class_exists('\Collator')) {
44
            return null;
45
        }
46
47
        // load collator if not available yet
48
        if ($reload || !isset(self::$collator[$lc])) {
49
            $collator = \Collator::create($lc);
50
            if (!isset($collator)) return null;
51
            $collator->setAttribute(\Collator::NUMERIC_COLLATION, \Collator::ON);
52
            self::$collator[$lc] = $collator;
53
        }
54
55
        return self::$collator[$lc];
56
    }
57
58
    /**
59
     * Enable or disable the use of the intl extension collator
60
     *
61
     * This is mostly used for testing and should not be used in normal code
62
     *
63
     * @param bool $use
64
     */
65
    public static function useIntl($use = true)
66
    {
67
        self::$useIntl = $use;
68
        self::$collator = [];
69
    }
70
71
    /**
72
     * Drop-in replacement for strcmp(), strcasecmp(), strnatcmp() and strnatcasecmp().
73
     * It uses a collator-based comparison, or strnatcasecmp() as a fallback.
74
     *
75
     * @param string $str1 The first string.
76
     * @param string $str2 The second string.
77
     * @return int Returns < 0 if $str1 is less than $str2; > 0 if $str1 is greater than $str2, and 0 if they are equal.
78
     *
79
     * @author Moisés Braga Ribeiro <[email protected]>
80
     */
81
    public static function strcmp($str1, $str2)
82
    {
83
        $collator = self::getCollator();
84
        if (isset($collator)) {
85
            return $collator->compare($str1, $str2);
86
        } else {
87
            return strnatcasecmp($str1, $str2);
88
        }
89
    }
90
91
    /**
92
     * Drop-in replacement for sort().
93
     * It uses a collator-based sort, or sort() with flags SORT_NATURAL and SORT_FLAG_CASE as a fallback.
94
     *
95
     * @param array $array The input array.
96
     * @return bool Returns true on success or false on failure.
97
     *
98
     * @author Moisés Braga Ribeiro <[email protected]>
99
     */
100
    public static function sort(&$array)
101
    {
102
        $collator = self::getCollator();
103
        if (isset($collator)) {
104
            return $collator->sort($array);
105
        } else {
106
            return sort($array, SORT_NATURAL | SORT_FLAG_CASE);
107
        }
108
    }
109
110
    /**
111
     * Drop-in replacement for ksort().
112
     * It uses a collator-based sort, or ksort() with flags SORT_NATURAL and SORT_FLAG_CASE as a fallback.
113
     *
114
     * @param array $array The input array.
115
     * @return bool Returns true on success or false on failure.
116
     *
117
     * @author Moisés Braga Ribeiro <[email protected]>
118
     */
119
    public static function ksort(&$array)
120
    {
121
        $collator = self::getCollator();
122
        if (isset($collator)) {
123
            return uksort($array, array($collator, 'compare'));
124
        } else {
125
            return ksort($array, SORT_NATURAL | SORT_FLAG_CASE);
126
        }
127
    }
128
129
    /**
130
     * Drop-in replacement for asort(), natsort() and natcasesort().
131
     * It uses a collator-based sort, or asort() with flags SORT_NATURAL and SORT_FLAG_CASE as a fallback.
132
     *
133
     * @param array $array The input array.
134
     * @return bool Returns true on success or false on failure.
135
     *
136
     * @author Moisés Braga Ribeiro <[email protected]>
137
     */
138
    public static function asort(&$array)
139
    {
140
        $collator = self::getCollator();
141
        if (isset($collator)) {
142
            return $collator->asort($array);
143
        } else {
144
            return asort($array, SORT_NATURAL | SORT_FLAG_CASE);
145
        }
146
    }
147
148
    /**
149
     * Drop-in replacement for asort(), natsort() and natcasesort() when the parameter is an array of filenames.
150
     * Filenames may not be equal to page names, depending on the setting in $conf['fnencode'],
151
     * so the correct behavior is to sort page names and reflect this sorting in the filename array.
152
     *
153
     * @param array $array The input array.
154
     * @return bool Returns true on success or false on failure.
155
     *
156
     * @author Moisés Braga Ribeiro <[email protected]>
157
     */
158
    public static function asortFN(&$array)
159
    {
160
        $collator = self::getCollator();
161
        return uasort($array, function ($fn1, $fn2) use ($collator) {
162
            if (isset($collator)) {
163
                return $collator->compare(utf8_decodeFN($fn1), utf8_decodeFN($fn2));
164
            } else {
165
                return strnatcasecmp(utf8_decodeFN($fn1), utf8_decodeFN($fn2));
166
            }
167
        });
168
    }
169
170
}
171