Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
| Total | |
66.67% |
68 / 102 |
|
80.00% |
12 / 15 |
CRAP | |
0.00% |
0 / 1 |
| ZTypeRegistry | |
66.67% |
68 / 102 |
|
80.00% |
12 / 15 |
80.37 | |
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 | |
77.78% |
14 / 18 |
|
0.00% |
0 / 1 |
6.40 | |||
| 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_HTML_FRAGMENT = 'Z89'; |
| 159 | public const Z_HTML_FRAGMENT_VALUE = 'Z89K1'; |
| 160 | |
| 161 | public const Z_QUOTE = 'Z99'; |
| 162 | public const Z_QUOTE_VALUE = 'Z99K1'; |
| 163 | |
| 164 | public const Z_ERRORTYPE = 'Z50'; |
| 165 | public const Z_ERRORTYPE_KEYS = 'Z50K1'; |
| 166 | public const Z_ERRORTYPE_ID = 'Z50K2'; |
| 167 | |
| 168 | // Wikidata Entity Types |
| 169 | public const Z_WIKIDATA_ITEM = 'Z6001'; |
| 170 | public const Z_WIKIDATA_PROPERTY = 'Z6002'; |
| 171 | public const Z_WIKIDATA_STATEMENT = 'Z6003'; |
| 172 | public const Z_WIKIDATA_LEXEME_FORM = 'Z6004'; |
| 173 | public const Z_WIKIDATA_LEXEME = 'Z6005'; |
| 174 | public const Z_WIKIDATA_LEXEME_SENSE = 'Z6006'; |
| 175 | |
| 176 | public const WIKIDATA_TYPES = [ |
| 177 | self::Z_WIKIDATA_ITEM, |
| 178 | self::Z_WIKIDATA_PROPERTY, |
| 179 | self::Z_WIKIDATA_STATEMENT, |
| 180 | self::Z_WIKIDATA_LEXEME, |
| 181 | self::Z_WIKIDATA_LEXEME_FORM, |
| 182 | self::Z_WIKIDATA_LEXEME_SENSE |
| 183 | ]; |
| 184 | |
| 185 | // Wikidata Reference Types: |
| 186 | public const Z_WIKIDATA_REFERENCE_ITEM = 'Z6091'; |
| 187 | public const Z_WIKIDATA_REFERENCE_ITEM_ID = 'Z6091K1'; |
| 188 | public const Z_WIKIDATA_REFERENCE_PROPERTY = 'Z6092'; |
| 189 | public const Z_WIKIDATA_REFERENCE_PROPERTY_ID = 'Z6092K1'; |
| 190 | public const Z_WIKIDATA_REFERENCE_LEXEME_FORM = 'Z6094'; |
| 191 | public const Z_WIKIDATA_REFERENCE_LEXEME_FORM_ID = 'Z6094K1'; |
| 192 | public const Z_WIKIDATA_REFERENCE_LEXEME = 'Z6095'; |
| 193 | public const Z_WIKIDATA_REFERENCE_LEXEME_ID = 'Z6095K1'; |
| 194 | public const Z_WIKIDATA_REFERENCE_LEXEME_SENSE = 'Z6096'; |
| 195 | public const Z_WIKIDATA_REFERENCE_LEXEME_SENSE_ID = 'Z6096K1'; |
| 196 | |
| 197 | public const WIKIDATA_REFERENCE_TYPES = [ |
| 198 | self::Z_WIKIDATA_REFERENCE_ITEM, |
| 199 | self::Z_WIKIDATA_REFERENCE_PROPERTY, |
| 200 | self::Z_WIKIDATA_REFERENCE_LEXEME, |
| 201 | self::Z_WIKIDATA_REFERENCE_LEXEME_FORM, |
| 202 | self::Z_WIKIDATA_REFERENCE_LEXEME_SENSE |
| 203 | ]; |
| 204 | |
| 205 | // Wikidata Entity Fetch Functions: |
| 206 | public const Z_WIKIDATA_FETCH_ITEM = 'Z6821'; |
| 207 | public const Z_WIKIDATA_FETCH_ITEM_ID = 'Z6821K1'; |
| 208 | public const Z_WIKIDATA_FETCH_PROPERTY = 'Z6822'; |
| 209 | public const Z_WIKIDATA_FETCH_PROPERTY_ID = 'Z6822K1'; |
| 210 | public const Z_WIKIDATA_FETCH_LEXEME_FORM = 'Z6824'; |
| 211 | public const Z_WIKIDATA_FETCH_LEXEME_FORM_ID = 'Z6824K1'; |
| 212 | public const Z_WIKIDATA_FETCH_LEXEME = 'Z6825'; |
| 213 | public const Z_WIKIDATA_FETCH_LEXEME_ID = 'Z6825K1'; |
| 214 | public const Z_WIKIDATA_FETCH_LEXEME_SENSE = 'Z6826'; |
| 215 | public const Z_WIKIDATA_FETCH_LEXEME_SENSE_ID = 'Z6826K1'; |
| 216 | |
| 217 | // Wikidata enum |
| 218 | public const Z_WIKIDATA_ENUM = 'Z6884'; |
| 219 | |
| 220 | // Abstract Wiki |
| 221 | public const Z_RUN_ABSTRACT_FRAGMENT = 'Z825'; |
| 222 | public const Z_RUN_ABSTRACT_FRAGMENT_QID = 'Z825K1'; |
| 223 | public const Z_RUN_ABSTRACT_FRAGMENT_LANGUAGE = 'Z825K2'; |
| 224 | public const Z_RUN_ABSTRACT_FRAGMENT_DATE = 'Z825K3'; |
| 225 | |
| 226 | public const Z_DATE_PARSER = 'Z20808'; |
| 227 | public const Z_DATE_PARSER_STRING = 'Z20808K1'; |
| 228 | public const Z_DATE_PARSER_LANGUAGE = 'Z20808K2'; |
| 229 | |
| 230 | /** |
| 231 | * DRY mapping for Wikidata entity types to their fetch function and reference key. |
| 232 | * |
| 233 | * @internal |
| 234 | */ |
| 235 | public const WIKIDATA_ENTITY_TYPE_MAP = [ |
| 236 | self::Z_WIKIDATA_ITEM => [ |
| 237 | 'fetch_function' => self::Z_WIKIDATA_FETCH_ITEM, |
| 238 | 'fetch_key' => self::Z_WIKIDATA_FETCH_ITEM_ID, |
| 239 | 'reference_type' => self::Z_WIKIDATA_REFERENCE_ITEM, |
| 240 | 'reference_key' => self::Z_WIKIDATA_REFERENCE_ITEM_ID, |
| 241 | ], |
| 242 | self::Z_WIKIDATA_PROPERTY => [ |
| 243 | 'fetch_function' => self::Z_WIKIDATA_FETCH_PROPERTY, |
| 244 | 'fetch_key' => self::Z_WIKIDATA_FETCH_PROPERTY_ID, |
| 245 | 'reference_type' => self::Z_WIKIDATA_REFERENCE_PROPERTY, |
| 246 | 'reference_key' => self::Z_WIKIDATA_REFERENCE_PROPERTY_ID, |
| 247 | ], |
| 248 | self::Z_WIKIDATA_LEXEME => [ |
| 249 | 'fetch_function' => self::Z_WIKIDATA_FETCH_LEXEME, |
| 250 | 'fetch_key' => self::Z_WIKIDATA_FETCH_LEXEME_ID, |
| 251 | 'reference_type' => self::Z_WIKIDATA_REFERENCE_LEXEME, |
| 252 | 'reference_key' => self::Z_WIKIDATA_REFERENCE_LEXEME_ID, |
| 253 | ], |
| 254 | self::Z_WIKIDATA_LEXEME_FORM => [ |
| 255 | 'fetch_function' => self::Z_WIKIDATA_FETCH_LEXEME_FORM, |
| 256 | 'fetch_key' => self::Z_WIKIDATA_FETCH_LEXEME_FORM_ID, |
| 257 | 'reference_type' => self::Z_WIKIDATA_REFERENCE_LEXEME_FORM, |
| 258 | 'reference_key' => self::Z_WIKIDATA_REFERENCE_LEXEME_FORM_ID, |
| 259 | ], |
| 260 | ]; |
| 261 | |
| 262 | /** |
| 263 | * Types that are considered parseable for Visual Editor integration. |
| 264 | * These types can be used as inputs in functions that will be discoverable |
| 265 | * in the Visual Editor menu. |
| 266 | * |
| 267 | * To add new parseable types, simply add them to this array. |
| 268 | */ |
| 269 | public const PARSEABLE_INPUT_TYPES = [ |
| 270 | self::Z_STRING, |
| 271 | self::Z_LANGUAGE, |
| 272 | |
| 273 | // Wikidata types |
| 274 | self::Z_WIKIDATA_ITEM, |
| 275 | self::Z_WIKIDATA_LEXEME, |
| 276 | self::Z_WIKIDATA_REFERENCE_ITEM, |
| 277 | self::Z_WIKIDATA_REFERENCE_LEXEME, |
| 278 | ]; |
| 279 | |
| 280 | /** |
| 281 | * Types that are considered renderable for Visual Editor integration. |
| 282 | * These types can be used as outputs in functions that will be discoverable |
| 283 | * in the Visual Editor menu. |
| 284 | * |
| 285 | * To add new renderable types, simply add them to this array. |
| 286 | */ |
| 287 | public const RENDERABLE_OUTPUT_TYPES = [ |
| 288 | self::Z_STRING, |
| 289 | self::Z_HTML_FRAGMENT, |
| 290 | ]; |
| 291 | |
| 292 | // Keep in sync with function-schemata's `typesBuiltIntoWikiLambda` |
| 293 | private const BUILT_IN_TYPES = [ |
| 294 | self::Z_OBJECT => 'ZObject', |
| 295 | self::Z_PERSISTENTOBJECT => 'ZPersistentObject', |
| 296 | self::Z_KEY => 'ZKey', |
| 297 | self::Z_ERROR => 'ZError', |
| 298 | self::Z_TYPE => 'ZType', |
| 299 | self::Z_STRING => 'ZString', |
| 300 | self::Z_FUNCTIONCALL => 'ZFunctionCall', |
| 301 | self::Z_FUNCTION => 'ZFunction', |
| 302 | self::Z_REFERENCE => 'ZReference', |
| 303 | self::Z_MONOLINGUALSTRING => 'ZMonoLingualString', |
| 304 | self::Z_MULTILINGUALSTRING => 'ZMultiLingualString', |
| 305 | self::Z_RESPONSEENVELOPE => 'ZResponseEnvelope', |
| 306 | self::Z_MONOLINGUALSTRINGSET => 'ZMonoLingualStringSet', |
| 307 | self::Z_MULTILINGUALSTRINGSET => 'ZMultiLingualStringSet', |
| 308 | self::Z_KEYREFERENCE => 'ZKeyReference', |
| 309 | self::Z_BOOLEAN => 'ZBoolean', |
| 310 | self::Z_LANGUAGE => 'ZNaturalLanguage', |
| 311 | self::Z_QUOTE => 'ZQuote', |
| 312 | self::Z_HTML_FRAGMENT => 'ZHTMLFragment', |
| 313 | ]; |
| 314 | |
| 315 | public const TERMINAL_KEYS = [ |
| 316 | self::Z_STRING_VALUE, |
| 317 | self::Z_REFERENCE_VALUE, |
| 318 | self::Z_QUOTE_VALUE |
| 319 | ]; |
| 320 | |
| 321 | /** |
| 322 | * An array of ZTypes which are prohibited from creation by any user. (T278175) |
| 323 | */ |
| 324 | public const DISALLOWED_ROOT_ZOBJECTS = [ |
| 325 | self::Z_PERSISTENTOBJECT, |
| 326 | self::Z_KEY, |
| 327 | self::Z_REFERENCE, |
| 328 | self::Z_ARGUMENTDECLARATION, |
| 329 | self::Z_ARGUMENTREFERENCE, |
| 330 | self::Z_KEYREFERENCE, |
| 331 | self::Z_ERROR, |
| 332 | self::Z_CODE, |
| 333 | self::Z_ABSTRACTCONTENT, |
| 334 | |
| 335 | // Wikidata types |
| 336 | ...self::WIKIDATA_TYPES, |
| 337 | ...self::WIKIDATA_REFERENCE_TYPES |
| 338 | // TODO (T309302): Uncomment when fixed Z24 insertion issue |
| 339 | // self::Z_UNIT, |
| 340 | ]; |
| 341 | |
| 342 | public const EXCLUDE_TYPES_FROM_ENUMS = [ |
| 343 | self::Z_TYPE, |
| 344 | self::Z_ERRORTYPE, |
| 345 | self::Z_FUNCTION, |
| 346 | self::Z_DESERIALISER, |
| 347 | self::Z_SERIALISER |
| 348 | ]; |
| 349 | |
| 350 | public const IGNORE_KEY_VALUES_FOR_LABELLING = [ |
| 351 | self::Z_QUOTE_VALUE, |
| 352 | self::Z_KEYREFERENCE_VALUE, |
| 353 | self::Z_KEY_ID, |
| 354 | self::Z_PERSISTENTOBJECT_ID, |
| 355 | self::Z_TYPE_IDENTITY, |
| 356 | ]; |
| 357 | |
| 358 | public const SELF_REFERENTIAL_KEYS = [ |
| 359 | self::Z_TYPE_IDENTITY, |
| 360 | self::Z_PERSISTENTOBJECT_ID, |
| 361 | self::Z_FUNCTION_IDENTITY |
| 362 | ]; |
| 363 | |
| 364 | // These consts are currently only used by ZObjectStore to prohibit creation, and are not (yet) built-in. |
| 365 | public const Z_CODE = 'Z16'; |
| 366 | public const Z_CODE_LANGUAGE = 'Z16K1'; |
| 367 | public const Z_CODE_CODE = 'Z16K2'; |
| 368 | public const Z_ARGUMENTREFERENCE = 'Z18'; |
| 369 | public const Z_UNIT = 'Z21'; |
| 370 | public const Z_NULL = 'Z23'; |
| 371 | public const Z_ABSTRACTCONTENT = 'Z25'; |
| 372 | |
| 373 | // These are provided for ease of use |
| 374 | public const Z_VOID = 'Z24'; |
| 375 | public const Z_VOID_INSTANCE = [ self::Z_OBJECT_TYPE => self::Z_UNIT ]; |
| 376 | |
| 377 | public const Z_BOOLEAN = 'Z40'; |
| 378 | public const Z_BOOLEAN_VALUE = 'Z40K1'; |
| 379 | public const Z_BOOLEAN_TRUE = 'Z41'; |
| 380 | public const Z_BOOLEAN_FALSE = 'Z42'; |
| 381 | |
| 382 | public const IGNORE_KEY_NORMALIZATION = [ |
| 383 | self::Z_OBJECT_TYPE, |
| 384 | self::Z_MONOLINGUALSTRING_LANGUAGE, |
| 385 | self::Z_MONOLINGUALSTRING_VALUE, |
| 386 | self::Z_MULTILINGUALSTRING_VALUE |
| 387 | ]; |
| 388 | |
| 389 | public const Z_FUNCTION_TYPED_LIST = 'Z881'; |
| 390 | public const Z_FUNCTION_TYPED_LIST_TYPE = 'Z881K1'; |
| 391 | |
| 392 | public const Z_FUNCTION_TYPED_PAIR = 'Z882'; |
| 393 | public const Z_FUNCTION_TYPED_FIRST_TYPE = 'Z882K1'; |
| 394 | public const Z_FUNCTION_TYPED_SECOND_TYPE = 'Z882K2'; |
| 395 | |
| 396 | public const Z_FUNCTION_TYPED_MAP = 'Z883'; |
| 397 | public const Z_FUNCTION_TYPED_MAP_KEY_TYPE = 'Z883K1'; |
| 398 | public const Z_FUNCTION_TYPED_MAP_VALUE_TYPE = 'Z883K2'; |
| 399 | |
| 400 | public const Z_FUNCTION_ERRORTYPE_TO_TYPE = 'Z885'; |
| 401 | public const Z_FUNCTION_ERRORTYPE_TYPE = 'Z885K1'; |
| 402 | |
| 403 | // These are built-in functions that return a type, so we will |
| 404 | // always find them as ZObject type values (Z1K1) |
| 405 | public const BUILT_IN_TYPE_FUNCTIONS = [ |
| 406 | self::Z_FUNCTION_TYPED_LIST => 'ZTypedList', |
| 407 | self::Z_FUNCTION_TYPED_PAIR => 'ZTypedPair', |
| 408 | self::Z_FUNCTION_TYPED_MAP => 'ZTypedMap', |
| 409 | self::Z_FUNCTION_ERRORTYPE_TO_TYPE => 'ZTypedError' |
| 410 | ]; |
| 411 | |
| 412 | /** |
| 413 | * Initialize ZTypeRegistry |
| 414 | */ |
| 415 | protected function initialize(): void { |
| 416 | // Registry for ZObjects of type ZType/Z4 |
| 417 | $this->type = self::Z_TYPE; |
| 418 | |
| 419 | foreach ( self::BUILT_IN_TYPES as $zKey => $classname ) { |
| 420 | $this->register( $zKey, $classname ); |
| 421 | } |
| 422 | } |
| 423 | |
| 424 | /** |
| 425 | * Registers the given ZType id and name in the type cache. |
| 426 | * |
| 427 | * @param string $key |
| 428 | * @param string $type |
| 429 | * @throws ZErrorException |
| 430 | */ |
| 431 | public function register( string $key, string $type ): void { |
| 432 | if ( $this->isZObjectKeyCached( $key ) ) { |
| 433 | $conflictingType = $this->getZObjectKeyFromType( $key ); |
| 434 | throw new ZErrorException( |
| 435 | ZErrorFactory::createZErrorInstance( |
| 436 | ZErrorTypeRegistry::Z_ERROR_CONFLICTING_TYPE_NAMES, |
| 437 | [ |
| 438 | 'zid' => $key, |
| 439 | 'name' => $type, |
| 440 | 'existing' => $conflictingType |
| 441 | ] |
| 442 | ) |
| 443 | ); |
| 444 | } |
| 445 | |
| 446 | if ( $this->isZObjectTypeCached( $type ) ) { |
| 447 | $conflictingKey = $this->getZObjectTypeFromKey( $type ); |
| 448 | throw new ZErrorException( |
| 449 | ZErrorFactory::createZErrorInstance( |
| 450 | ZErrorTypeRegistry::Z_ERROR_CONFLICTING_TYPE_ZIDS, |
| 451 | [ |
| 452 | 'zid' => $key, |
| 453 | 'name' => $type, |
| 454 | 'existing' => $conflictingKey |
| 455 | ] |
| 456 | ) |
| 457 | ); |
| 458 | } |
| 459 | |
| 460 | if ( |
| 461 | $this->isZTypeBuiltIn( $key ) |
| 462 | && !class_exists( 'MediaWiki\Extension\WikiLambda\ZObjects\\' . self::BUILT_IN_TYPES[ $key ] ) |
| 463 | ) { |
| 464 | throw new ZErrorException( |
| 465 | ZErrorFactory::createZErrorInstance( |
| 466 | ZErrorTypeRegistry::Z_ERROR_CONFLICTING_TYPE_ZIDS, |
| 467 | [ |
| 468 | 'zid' => $key, |
| 469 | 'name' => $type |
| 470 | ] |
| 471 | ) |
| 472 | ); |
| 473 | } |
| 474 | |
| 475 | $this->registry[ $key ] = $type; |
| 476 | } |
| 477 | |
| 478 | /** |
| 479 | * Removes the given ZType from the type cache, except for builtin types. |
| 480 | * |
| 481 | * @param string $key |
| 482 | */ |
| 483 | public function unregister( string $key ): void { |
| 484 | if ( !array_key_exists( $key, self::BUILT_IN_TYPES ) ) { |
| 485 | unset( $this->registry[ $key ] ); |
| 486 | } |
| 487 | } |
| 488 | |
| 489 | /** |
| 490 | * Get the array of the keys of the ZTypes stored in the cache |
| 491 | * |
| 492 | * @return string[] Keys of the ZTypes stored in the cache |
| 493 | */ |
| 494 | public function getCachedZObjectKeys(): array { |
| 495 | return array_keys( $this->registry ); |
| 496 | } |
| 497 | |
| 498 | /** |
| 499 | * Whether the provided ZType is 'built-in' to the WikiLambda extension, and thus its validator |
| 500 | * is provided in PHP code. |
| 501 | * |
| 502 | * @param string $key The key of the ZType to check. |
| 503 | * @return bool |
| 504 | */ |
| 505 | public function isZTypeBuiltIn( string $key ): bool { |
| 506 | return array_key_exists( $key, self::BUILT_IN_TYPES ); |
| 507 | } |
| 508 | |
| 509 | /** |
| 510 | * Whether the provided ZFunction is 'built-in' to the WikiLambda extension, and thus its validator |
| 511 | * is provided in PHP code. |
| 512 | * |
| 513 | * @param string $key The key of the ZFunction to check. |
| 514 | * @return bool |
| 515 | */ |
| 516 | public function isZFunctionBuiltIn( string $key ): bool { |
| 517 | return array_key_exists( $key, self::BUILT_IN_TYPE_FUNCTIONS ); |
| 518 | } |
| 519 | |
| 520 | /** |
| 521 | * Returns the class name given a built-in function Zid |
| 522 | * |
| 523 | * @param string $zid The zid of the built-in ZFunction |
| 524 | * @return ?string Class name for the built-in, or null if not known |
| 525 | */ |
| 526 | public function getZFunctionBuiltInName( string $zid ): ?string { |
| 527 | return self::BUILT_IN_TYPE_FUNCTIONS[ $zid ] ?? null; |
| 528 | } |
| 529 | |
| 530 | /** |
| 531 | * Whether the provided ZType is cached. |
| 532 | * |
| 533 | * @param string $key The key of the ZType to check |
| 534 | * @return bool |
| 535 | */ |
| 536 | public function isZObjectKeyCached( string $key ): bool { |
| 537 | return in_array( $key, $this->getCachedZObjectKeys(), true ); |
| 538 | } |
| 539 | |
| 540 | /** |
| 541 | * Whether the provided ZType is known, either because it's a registered type |
| 542 | * or because it's persisted in the database. If the type is not yet cached but |
| 543 | * it's a valid type, it registers it. |
| 544 | * |
| 545 | * @param string $key The key of the ZType to check |
| 546 | * @return bool |
| 547 | */ |
| 548 | public function isZObjectKeyKnown( string $key ): bool { |
| 549 | if ( $this->isZObjectKeyCached( $key ) ) { |
| 550 | return true; |
| 551 | } |
| 552 | |
| 553 | $title = Title::newFromText( $key, NS_MAIN ); |
| 554 | |
| 555 | // TODO (T300530): This is quite expensive. Store this in a metadata DB table, instead of fetching it live? |
| 556 | $zObjectStore = WikiLambdaServices::getZObjectStore(); |
| 557 | $content = $zObjectStore->fetchZObjectByTitle( $title ); |
| 558 | |
| 559 | if ( $content === false ) { |
| 560 | return false; |
| 561 | } |
| 562 | |
| 563 | // (T374241, T375065) Check that the object is a type without running validation |
| 564 | // to avoid going into an infinite loop: |
| 565 | $zObject = $content->getObject(); |
| 566 | $innerType = $zObject->{ self::Z_PERSISTENTOBJECT_VALUE }->{ self::Z_OBJECT_TYPE }; |
| 567 | |
| 568 | // check if it's Z_TYPE |
| 569 | if ( $innerType === self::Z_TYPE ) { |
| 570 | $this->register( $key, $key ); |
| 571 | return true; |
| 572 | } |
| 573 | |
| 574 | // check if it's a Z_FUNCTIONCALL to a Wikidata Enum generic type |
| 575 | if ( $innerType === self::Z_FUNCTIONCALL ) { |
| 576 | $functionZid = $zObject->{ self::Z_PERSISTENTOBJECT_VALUE }->{ self::Z_FUNCTIONCALL_FUNCTION }; |
| 577 | if ( $functionZid === self::Z_WIKIDATA_ENUM ) { |
| 578 | $this->register( $key, $key ); |
| 579 | return true; |
| 580 | } |
| 581 | } |
| 582 | |
| 583 | return false; |
| 584 | } |
| 585 | |
| 586 | /** |
| 587 | * Returns the ZType class name given its ZID |
| 588 | * |
| 589 | * @param string $key The key of the ZType to check |
| 590 | * @return string Class name for the ZType |
| 591 | * @throws ZErrorException |
| 592 | */ |
| 593 | public function getZObjectTypeFromKey( string $key ): string { |
| 594 | if ( !$this->isZObjectKeyKnown( $key ) ) { |
| 595 | // Error Z504: Zid not found |
| 596 | throw new ZErrorException( |
| 597 | ZErrorFactory::createZErrorInstance( |
| 598 | ZErrorTypeRegistry::Z_ERROR_ZID_NOT_FOUND, |
| 599 | [ 'data' => $key ] |
| 600 | ) |
| 601 | ); |
| 602 | } |
| 603 | return $this->registry[ $key ]; |
| 604 | } |
| 605 | |
| 606 | /** |
| 607 | * Returns the array of names of the cached ZTypes. |
| 608 | * |
| 609 | * @return string[] Array of cached ZTypes |
| 610 | */ |
| 611 | public function getCachedZObjectTypes(): array { |
| 612 | return array_values( $this->registry ); |
| 613 | } |
| 614 | |
| 615 | /** |
| 616 | * Whether the given ZType is saved in the cache. |
| 617 | * |
| 618 | * @param string $type Name of the ZType |
| 619 | * @return bool |
| 620 | */ |
| 621 | public function isZObjectTypeCached( string $type ): bool { |
| 622 | return in_array( $type, $this->getCachedZObjectTypes(), true ); |
| 623 | } |
| 624 | |
| 625 | /** |
| 626 | * Whether the given ZType is known, either because it's saved in the cache or because |
| 627 | * it is persisted in the database. |
| 628 | * |
| 629 | * @param string $type Name of the ZType |
| 630 | * @return bool |
| 631 | */ |
| 632 | public function isZObjectTypeKnown( string $type ): bool { |
| 633 | if ( $this->isZObjectTypeCached( $type ) ) { |
| 634 | return true; |
| 635 | } |
| 636 | |
| 637 | // TODO (T300530): The registry is just a cache; also walk the DB to find if this type is registered. |
| 638 | return false; |
| 639 | } |
| 640 | |
| 641 | /** |
| 642 | * Returns the ZType id of a given type. |
| 643 | * |
| 644 | * @param string $type Name of the ZType |
| 645 | * @return string ZID of the ZType |
| 646 | * @throws ZErrorException |
| 647 | */ |
| 648 | public function getZObjectKeyFromType( string $type ): string { |
| 649 | if ( !$this->isZObjectTypeKnown( $type ) ) { |
| 650 | // Error Z543: ZType not found |
| 651 | throw new ZErrorException( |
| 652 | ZErrorFactory::createZErrorInstance( |
| 653 | ZErrorTypeRegistry::Z_ERROR_ZTYPE_NOT_FOUND, |
| 654 | [ 'type' => $type ] |
| 655 | ) |
| 656 | ); |
| 657 | } |
| 658 | return array_search( $type, $this->registry ); |
| 659 | } |
| 660 | |
| 661 | /** |
| 662 | * Checks if a given ZObject is an instance of a given type. |
| 663 | * |
| 664 | * @param ZObject $object |
| 665 | * @param string $type ZID of the type to test |
| 666 | * @return bool |
| 667 | * @throws ZErrorException |
| 668 | */ |
| 669 | public function isZObjectInstanceOfType( ZObject $object, string $type ): bool { |
| 670 | if ( !$this->isZObjectKeyKnown( $type ) ) { |
| 671 | // Error Z504: ZID not found |
| 672 | throw new ZErrorException( |
| 673 | ZErrorFactory::createZErrorInstance( |
| 674 | ZErrorTypeRegistry::Z_ERROR_ZID_NOT_FOUND, |
| 675 | [ 'data' => $type ] |
| 676 | ) |
| 677 | ); |
| 678 | } |
| 679 | |
| 680 | if ( $type === self::Z_OBJECT || $type === $object->getZType() ) { |
| 681 | return true; |
| 682 | } |
| 683 | |
| 684 | if ( !( $object instanceof ZReference ) ) { |
| 685 | return false; |
| 686 | } |
| 687 | |
| 688 | if ( !ZObjectUtils::isValidZObjectReference( $object->getZValue() ) ) { |
| 689 | return false; |
| 690 | } |
| 691 | |
| 692 | $zObjectStore = WikiLambdaServices::getZObjectStore(); |
| 693 | $objectTitle = Title::newFromText( $object->getZValue(), NS_MAIN ); |
| 694 | $fetchedObject = $zObjectStore->fetchZObjectByTitle( $objectTitle ); |
| 695 | |
| 696 | return $fetchedObject && $type === $fetchedObject->getZType(); |
| 697 | } |
| 698 | } |