Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
97.22% covered (success)
97.22%
35 / 36
75.00% covered (warning)
75.00%
3 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
InterwikiSorter
97.22% covered (success)
97.22%
35 / 36
75.00% covered (warning)
75.00%
3 / 4
16
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 sortLinks
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
4
 compareLinks
92.31% covered (success)
92.31%
12 / 13
0.00% covered (danger)
0.00%
0 / 1
7.02
 buildSortOrder
100.00% covered (success)
100.00%
12 / 12
100.00% covered (success)
100.00%
1 / 1
4
1<?php
2
3namespace InterwikiSorting;
4
5/**
6 * Language sorting utility functions.
7 *
8 * @license GPL-2.0-or-later
9 * @author Nikola Smolenski <smolensk@eunet.rs>
10 * @author Katie Filbert < aude.wiki@gmail.com >
11 * @author Thiemo Kreuz
12 */
13class InterwikiSorter {
14
15    private const SORT_CODE = 'code';
16
17    /**
18     * @var array[]
19     */
20    private $sortOrders;
21
22    /**
23     * @var string
24     */
25    private $sort;
26
27    /**
28     * @var string[]
29     */
30    private $sortPrepend;
31
32    /**
33     * @var int[]|null
34     */
35    private $sortOrder = null;
36
37    /**
38     * @param string $sort
39     * @param array[] $sortOrders
40     * @param string[] $sortPrepend
41     */
42    public function __construct( $sort, array $sortOrders = [], array $sortPrepend = [] ) {
43        $this->sort = $sort;
44        $this->sortOrders = $sortOrders;
45        $this->sortPrepend = $sortPrepend;
46    }
47
48    /**
49     * Sort an array of links in-place
50     * Copied from InterlanguageExtension rev 114818
51     *
52     * @param string[] $links
53     *
54     * @return string[]
55     */
56    public function sortLinks( array $links ) {
57        if ( $this->sortOrder === null ) {
58            $this->sortOrder = $this->buildSortOrder( $this->sort, $this->sortOrders );
59        }
60
61        // Prepare the array for sorting.
62        foreach ( $links as $k => $langLink ) {
63            $links[$k] = explode( ':', $langLink, 2 );
64        }
65
66        usort( $links, [ $this, 'compareLinks' ] );
67
68        // Restore the sorted array.
69        foreach ( $links as $k => $langLink ) {
70            $links[$k] = implode( ':', $langLink );
71        }
72
73        return $links;
74    }
75
76    /**
77     * usort() callback function, compares the links on the basis of $sortOrder
78     *
79     * @param string[] $a
80     * @param string[] $b
81     *
82     * @return int
83     */
84    private function compareLinks( array $a, array $b ) {
85        $a = $a[0];
86        $b = $b[0];
87
88        if ( $a === $b ) {
89            return 0;
90        }
91
92        $aIndex = array_key_exists( $a, $this->sortOrder ) ? $this->sortOrder[$a] : null;
93        $bIndex = array_key_exists( $b, $this->sortOrder ) ? $this->sortOrder[$b] : null;
94
95        if ( $aIndex === $bIndex ) {
96            // If we encounter multiple unknown languages, which may happen if the sort table is not
97            // updated, we list them alphabetically.
98            return strcmp( $a, $b );
99        } elseif ( $aIndex === null ) {
100            // Unknown languages must go under the known languages.
101            return 1;
102        } elseif ( $bIndex === null ) {
103            return -1;
104        } else {
105            return $aIndex - $bIndex;
106        }
107    }
108
109    /**
110     * Build sort order to be used by compareLinks().
111     *
112     * @param string $sort
113     * @param array[] $sortOrders
114     *
115     * @return int[]
116     */
117    private function buildSortOrder( $sort, array $sortOrders ) {
118        if ( $sort === self::SORT_CODE ) {
119            // The concept of known/unknown languages is irrelevant in strict code order.
120            $sortOrder = [];
121        } elseif ( !array_key_exists( $sort, $sortOrders ) ) {
122            // Something went wrong, but we can use default "code" order.
123            wfDebugLog(
124                __CLASS__,
125                __FUNCTION__ . ': Invalid or unknown sort order specified for interwiki links.'
126            );
127            $sortOrder = [];
128        } else {
129            $sortOrder = $sortOrders[$sort];
130        }
131
132        if ( $this->sortPrepend !== [] ) {
133            $sortOrder = array_unique( array_merge( $this->sortPrepend, $sortOrder ) );
134        }
135
136        return array_flip( $sortOrder );
137    }
138
139}