Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
90.48% covered (success)
90.48%
57 / 63
80.00% covered (warning)
80.00%
8 / 10
CRAP
0.00% covered (danger)
0.00%
0 / 1
ZMultiLingualStringSet
90.48% covered (success)
90.48%
57 / 63
80.00% covered (warning)
80.00%
8 / 10
23.46
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
3
 getDefinition
100.00% covered (success)
100.00%
12 / 12
100.00% covered (success)
100.00%
1 / 1
1
 isValid
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
3
 getZValue
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getValueAsList
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 getAliasesForLanguageCode
66.67% covered (warning)
66.67%
4 / 6
0.00% covered (danger)
0.00%
0 / 1
3.33
 getAliasesForLanguage
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
4
 isLanguageProvidedValue
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
2
 setMonoLingualStringSet
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
1 / 1
3
 getSerialized
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2/**
3 * WikiLambda ZMultiLingualStringSet
4 *
5 * @file
6 * @ingroup Extensions
7 * @copyright 2020– Abstract Wikipedia team; see AUTHORS.txt
8 * @license MIT
9 */
10
11namespace MediaWiki\Extension\WikiLambda\ZObjects;
12
13use MediaWiki\Extension\WikiLambda\Registry\ZErrorTypeRegistry;
14use MediaWiki\Extension\WikiLambda\Registry\ZLangRegistry;
15use MediaWiki\Extension\WikiLambda\Registry\ZTypeRegistry;
16use MediaWiki\Extension\WikiLambda\ZErrorException;
17use MediaWiki\Extension\WikiLambda\ZErrorFactory;
18use MediaWiki\Extension\WikiLambda\ZObjectUtils;
19use MediaWiki\Language\Language;
20use MediaWiki\Language\LanguageFallback;
21use MediaWiki\MediaWikiServices;
22
23class ZMultiLingualStringSet extends ZObject {
24
25    /**
26     * Construct a ZMultiLingualStringSet instance given an array or a ZTypedList
27     * of ZMonoLingualStringSet instances.
28     *
29     * @param ZTypedList|array $strings
30     */
31    public function __construct( $strings = [] ) {
32        foreach ( ZObjectUtils::getIterativeList( $strings ) as $index => $monoLingualStringSet ) {
33            if ( $monoLingualStringSet instanceof ZMonoLingualStringSet ) {
34                $this->setMonoLingualStringSet( $monoLingualStringSet );
35            }
36        }
37    }
38
39    /**
40     * @inheritDoc
41     */
42    public static function getDefinition(): array {
43        return [
44            'type' => [
45                'type' => ZTypeRegistry::Z_REFERENCE,
46                'value' => ZTypeRegistry::Z_MULTILINGUALSTRINGSET,
47            ],
48            'keys' => [
49                ZTypeRegistry::Z_MULTILINGUALSTRINGSET_VALUE => [
50                    'type' => ZTypeRegistry::HACK_ARRAY_Z_MONOLINGUALSTRINGSET,
51                    'required' => true,
52                ],
53            ],
54        ];
55    }
56
57    /**
58     * @inheritDoc
59     */
60    public function isValid(): bool {
61        $stringsets = $this->data[ ZTypeRegistry::Z_MULTILINGUALSTRINGSET_VALUE ] ?? [];
62        // @phan-suppress-next-line PhanTypeSuspiciousNonTraversableForeach; it's a ZMonoLingualStringSet[]
63        foreach ( $stringsets as $lang => $monolingualString ) {
64            if ( !$monolingualString->isValid() ) {
65                return false;
66            }
67        }
68        return true;
69    }
70
71    /**
72     * Get the list of ZMonoLingualStringSets that represent the value of
73     * this ZMultiLingualStringSet
74     *
75     * @return array
76     */
77    public function getZValue() {
78        return $this->data[ ZTypeRegistry::Z_MULTILINGUALSTRINGSET_VALUE ] ?? [];
79    }
80
81    /**
82     * Get the values of this ZMultiLingualStringSet in the shape of an
83     * array with language as key and array of strings as value.
84     *
85     * @return array
86     */
87    public function getValueAsList() {
88        $multi = [];
89        foreach ( $this->getZValue() as $mono ) {
90            $multi[ $mono->getLanguage() ] = $mono->getStringSet();
91        }
92        return $multi;
93    }
94
95    /**
96     * Fetch the ZMultiLingualStringSet's stored values for a given MediaWiki language code (e.g.
97     * 'en' or 'zh-hant'). Note that this is a raw fetch and does not walk the language fallback
98     * chain; users are expected to use getStringForLanguage() which does.
99     *
100     * @param string $languageCode The MediaWiki language code in which the string is wanted.
101     * @return string[] The aliases list, possibly empty.
102     */
103    public function getAliasesForLanguageCode( string $languageCode ): array {
104        try {
105            $languageZid = ZLangRegistry::singleton()->getLanguageZidFromCode( $languageCode );
106        } catch ( ZErrorException ) {
107            return [];
108        }
109        return array_key_exists( $languageZid, $this->getZValue() )
110            ? $this->getZValue()[ $languageZid ]->getStringSet()
111            : [];
112    }
113
114    /**
115     * Fetch the ZMultiLingualStringSet's stored value for a given MediaWiki language class. This will
116     * walk the language fallback chain until there is a set value to return.
117     *
118     * @param Language $language The MediaWiki language class in which the string is wanted.
119     * @return string[] The aliases, possibly empty.
120     */
121    public function getAliasesForLanguage( Language $language ): array {
122        if ( $this->isLanguageProvidedValue( $language->getCode() ) ) {
123            return $this->getAliasesForLanguageCode( $language->getCode() );
124        }
125
126        // TODO (T362246): Dependency-inject
127        $fallbacks = MediaWikiServices::getInstance()->getLanguageFallback()->getAll(
128            $language->getCode(),
129            /* Don't try for en unless it's an accepted fallback. */ LanguageFallback::STRICT
130        );
131
132        foreach ( $fallbacks as $index => $languageCode ) {
133            if ( $this->isLanguageProvidedValue( $languageCode ) ) {
134                return $this->getAliasesForLanguageCode( $languageCode );
135            }
136        }
137
138        return [];
139    }
140
141    /**
142     * Check if the ZMultiLingualStringSet has a stored value for a given MediaWiki language code (e.g.
143     * 'en' or 'zh-hant'). Note that this is a raw check and does not walk the language fallback
144     * chain.
145     *
146     * @param string $languageCode The MediaWiki language code in which the string is wanted.
147     * @return bool If there is a list stored.
148     */
149    public function isLanguageProvidedValue( string $languageCode ): bool {
150        try {
151            $languageZid = ZLangRegistry::singleton()->getLanguageZidFromCode( $languageCode );
152        } catch ( ZErrorException ) {
153            return false;
154        }
155        $aliases = $this->data[ ZTypeRegistry::Z_MULTILINGUALSTRINGSET_VALUE ][ $languageZid ] ?? [];
156        return ( $aliases !== [] );
157    }
158
159    /**
160     * Add or replace a ZMonoLingualStringSet.
161     *
162     * @param ZMonoLingualStringSet $value The new value to set.
163     * @throws ZErrorException
164     */
165    public function setMonoLingualStringSet( ZMonoLingualStringSet $value ): void {
166        $language = $value->getLanguage();
167
168        // (T391528) Don't let bad user input trigger an odd exception.
169        if ( !$language || !is_string( $language ) ) {
170            throw new ZErrorException(
171                ZErrorFactory::createZErrorInstance(
172                    ZErrorTypeRegistry::Z_ERROR_INVALID_LANG_CODE,
173                    [
174                        'lang' => $value
175                    ]
176                )
177            );
178        }
179
180        $this->data[ ZTypeRegistry::Z_MULTILINGUALSTRINGSET_VALUE ][ $language ] = $value;
181    }
182
183    /**
184     * Convert this ZObject into its serialized canonical representation
185     *
186     * @param int $form
187     * @return \stdClass|array|string
188     */
189    public function getSerialized( $form = self::FORM_CANONICAL ) {
190        $listType = new ZReference( ZTypeRegistry::Z_MONOLINGUALSTRINGSET );
191        $typedList = new ZTypedList( ZTypedList::buildType( $listType ), array_values( $this->getZValue() ) );
192        return (object)[
193            ZTypeRegistry::Z_OBJECT_TYPE => $this->getZTypeObject()->getSerialized( $form ),
194            ZTypeRegistry::Z_MULTILINGUALSTRINGSET_VALUE => $typedList->getSerialized( $form )
195        ];
196    }
197}