Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
69.07% |
67 / 97 |
|
86.67% |
13 / 15 |
CRAP | |
0.00% |
0 / 1 |
ZTypeRegistry | |
69.07% |
67 / 97 |
|
86.67% |
13 / 15 |
65.22 | |
0.00% |
0 / 1 |
initialize | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
register | |
19.44% |
7 / 36 |
|
0.00% |
0 / 1 |
18.07 | |||
unregister | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
2 | |||
getCachedZObjectKeys | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
isZTypeBuiltIn | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
isZFunctionBuiltIn | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getZFunctionBuiltInName | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
isZObjectKeyCached | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
isZObjectKeyKnown | |
100.00% |
13 / 13 |
|
100.00% |
1 / 1 |
4 | |||
getZObjectTypeFromKey | |
100.00% |
8 / 8 |
|
100.00% |
1 / 1 |
2 | |||
getCachedZObjectTypes | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
isZObjectTypeCached | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
isZObjectTypeKnown | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
getZObjectKeyFromType | |
100.00% |
8 / 8 |
|
100.00% |
1 / 1 |
2 | |||
isZObjectInstanceOfType | |
94.12% |
16 / 17 |
|
0.00% |
0 / 1 |
7.01 |
1 | <?php |
2 | /** |
3 | * WikiLambda ZTypeRegistry |
4 | * |
5 | * @file |
6 | * @ingroup Extensions |
7 | * @copyright 2020– Abstract Wikipedia team; see AUTHORS.txt |
8 | * @license MIT |
9 | */ |
10 | |
11 | namespace MediaWiki\Extension\WikiLambda\Registry; |
12 | |
13 | use MediaWiki\Extension\WikiLambda\WikiLambdaServices; |
14 | use MediaWiki\Extension\WikiLambda\ZErrorException; |
15 | use MediaWiki\Extension\WikiLambda\ZErrorFactory; |
16 | use MediaWiki\Extension\WikiLambda\ZObjects\ZObject; |
17 | use MediaWiki\Extension\WikiLambda\ZObjects\ZReference; |
18 | use MediaWiki\Extension\WikiLambda\ZObjectUtils; |
19 | use MediaWiki\Title\Title; |
20 | |
21 | /** |
22 | * A registry service for ZObject implementations. |
23 | */ |
24 | class ZTypeRegistry extends ZObjectRegistry { |
25 | |
26 | // Builtin Types that need special treatment while ZObject creation: |
27 | |
28 | // Needed for quote type, it can be anything |
29 | public const BUILTIN_ANY = 'Any'; |
30 | // Is there a better way to represent this direct string (as opposed to a ZString?) |
31 | public const BUILTIN_STRING = 'String'; |
32 | // Is there a better way to represent this direct array |
33 | public const BUILTIN_ARRAY = 'Array'; |
34 | // Needed until we have a ZReference type |
35 | public const BUILTIN_REFERENCE = 'Reference'; |
36 | // Needed until we have a better way to cut the Gordian Knot of Z0 references? |
37 | public const BUILTIN_REFERENCE_NULLABLE = 'NullableReference'; |
38 | // Needed until we have sub-types |
39 | public const HACK_REFERENCE_TYPE = 'Reference(Type)'; |
40 | // Needed until we have sub-types |
41 | public const HACK_REFERENCE_LANGUAGE = 'Reference(Language)'; |
42 | // Needed until we have a ZLanguage type (or similar) |
43 | public const HACK_LANGUAGE = 'Language'; |
44 | // Needed until we have sub-types |
45 | public const HACK_ARRAY_Z_KEY = 'Array(ZKey)'; |
46 | // Needed until we have sub-types |
47 | public const HACK_ARRAY_Z_STRING = 'Array(ZString)'; |
48 | // Needed until we have sub-types |
49 | public const HACK_ARRAY_Z_MONOLINGUALSTRING = 'Array(ZMonoLingualString)'; |
50 | // Needed until we have sub-types |
51 | public const HACK_ARRAY_Z_MONOLINGUALSTRINGSET = 'Array(ZMonoLingualStringSet)'; |
52 | |
53 | public const Z_NULL_REFERENCE = 'Z0'; |
54 | |
55 | public const Z_OBJECT = 'Z1'; |
56 | public const Z_OBJECT_TYPE = 'Z1K1'; |
57 | |
58 | public const Z_PERSISTENTOBJECT = 'Z2'; |
59 | public const Z_PERSISTENTOBJECT_ID = 'Z2K1'; |
60 | public const Z_PERSISTENTOBJECT_VALUE = 'Z2K2'; |
61 | public const Z_PERSISTENTOBJECT_LABEL = 'Z2K3'; |
62 | public const Z_PERSISTENTOBJECT_ALIASES = 'Z2K4'; |
63 | public const Z_PERSISTENTOBJECT_DESCRIPTION = 'Z2K5'; |
64 | |
65 | public const Z_KEY = 'Z3'; |
66 | public const Z_KEY_TYPE = 'Z3K1'; |
67 | public const Z_KEY_ID = 'Z3K2'; |
68 | public const Z_KEY_LABEL = 'Z3K3'; |
69 | public const Z_KEY_IS_IDENTITY = 'Z3K4'; |
70 | |
71 | public const Z_TYPE = 'Z4'; |
72 | public const Z_TYPE_IDENTITY = 'Z4K1'; |
73 | public const Z_TYPE_KEYS = 'Z4K2'; |
74 | public const Z_TYPE_VALIDATOR = 'Z4K3'; |
75 | public const Z_TYPE_EQUALITY = 'Z4K4'; |
76 | public const Z_TYPE_RENDERER = 'Z4K5'; |
77 | public const Z_TYPE_PARSER = 'Z4K6'; |
78 | public const Z_TYPE_DESERIALISERS = 'Z4K7'; |
79 | public const Z_TYPE_SERIALISERS = 'Z4K8'; |
80 | |
81 | public const Z_ERROR = 'Z5'; |
82 | public const Z_ERROR_TYPE = 'Z5K1'; |
83 | public const Z_ERROR_VALUE = 'Z5K2'; |
84 | |
85 | public const Z_STRING = 'Z6'; |
86 | public const Z_STRING_VALUE = 'Z6K1'; |
87 | |
88 | public const Z_FUNCTIONCALL = 'Z7'; |
89 | public const Z_FUNCTIONCALL_FUNCTION = 'Z7K1'; |
90 | |
91 | public const Z_FUNCTION = 'Z8'; |
92 | public const Z_FUNCTION_ARGUMENTS = 'Z8K1'; |
93 | public const Z_FUNCTION_RETURN_TYPE = 'Z8K2'; |
94 | public const Z_FUNCTION_TESTERS = 'Z8K3'; |
95 | public const Z_FUNCTION_IMPLEMENTATIONS = 'Z8K4'; |
96 | public const Z_FUNCTION_IDENTITY = 'Z8K5'; |
97 | |
98 | public const Z_REFERENCE = 'Z9'; |
99 | public const Z_REFERENCE_VALUE = 'Z9K1'; |
100 | |
101 | public const Z_MONOLINGUALSTRING = 'Z11'; |
102 | public const Z_MONOLINGUALSTRING_LANGUAGE = 'Z11K1'; |
103 | public const Z_MONOLINGUALSTRING_VALUE = 'Z11K2'; |
104 | |
105 | public const Z_MULTILINGUALSTRING = 'Z12'; |
106 | public const Z_MULTILINGUALSTRING_VALUE = 'Z12K1'; |
107 | |
108 | public const Z_IMPLEMENTATION = 'Z14'; |
109 | public const Z_IMPLEMENTATION_FUNCTION = 'Z14K1'; |
110 | public const Z_IMPLEMENTATION_COMPOSITION = 'Z14K2'; |
111 | public const Z_IMPLEMENTATION_CODE = 'Z14K3'; |
112 | public const Z_IMPLEMENTATION_BUILTIN = 'Z14K4'; |
113 | |
114 | public const Z_ARGUMENTDECLARATION = 'Z17'; |
115 | public const Z_ARGUMENTDECLARATION_TYPE = 'Z17K1'; |
116 | public const Z_ARGUMENTDECLARATION_ID = 'Z17K2'; |
117 | public const Z_ARGUMENTDECLARATION_LABEL = 'Z17K3'; |
118 | |
119 | public const Z_TESTER = 'Z20'; |
120 | public const Z_TESTER_FUNCTION = 'Z20K1'; |
121 | public const Z_TESTER_CALL = 'Z20K2'; |
122 | public const Z_TESTER_VALIDATION = 'Z20K3'; |
123 | |
124 | public const Z_RESPONSEENVELOPE = 'Z22'; |
125 | public const Z_RESPONSEENVELOPE_VALUE = 'Z22K1'; |
126 | public const Z_RESPONSEENVELOPE_METADATA = 'Z22K2'; |
127 | |
128 | public const Z_MONOLINGUALSTRINGSET = 'Z31'; |
129 | public const Z_MONOLINGUALSTRINGSET_LANGUAGE = 'Z31K1'; |
130 | public const Z_MONOLINGUALSTRINGSET_VALUE = 'Z31K2'; |
131 | |
132 | public const Z_MULTILINGUALSTRINGSET = 'Z32'; |
133 | public const Z_MULTILINGUALSTRINGSET_VALUE = 'Z32K1'; |
134 | |
135 | public const Z_KEYREFERENCE = 'Z39'; |
136 | public const Z_KEYREFERENCE_VALUE = 'Z39K1'; |
137 | |
138 | public const Z_DESERIALISER = 'Z46'; |
139 | public const Z_DESERIALISER_IDENTITY = 'Z46K1'; |
140 | public const Z_DESERIALISER_TYPE = 'Z46K2'; |
141 | public const Z_DESERIALISER_LANGUAGE = 'Z46K3'; |
142 | public const Z_DESERIALISER_TARGET = 'Z46K4'; |
143 | public const Z_DESERIALISER_CODE = 'Z46K5'; |
144 | |
145 | public const Z_LANGUAGE = 'Z60'; |
146 | public const Z_LANGUAGE_CODE = 'Z60K1'; |
147 | public const Z_LANGUAGE_SECONDARYCODES = 'Z60K2'; |
148 | |
149 | public const Z_PROGRAMMINGLANGUAGE = 'Z61'; |
150 | |
151 | public const Z_SERIALISER = 'Z64'; |
152 | public const Z_SERIALISER_IDENTITY = 'Z64K1'; |
153 | public const Z_SERIALISER_TYPE = 'Z64K2'; |
154 | public const Z_SERIALISER_LANGUAGE = 'Z64K3'; |
155 | public const Z_SERIALISER_SOURCE = 'Z64K4'; |
156 | public const Z_SERIALISER_CODE = 'Z64K5'; |
157 | |
158 | public const Z_QUOTE = 'Z99'; |
159 | public const Z_QUOTE_VALUE = 'Z99K1'; |
160 | |
161 | public const Z_ERRORTYPE = 'Z50'; |
162 | public const Z_ERRORTYPE_KEYS = 'Z50K1'; |
163 | |
164 | // Wikidata Entity Types |
165 | public const Z_WIKIDATA_ITEM = 'Z6001'; |
166 | public const Z_WIKIDATA_PROPERTY = 'Z6002'; |
167 | public const Z_WIKIDATA_STATEMENT = 'Z6003'; |
168 | public const Z_WIKIDATA_LEXEME_FORM = 'Z6004'; |
169 | public const Z_WIKIDATA_LEXEME = 'Z6005'; |
170 | public const Z_WIKIDATA_LEXEME_SENSE = 'Z6006'; |
171 | |
172 | public const WIKIDATA_TYPES = [ |
173 | self::Z_WIKIDATA_ITEM, |
174 | self::Z_WIKIDATA_PROPERTY, |
175 | self::Z_WIKIDATA_STATEMENT, |
176 | self::Z_WIKIDATA_LEXEME_FORM, |
177 | self::Z_WIKIDATA_LEXEME, |
178 | self::Z_WIKIDATA_LEXEME_SENSE |
179 | ]; |
180 | |
181 | // Wikidata Reference Types: |
182 | public const Z_WIKIDATA_REFERENCE_ITEM = 'Z6091'; |
183 | public const Z_WIKIDATA_REFERENCE_PROPERTY = 'Z6092'; |
184 | public const Z_WIKIDATA_REFERENCE_LEXEME_FORM = 'Z6094'; |
185 | public const Z_WIKIDATA_REFERENCE_LEXEME = 'Z6095'; |
186 | public const Z_WIKIDATA_REFERENCE_LEXEME_ID = 'Z6095K1'; |
187 | public const Z_WIKIDATA_REFERENCE_LEXEME_SENSE = 'Z6096'; |
188 | |
189 | public const WIKIDATA_REFERENCE_TYPES = [ |
190 | self::Z_WIKIDATA_REFERENCE_ITEM, |
191 | self::Z_WIKIDATA_REFERENCE_PROPERTY, |
192 | self::Z_WIKIDATA_REFERENCE_LEXEME_FORM, |
193 | self::Z_WIKIDATA_REFERENCE_LEXEME, |
194 | self::Z_WIKIDATA_REFERENCE_LEXEME_SENSE |
195 | ]; |
196 | |
197 | // Wikidata Entity Fetch Functions: |
198 | public const Z_WIKIDATA_FETCH_LEXEME_FORM = 'Z6824'; |
199 | public const Z_WIKIDATA_FETCH_LEXEME_FORM_ID = 'Z6824K1'; |
200 | public const Z_WIKIDATA_FETCH_LEXEME = 'Z6825'; |
201 | public const Z_WIKIDATA_FETCH_LEXEME_ID = 'Z6825K1'; |
202 | |
203 | public const WIKIDATA_FETCH_FUNCTIONS = [ |
204 | self::Z_WIKIDATA_FETCH_LEXEME_FORM, |
205 | self::Z_WIKIDATA_FETCH_LEXEME |
206 | ]; |
207 | |
208 | // Keep in sync with function-schemata's `typesBuiltIntoWikiLambda` |
209 | private const BUILT_IN_TYPES = [ |
210 | self::Z_OBJECT => 'ZObject', |
211 | self::Z_PERSISTENTOBJECT => 'ZPersistentObject', |
212 | self::Z_KEY => 'ZKey', |
213 | self::Z_ERROR => 'ZError', |
214 | self::Z_TYPE => 'ZType', |
215 | self::Z_STRING => 'ZString', |
216 | self::Z_FUNCTIONCALL => 'ZFunctionCall', |
217 | self::Z_FUNCTION => 'ZFunction', |
218 | self::Z_REFERENCE => 'ZReference', |
219 | self::Z_MONOLINGUALSTRING => 'ZMonoLingualString', |
220 | self::Z_MULTILINGUALSTRING => 'ZMultiLingualString', |
221 | self::Z_RESPONSEENVELOPE => 'ZResponseEnvelope', |
222 | self::Z_MONOLINGUALSTRINGSET => 'ZMonoLingualStringSet', |
223 | self::Z_MULTILINGUALSTRINGSET => 'ZMultiLingualStringSet', |
224 | self::Z_KEYREFERENCE => 'ZKeyReference', |
225 | self::Z_BOOLEAN => 'ZBoolean', |
226 | self::Z_QUOTE => 'ZQuote', |
227 | ]; |
228 | |
229 | public const TERMINAL_KEYS = [ |
230 | self::Z_STRING_VALUE, |
231 | self::Z_REFERENCE_VALUE, |
232 | self::Z_QUOTE_VALUE |
233 | ]; |
234 | |
235 | /** |
236 | * An array of ZTypes which are prohibited from creation by any user. (T278175) |
237 | */ |
238 | public const DISALLOWED_ROOT_ZOBJECTS = [ |
239 | self::Z_PERSISTENTOBJECT, |
240 | self::Z_KEY, |
241 | self::Z_REFERENCE, |
242 | self::Z_ARGUMENTDECLARATION, |
243 | self::Z_ARGUMENTREFERENCE, |
244 | self::Z_KEYREFERENCE, |
245 | self::Z_ERROR, |
246 | self::Z_CODE, |
247 | // Wikidata types |
248 | ...self::WIKIDATA_TYPES, |
249 | ...self::WIKIDATA_REFERENCE_TYPES |
250 | // TODO (T309302): Uncomment when fixed Z24 insertion issue |
251 | // self::Z_UNIT, |
252 | ]; |
253 | |
254 | public const EXCLUDE_TYPES_FROM_ENUMS = [ |
255 | self::Z_TYPE, |
256 | self::Z_FUNCTION, |
257 | self::Z_DESERIALISER, |
258 | self::Z_SERIALISER |
259 | ]; |
260 | |
261 | public const IGNORE_KEY_VALUES_FOR_LABELLING = [ |
262 | self::Z_QUOTE_VALUE, |
263 | self::Z_KEYREFERENCE_VALUE, |
264 | self::Z_KEY_ID, |
265 | self::Z_PERSISTENTOBJECT_ID, |
266 | self::Z_TYPE_IDENTITY, |
267 | ]; |
268 | |
269 | public const SELF_REFERENTIAL_KEYS = [ |
270 | self::Z_TYPE_IDENTITY, |
271 | self::Z_PERSISTENTOBJECT_ID, |
272 | self::Z_FUNCTION_IDENTITY |
273 | ]; |
274 | |
275 | // These consts are currently only used by ZObjectStore to prohibit creation, and are not (yet) built-in. |
276 | public const Z_CODE = 'Z16'; |
277 | public const Z_CODE_LANGUAGE = 'Z16K1'; |
278 | public const Z_CODE_CODE = 'Z16K2'; |
279 | public const Z_ARGUMENTREFERENCE = 'Z18'; |
280 | public const Z_UNIT = 'Z21'; |
281 | public const Z_NULL = 'Z23'; |
282 | |
283 | // These are provided for ease of use |
284 | public const Z_VOID = 'Z24'; |
285 | public const Z_VOID_INSTANCE = [ self::Z_OBJECT_TYPE => self::Z_UNIT ]; |
286 | |
287 | public const Z_BOOLEAN = 'Z40'; |
288 | public const Z_BOOLEAN_VALUE = 'Z40K1'; |
289 | public const Z_BOOLEAN_TRUE = 'Z41'; |
290 | public const Z_BOOLEAN_FALSE = 'Z42'; |
291 | |
292 | public const IGNORE_KEY_NORMALIZATION = [ |
293 | self::Z_OBJECT_TYPE, |
294 | self::Z_MONOLINGUALSTRING_LANGUAGE, |
295 | self::Z_MONOLINGUALSTRING_VALUE, |
296 | self::Z_MULTILINGUALSTRING_VALUE |
297 | ]; |
298 | |
299 | public const Z_FUNCTION_TYPED_LIST = 'Z881'; |
300 | public const Z_FUNCTION_TYPED_LIST_TYPE = 'Z881K1'; |
301 | |
302 | public const Z_FUNCTION_TYPED_PAIR = 'Z882'; |
303 | public const Z_FUNCTION_TYPED_FIRST_TYPE = 'Z882K1'; |
304 | public const Z_FUNCTION_TYPED_SECOND_TYPE = 'Z882K2'; |
305 | |
306 | public const Z_FUNCTION_TYPED_MAP = 'Z883'; |
307 | public const Z_FUNCTION_TYPED_MAP_KEY_TYPE = 'Z883K1'; |
308 | public const Z_FUNCTION_TYPED_MAP_VALUE_TYPE = 'Z883K2'; |
309 | |
310 | public const Z_FUNCTION_ERRORTYPE_TO_TYPE = 'Z885'; |
311 | public const Z_FUNCTION_ERRORTYPE_TYPE = 'Z885K1'; |
312 | |
313 | // These are built-in functions that return a type, so we will |
314 | // always find them as ZObject type values (Z1K1) |
315 | public const BUILT_IN_TYPE_FUNCTIONS = [ |
316 | self::Z_FUNCTION_TYPED_LIST => 'ZTypedList', |
317 | self::Z_FUNCTION_TYPED_PAIR => 'ZTypedPair', |
318 | self::Z_FUNCTION_TYPED_MAP => 'ZTypedMap', |
319 | self::Z_FUNCTION_ERRORTYPE_TO_TYPE => 'ZTypedError' |
320 | ]; |
321 | |
322 | /** |
323 | * Initialize ZTypeRegistry |
324 | */ |
325 | protected function initialize(): void { |
326 | // Registry for ZObjects of type ZType/Z4 |
327 | $this->type = self::Z_TYPE; |
328 | |
329 | foreach ( self::BUILT_IN_TYPES as $zKey => $classname ) { |
330 | $this->register( $zKey, $classname ); |
331 | } |
332 | } |
333 | |
334 | /** |
335 | * Registers the given ZType id and name in the type cache. |
336 | * |
337 | * @param string $key |
338 | * @param string $type |
339 | * @throws ZErrorException |
340 | */ |
341 | public function register( string $key, string $type ): void { |
342 | if ( $this->isZObjectKeyCached( $key ) ) { |
343 | $conflictingType = $this->getZObjectKeyFromType( $key ); |
344 | throw new ZErrorException( |
345 | ZErrorFactory::createZErrorInstance( |
346 | ZErrorTypeRegistry::Z_ERROR_CONFLICTING_TYPE_NAMES, |
347 | [ |
348 | 'zid' => $key, |
349 | 'name' => $type, |
350 | 'existing' => $conflictingType |
351 | ] |
352 | ) |
353 | ); |
354 | } |
355 | |
356 | if ( $this->isZObjectTypeCached( $type ) ) { |
357 | $conflictingKey = $this->getZObjectTypeFromKey( $type ); |
358 | throw new ZErrorException( |
359 | ZErrorFactory::createZErrorInstance( |
360 | ZErrorTypeRegistry::Z_ERROR_CONFLICTING_TYPE_ZIDS, |
361 | [ |
362 | 'zid' => $key, |
363 | 'name' => $type, |
364 | 'existing' => $conflictingKey |
365 | ] |
366 | ) |
367 | ); |
368 | } |
369 | |
370 | if ( |
371 | $this->isZTypeBuiltIn( $key ) |
372 | && !class_exists( 'MediaWiki\Extension\WikiLambda\ZObjects\\' . self::BUILT_IN_TYPES[ $key ] ) |
373 | ) { |
374 | throw new ZErrorException( |
375 | ZErrorFactory::createZErrorInstance( |
376 | ZErrorTypeRegistry::Z_ERROR_CONFLICTING_TYPE_ZIDS, |
377 | [ |
378 | 'zid' => $key, |
379 | 'name' => $type |
380 | ] |
381 | ) |
382 | ); |
383 | } |
384 | |
385 | $this->registry[ $key ] = $type; |
386 | } |
387 | |
388 | /** |
389 | * Removes the given ZType from the type cache, except for builtin types. |
390 | * |
391 | * @param string $key |
392 | */ |
393 | public function unregister( string $key ): void { |
394 | if ( !array_key_exists( $key, self::BUILT_IN_TYPES ) ) { |
395 | unset( $this->registry[ $key ] ); |
396 | } |
397 | } |
398 | |
399 | /** |
400 | * Get the array of the keys of the ZTypes stored in the cache |
401 | * |
402 | * @return string[] Keys of the ZTypes stored in the cache |
403 | */ |
404 | public function getCachedZObjectKeys(): array { |
405 | return array_keys( $this->registry ); |
406 | } |
407 | |
408 | /** |
409 | * Whether the provided ZType is 'built-in' to the WikiLambda extension, and thus its validator |
410 | * is provided in PHP code. |
411 | * |
412 | * @param string $key The key of the ZType to check. |
413 | * @return bool |
414 | */ |
415 | public function isZTypeBuiltIn( string $key ): bool { |
416 | return array_key_exists( $key, self::BUILT_IN_TYPES ); |
417 | } |
418 | |
419 | /** |
420 | * Whether the provided ZFunction is 'built-in' to the WikiLambda extension, and thus its validator |
421 | * is provided in PHP code. |
422 | * |
423 | * @param string $key The key of the ZFunction to check. |
424 | * @return bool |
425 | */ |
426 | public function isZFunctionBuiltIn( string $key ): bool { |
427 | return array_key_exists( $key, self::BUILT_IN_TYPE_FUNCTIONS ); |
428 | } |
429 | |
430 | /** |
431 | * Returns the class name given a built-in function Zid |
432 | * |
433 | * @param string $zid The zid of the built-in ZFunction |
434 | * @return ?string Class name for the built-in, or null if not known |
435 | */ |
436 | public function getZFunctionBuiltInName( string $zid ): ?string { |
437 | return self::BUILT_IN_TYPE_FUNCTIONS[ $zid ] ?? null; |
438 | } |
439 | |
440 | /** |
441 | * Whether the provided ZType is cached. |
442 | * |
443 | * @param string $key The key of the ZType to check |
444 | * @return bool |
445 | */ |
446 | public function isZObjectKeyCached( string $key ): bool { |
447 | return in_array( $key, $this->getCachedZObjectKeys(), true ); |
448 | } |
449 | |
450 | /** |
451 | * Whether the provided ZType is known, either because it's a registered type |
452 | * or because it's persisted in the database. If the type is not yet cached but |
453 | * it's a valid type, it registers it. |
454 | * |
455 | * @param string $key The key of the ZType to check |
456 | * @return bool |
457 | */ |
458 | public function isZObjectKeyKnown( string $key ): bool { |
459 | if ( $this->isZObjectKeyCached( $key ) ) { |
460 | return true; |
461 | } |
462 | |
463 | $title = Title::newFromText( $key, NS_MAIN ); |
464 | |
465 | // TODO (T300530): This is quite expensive. Store this in a metadata DB table, instead of fetching it live? |
466 | $zObjectStore = WikiLambdaServices::getZObjectStore(); |
467 | $content = $zObjectStore->fetchZObjectByTitle( $title ); |
468 | |
469 | if ( $content === false ) { |
470 | return false; |
471 | } |
472 | |
473 | // (T374241, T375065) Check that the object is a type without running validation |
474 | // to avoid going into an infinite loop: |
475 | $zObject = $content->getObject(); |
476 | $innerType = $zObject->{ self::Z_PERSISTENTOBJECT_VALUE }->{ self::Z_OBJECT_TYPE }; |
477 | if ( $innerType !== self::Z_TYPE ) { |
478 | return false; |
479 | } |
480 | |
481 | // We just need to store the index in the registry for newly fetched objects |
482 | $this->register( $key, $key ); |
483 | |
484 | return true; |
485 | } |
486 | |
487 | /** |
488 | * Returns the ZType class name given its ZID |
489 | * |
490 | * @param string $key The key of the ZType to check |
491 | * @return string Class name for the ZType |
492 | * @throws ZErrorException |
493 | */ |
494 | public function getZObjectTypeFromKey( string $key ): string { |
495 | if ( !$this->isZObjectKeyKnown( $key ) ) { |
496 | // Error Z504: Zid not found |
497 | throw new ZErrorException( |
498 | ZErrorFactory::createZErrorInstance( |
499 | ZErrorTypeRegistry::Z_ERROR_ZID_NOT_FOUND, |
500 | [ 'data' => $key ] |
501 | ) |
502 | ); |
503 | } |
504 | return $this->registry[ $key ]; |
505 | } |
506 | |
507 | /** |
508 | * Returns the array of names of the cached ZTypes. |
509 | * |
510 | * @return string[] Array of cached ZTypes |
511 | */ |
512 | public function getCachedZObjectTypes(): array { |
513 | return array_values( $this->registry ); |
514 | } |
515 | |
516 | /** |
517 | * Whether the given ZType is saved in the cache. |
518 | * |
519 | * @param string $type Name of the ZType |
520 | * @return bool |
521 | */ |
522 | public function isZObjectTypeCached( string $type ): bool { |
523 | return in_array( $type, $this->getCachedZObjectTypes(), true ); |
524 | } |
525 | |
526 | /** |
527 | * Whether the given ZType is known, either because it's saved in the cache or because |
528 | * it is persisted in the database. |
529 | * |
530 | * @param string $type Name of the ZType |
531 | * @return bool |
532 | */ |
533 | public function isZObjectTypeKnown( string $type ): bool { |
534 | if ( $this->isZObjectTypeCached( $type ) ) { |
535 | return true; |
536 | } |
537 | |
538 | // TODO (T300530): The registry is just a cache; also walk the DB to find if this type is registered. |
539 | return false; |
540 | } |
541 | |
542 | /** |
543 | * Returns the ZType id of a given type. |
544 | * |
545 | * @param string $type Name of the ZType |
546 | * @return string ZID of the ZType |
547 | * @throws ZErrorException |
548 | */ |
549 | public function getZObjectKeyFromType( string $type ): string { |
550 | if ( !$this->isZObjectTypeKnown( $type ) ) { |
551 | // Error Z543: ZType not found |
552 | throw new ZErrorException( |
553 | ZErrorFactory::createZErrorInstance( |
554 | ZErrorTypeRegistry::Z_ERROR_ZTYPE_NOT_FOUND, |
555 | [ 'type' => $type ] |
556 | ) |
557 | ); |
558 | } |
559 | return array_search( $type, $this->registry ); |
560 | } |
561 | |
562 | /** |
563 | * Checks if a given ZObject is an instance of a given type. |
564 | * |
565 | * @param ZObject $object |
566 | * @param string $type ZID of the type to test |
567 | * @return bool |
568 | * @throws ZErrorException |
569 | */ |
570 | public function isZObjectInstanceOfType( ZObject $object, string $type ): bool { |
571 | if ( !$this->isZObjectKeyKnown( $type ) ) { |
572 | // Error Z504: ZID not found |
573 | throw new ZErrorException( |
574 | ZErrorFactory::createZErrorInstance( |
575 | ZErrorTypeRegistry::Z_ERROR_ZID_NOT_FOUND, |
576 | [ 'data' => $type ] |
577 | ) |
578 | ); |
579 | } |
580 | |
581 | if ( $type === self::Z_OBJECT || $type === $object->getZType() ) { |
582 | return true; |
583 | } |
584 | |
585 | if ( !( $object instanceof ZReference ) ) { |
586 | return false; |
587 | } |
588 | |
589 | if ( !ZObjectUtils::isValidZObjectReference( $object->getZValue() ) ) { |
590 | return false; |
591 | } |
592 | |
593 | $zObjectStore = WikiLambdaServices::getZObjectStore(); |
594 | $objectTitle = Title::newFromText( $object->getZValue(), NS_MAIN ); |
595 | $fetchedObject = $zObjectStore->fetchZObjectByTitle( $objectTitle ); |
596 | |
597 | return $fetchedObject && $type === $fetchedObject->getZType(); |
598 | } |
599 | } |