Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
75.00% |
57 / 76 |
|
90.00% |
9 / 10 |
CRAP | |
0.00% |
0 / 1 |
ZResponseEnvelope | |
75.00% |
57 / 76 |
|
90.00% |
9 / 10 |
52.06 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
getDefinition | |
100.00% |
14 / 14 |
|
100.00% |
1 / 1 |
1 | |||
isValid | |
100.00% |
6 / 6 |
|
100.00% |
1 / 1 |
4 | |||
getZValue | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getZMetadata | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
hasErrors | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getErrors | |
36.67% |
11 / 30 |
|
0.00% |
0 / 1 |
121.61 | |||
setMetaDataValue | |
100.00% |
6 / 6 |
|
100.00% |
1 / 1 |
3 | |||
wrapInResponseMap | |
100.00% |
14 / 14 |
|
100.00% |
1 / 1 |
1 | |||
wrapErrorInResponseMap | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 |
1 | <?php |
2 | /** |
3 | * WikiLambda ZResponseEnvelope |
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\ZObjects; |
12 | |
13 | use MediaWiki\Extension\WikiLambda\Registry\ZTypeRegistry; |
14 | |
15 | class ZResponseEnvelope extends ZObject { |
16 | |
17 | /** |
18 | * Construct a new ZResponseEnvelope instance |
19 | * |
20 | * @param ?ZObject $response Value of the response |
21 | * @param ?ZObject $metadata Meta-data response |
22 | */ |
23 | public function __construct( $response, $metadata ) { |
24 | $this->data[ ZTypeRegistry::Z_RESPONSEENVELOPE_VALUE ] = $response ?? (object)ZTypeRegistry::Z_VOID_INSTANCE; |
25 | $this->data[ ZTypeRegistry::Z_RESPONSEENVELOPE_METADATA ] = $metadata ?? (object)ZTypeRegistry::Z_VOID_INSTANCE; |
26 | } |
27 | |
28 | /** |
29 | * @inheritDoc |
30 | */ |
31 | public static function getDefinition(): array { |
32 | return [ |
33 | 'type' => [ |
34 | 'type' => ZTypeRegistry::Z_REFERENCE, |
35 | 'value' => ZTypeRegistry::Z_RESPONSEENVELOPE, |
36 | ], |
37 | 'keys' => [ |
38 | ZTypeRegistry::Z_RESPONSEENVELOPE_VALUE => [ |
39 | 'type' => ZTypeRegistry::Z_OBJECT |
40 | ], |
41 | ZTypeRegistry::Z_RESPONSEENVELOPE_METADATA => [ |
42 | 'type' => ZTypeRegistry::Z_OBJECT |
43 | ], |
44 | ], |
45 | ]; |
46 | } |
47 | |
48 | /** |
49 | * @inheritDoc |
50 | */ |
51 | public function isValid(): bool { |
52 | return ( |
53 | ( $this->data[ ZTypeRegistry::Z_RESPONSEENVELOPE_VALUE ] instanceof ZObject && |
54 | $this->data[ ZTypeRegistry::Z_RESPONSEENVELOPE_VALUE ]->isValid() ) || |
55 | ( $this->data[ ZTypeRegistry::Z_RESPONSEENVELOPE_METADATA ] instanceof ZObject && |
56 | $this->data[ ZTypeRegistry::Z_RESPONSEENVELOPE_METADATA ]->isValid() ) |
57 | ); |
58 | } |
59 | |
60 | /** |
61 | * Get the value part of the response envelope |
62 | * |
63 | * @return ZObject |
64 | */ |
65 | public function getZValue() { |
66 | return $this->data[ ZTypeRegistry::Z_RESPONSEENVELOPE_VALUE ]; |
67 | } |
68 | |
69 | /** |
70 | * Get the Meta-data part of the response envelope |
71 | * |
72 | * TODO (T307483): This should ideally be type-hinted as a PHP implementation of ZMap, |
73 | * so that it'd be much easier to interact with. |
74 | * |
75 | * @return ZObject |
76 | */ |
77 | public function getZMetadata() { |
78 | return $this->data[ ZTypeRegistry::Z_RESPONSEENVELOPE_METADATA ]; |
79 | } |
80 | |
81 | /** |
82 | * Does the meta-data in the response envelope have one or more fatal errors? |
83 | * |
84 | * @return bool |
85 | */ |
86 | public function hasErrors(): bool { |
87 | return $this->getErrors() !== null; |
88 | } |
89 | |
90 | /** |
91 | * Does the meta-data in the response envelope have one or more fatal errors? |
92 | * |
93 | * @return ZError|null |
94 | */ |
95 | public function getErrors(): ?ZError { |
96 | $metaData = $this->getZMetadata(); |
97 | |
98 | if ( !$metaData || !is_object( $metaData ) ) { |
99 | return null; |
100 | } |
101 | |
102 | if ( $metaData instanceof ZObject ) { |
103 | if ( $metaData instanceof ZTypedMap ) { |
104 | // @phan-suppress-next-line PhanTypeMismatchReturnSuperType phan can't tell this must be a ZError |
105 | return $metaData->getValueGivenKey( new ZString( 'errors' ) ); |
106 | } |
107 | |
108 | if ( $metaData instanceof ZReference && $metaData->getZValue() === ZTypeRegistry::Z_VOID ) { |
109 | return null; |
110 | } |
111 | } |
112 | |
113 | // A this point, we know we've been initialised with a raw object, presumably in the form of a ZTypedMap. |
114 | |
115 | if ( !property_exists( $metaData, ZTypeRegistry::Z_OBJECT_TYPE ) ) { |
116 | return null; |
117 | } |
118 | |
119 | // @phan-suppress-next-line PhanUndeclaredProperty phan can't tell this must exist per line #115 |
120 | $metaDataType = $metaData->{ ZTypeRegistry::Z_OBJECT_TYPE }; |
121 | |
122 | if ( $metaDataType === ZTypeRegistry::Z_UNIT ) { |
123 | return null; |
124 | } |
125 | |
126 | if ( |
127 | !is_object( $metaDataType ) || |
128 | !property_exists( $metaDataType, ZTypeRegistry::Z_OBJECT_TYPE ) || |
129 | $metaDataType->{ ZTypeRegistry::Z_OBJECT_TYPE } !== ZTypeRegistry::Z_FUNCTIONCALL || |
130 | !property_exists( $metaDataType, ZTypeRegistry::Z_FUNCTIONCALL_FUNCTION ) || |
131 | $metaDataType->{ ZTypeRegistry::Z_FUNCTIONCALL_FUNCTION } !== 'Z883' || |
132 | !property_exists( $metaDataType, 'Z883K1' ) || |
133 | $metaDataType->{ 'Z883K1' } !== 'Z6' || |
134 | !property_exists( $metaDataType, 'Z883K2' ) || |
135 | $metaDataType->{ 'Z883K2' } !== 'Z1' |
136 | ) { |
137 | // Meta-data map is a non-object or otherwise wrong; something's gone wrong, return nothing. |
138 | return null; |
139 | } |
140 | |
141 | '@phan-var \stdClass $metaData'; |
142 | $metaDataMapContents = $metaData->{ 'K1' }; |
143 | |
144 | while ( property_exists( $metaDataMapContents, 'K1' ) ) { |
145 | $currentValue = $metaDataMapContents->{ 'K1' }; |
146 | |
147 | if ( $currentValue->{ 'K1' } === 'errors' ) { |
148 | return $currentValue->{ 'K2' }; |
149 | } |
150 | |
151 | // Not found in this value, so recurse to the next value in the map |
152 | $metaDataMapContents = $metaDataMapContents->{ 'K2' }; |
153 | } |
154 | |
155 | // Nothing found in the map, so there are no errors |
156 | return null; |
157 | } |
158 | |
159 | /** |
160 | * Set the meta-data in the response envelope to the given key. |
161 | * |
162 | * @param ZString|string $key The key to use |
163 | * @param ZObject $value The value to set |
164 | */ |
165 | public function setMetaDataValue( $key, ZObject $value ) { |
166 | $metaData = $this->data[ ZTypeRegistry::Z_RESPONSEENVELOPE_METADATA ]; |
167 | |
168 | if ( !( $metaData instanceof ZTypedMap ) ) { |
169 | $metaData = self::wrapInResponseMap( $key, $value ); |
170 | } else { |
171 | $key = $key instanceof ZString ? $key : new ZString( $key ); |
172 | $metaData->setValueForKey( $key, $value ); |
173 | } |
174 | |
175 | $this->data[ ZTypeRegistry::Z_RESPONSEENVELOPE_METADATA ] = $metaData; |
176 | } |
177 | |
178 | /** |
179 | * Convenience method to make a ZTypedMap for a ZResponseEnvelope with a |
180 | * given key/value pair |
181 | * |
182 | * @param string $key The key to use |
183 | * @param ZObject $value The value to set |
184 | * @return ZTypedMap |
185 | */ |
186 | public static function wrapInResponseMap( $key, $value ): ZTypedMap { |
187 | $pairType = ZTypedPair::buildType( 'Z6', 'Z1' ); |
188 | |
189 | return new ZTypedMap( |
190 | ZTypedMap::buildType( 'Z6', 'Z1' ), |
191 | new ZTypedList( |
192 | ZTypedList::buildType( $pairType ), |
193 | [ |
194 | new ZTypedPair( |
195 | $pairType, |
196 | new ZString( $key ), |
197 | $value |
198 | ), |
199 | ] |
200 | ) |
201 | ); |
202 | } |
203 | |
204 | /** |
205 | * Convenience method to make a ZTypedMap for a ZResponseEnvelope for a ZError |
206 | * |
207 | * @param ZError $error The error to set |
208 | * @return ZTypedMap |
209 | */ |
210 | public static function wrapErrorInResponseMap( $error ): ZTypedMap { |
211 | return self::wrapInResponseMap( 'errors', $error ); |
212 | } |
213 | } |