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