Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
78.75% covered (warning)
78.75%
63 / 80
50.00% covered (danger)
50.00%
1 / 2
CRAP
0.00% covered (danger)
0.00%
0 / 1
ZObjectSecondaryDataUpdate
78.75% covered (warning)
78.75%
63 / 80
50.00% covered (danger)
50.00%
1 / 2
29.53
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 doUpdate
78.21% covered (warning)
78.21%
61 / 78
0.00% covered (danger)
0.00%
0 / 1
28.48
1<?php
2/**
3 * WikiLambda ZObject secondary data updater for when ZObjects are edited
4 *
5 * @file
6 * @ingroup Extensions
7 * @copyright 2020– Abstract Wikipedia team; see AUTHORS.txt
8 * @license MIT
9 */
10
11namespace MediaWiki\Extension\WikiLambda;
12
13use Content;
14use MediaWiki\Deferred\DataUpdate;
15use MediaWiki\Extension\WikiLambda\Registry\ZTypeRegistry;
16use MediaWiki\Extension\WikiLambda\ZObjects\ZReference;
17use MediaWiki\Title\Title;
18
19class ZObjectSecondaryDataUpdate extends DataUpdate {
20
21    private Title $title;
22    private ZObjectContent $zObject;
23
24    /**
25     * @param Title $title
26     * @param Content $zObject
27     */
28    public function __construct( Title $title, $zObject ) {
29        $this->title = $title;
30        $this->zObject = $zObject;
31    }
32
33    public function doUpdate() {
34        // Given this title, gets ZID
35        // Given this zObject, gets ZType
36        // 1. Delete labels from wikilambda_zobject_labels for this ZID
37        // 2. Delete labels from wikilambda_zobject_label_conflicts for this ZID
38        // 3. Gets labels from this zObject (Z2K3 of the ZObjectContent)
39        // 4. Finds conflicting labels, e.g. existing labels from other ZIDs that have same language-value
40        // 5. Saves conflicting labels in wikilambda_zobject_label_conflicts and
41        // 6. Saves non-conflicting labels in wikilambda_zobject_labels
42        // 7. If appropriate, clear wikilambda_ztester_results for this ZID
43        // 8. If appropriate, add entry to wikilambda_zlanguages for this ZID
44        // 9. Add related zobjects, if any, to wikilambda_zobject_join for this ZID
45
46        // TODO (T300522): Only re-write the labels if they've changed.
47        // TODO (T300522): Use a single fancy upsert to remove/update/insert instead?
48
49        $zid = $this->title->getDBkey();
50
51        $zObjectStore = WikiLambdaServices::getZObjectStore();
52
53        // Delete all labels: primary ones and aliases
54        $zObjectStore->deleteZObjectLabelsByZid( $zid );
55        $zObjectStore->deleteZObjectLabelConflictsByZid( $zid );
56
57        // Delete language entries, if appropriate
58        $zObjectStore->deleteZLanguageFromLanguagesCache( $zid );
59
60        $labels = $this->zObject->getLabels()->getValueAsList();
61
62        // TODO (T357552): This should write the shortform, encoded type (e.g. `Z881(Z6)`)
63        $ztype = $this->zObject->getZType();
64
65        $innerZObject = $this->zObject->getInnerZObject();
66
67        // (T262089) Save output type in labels table for function and function call
68        $returnType = null;
69        // Get Z_FUNCTION_RETURN_TYPE if the ZObject is a Z8 Function
70        if ( $ztype === ZTypeRegistry::Z_FUNCTION ) {
71            $returnRef = $innerZObject->getValueByKey( ZTypeRegistry::Z_FUNCTION_RETURN_TYPE );
72            if ( $returnRef instanceof ZReference ) {
73                $returnType = $returnRef->getZValue();
74            }
75        }
76        // Get saved Z_FUNCTION_RETURN_TYPE of the Z_FUNCTIONCALL_FUNCTION if it's a Z7
77        if ( $ztype === ZTypeRegistry::Z_FUNCTIONCALL ) {
78            $functionRef = $innerZObject->getValueByKey( ZTypeRegistry::Z_FUNCTIONCALL_FUNCTION );
79            if ( $functionRef instanceof ZReference ) {
80                $returnType = $zObjectStore->fetchZFunctionReturnType( $functionRef->getZValue() );
81            }
82        }
83
84        $conflicts = $zObjectStore->findZObjectLabelConflicts( $zid, $ztype, $labels );
85        $newLabels = array_filter( $labels, static function ( $value, $lang ) use ( $conflicts ) {
86            return !isset( $conflicts[$lang] );
87        }, ARRAY_FILTER_USE_BOTH );
88
89        $zObjectStore->insertZObjectLabels( $zid, $ztype, $newLabels, $returnType );
90        $zObjectStore->insertZObjectLabelConflicts( $zid, $conflicts );
91
92        // (T285368) Write aliases in the labels table
93        $aliases = $this->zObject->getAliases()->getValueAsList();
94        if ( count( $aliases ) > 0 ) {
95            $zObjectStore->insertZObjectAliases( $zid, $ztype, $aliases, $returnType );
96        }
97
98        // Save function information in function table, if appropriate
99        // TODO (T362248): Have insertZFunctionReference do an update, and only delete if changing the type/target?
100        $zObjectStore->deleteZFunctionReference( $zid );
101        switch ( $ztype ) {
102            case ZTypeRegistry::Z_IMPLEMENTATION:
103                $zFunction = $innerZObject->getValueByKey( ZTypeRegistry::Z_IMPLEMENTATION_FUNCTION );
104                break;
105
106            case ZTypeRegistry::Z_TESTER:
107                $zFunction = $innerZObject->getValueByKey( ZTypeRegistry::Z_TESTER_FUNCTION );
108                break;
109
110            default:
111                $zFunction = null;
112                break;
113        }
114
115        if ( $zFunction && $zFunction->getZValue() ) {
116            $zObjectStore->insertZFunctionReference( $zid, $zFunction->getZValue(), $ztype );
117        }
118
119        // If $zid is a function, record each of its input types and its return type
120        $zObjectStore->deleteRelatedZObjects( $zid );
121        if ( $ztype === ZTypeRegistry::Z_FUNCTION ) {
122            $inputArgumentsObject = $innerZObject->getValueByKey( ZTypeRegistry::Z_FUNCTION_ARGUMENTS );
123            '@phan-var \MediaWiki\Extension\WikiLambda\ZObjects\ZTypedList $inputArgumentsObject';
124            if ( $inputArgumentsObject !== null ) {
125                $inputArguments = $inputArgumentsObject->getAsArray();
126                foreach ( $inputArguments as $key => $inputArgument ) {
127                    $inputTypeObject = $inputArgument->getValueByKey( ZTypeRegistry::Z_ARGUMENTDECLARATION_TYPE );
128                    $inputTypeString = ZObjectUtils::makeTypeFingerprint( $inputTypeObject->getSerialized() );
129                    if ( $inputTypeString !== null ) {
130                        $zObjectStore->insertRelatedZObjects( $zid, $ztype,
131                            ZTypeRegistry::Z_FUNCTION_ARGUMENTS,
132                            $inputTypeString, ZTypeRegistry::Z_TYPE );
133                    }
134                }
135            }
136            // Here we report more return type cases than above; use new vars to avoid confusion
137            $returnTypeObject = $innerZObject->getValueByKey(
138                ZTypeRegistry::Z_FUNCTION_RETURN_TYPE );
139            $returnTypeString = ZObjectUtils::makeTypeFingerprint( $returnTypeObject->getSerialized() );
140            if ( $returnTypeString !== null ) {
141                $zObjectStore->insertRelatedZObjects( $zid, $ztype,
142                    ZTypeRegistry::Z_FUNCTION_RETURN_TYPE,
143                    $returnTypeString, ZTypeRegistry::Z_TYPE );
144            }
145        }
146
147        // If appropriate, clear wikilambda_ztester_results for this ZID
148        // TODO (T338247): Only do this for the old revision not the new one.
149        switch ( $ztype ) {
150            case ZTypeRegistry::Z_FUNCTION:
151                $zObjectStore->deleteZFunctionFromZTesterResultsCache( $zid );
152                break;
153
154            case ZTypeRegistry::Z_IMPLEMENTATION:
155                $zObjectStore->deleteZImplementationFromZTesterResultsCache( $zid );
156                break;
157
158            case ZTypeRegistry::Z_TESTER:
159                $zObjectStore->deleteZTesterFromZTesterResultsCache( $zid );
160                break;
161
162            default:
163                // No action.
164        }
165
166        // If appropriate, add entry to wikilambda_zlanguages for this ZID
167        if ( $ztype === ZTypeRegistry::Z_LANGUAGE ) {
168            // Clear old values, if any
169            $zObjectStore->deleteZLanguageFromLanguagesCache( $zid );
170
171            // Set primary language code
172            $targetLanguage = $innerZObject->getValueByKey( ZTypeRegistry::Z_LANGUAGE_CODE )->getZValue();
173            $languageCodes = [ $targetLanguage ];
174            $zObjectStore->insertZLanguageToLanguagesCache( $zid, $targetLanguage );
175
176            // Set secondary language codes, if any
177            $secondaryLanguagesObject = $innerZObject->getValueByKey( ZTypeRegistry::Z_LANGUAGE_SECONDARYCODES );
178            if ( $secondaryLanguagesObject !== null ) {
179                '@phan-var \MediaWiki\Extension\WikiLambda\ZObjects\ZTypedList $secondaryLanguagesObject';
180                $secondaryLanguages = $secondaryLanguagesObject->getAsArray();
181
182                foreach ( $secondaryLanguages as $key => $secondaryLanguage ) {
183                    // $secondaryLanguage is a ZString but we want the actual string
184                    $secondaryLanguageString = $secondaryLanguage->getZValue();
185                    $languageCodes[] = $secondaryLanguageString;
186                    $zObjectStore->insertZLanguageToLanguagesCache( $zid, $secondaryLanguageString );
187                }
188            }
189
190            // (T343465) Add the language codes as fake aliases under Z1360/MUL (multi-lingual value)
191            $zObjectStore->insertZObjectAliases(
192                $zid,
193                $ztype,
194                [ 'Z1360' => $languageCodes ],
195                $returnType
196            );
197        }
198    }
199}