Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
93.22% covered (success)
93.22%
55 / 59
77.78% covered (warning)
77.78%
7 / 9
CRAP
0.00% covered (danger)
0.00%
0 / 1
ZErrorTypeRegistry
93.22% covered (success)
93.22%
55 / 59
77.78% covered (warning)
77.78%
7 / 9
26.21
0.00% covered (danger)
0.00%
0 / 1
 initialize
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 registerZErrorTypeLabel
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
1 / 1
3
 unregister
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
3
 isZErrorTypeKnown
100.00% covered (success)
100.00%
13 / 13
100.00% covered (success)
100.00%
1 / 1
4
 fetchZErrorType
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
3
 instanceOfZErrorType
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 isZErrorTypeCached
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
4
 isBuiltinZErrorType
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 getZErrorTypeLabel
90.00% covered (success)
90.00%
9 / 10
0.00% covered (danger)
0.00%
0 / 1
4.02
1<?php
2/**
3 * WikiLambda ZErrorTypeRegistry
4 *
5 * @file
6 * @ingroup Extensions
7 * @copyright 2020– Abstract Wikipedia team; see AUTHORS.txt
8 * @license MIT
9 */
10
11namespace MediaWiki\Extension\WikiLambda\Registry;
12
13use MediaWiki\Context\RequestContext;
14use MediaWiki\Extension\WikiLambda\WikiLambdaServices;
15use MediaWiki\Extension\WikiLambda\ZErrorException;
16use MediaWiki\Extension\WikiLambda\ZObjects\ZPersistentObject;
17use MediaWiki\MediaWikiServices;
18use ReflectionClass;
19
20/**
21 * A registry service for ZErrorType
22 */
23class ZErrorTypeRegistry extends ZObjectRegistry {
24
25    // TODO (T300512): Bring all constants (error types ZIDs) to schemata
26    public const Z_ERROR_UNKNOWN = 'Z500';
27    public const Z_ERROR_INVALID_SYNTAX = 'Z501';
28    public const Z_ERROR_NOT_WELLFORMED = 'Z502';
29    public const Z_ERROR_NOT_IMPLEMENTED_YET = 'Z503';
30    public const Z_ERROR_ZID_NOT_FOUND = 'Z504';
31    public const Z_ERROR_ARGUMENT_COUNT_MISMATCH = 'Z505';
32    public const Z_ERROR_ARGUMENT_TYPE_MISMATCH = 'Z506';
33    public const Z_ERROR_EVALUATION = 'Z507';
34    public const Z_ERROR_LIST = 'Z509';
35    public const Z_ERROR_MISSING_KEY = 'Z511';
36    public const Z_ERROR_MISSING_PERSISTENT_VALUE = 'Z513';
37    public const Z_ERROR_RETURN_TYPE_MISMATCH = 'Z517';
38    public const Z_ERROR_OBJECT_TYPE_MISMATCH = 'Z518';
39    public const Z_ERROR_UNDEFINED_LIST_TYPE = 'Z519';
40    public const Z_ERROR_WRONG_LIST_TYPE = 'Z520';
41    public const Z_ERROR_NOT_NUMBER_BOOLEAN_NULL = 'Z521';
42    public const Z_ERROR_ARRAY_ELEMENT_NOT_WELLFORMED = 'Z522';
43    public const Z_ERROR_MISSING_TYPE = 'Z523';
44    public const Z_ERROR_TYPE_NOT_STRING_ARRAY = 'Z524';
45    public const Z_ERROR_INVALID_KEY = 'Z525';
46    public const Z_ERROR_KEY_VALUE_NOT_WELLFORMED = 'Z526';
47    public const Z_ERROR_CONNECTION_FAILURE = 'Z529';
48    public const Z_ERROR_API_FAILURE = 'Z530';
49    public const Z_ERROR_STRING_VALUE_MISSING = 'Z532';
50    public const Z_ERROR_STRING_VALUE_WRONG_TYPE = 'Z533';
51    public const Z_ERROR_REFERENCE_VALUE_MISSING = 'Z535';
52    public const Z_ERROR_REFERENCE_VALUE_WRONG_TYPE = 'Z536';
53    public const Z_ERROR_REFERENCE_VALUE_INVALID = 'Z537';
54    public const Z_ERROR_WRONG_NAMESPACE = 'Z538';
55    public const Z_ERROR_WRONG_CONTENT_TYPE = 'Z539';
56    public const Z_ERROR_INVALID_LANG_CODE = 'Z540';
57    public const Z_ERROR_LANG_NOT_FOUND = 'Z541';
58    public const Z_ERROR_UNEXPECTED_ZTYPE = 'Z542';
59    public const Z_ERROR_ZTYPE_NOT_FOUND = 'Z543';
60    public const Z_ERROR_CONFLICTING_TYPE_NAMES = 'Z544';
61    public const Z_ERROR_CONFLICTING_TYPE_ZIDS = 'Z545';
62    public const Z_ERROR_BUILTIN_TYPE_NOT_FOUND = 'Z546';
63    public const Z_ERROR_INVALID_FORMAT = 'Z547';
64    public const Z_ERROR_INVALID_JSON = 'Z548';
65    public const Z_ERROR_INVALID_REFERENCE = 'Z549';
66    public const Z_ERROR_UNKNOWN_REFERENCE = 'Z550';
67    public const Z_ERROR_SCHEMA_TYPE_MISMATCH = 'Z551';
68    public const Z_ERROR_ARRAY_TYPE_MISMATCH = 'Z552';
69    public const Z_ERROR_DISALLOWED_ROOT_ZOBJECT = 'Z553';
70    public const Z_ERROR_LABEL_CLASH = 'Z554';
71    public const Z_ERROR_UNMATCHING_ZID = 'Z555';
72    public const Z_ERROR_INVALID_TITLE = 'Z556';
73    public const Z_ERROR_USER_CANNOT_EDIT = 'Z557';
74    public const Z_ERROR_USER_CANNOT_RUN = 'Z559';
75    public const Z_ERROR_INVALID_EVALUATION_RESULT = 'Z560';
76    public const Z_ERROR_ORCHESTRATOR_RATE_LIMIT = 'Z570';
77    public const Z_ERROR_EVALUATOR_RATE_LIMIT = 'Z571';
78
79    public const Z_ERROR_DUPLICATE_LANGUAGES = 'Z580';
80
81    /**
82     * Initialize ZErrorTypeRegistry
83     */
84    protected function initialize(): void {
85        // Registry for ZObjects of type ZErrorType/Z500
86        $this->type = ZTypeRegistry::Z_ERRORTYPE;
87    }
88
89    /**
90     * @param string $errorType
91     * @param ZPersistentObject $zobject
92     * @param string $languageCode
93     * @return ?string
94     */
95    protected function registerZErrorTypeLabel( $errorType, $zobject, $languageCode = 'en' ): ?string {
96        $langRegistry = ZLangRegistry::singleton();
97        if ( !$langRegistry->isLanguageKnownGivenCode( $languageCode ) ) {
98            $languageCode = 'en';
99        }
100
101        $registryKey = $errorType . ':' . $languageCode;
102
103        $language = MediaWikiServices::getInstance()
104            ->getLanguageFactory()
105            ->getLanguage( $languageCode );
106
107        $errorLabel = $zobject->getLabel( $language, true );
108        if ( $errorLabel ) {
109            $this->register( $registryKey, $errorLabel );
110        }
111
112        return $errorLabel;
113    }
114
115    /**
116     * Unregisters the given error type zid from the registry
117     * by deleting all the language entries associated for this zid
118     *
119     * @param string $zid
120     */
121    public function unregister( string $zid ): void {
122        $prefix = $zid . ':';
123        foreach ( $this->registry as $errorKey => $errorLabel ) {
124            if ( str_starts_with( $errorKey, $prefix ) ) {
125                unset( $this->registry[ $errorKey ] );
126            }
127        }
128    }
129
130    /**
131     * Check if the given ZErrorType Zid is known
132     *
133     * @param string $errorType
134     * @return bool
135     */
136    public function isZErrorTypeKnown( string $errorType ): bool {
137        if ( $this->isZErrorTypeCached( $errorType ) ) {
138            return true;
139        }
140
141        if ( self::isBuiltinZErrorType( $errorType ) ) {
142            return true;
143        }
144
145        $zobject = $this->fetchZErrorType( $errorType );
146        if ( !$zobject ) {
147            return false;
148        }
149
150        $this->registerZErrorTypeLabel(
151            $errorType,
152            $zobject,
153            RequestContext::getMain()->getLanguage()->getCode()
154        );
155
156        return true;
157    }
158
159    /**
160     * Fetches a given error type Zid from the database, throwing error if the ZErrorType does not
161     * exist or if the fetched object is not of the wanted type
162     *
163     * @param string $errorType
164     * @return ZPersistentObject|bool Found ZObject
165     */
166    private function fetchZErrorType( string $errorType ) {
167        $zObjectStore = WikiLambdaServices::getZObjectStore();
168
169        // This can throw a ZErrorException if the ZID is not found, which will bubble up
170        $zobjectContent = $zObjectStore->fetchZObject( $errorType );
171
172        // ZObjectContent->getZType is a shortcut to ZObjectContent->getInnerZObject->getZType
173        if ( !$zobjectContent || $zobjectContent->getZType() !== ZTypeRegistry::Z_ERRORTYPE ) {
174            return false;
175        }
176
177        return $zobjectContent->getZObject();
178    }
179
180    /**
181     * Check if the given Zid belongs to a ZErrorType
182     *
183     * @param string $zid
184     * @return bool
185     */
186    public function instanceOfZErrorType( string $zid ): bool {
187        try {
188            return $this->isZErrorTypeKnown( $zid );
189        } catch ( ZErrorException ) {
190            return false;
191        }
192    }
193
194    /**
195     * Check if the given Zid is an already cached ZErrorType (Z50)
196     * If a language code is given, check it the label in that language is stored
197     *
198     * @param string $errorType
199     * @param string|bool $langCode
200     * @return bool
201     */
202    private function isZErrorTypeCached( string $errorType, $langCode = false ): bool {
203        if ( $langCode ) {
204            $registryKey = $errorType . ':' . $langCode;
205            return array_key_exists( $registryKey, $this->registry );
206        }
207
208        $prefix = $errorType . ':';
209        foreach ( $this->registry as $errorKey => $errorLabel ) {
210            if ( str_starts_with( $errorKey, $prefix ) ) {
211                return true;
212            }
213        }
214
215        return false;
216    }
217
218    /**
219     * Check if the given Zid belongs to a builtin ZErrorType (from the ones declared above)
220     *
221     * @param string $errorType
222     * @return bool
223     */
224    private static function isBuiltinZErrorType( string $errorType ): bool {
225        static $lookup = null;
226        if ( $lookup === null ) {
227            $lookup = array_flip( ( new ReflectionClass( self::class ) )->getConstants() );
228        }
229        return isset( $lookup[ $errorType ] );
230    }
231
232    /**
233     * Gets the ZErrorType label in English
234     *
235     * @param string $errorType
236     * @param string $errorLanguageCode Language code, defaulting to English
237     * @return string
238     */
239    public function getZErrorTypeLabel( string $errorType, string $errorLanguageCode = 'en' ): string {
240        $registryCacheKey = $errorType . ':' . $errorLanguageCode;
241
242        if ( $this->isZErrorTypeCached( $errorType, $errorLanguageCode ) ) {
243            return $this->registry[ $registryCacheKey ];
244        }
245
246        $zobject = $this->fetchZErrorType( $errorType );
247        if ( !$zobject ) {
248            return "Unknown error $errorType";
249        }
250
251        $errorLabel = $this->registerZErrorTypeLabel( $errorType, $zobject, $errorLanguageCode );
252        if ( !$errorLabel ) {
253            return "Unknown error $errorType";
254        }
255
256        return $errorLabel;
257    }
258}