This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | |||
3 | declare( strict_types = 1 ); |
||
4 | |||
5 | namespace Wikibase\Repo\Api; |
||
6 | |||
7 | use ApiBase; |
||
8 | use ApiMain; |
||
9 | use ApiResult; |
||
10 | use DataValues\DataValue; |
||
11 | use Exception; |
||
12 | use InvalidArgumentException; |
||
13 | use LogicException; |
||
14 | use OutOfBoundsException; |
||
15 | use Status; |
||
16 | use ValueParsers\ParseException; |
||
17 | use ValueParsers\ParserOptions; |
||
18 | use ValueParsers\ValueParser; |
||
19 | use ValueValidators\Error; |
||
20 | use ValueValidators\ValueValidator; |
||
21 | use Wikibase\DataModel\Entity\PropertyId; |
||
22 | use Wikibase\DataModel\Services\Lookup\PropertyDataTypeLookup; |
||
23 | use Wikibase\DataModel\Services\Lookup\PropertyDataTypeLookupException; |
||
24 | use Wikibase\Lib\DataTypeFactory; |
||
25 | use Wikibase\Repo\DataTypeValidatorFactory; |
||
26 | use Wikibase\Repo\Localizer\ExceptionLocalizer; |
||
27 | use Wikibase\Repo\Validators\CompositeValidator; |
||
28 | use Wikibase\Repo\Validators\ValidatorErrorLocalizer; |
||
29 | use Wikibase\Repo\ValueParserFactory; |
||
30 | use Wikibase\Repo\WikibaseRepo; |
||
31 | |||
32 | /** |
||
33 | * API module for using value parsers. |
||
34 | * |
||
35 | * @license GPL-2.0-or-later |
||
36 | * @author Jeroen De Dauw < [email protected] > |
||
37 | * @author Daniel Kinzler |
||
38 | * @author Addshore |
||
39 | */ |
||
40 | class ParseValue extends ApiBase { |
||
41 | |||
42 | /** |
||
43 | * @var DataTypeFactory |
||
44 | */ |
||
45 | private $dataTypeFactory; |
||
46 | |||
47 | /** |
||
48 | * @var ValueParserFactory |
||
49 | */ |
||
50 | private $valueParserFactory; |
||
51 | |||
52 | /** |
||
53 | * @var DataTypeValidatorFactory |
||
54 | */ |
||
55 | private $dataTypeValidatorFactory; |
||
56 | |||
57 | /** |
||
58 | * @var ValidatorErrorLocalizer |
||
59 | */ |
||
60 | private $validatorErrorLocalizer; |
||
61 | |||
62 | /** |
||
63 | * @var ExceptionLocalizer |
||
64 | */ |
||
65 | private $exceptionLocalizer; |
||
66 | |||
67 | /** |
||
68 | * @var PropertyDataTypeLookup |
||
69 | */ |
||
70 | private $propertyDataTypeLookup; |
||
71 | |||
72 | /** |
||
73 | * @var ApiErrorReporter |
||
74 | */ |
||
75 | private $errorReporter; |
||
76 | |||
77 | /** |
||
78 | * @see ApiBase::__construct |
||
79 | * |
||
80 | * @param ApiMain $mainModule |
||
81 | * @param string $moduleName |
||
82 | * @param DataTypeFactory $dataTypeFactory |
||
83 | * @param ValueParserFactory $valueParserFactory |
||
84 | * @param DataTypeValidatorFactory $dataTypeValidatorFactory |
||
85 | * @param ExceptionLocalizer $exceptionLocalizer |
||
86 | * @param ValidatorErrorLocalizer $validatorErrorLocalizer |
||
87 | * @param PropertyDataTypeLookup $propertyDataTypeLookup |
||
88 | * @param ApiErrorReporter $errorReporter |
||
89 | */ |
||
90 | public function __construct( |
||
91 | ApiMain $mainModule, |
||
92 | string $moduleName, |
||
93 | DataTypeFactory $dataTypeFactory, |
||
94 | ValueParserFactory $valueParserFactory, |
||
95 | DataTypeValidatorFactory $dataTypeValidatorFactory, |
||
96 | ExceptionLocalizer $exceptionLocalizer, |
||
97 | ValidatorErrorLocalizer $validatorErrorLocalizer, |
||
98 | PropertyDataTypeLookup $propertyDataTypeLookup, |
||
99 | ApiErrorReporter $errorReporter |
||
100 | ) { |
||
101 | parent::__construct( $mainModule, $moduleName ); |
||
102 | $this->dataTypeFactory = $dataTypeFactory; |
||
103 | $this->valueParserFactory = $valueParserFactory; |
||
104 | $this->dataTypeValidatorFactory = $dataTypeValidatorFactory; |
||
105 | $this->exceptionLocalizer = $exceptionLocalizer; |
||
106 | $this->validatorErrorLocalizer = $validatorErrorLocalizer; |
||
107 | $this->propertyDataTypeLookup = $propertyDataTypeLookup; |
||
108 | $this->errorReporter = $errorReporter; |
||
109 | } |
||
110 | |||
111 | public static function factory( ApiMain $mainModule, string $moduleName ): self { |
||
112 | $wikibaseRepo = WikibaseRepo::getDefaultInstance(); |
||
113 | $apiHelperFactory = $wikibaseRepo->getApiHelperFactory( $mainModule->getContext() ); |
||
114 | |||
115 | return new self( |
||
116 | $mainModule, |
||
117 | $moduleName, |
||
118 | $wikibaseRepo->getDataTypeFactory(), |
||
119 | $wikibaseRepo->getValueParserFactory(), |
||
120 | $wikibaseRepo->getDataTypeValidatorFactory(), |
||
121 | $wikibaseRepo->getExceptionLocalizer(), |
||
122 | $wikibaseRepo->getValidatorErrorLocalizer(), |
||
123 | $wikibaseRepo->getPropertyDataTypeLookup(), |
||
124 | $apiHelperFactory->getErrorReporter( $mainModule ) |
||
125 | ); |
||
126 | } |
||
127 | |||
128 | /** |
||
129 | * @inheritDoc |
||
130 | */ |
||
131 | public function execute(): void { |
||
132 | $this->getMain()->setCacheMode( 'public' ); |
||
133 | |||
134 | $parser = $this->getParser(); |
||
135 | |||
136 | $results = []; |
||
137 | |||
138 | $params = $this->extractRequestParams(); |
||
139 | $this->requireMaxOneParameter( $params, 'property', 'datatype', 'parser' ); |
||
140 | $validator = $params['validate'] ? $this->getValidator() : null; |
||
141 | |||
142 | foreach ( $params['values'] as $value ) { |
||
143 | $results[] = $this->parseStringValue( $parser, $value, $validator ); |
||
144 | } |
||
145 | |||
146 | $this->outputResults( $results ); |
||
147 | } |
||
148 | |||
149 | /** |
||
150 | * @return ValueParser |
||
151 | * @throws LogicException |
||
152 | */ |
||
153 | private function getParser(): ValueParser { |
||
154 | $params = $this->extractRequestParams(); |
||
155 | |||
156 | $options = $this->getOptionsObject( $params['options'] ); |
||
157 | |||
158 | // Parsers are registered by datatype. |
||
159 | // Note: parser used to be addressed by a name independent of datatype, using the 'parser' |
||
160 | // parameter. For backwards compatibility, parsers are also registered under their old names |
||
161 | // in the ValueParserFactory (WikibaseRepo::getValueParserFactory). |
||
162 | $name = $params['datatype'] ?: $params['parser']; |
||
163 | |||
164 | if ( empty( $name ) && isset( $params['property'] ) ) { |
||
165 | try { |
||
166 | $propertyId = new PropertyId( $params['property'] ); |
||
167 | } catch ( InvalidArgumentException $ex ) { |
||
168 | $this->errorReporter->dieWithError( |
||
169 | 'wikibase-api-invalid-property-id', |
||
170 | 'param-illegal' |
||
171 | ); |
||
172 | } |
||
173 | try { |
||
174 | $name = $this->propertyDataTypeLookup->getDataTypeIdForProperty( $propertyId ); |
||
175 | } catch ( PropertyDataTypeLookupException $ex ) { |
||
176 | $this->errorReporter->dieWithError( |
||
177 | 'wikibase-api-invalid-property-id', // TODO separate error for valid-but-missing property ID? |
||
178 | 'param-illegal' |
||
179 | ); |
||
180 | } |
||
181 | } |
||
182 | |||
183 | if ( empty( $name ) ) { |
||
184 | // If neither 'datatype' not 'parser' is given, tell the client to use 'datatype'. |
||
185 | $this->errorReporter->dieWithError( |
||
186 | 'wikibase-api-not-recognized-datatype', |
||
187 | 'param-illegal' |
||
188 | ); |
||
189 | } |
||
190 | |||
191 | try { |
||
192 | $parser = $this->valueParserFactory->newParser( $name, $options ); |
||
193 | return $parser; |
||
194 | } catch ( OutOfBoundsException $ex ) { |
||
195 | $this->errorReporter->dieWithError( |
||
196 | 'wikibase-api-not-recognized-datatype', |
||
197 | 'unknown-datatype' |
||
198 | ); |
||
199 | throw new LogicException( 'dieError() did not throw an exception' ); |
||
200 | } |
||
201 | } |
||
202 | |||
203 | private function getValidator(): ValueValidator { |
||
204 | $params = $this->extractRequestParams(); |
||
205 | |||
206 | $name = $params['datatype']; |
||
207 | |||
208 | if ( empty( $name ) && isset( $params['property'] ) ) { |
||
209 | $propertyId = new PropertyId( $params['property'] ); |
||
210 | $name = $this->propertyDataTypeLookup->getDataTypeIdForProperty( $propertyId ); |
||
211 | } |
||
212 | |||
213 | if ( empty( $name ) ) { |
||
214 | // 'datatype' parameter is required for validation. |
||
215 | $this->errorReporter->dieWithError( |
||
216 | 'wikibase-api-not-recognized-datatype', |
||
217 | 'param-illegal' |
||
218 | ); |
||
219 | } |
||
220 | |||
221 | // Note: For unknown datatype, we'll get an empty list. |
||
222 | $validators = $this->dataTypeValidatorFactory->getValidators( $name ); |
||
223 | return $this->wrapValidators( $validators ); |
||
224 | } |
||
225 | |||
226 | /** |
||
227 | * @param ValueValidator[] $validators |
||
228 | * |
||
229 | * @return ValueValidator |
||
230 | */ |
||
231 | private function wrapValidators( array $validators ): ValueValidator { |
||
232 | if ( count( $validators ) === 1 ) { |
||
233 | return reset( $validators ); |
||
234 | } |
||
235 | |||
236 | return new CompositeValidator( $validators, true ); |
||
237 | } |
||
238 | |||
239 | private function parseStringValue( ValueParser $parser, string $value, ?ValueValidator $validator ): array { |
||
240 | $result = [ |
||
241 | 'raw' => $value |
||
242 | ]; |
||
243 | |||
244 | try { |
||
245 | $parseResult = $parser->parse( $value ); |
||
246 | } catch ( ParseException $parseError ) { |
||
247 | $this->addParseErrorToResult( $result, $parseError ); |
||
248 | return $result; |
||
249 | } |
||
250 | |||
251 | if ( $parseResult instanceof DataValue ) { |
||
252 | $result['value'] = $parseResult->getArrayValue(); |
||
253 | $result['type'] = $parseResult->getType(); |
||
254 | } else { |
||
255 | $result['value'] = $parseResult; |
||
256 | } |
||
257 | |||
258 | if ( $validator ) { |
||
259 | $validatorResult = $validator->validate( $parseResult ); |
||
260 | $validationStatus = $this->validatorErrorLocalizer->getResultStatus( $validatorResult ); |
||
261 | |||
262 | $result['valid'] = $validationStatus->isOK(); |
||
263 | |||
264 | if ( !$validationStatus->isOK() ) { |
||
265 | $result['error'] = 'ValidationError'; |
||
266 | $this->errorReporter->addStatusToResult( $validationStatus, $result ); |
||
267 | $result['validation-errors'] = $this->getValidatorErrorCodes( $validatorResult->getErrors() ); |
||
268 | } |
||
269 | } |
||
270 | |||
271 | return $result; |
||
272 | } |
||
273 | |||
274 | /** |
||
275 | * @param Error[] $errors |
||
276 | * |
||
277 | * @return string[] |
||
278 | */ |
||
279 | private function getValidatorErrorCodes( array $errors ): array { |
||
280 | return array_map( |
||
281 | function ( Error $error ) { |
||
282 | return $error->getCode(); |
||
283 | }, |
||
284 | $errors |
||
285 | ); |
||
286 | } |
||
287 | |||
288 | private function addParseErrorToResult( array &$result, ParseException $parseError ): void { |
||
289 | $result['error'] = get_class( $parseError ); |
||
290 | |||
291 | $result['error-info'] = $parseError->getMessage(); |
||
292 | $result['expected-format'] = $parseError->getExpectedFormat(); |
||
293 | |||
294 | $status = $this->getExceptionStatus( $parseError ); |
||
295 | $this->errorReporter->addStatusToResult( $status, $result ); |
||
296 | } |
||
297 | |||
298 | private function outputResults( array $results ): void { |
||
299 | ApiResult::setIndexedTagName( $results, 'result' ); |
||
300 | |||
301 | $this->getResult()->addValue( |
||
302 | null, |
||
303 | 'results', |
||
304 | $results |
||
305 | ); |
||
306 | } |
||
307 | |||
308 | private function getOptionsObject( ?string $optionsParam ): ParserOptions { |
||
309 | $parserOptions = new ParserOptions(); |
||
310 | $parserOptions->setOption( ValueParser::OPT_LANG, $this->getLanguage()->getCode() ); |
||
311 | |||
312 | if ( is_string( $optionsParam ) && $optionsParam !== '' ) { |
||
313 | $options = json_decode( $optionsParam, true ); |
||
314 | |||
315 | if ( !is_array( $options ) ) { |
||
316 | $this->errorReporter->dieError( 'Malformed options parameter', 'malformed-options' ); |
||
0 ignored issues
–
show
|
|||
317 | } |
||
318 | |||
319 | foreach ( $options as $name => $value ) { |
||
320 | $parserOptions->setOption( $name, $value ); |
||
321 | } |
||
322 | } |
||
323 | |||
324 | return $parserOptions; |
||
325 | } |
||
326 | |||
327 | /** |
||
328 | * Returns a Status object representing the given exception using a localized message. |
||
329 | * |
||
330 | * @note The returned Status will always be fatal, that is, $status->isOK() will return false. |
||
331 | * |
||
332 | * @see getExceptionMessage(). |
||
333 | * |
||
334 | * @param Exception $error |
||
335 | * |
||
336 | * @return Status |
||
337 | */ |
||
338 | protected function getExceptionStatus( Exception $error ): Status { |
||
339 | $msg = $this->exceptionLocalizer->getExceptionMessage( $error ); |
||
340 | $status = Status::newFatal( $msg ); |
||
341 | $status->setResult( false, $error->getMessage() ); |
||
342 | |||
343 | return $status; |
||
344 | } |
||
345 | |||
346 | /** |
||
347 | * @inheritDoc |
||
348 | */ |
||
349 | public function getAllowedParams(): array { |
||
350 | return [ |
||
351 | 'datatype' => [ |
||
352 | self::PARAM_TYPE => $this->dataTypeFactory->getTypeIds(), |
||
353 | self::PARAM_REQUIRED => false, |
||
354 | ], |
||
355 | 'property' => [ |
||
356 | self::PARAM_TYPE => 'string', |
||
357 | self::PARAM_REQUIRED => false, |
||
358 | ], |
||
359 | 'parser' => [ |
||
360 | self::PARAM_TYPE => $this->valueParserFactory->getParserIds(), |
||
361 | // Use 'datatype' instead! |
||
362 | self::PARAM_DEPRECATED => true, |
||
363 | self::PARAM_REQUIRED => false, |
||
364 | ], |
||
365 | 'values' => [ |
||
366 | self::PARAM_TYPE => 'string', |
||
367 | self::PARAM_REQUIRED => true, |
||
368 | self::PARAM_ISMULTI => true, |
||
369 | ], |
||
370 | 'options' => [ |
||
371 | self::PARAM_TYPE => 'text', |
||
372 | self::PARAM_REQUIRED => false, |
||
373 | ], |
||
374 | 'validate' => [ |
||
375 | self::PARAM_TYPE => 'boolean', |
||
376 | ], |
||
377 | ]; |
||
378 | } |
||
379 | |||
380 | /** |
||
381 | * @inheritDoc |
||
382 | */ |
||
383 | protected function getExamplesMessages(): array { |
||
384 | return [ |
||
385 | 'action=wbparsevalue&datatype=string&values=foo|bar' => |
||
386 | 'apihelp-wbparsevalue-example-1', |
||
387 | 'action=wbparsevalue&datatype=time&values=1994-02-08&options={"precision":9}' => |
||
388 | 'apihelp-wbparsevalue-example-2', |
||
389 | 'action=wbparsevalue&datatype=time&validate&values=1994-02-08&options={"precision":14}' => |
||
390 | 'apihelp-wbparsevalue-example-3', |
||
391 | 'action=wbparsevalue&property=P123&validate&values=foo' => |
||
392 | 'apihelp-wbparsevalue-example-4', |
||
393 | ]; |
||
394 | } |
||
395 | |||
396 | } |
||
397 |
This method has been deprecated. The supplier of the class has supplied an explanatory message.
The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.