Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
95.45% covered (success)
95.45%
42 / 44
71.43% covered (warning)
71.43%
5 / 7
CRAP
0.00% covered (danger)
0.00%
0 / 1
RemoteIcuCollation
95.45% covered (success)
95.45%
42 / 44
71.43% covered (warning)
71.43%
5 / 7
13
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
 getSortKey
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 encode
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 decode
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
2
 getSortKeys
100.00% covered (success)
100.00%
18 / 18
100.00% covered (success)
100.00%
1 / 1
2
 getFirstLetter
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 doGetSortKeys
88.89% covered (warning)
88.89%
8 / 9
0.00% covered (danger)
0.00%
0 / 1
4.02
1<?php
2
3use 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 */
10class 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}