Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
85.87% covered (warning)
85.87%
79 / 92
66.67% covered (warning)
66.67%
6 / 9
CRAP
0.00% covered (danger)
0.00%
0 / 1
ZFunction
85.87% covered (warning)
85.87%
79 / 92
66.67% covered (warning)
66.67%
6 / 9
36.07
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
1
 getDefinition
100.00% covered (success)
100.00%
28 / 28
100.00% covered (success)
100.00%
1 / 1
1
 isValid
100.00% covered (success)
100.00%
25 / 25
100.00% covered (success)
100.00%
1 / 1
15
 getReturnType
80.00% covered (warning)
80.00%
4 / 5
0.00% covered (danger)
0.00%
0 / 1
3.07
 getIdentity
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 getArgumentDeclarations
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 getTesterZids
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getImplementationZids
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getAssociatedZids
55.00% covered (warning)
55.00%
11 / 20
0.00% covered (danger)
0.00%
0 / 1
11.47
1<?php
2/**
3 * WikiLambda ZFunction
4 *
5 * @file
6 * @ingroup Extensions
7 * @copyright 2020– WikiLambda team; see AUTHORS.txt
8 * @license MIT
9 */
10
11namespace MediaWiki\Extension\WikiLambda\ZObjects;
12
13use FormatJson;
14use MediaWiki\Extension\WikiLambda\Registry\ZTypeRegistry;
15use MediaWiki\Extension\WikiLambda\ZErrorException;
16use MediaWiki\Extension\WikiLambda\ZObjectFactory;
17
18class ZFunction extends ZObject {
19
20    /**
21     * Construct a ZFunction instance
22     *
23     * @param ZTypedList $arguments
24     * @param ZObject $returnType
25     * @param ZTypedList $testers
26     * @param ZTypedList $implementations
27     * @param ZReference $identity
28     */
29    public function __construct( $arguments, $returnType, $testers, $implementations, $identity ) {
30        $this->data[ ZTypeRegistry::Z_FUNCTION_ARGUMENTS ] = $arguments;
31        $this->data[ ZTypeRegistry::Z_FUNCTION_RETURN_TYPE ] = $returnType;
32        $this->data[ ZTypeRegistry::Z_FUNCTION_TESTERS ] = $testers;
33        $this->data[ ZTypeRegistry::Z_FUNCTION_IMPLEMENTATIONS ] = $implementations;
34        $this->data[ ZTypeRegistry::Z_FUNCTION_IDENTITY ] = $identity;
35    }
36
37    /**
38     * @inheritDoc
39     */
40    public static function getDefinition(): array {
41        return [
42            'type' => [
43                'type' => ZTypeRegistry::Z_REFERENCE,
44                'value' => ZTypeRegistry::Z_FUNCTION,
45            ],
46            'keys' => [
47                ZTypeRegistry::Z_FUNCTION_ARGUMENTS => [
48                    'type' => ZTypeRegistry::Z_FUNCTION_TYPED_LIST,
49                    'required' => true
50                ],
51                ZTypeRegistry::Z_FUNCTION_RETURN_TYPE => [
52                    'type' => ZTypeRegistry::Z_REFERENCE,
53                    'required' => true
54                ],
55                ZTypeRegistry::Z_FUNCTION_TESTERS => [
56                    'type' => ZTypeRegistry::Z_FUNCTION_TYPED_LIST,
57                    'required' => true
58                ],
59                ZTypeRegistry::Z_FUNCTION_IMPLEMENTATIONS => [
60                    'type' => ZTypeRegistry::Z_FUNCTION_TYPED_LIST,
61                    'required' => true
62                ],
63                ZTypeRegistry::Z_FUNCTION_IDENTITY => [
64                    'type' => ZTypeRegistry::Z_REFERENCE,
65                    'required' => true
66                ],
67            ]
68        ];
69    }
70
71    /**
72     * @inheritDoc
73     */
74    public function isValid(): bool {
75        // Check Z8K1 is a list of argument declarations
76        if ( !( $this->data[ ZTypeRegistry::Z_FUNCTION_ARGUMENTS ] instanceof ZTypedList ) ) {
77            return false;
78        }
79        $list = $this->data[ ZTypeRegistry::Z_FUNCTION_ARGUMENTS ];
80        if ( !$list->isEmpty() && ( $list->getElementType()->getZValue() !== ZTypeRegistry::Z_ARGUMENTDECLARATION ) ) {
81            return false;
82        }
83
84        // Check Z8K2 is either a valid reference or a valid function call
85        if (
86            !( $this->data[ ZTypeRegistry::Z_FUNCTION_RETURN_TYPE ] instanceof ZReference ) &&
87            !( $this->data[ ZTypeRegistry::Z_FUNCTION_RETURN_TYPE ] instanceof ZFunctionCall ) ) {
88            return false;
89        }
90        if ( !( $this->data[ ZTypeRegistry::Z_FUNCTION_RETURN_TYPE ]->isValid() ) ) {
91            return false;
92        }
93
94        // Check Z8K3 is a list of testers
95        if ( !( $this->data[ ZTypeRegistry::Z_FUNCTION_TESTERS ] instanceof ZTypedList ) ) {
96            return false;
97        }
98        $list = $this->data[ ZTypeRegistry::Z_FUNCTION_TESTERS ];
99        if ( !$list->isEmpty() && ( $list->getElementType()->getZValue() !== ZTypeRegistry::Z_TESTER ) ) {
100            return false;
101        }
102
103        // Check Z8K4 is a list of implementations
104        if ( !( $this->data[ ZTypeRegistry::Z_FUNCTION_IMPLEMENTATIONS ] instanceof ZTypedList ) ) {
105            return false;
106        }
107        $list = $this->data[ ZTypeRegistry::Z_FUNCTION_IMPLEMENTATIONS ];
108        if ( !$list->isEmpty() && ( $list->getElementType()->getZValue() !== ZTypeRegistry::Z_IMPLEMENTATION ) ) {
109            return false;
110        }
111
112        // Check Z8K5 is a reference
113        if ( !( $this->data[ ZTypeRegistry::Z_FUNCTION_IDENTITY ] instanceof ZReference ) ) {
114            return false;
115        }
116        if ( !( $this->data[ ZTypeRegistry::Z_FUNCTION_IDENTITY ]->isValid() ) ) {
117            return false;
118        }
119
120        return true;
121    }
122
123    /**
124     * Expected return type of this ZFunction.
125     *
126     * @return string|null
127     */
128    public function getReturnType() {
129        if ( $this->data[ ZTypeRegistry::Z_FUNCTION_RETURN_TYPE ] instanceof ZReference ) {
130            return $this->data[ ZTypeRegistry::Z_FUNCTION_RETURN_TYPE ]->getZValue();
131        }
132
133        if ( $this->data[ ZTypeRegistry::Z_FUNCTION_RETURN_TYPE ] instanceof ZFunctionCall ) {
134            return $this->data[ ZTypeRegistry::Z_FUNCTION_RETURN_TYPE ]->getReturnType();
135        }
136
137        return null;
138    }
139
140    /**
141     * Return the string value of its identity.
142     *
143     * @return string|null
144     */
145    public function getIdentity() {
146        $functionId = $this->data[ ZTypeRegistry::Z_FUNCTION_IDENTITY ];
147        return ( $functionId instanceof ZReference )
148            ? $functionId->getZValue()
149            : null;
150    }
151
152    /**
153     * Return an array with the argument declarations, or empty array if undefined
154     *
155     * @return array
156     */
157    public function getArgumentDeclarations(): array {
158        if ( !( $this->data[ ZTypeRegistry::Z_FUNCTION_ARGUMENTS ] instanceof ZTypedList ) ) {
159            return [];
160        }
161        return $this->data[ ZTypeRegistry::Z_FUNCTION_ARGUMENTS ]->getAsArray();
162    }
163
164    /**
165     * Get the Z8K3/Testers and return a list of ZIDs for them.
166     *
167     * @return string[]
168     * @throws ZErrorException from ZObjectFactory::create
169     */
170    public function getTesterZids() {
171        return $this->getAssociatedZids( ZTypeRegistry::Z_FUNCTION_TESTERS );
172    }
173
174    /**
175     * Get the Z8K4/Implementations and return a list of ZIDs for them.
176     *
177     * @return string[]
178     * @throws ZErrorException from ZObjectFactory::create
179     */
180    public function getImplementationZids() {
181        return $this->getAssociatedZids( ZTypeRegistry::Z_FUNCTION_IMPLEMENTATIONS );
182    }
183
184    /**
185     * @param string $key One of ZTypeRegistry::Z_FUNCTION_IMPLEMENTATIONS or ZTypeRegistry::Z_FUNCTION_TESTERS
186     * @return string[]
187     * @throws ZErrorException from ZObjectFactory::create
188     */
189    private function getAssociatedZids( $key ) {
190        $zids = [];
191        $associatedList = $this->getValueByKey( $key );
192
193        if ( !$associatedList ) {
194            return [];
195        }
196
197        '@phan-var \MediaWiki\Extension\WikiLambda\ZObjects\ZTypedList $associatedList';
198        $associatedArray = $associatedList->getAsArray();
199
200        foreach ( $associatedArray as $item ) {
201            if ( is_string( $item ) ) {
202                $decodedJson = FormatJson::decode( $item );
203                // If not JSON, assume we have received a ZID.
204                if ( $decodedJson ) {
205                    $item = ZObjectFactory::create( $decodedJson );
206                } else {
207                    $item = new ZReference( $item );
208                }
209            }
210
211            $zids[] = ( $item->getZType() === ZTypeRegistry::Z_REFERENCE ) ?
212                $item->getValueByKey( ZTypeRegistry::Z_REFERENCE_VALUE ) :
213                (
214                    ( $item->getZType() === ZTypeRegistry::Z_PERSISTENTOBJECT_ID ) ?
215                        $item->getValueByKey( ZTypeRegistry::Z_PERSISTENTOBJECT_ID )->
216                            getValueByKey( ZTypeRegistry::Z_STRING_VALUE ) :
217                        ZTypeRegistry::Z_NULL_REFERENCE
218                );
219        }
220        return $zids;
221    }
222
223}