Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
95.45% |
42 / 44 |
|
71.43% |
5 / 7 |
CRAP | |
0.00% |
0 / 1 |
RemoteIcuCollation | |
95.45% |
42 / 44 |
|
71.43% |
5 / 7 |
13 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
1 | |||
getSortKey | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
encode | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
2 | |||
decode | |
100.00% |
8 / 8 |
|
100.00% |
1 / 1 |
2 | |||
getSortKeys | |
100.00% |
18 / 18 |
|
100.00% |
1 / 1 |
2 | |||
getFirstLetter | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
doGetSortKeys | |
88.89% |
8 / 9 |
|
0.00% |
0 / 1 |
4.02 |
1 | <?php |
2 | |
3 | use MediaWiki\Shell\ShellboxClientFactory; |
4 | |
5 | /** |
6 | * An ICU collation that uses a remote server to compute sort keys. This can be |
7 | * used in conjunction with $wgTempCategoryCollations to migrate to a different |
8 | * version of ICU. |
9 | */ |
10 | class RemoteIcuCollation extends Collation { |
11 | private $rpcClient; |
12 | private $locale; |
13 | |
14 | /** |
15 | * @param ShellboxClientFactory $shellboxClientFactory |
16 | * @param string $locale |
17 | */ |
18 | public function __construct( ShellboxClientFactory $shellboxClientFactory, $locale ) { |
19 | $this->rpcClient = $shellboxClientFactory->getRpcClient( |
20 | [ 'service' => 'icu-collation' ] ); |
21 | $this->locale = $locale; |
22 | } |
23 | |
24 | public function getSortKey( $string ) { |
25 | return $this->getSortKeys( [ $string ] )[0]; |
26 | } |
27 | |
28 | /** |
29 | * Encode an array of binary strings as a string |
30 | * |
31 | * @param string[] $strings |
32 | * @return string |
33 | */ |
34 | private static function encode( $strings ) { |
35 | $ret = ''; |
36 | foreach ( $strings as $s ) { |
37 | $ret .= sprintf( "%08x", strlen( $s ) ) . $s; |
38 | } |
39 | return $ret; |
40 | } |
41 | |
42 | /** |
43 | * Decode the value returned by encode() |
44 | * |
45 | * @param string $blob |
46 | * @return string[] |
47 | */ |
48 | private static function decode( $blob ) { |
49 | $p = 0; |
50 | $ret = []; |
51 | while ( $p < strlen( $blob ) ) { |
52 | $len = intval( substr( $blob, $p, 8 ), 16 ); |
53 | $p += 8; |
54 | $ret[] = substr( $blob, $p, $len ); |
55 | $p += $len; |
56 | } |
57 | return $ret; |
58 | } |
59 | |
60 | public function getSortKeys( $strings ) { |
61 | if ( !count( $strings ) ) { |
62 | return []; |
63 | } |
64 | $blob = $this->rpcClient->call( |
65 | 'icu-collation', |
66 | self::class . '::' . 'doGetSortKeys', |
67 | [ |
68 | $this->locale, |
69 | self::encode( array_values( $strings ) ) |
70 | ], |
71 | [ |
72 | 'classes' => [ parent::class, self::class ], |
73 | 'binary' => true |
74 | ] |
75 | ); |
76 | return array_combine( |
77 | array_keys( $strings ), |
78 | self::decode( $blob ) |
79 | ); |
80 | } |
81 | |
82 | public function getFirstLetter( $string ) { |
83 | // @phan-suppress-previous-line PhanPluginNeverReturnMethod |
84 | throw new RuntimeException( __METHOD__ . ': not implemented' ); |
85 | } |
86 | |
87 | /** |
88 | * The remote entry point. Get sort keys for an encoded list of inputs. |
89 | * |
90 | * @param string $locale The ICU locale |
91 | * @param string $blob The input array encoded with encode() |
92 | * @return string The encoded result |
93 | */ |
94 | public static function doGetSortKeys( $locale, $blob ) { |
95 | $mainCollator = Collator::create( $locale ); |
96 | if ( !$mainCollator ) { |
97 | throw new RuntimeException( "Invalid ICU locale specified for collation: $locale" ); |
98 | } |
99 | |
100 | // If the special suffix for numeric collation is present, turn on numeric collation. |
101 | if ( str_ends_with( $locale, '-u-kn' ) ) { |
102 | $mainCollator->setAttribute( Collator::NUMERIC_COLLATION, Collator::ON ); |
103 | } |
104 | $ret = []; |
105 | foreach ( self::decode( $blob ) as $string ) { |
106 | $ret[] = $mainCollator->getSortKey( $string ); |
107 | } |
108 | return self::encode( $ret ); |
109 | } |
110 | } |