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 | namespace Wikibase\Repo\Api; |
||
| 4 | |||
| 5 | use ApiResult; |
||
| 6 | use Serializers\Serializer; |
||
| 7 | use SiteLookup; |
||
| 8 | use Status; |
||
| 9 | use Wikibase\DataModel\Entity\EntityDocument; |
||
| 10 | use Wikibase\DataModel\Entity\EntityId; |
||
| 11 | use Wikibase\DataModel\Reference; |
||
| 12 | use Wikibase\DataModel\SerializerFactory; |
||
| 13 | use Wikibase\DataModel\Services\Lookup\PropertyDataTypeLookup; |
||
| 14 | use Wikibase\DataModel\SiteLinkList; |
||
| 15 | use Wikibase\DataModel\Statement\Statement; |
||
| 16 | use Wikibase\DataModel\Statement\StatementList; |
||
| 17 | use Wikibase\DataModel\Term\AliasGroupList; |
||
| 18 | use Wikibase\DataModel\Term\TermList; |
||
| 19 | use Wikibase\Lib\Serialization\CallbackFactory; |
||
| 20 | use Wikibase\Lib\Serialization\SerializationModifier; |
||
| 21 | use Wikibase\Lib\Store\EntityRevision; |
||
| 22 | use Wikibase\Lib\Store\EntityTitleLookup; |
||
| 23 | use Wikibase\Lib\TermLanguageFallbackChain; |
||
| 24 | use Wikibase\Repo\Dumpers\JsonDataTypeInjector; |
||
| 25 | use Wikimedia\Assert\Assert; |
||
| 26 | |||
| 27 | /** |
||
| 28 | * Builder of MediaWiki ApiResult objects with various convenience functions for adding Wikibase concepts |
||
| 29 | * and result parts to results in a uniform way. |
||
| 30 | * |
||
| 31 | * This class was introduced when Wikibase was reduced from 2 sets of serializers (lib & data-model) to one. |
||
| 32 | * This class makes various modifications to the 1 standard serialization of Wikibase concepts for public exposure. |
||
| 33 | * The resulting format can be seen as the public serialization of Wikibase concepts. |
||
| 34 | * |
||
| 35 | * Many concepts such as "tag name" relate to concepts explained within ApiResult. |
||
| 36 | * |
||
| 37 | * @license GPL-2.0-or-later |
||
| 38 | * @author Addshore |
||
| 39 | * @author Daniel Kinzler |
||
| 40 | */ |
||
| 41 | class ResultBuilder { |
||
| 42 | |||
| 43 | /** |
||
| 44 | * @var ApiResult |
||
| 45 | */ |
||
| 46 | private $result; |
||
| 47 | |||
| 48 | /** |
||
| 49 | * @var EntityTitleLookup |
||
| 50 | */ |
||
| 51 | private $entityTitleLookup; |
||
| 52 | |||
| 53 | /** |
||
| 54 | * @var SerializerFactory |
||
| 55 | */ |
||
| 56 | private $serializerFactory; |
||
| 57 | |||
| 58 | /** |
||
| 59 | * @var Serializer |
||
| 60 | */ |
||
| 61 | private $entitySerializer; |
||
| 62 | |||
| 63 | /** |
||
| 64 | * @var SiteLookup |
||
| 65 | */ |
||
| 66 | private $siteLookup; |
||
| 67 | |||
| 68 | /** |
||
| 69 | * @var PropertyDataTypeLookup |
||
| 70 | */ |
||
| 71 | private $dataTypeLookup; |
||
| 72 | |||
| 73 | /** |
||
| 74 | * @var bool|null when special elements such as '_element' are needed by the formatter. |
||
| 75 | */ |
||
| 76 | private $addMetaData; |
||
| 77 | |||
| 78 | /** |
||
| 79 | * @var SerializationModifier |
||
| 80 | */ |
||
| 81 | private $modifier; |
||
| 82 | |||
| 83 | /** |
||
| 84 | * @var CallbackFactory |
||
| 85 | */ |
||
| 86 | private $callbackFactory; |
||
| 87 | |||
| 88 | /** |
||
| 89 | * @var int |
||
| 90 | */ |
||
| 91 | private $missingEntityCounter = -1; |
||
| 92 | |||
| 93 | /** |
||
| 94 | * @var JsonDataTypeInjector |
||
| 95 | */ |
||
| 96 | private $dataTypeInjector; |
||
| 97 | |||
| 98 | /** |
||
| 99 | * @param ApiResult $result |
||
| 100 | * @param EntityTitleLookup $entityTitleLookup |
||
| 101 | * @param SerializerFactory $serializerFactory |
||
| 102 | * @param Serializer $entitySerializer |
||
| 103 | * @param SiteLookup $siteLookup |
||
| 104 | * @param PropertyDataTypeLookup $dataTypeLookup |
||
| 105 | * @param bool|null $addMetaData when special elements such as '_element' are needed |
||
| 106 | */ |
||
| 107 | public function __construct( |
||
| 108 | ApiResult $result, |
||
| 109 | EntityTitleLookup $entityTitleLookup, |
||
| 110 | SerializerFactory $serializerFactory, |
||
| 111 | Serializer $entitySerializer, |
||
| 112 | SiteLookup $siteLookup, |
||
| 113 | PropertyDataTypeLookup $dataTypeLookup, |
||
| 114 | $addMetaData = null |
||
| 115 | ) { |
||
| 116 | $this->result = $result; |
||
| 117 | $this->entityTitleLookup = $entityTitleLookup; |
||
| 118 | $this->serializerFactory = $serializerFactory; |
||
| 119 | $this->entitySerializer = $entitySerializer; |
||
| 120 | $this->siteLookup = $siteLookup; |
||
| 121 | $this->dataTypeLookup = $dataTypeLookup; |
||
| 122 | $this->addMetaData = $addMetaData; |
||
| 123 | |||
| 124 | $this->modifier = new SerializationModifier(); |
||
| 125 | $this->callbackFactory = new CallbackFactory(); |
||
| 126 | |||
| 127 | $this->dataTypeInjector = new JsonDataTypeInjector( |
||
| 128 | $this->modifier, |
||
| 129 | $this->callbackFactory, |
||
| 130 | $dataTypeLookup |
||
| 131 | ); |
||
| 132 | } |
||
| 133 | |||
| 134 | /** |
||
| 135 | * Mark the ApiResult as successful. |
||
| 136 | * |
||
| 137 | * { "success": 1 } |
||
| 138 | * |
||
| 139 | * @param bool|int|null $success |
||
| 140 | */ |
||
| 141 | public function markSuccess( $success = true ) { |
||
| 142 | $value = (int)$success; |
||
| 143 | |||
| 144 | Assert::parameter( |
||
| 145 | $value == 1 || $value == 0, |
||
| 146 | '$success', |
||
| 147 | '$success must evaluate to either 1 or 0 when casted to integer' |
||
| 148 | ); |
||
| 149 | |||
| 150 | $this->result->addValue( null, 'success', $value ); |
||
| 151 | } |
||
| 152 | |||
| 153 | /** |
||
| 154 | * Adds a list of values for the given path and name. |
||
| 155 | * This automatically sets the indexed tag name, if appropriate. |
||
| 156 | * |
||
| 157 | * To set atomic values or records, use setValue() or appendValue(). |
||
| 158 | * |
||
| 159 | * @see ApiResult::addValue |
||
| 160 | * @see ApiResult::setIndexedTagName |
||
| 161 | * @see ResultBuilder::setValue() |
||
| 162 | * @see ResultBuilder::appendValue() |
||
| 163 | * |
||
| 164 | * @param array|string|null $path |
||
| 165 | * @param string $name |
||
| 166 | * @param array $values |
||
| 167 | * @param string $tag tag name to use for elements of $values if not already present |
||
| 168 | */ |
||
| 169 | public function setList( $path, $name, array $values, $tag ) { |
||
| 170 | $this->checkPathType( $path ); |
||
| 171 | Assert::parameterType( 'string', $name, '$name' ); |
||
| 172 | Assert::parameterType( 'string', $tag, '$tag' ); |
||
| 173 | |||
| 174 | if ( $this->addMetaData ) { |
||
| 175 | if ( !array_key_exists( ApiResult::META_TYPE, $values ) ) { |
||
| 176 | ApiResult::setArrayType( $values, 'array' ); |
||
| 177 | } |
||
| 178 | if ( !array_key_exists( ApiResult::META_INDEXED_TAG_NAME, $values ) ) { |
||
| 179 | ApiResult::setIndexedTagName( $values, $tag ); |
||
| 180 | } |
||
| 181 | } |
||
| 182 | |||
| 183 | $this->result->addValue( $path, $name, $values ); |
||
| 184 | } |
||
| 185 | |||
| 186 | /** |
||
| 187 | * Set an atomic value (or record) for the given path and name. |
||
| 188 | * If the value is an array, it should be a record (associative), not a list. |
||
| 189 | * For adding lists, use setList(). |
||
| 190 | * |
||
| 191 | * @see ResultBuilder::setList() |
||
| 192 | * @see ResultBuilder::appendValue() |
||
| 193 | * @see ApiResult::addValue |
||
| 194 | * |
||
| 195 | * @param array|string|null $path |
||
| 196 | * @param string $name |
||
| 197 | * @param mixed $value |
||
| 198 | */ |
||
| 199 | public function setValue( $path, $name, $value ) { |
||
| 200 | $this->checkPathType( $path ); |
||
| 201 | Assert::parameterType( 'string', $name, '$name' ); |
||
| 202 | $this->checkValueIsNotList( $value ); |
||
| 203 | |||
| 204 | $this->result->addValue( $path, $name, $value ); |
||
| 205 | } |
||
| 206 | |||
| 207 | /** |
||
| 208 | * Appends a value to the list at the given path. |
||
| 209 | * This automatically sets the indexed tag name, if appropriate. |
||
| 210 | * |
||
| 211 | * If the value is an array, it should be associative, not a list. |
||
| 212 | * For adding lists, use setList(). |
||
| 213 | * |
||
| 214 | * @see ResultBuilder::setList() |
||
| 215 | * @see ResultBuilder::setValue() |
||
| 216 | * @see ApiResult::addValue |
||
| 217 | * @see ApiResult::setIndexedTagName_internal |
||
| 218 | * |
||
| 219 | * @param array|string|null $path |
||
| 220 | * @param int|string|null $key the key to use when appending, or null for automatic. |
||
| 221 | * May be ignored even if given, based on $this->addMetaData. |
||
| 222 | * @param mixed $value |
||
| 223 | * @param string $tag tag name to use for $value in indexed mode |
||
| 224 | */ |
||
| 225 | public function appendValue( $path, $key, $value, $tag ) { |
||
| 226 | $this->checkPathType( $path ); |
||
| 227 | $this->checkKeyType( $key ); |
||
| 228 | Assert::parameterType( 'string', $tag, '$tag' ); |
||
| 229 | $this->checkValueIsNotList( $value ); |
||
| 230 | |||
| 231 | $this->result->addValue( $path, $key, $value ); |
||
| 232 | if ( $this->addMetaData ) { |
||
| 233 | $this->result->addIndexedTagName( $path, $tag ); |
||
| 234 | } |
||
| 235 | } |
||
| 236 | |||
| 237 | /** |
||
| 238 | * @param array|string|null $path |
||
| 239 | */ |
||
| 240 | private function checkPathType( $path ) { |
||
| 241 | Assert::parameter( |
||
| 242 | is_string( $path ) || is_array( $path ) || $path === null, |
||
| 243 | '$path', |
||
| 244 | '$path must be an array (or null)' |
||
| 245 | ); |
||
| 246 | } |
||
| 247 | |||
| 248 | /** |
||
| 249 | * @param int|string|null $key the key to use when appending, or null for automatic. |
||
| 250 | */ |
||
| 251 | private function checkKeyType( $key ) { |
||
| 252 | Assert::parameter( |
||
| 253 | is_string( $key ) || is_int( $key ) || $key === null, |
||
| 254 | '$key', |
||
| 255 | '$key must be an array (or null)' |
||
| 256 | ); |
||
| 257 | } |
||
| 258 | |||
| 259 | /** |
||
| 260 | * @param mixed $value |
||
| 261 | */ |
||
| 262 | private function checkValueIsNotList( $value ) { |
||
| 263 | Assert::parameter( |
||
| 264 | !( is_array( $value ) && isset( $value[0] ) ), |
||
| 265 | '$value', |
||
| 266 | '$value must not be a list' |
||
| 267 | ); |
||
| 268 | } |
||
| 269 | |||
| 270 | /** |
||
| 271 | * Get serialized entity for the EntityRevision and add it to the result alongside other needed properties. |
||
| 272 | * |
||
| 273 | * |
||
| 274 | * @param string|null $sourceEntityIdSerialization EntityId used to retrieve $entityRevision |
||
| 275 | * Used as the key for the entity in the 'entities' structure and for adding redirect |
||
| 276 | * info Will default to the entity's serialized ID if null. If given this must be the |
||
| 277 | * entity id before any redirects were resolved. |
||
| 278 | * @param EntityRevision $entityRevision |
||
| 279 | * @param string[]|string $props a list of fields to include, or "all" |
||
| 280 | * @param string[]|null $filterSiteIds A list of site IDs to filter by |
||
| 281 | * @param string[] $filterLangCodes A list of language codes to filter by |
||
| 282 | * @param TermLanguageFallbackChain[] $termFallbackChains with keys of the origional language |
||
| 283 | */ |
||
| 284 | public function addEntityRevision( |
||
| 285 | $sourceEntityIdSerialization, |
||
| 286 | EntityRevision $entityRevision, |
||
| 287 | $props = 'all', |
||
| 288 | array $filterSiteIds = null, |
||
| 289 | array $filterLangCodes = [], |
||
| 290 | array $termFallbackChains = [] |
||
| 291 | ) { |
||
| 292 | $entity = $entityRevision->getEntity(); |
||
| 293 | $entityId = $entity->getId(); |
||
| 294 | |||
| 295 | if ( $sourceEntityIdSerialization === null ) { |
||
| 296 | $sourceEntityIdSerialization = $entityId->getSerialization(); |
||
| 297 | } |
||
| 298 | |||
| 299 | $record = []; |
||
| 300 | |||
| 301 | // If there are no props defined only return type and id.. |
||
| 302 | // @phan-suppress-next-line PhanTypeComparisonToArray |
||
| 303 | if ( $props === [] ) { |
||
| 304 | $record = $this->addEntityInfoToRecord( $record, $entityId ); |
||
|
0 ignored issues
–
show
|
|||
| 305 | } else { |
||
| 306 | // @phan-suppress-next-line PhanTypeMismatchArgumentInternal False positive |
||
| 307 | if ( $props == 'all' || in_array( 'info', $props ) ) { |
||
| 308 | $record = $this->addPageInfoToRecord( $record, $entityRevision ); |
||
| 309 | } |
||
| 310 | if ( $sourceEntityIdSerialization !== $entityId->getSerialization() ) { |
||
| 311 | $record = $this->addEntityRedirectInfoToRecord( $record, $sourceEntityIdSerialization, $entityId ); |
||
|
0 ignored issues
–
show
It seems like
$entityId defined by $entity->getId() on line 293 can be null; however, Wikibase\Repo\Api\Result...yRedirectInfoToRecord() does not accept null, maybe add an additional type check?
Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code: /** @return stdClass|null */
function mayReturnNull() { }
function doesNotAcceptNull(stdClass $x) { }
// With potential error.
function withoutCheck() {
$x = mayReturnNull();
doesNotAcceptNull($x); // Potential error here.
}
// Safe - Alternative 1
function withCheck1() {
$x = mayReturnNull();
if ( ! $x instanceof stdClass) {
throw new \LogicException('$x must be defined.');
}
doesNotAcceptNull($x);
}
// Safe - Alternative 2
function withCheck2() {
$x = mayReturnNull();
if ($x instanceof stdClass) {
doesNotAcceptNull($x);
}
}
Loading history...
|
|||
| 312 | } |
||
| 313 | |||
| 314 | $entitySerialization = $this->getModifiedEntityArray( |
||
| 315 | $entity, |
||
| 316 | $props, |
||
| 317 | $filterSiteIds, |
||
| 318 | $filterLangCodes, |
||
| 319 | $termFallbackChains |
||
| 320 | ); |
||
| 321 | |||
| 322 | $record = array_merge( $record, $entitySerialization ); |
||
| 323 | } |
||
| 324 | |||
| 325 | $this->appendValue( [ 'entities' ], $sourceEntityIdSerialization, $record, 'entity' ); |
||
| 326 | if ( $this->addMetaData ) { |
||
| 327 | $this->result->addArrayType( [ 'entities' ], 'kvp', 'id' ); |
||
| 328 | $this->result->addValue( |
||
| 329 | [ 'entities' ], |
||
| 330 | ApiResult::META_KVP_MERGE, |
||
| 331 | true, |
||
| 332 | ApiResult::OVERRIDE |
||
| 333 | ); |
||
| 334 | } |
||
| 335 | } |
||
| 336 | |||
| 337 | private function addEntityInfoToRecord( array $record, EntityId $entityId ): array { |
||
| 338 | $record['id'] = $entityId->getSerialization(); |
||
| 339 | $record['type'] = $entityId->getEntityType(); |
||
| 340 | return $record; |
||
| 341 | } |
||
| 342 | |||
| 343 | private function addPageInfoToRecord( array $record, EntityRevision $entityRevision ): array { |
||
| 344 | $title = $this->entityTitleLookup->getTitleForId( $entityRevision->getEntity()->getId() ); |
||
|
0 ignored issues
–
show
It seems like
$entityRevision->getEntity()->getId() can be null; however, getTitleForId() does not accept null, maybe add an additional type check?
Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code: /** @return stdClass|null */
function mayReturnNull() { }
function doesNotAcceptNull(stdClass $x) { }
// With potential error.
function withoutCheck() {
$x = mayReturnNull();
doesNotAcceptNull($x); // Potential error here.
}
// Safe - Alternative 1
function withCheck1() {
$x = mayReturnNull();
if ( ! $x instanceof stdClass) {
throw new \LogicException('$x must be defined.');
}
doesNotAcceptNull($x);
}
// Safe - Alternative 2
function withCheck2() {
$x = mayReturnNull();
if ($x instanceof stdClass) {
doesNotAcceptNull($x);
}
}
Loading history...
|
|||
| 345 | $record['pageid'] = $title->getArticleID(); |
||
| 346 | $record['ns'] = $title->getNamespace(); |
||
| 347 | $record['title'] = $title->getPrefixedText(); |
||
| 348 | $record['lastrevid'] = $entityRevision->getRevisionId(); |
||
| 349 | $record['modified'] = wfTimestamp( TS_ISO_8601, $entityRevision->getTimestamp() ); |
||
| 350 | return $record; |
||
| 351 | } |
||
| 352 | |||
| 353 | private function addEntityRedirectInfoToRecord( array $record, $sourceEntityIdSerialization, EntityId $entityId ): array { |
||
| 354 | $record['redirects'] = [ |
||
| 355 | 'from' => $sourceEntityIdSerialization, |
||
| 356 | 'to' => $entityId->getSerialization() |
||
| 357 | ]; |
||
| 358 | return $record; |
||
| 359 | } |
||
| 360 | |||
| 361 | /** |
||
| 362 | * Gets the standard serialization of an EntityDocument and modifies it in a standard way. |
||
| 363 | * |
||
| 364 | * This code was created for Items and Properties and since new entity types have been introduced |
||
| 365 | * it may not work in the desired way. |
||
| 366 | * @see https://phabricator.wikimedia.org/T249206 |
||
| 367 | * |
||
| 368 | * @see ResultBuilder::addEntityRevision |
||
| 369 | * |
||
| 370 | * @param EntityDocument $entity |
||
| 371 | * @param array|string $props |
||
| 372 | * @param string[]|null $filterSiteIds |
||
| 373 | * @param string[] $filterLangCodes |
||
| 374 | * @param TermLanguageFallbackChain[] $termFallbackChains |
||
| 375 | * |
||
| 376 | * @return array |
||
| 377 | */ |
||
| 378 | private function getModifiedEntityArray( |
||
| 379 | EntityDocument $entity, |
||
| 380 | $props, |
||
| 381 | ?array $filterSiteIds, |
||
| 382 | array $filterLangCodes, |
||
| 383 | array $termFallbackChains |
||
| 384 | ) { |
||
| 385 | $serialization = $this->entitySerializer->serialize( $entity ); |
||
| 386 | |||
| 387 | $serialization = $this->filterEntitySerializationUsingProps( $serialization, $props ); |
||
| 388 | |||
| 389 | if ( $props == 'all' || in_array( 'sitelinks/urls', $props ) ) { |
||
| 390 | $serialization = $this->injectEntitySerializationWithSiteLinkUrls( $serialization ); |
||
| 391 | } |
||
| 392 | $serialization = $this->sortEntitySerializationSiteLinks( $serialization ); |
||
| 393 | $serialization = $this->dataTypeInjector->injectEntitySerializationWithDataTypes( $serialization ); |
||
| 394 | $serialization = $this->filterEntitySerializationUsingSiteIds( $serialization, $filterSiteIds ); |
||
| 395 | if ( !empty( $termFallbackChains ) ) { |
||
| 396 | $serialization = $this->addEntitySerializationFallbackInfo( $serialization, $termFallbackChains ); |
||
| 397 | } |
||
| 398 | $serialization = $this->filterEntitySerializationUsingLangCodes( |
||
| 399 | $serialization, |
||
| 400 | $filterLangCodes |
||
| 401 | ); |
||
| 402 | |||
| 403 | if ( $this->addMetaData ) { |
||
| 404 | $serialization = $this->getEntitySerializationWithMetaData( $serialization ); |
||
| 405 | } |
||
| 406 | |||
| 407 | return $serialization; |
||
| 408 | } |
||
| 409 | |||
| 410 | /** |
||
| 411 | * @param array $serialization |
||
| 412 | * @param string|array $props |
||
| 413 | * |
||
| 414 | * @return array |
||
| 415 | */ |
||
| 416 | private function filterEntitySerializationUsingProps( array $serialization, $props ) { |
||
| 417 | if ( $props !== 'all' ) { |
||
| 418 | if ( !in_array( 'labels', $props ) ) { |
||
| 419 | unset( $serialization['labels'] ); |
||
| 420 | } |
||
| 421 | if ( !in_array( 'descriptions', $props ) ) { |
||
| 422 | unset( $serialization['descriptions'] ); |
||
| 423 | } |
||
| 424 | if ( !in_array( 'aliases', $props ) ) { |
||
| 425 | unset( $serialization['aliases'] ); |
||
| 426 | } |
||
| 427 | if ( !in_array( 'claims', $props ) ) { |
||
| 428 | unset( $serialization['claims'] ); |
||
| 429 | } |
||
| 430 | if ( !in_array( 'sitelinks', $props ) ) { |
||
| 431 | unset( $serialization['sitelinks'] ); |
||
| 432 | } |
||
| 433 | } |
||
| 434 | return $serialization; |
||
| 435 | } |
||
| 436 | |||
| 437 | private function injectEntitySerializationWithSiteLinkUrls( array $serialization ) { |
||
| 438 | if ( isset( $serialization['sitelinks'] ) ) { |
||
| 439 | $serialization['sitelinks'] = $this->getSiteLinkListArrayWithUrls( $serialization['sitelinks'] ); |
||
| 440 | } |
||
| 441 | return $serialization; |
||
| 442 | } |
||
| 443 | |||
| 444 | private function sortEntitySerializationSiteLinks( array $serialization ) { |
||
| 445 | if ( isset( $serialization['sitelinks'] ) ) { |
||
| 446 | ksort( $serialization['sitelinks'] ); |
||
| 447 | } |
||
| 448 | return $serialization; |
||
| 449 | } |
||
| 450 | |||
| 451 | private function filterEntitySerializationUsingSiteIds( |
||
| 452 | array $serialization, |
||
| 453 | array $siteIds = null |
||
| 454 | ) { |
||
| 455 | if ( !empty( $siteIds ) && array_key_exists( 'sitelinks', $serialization ) ) { |
||
| 456 | foreach ( $serialization['sitelinks'] as $siteId => $siteLink ) { |
||
| 457 | if ( is_array( $siteLink ) && !in_array( $siteLink['site'], $siteIds ) ) { |
||
| 458 | unset( $serialization['sitelinks'][$siteId] ); |
||
| 459 | } |
||
| 460 | } |
||
| 461 | } |
||
| 462 | return $serialization; |
||
| 463 | } |
||
| 464 | |||
| 465 | /** |
||
| 466 | * @param array $serialization |
||
| 467 | * @param TermLanguageFallbackChain[] $termFallbackChains |
||
| 468 | * |
||
| 469 | * @return array |
||
| 470 | */ |
||
| 471 | private function addEntitySerializationFallbackInfo( |
||
| 472 | array $serialization, |
||
| 473 | array $termFallbackChains |
||
| 474 | ) { |
||
| 475 | if ( isset( $serialization['labels'] ) ) { |
||
| 476 | $serialization['labels'] = $this->getTermsSerializationWithFallbackInfo( |
||
| 477 | $serialization['labels'], |
||
| 478 | $termFallbackChains |
||
| 479 | ); |
||
| 480 | } |
||
| 481 | |||
| 482 | if ( isset( $serialization['descriptions'] ) ) { |
||
| 483 | $serialization['descriptions'] = $this->getTermsSerializationWithFallbackInfo( |
||
| 484 | $serialization['descriptions'], |
||
| 485 | $termFallbackChains |
||
| 486 | ); |
||
| 487 | } |
||
| 488 | |||
| 489 | return $serialization; |
||
| 490 | } |
||
| 491 | |||
| 492 | /** |
||
| 493 | * @param array $serialization |
||
| 494 | * @param TermLanguageFallbackChain[] $termFallbackChains |
||
| 495 | * |
||
| 496 | * @return array |
||
| 497 | */ |
||
| 498 | private function getTermsSerializationWithFallbackInfo( |
||
| 499 | array $serialization, |
||
| 500 | array $termFallbackChains |
||
| 501 | ) { |
||
| 502 | $newSerialization = $serialization; |
||
| 503 | foreach ( $termFallbackChains as $requestedLanguageCode => $fallbackChain ) { |
||
| 504 | if ( !array_key_exists( $requestedLanguageCode, $serialization ) ) { |
||
| 505 | $fallbackSerialization = $fallbackChain->extractPreferredValue( $serialization ); |
||
| 506 | if ( $fallbackSerialization !== null ) { |
||
| 507 | if ( $fallbackSerialization['source'] !== null ) { |
||
| 508 | $fallbackSerialization['source-language'] = $fallbackSerialization['source']; |
||
| 509 | } |
||
| 510 | unset( $fallbackSerialization['source'] ); |
||
| 511 | if ( $requestedLanguageCode !== $fallbackSerialization['language'] ) { |
||
|
0 ignored issues
–
show
|
|||
| 512 | $fallbackSerialization['for-language'] = $requestedLanguageCode; |
||
| 513 | } |
||
| 514 | $newSerialization[$requestedLanguageCode] = $fallbackSerialization; |
||
| 515 | } |
||
| 516 | } |
||
| 517 | } |
||
| 518 | return $newSerialization; |
||
| 519 | } |
||
| 520 | |||
| 521 | /** |
||
| 522 | * @param array $serialization |
||
| 523 | * @param string[] $langCodes |
||
| 524 | * |
||
| 525 | * @return array |
||
| 526 | */ |
||
| 527 | private function filterEntitySerializationUsingLangCodes( |
||
| 528 | array $serialization, |
||
| 529 | array $langCodes |
||
| 530 | ) { |
||
| 531 | if ( !empty( $langCodes ) ) { |
||
| 532 | if ( array_key_exists( 'labels', $serialization ) ) { |
||
| 533 | foreach ( $serialization['labels'] as $langCode => $languageArray ) { |
||
| 534 | if ( !in_array( $langCode, $langCodes ) ) { |
||
| 535 | unset( $serialization['labels'][$langCode] ); |
||
| 536 | } |
||
| 537 | } |
||
| 538 | } |
||
| 539 | if ( array_key_exists( 'descriptions', $serialization ) ) { |
||
| 540 | foreach ( $serialization['descriptions'] as $langCode => $languageArray ) { |
||
| 541 | if ( !in_array( $langCode, $langCodes ) ) { |
||
| 542 | unset( $serialization['descriptions'][$langCode] ); |
||
| 543 | } |
||
| 544 | } |
||
| 545 | } |
||
| 546 | if ( array_key_exists( 'aliases', $serialization ) ) { |
||
| 547 | foreach ( $serialization['aliases'] as $langCode => $languageArray ) { |
||
| 548 | if ( !in_array( $langCode, $langCodes ) ) { |
||
| 549 | unset( $serialization['aliases'][$langCode] ); |
||
| 550 | } |
||
| 551 | } |
||
| 552 | } |
||
| 553 | } |
||
| 554 | return $serialization; |
||
| 555 | } |
||
| 556 | |||
| 557 | private function getEntitySerializationWithMetaData( array $serialization ) { |
||
| 558 | $arrayTypes = [ |
||
| 559 | 'aliases' => 'id', |
||
| 560 | 'claims/*/*/references/*/snaks' => 'id', |
||
| 561 | 'claims/*/*/qualifiers' => 'id', |
||
| 562 | 'claims' => 'id', |
||
| 563 | 'descriptions' => 'language', |
||
| 564 | 'labels' => 'language', |
||
| 565 | 'sitelinks' => 'site', |
||
| 566 | ]; |
||
| 567 | foreach ( $arrayTypes as $path => $keyName ) { |
||
| 568 | $serialization = $this->modifier->modifyUsingCallback( |
||
| 569 | $serialization, |
||
| 570 | $path, |
||
| 571 | $this->callbackFactory->getCallbackToSetArrayType( 'kvp', $keyName ) |
||
| 572 | ); |
||
| 573 | } |
||
| 574 | |||
| 575 | $kvpMergeArrays = [ |
||
| 576 | 'descriptions', |
||
| 577 | 'labels', |
||
| 578 | 'sitelinks', |
||
| 579 | ]; |
||
| 580 | foreach ( $kvpMergeArrays as $path ) { |
||
| 581 | $serialization = $this->modifier->modifyUsingCallback( |
||
| 582 | $serialization, |
||
| 583 | $path, |
||
| 584 | function( $array ) { |
||
| 585 | if ( is_array( $array ) ) { |
||
| 586 | $array[ApiResult::META_KVP_MERGE] = true; |
||
| 587 | } |
||
| 588 | return $array; |
||
| 589 | } |
||
| 590 | ); |
||
| 591 | } |
||
| 592 | |||
| 593 | $indexTags = [ |
||
| 594 | 'labels' => 'label', |
||
| 595 | 'descriptions' => 'description', |
||
| 596 | 'aliases/*' => 'alias', |
||
| 597 | 'aliases' => 'language', |
||
| 598 | 'sitelinks/*/badges' => 'badge', |
||
| 599 | 'sitelinks' => 'sitelink', |
||
| 600 | 'claims/*/*/qualifiers/*' => 'qualifiers', |
||
| 601 | 'claims/*/*/qualifiers' => 'property', |
||
| 602 | 'claims/*/*/qualifiers-order' => 'property', |
||
| 603 | 'claims/*/*/references/*/snaks/*' => 'snak', |
||
| 604 | 'claims/*/*/references/*/snaks' => 'property', |
||
| 605 | 'claims/*/*/references/*/snaks-order' => 'property', |
||
| 606 | 'claims/*/*/references' => 'reference', |
||
| 607 | 'claims/*' => 'claim', |
||
| 608 | 'claims' => 'property', |
||
| 609 | ]; |
||
| 610 | foreach ( $indexTags as $path => $tag ) { |
||
| 611 | $serialization = $this->modifier->modifyUsingCallback( |
||
| 612 | $serialization, |
||
| 613 | $path, |
||
| 614 | $this->callbackFactory->getCallbackToIndexTags( $tag ) |
||
| 615 | ); |
||
| 616 | } |
||
| 617 | |||
| 618 | return $serialization; |
||
| 619 | } |
||
| 620 | |||
| 621 | /** |
||
| 622 | * Get serialized information for the EntityId and add them to result |
||
| 623 | * |
||
| 624 | * @param EntityId $entityId |
||
| 625 | * @param string|array|null $path |
||
| 626 | */ |
||
| 627 | public function addBasicEntityInformation( EntityId $entityId, $path ) { |
||
| 628 | $this->setValue( $path, 'id', $entityId->getSerialization() ); |
||
| 629 | $this->setValue( $path, 'type', $entityId->getEntityType() ); |
||
| 630 | } |
||
| 631 | |||
| 632 | /** |
||
| 633 | * Get serialized labels and add them to result |
||
| 634 | * |
||
| 635 | * @param TermList $labels the labels to insert in the result |
||
| 636 | * @param array|string $path where the data is located |
||
| 637 | */ |
||
| 638 | public function addLabels( TermList $labels, $path ) { |
||
| 639 | $this->addTermList( $labels, 'labels', 'label', $path ); |
||
| 640 | } |
||
| 641 | |||
| 642 | /** |
||
| 643 | * Adds fake serialization to show a label has been removed |
||
| 644 | * |
||
| 645 | * @param string $language |
||
| 646 | * @param array|string $path where the data is located |
||
| 647 | */ |
||
| 648 | public function addRemovedLabel( $language, $path ) { |
||
| 649 | $this->addRemovedTerm( $language, 'labels', 'label', $path ); |
||
| 650 | } |
||
| 651 | |||
| 652 | /** |
||
| 653 | * Get serialized descriptions and add them to result |
||
| 654 | * |
||
| 655 | * @param TermList $descriptions the descriptions to insert in the result |
||
| 656 | * @param array|string $path where the data is located |
||
| 657 | */ |
||
| 658 | public function addDescriptions( TermList $descriptions, $path ) { |
||
| 659 | $this->addTermList( $descriptions, 'descriptions', 'description', $path ); |
||
| 660 | } |
||
| 661 | |||
| 662 | /** |
||
| 663 | * Adds fake serialization to show a label has been removed |
||
| 664 | * |
||
| 665 | * @param string $language |
||
| 666 | * @param array|string $path where the data is located |
||
| 667 | */ |
||
| 668 | public function addRemovedDescription( $language, $path ) { |
||
| 669 | $this->addRemovedTerm( $language, 'descriptions', 'description', $path ); |
||
| 670 | } |
||
| 671 | |||
| 672 | /** |
||
| 673 | * Get serialized TermList and add it to the result |
||
| 674 | * |
||
| 675 | * @param TermList $termList |
||
| 676 | * @param string $name |
||
| 677 | * @param string $tag |
||
| 678 | * @param array|string $path where the data is located |
||
| 679 | */ |
||
| 680 | private function addTermList( TermList $termList, $name, $tag, $path ) { |
||
| 681 | $serializer = $this->serializerFactory->newTermListSerializer(); |
||
| 682 | $value = $serializer->serialize( $termList ); |
||
| 683 | if ( $this->addMetaData ) { |
||
| 684 | ApiResult::setArrayType( $value, 'kvp', 'language' ); |
||
| 685 | $value[ApiResult::META_KVP_MERGE] = true; |
||
| 686 | } |
||
| 687 | $this->setList( $path, $name, $value, $tag ); |
||
| 688 | } |
||
| 689 | |||
| 690 | /** |
||
| 691 | * Adds fake serialization to show a term has been removed |
||
| 692 | * |
||
| 693 | * @param string $language |
||
| 694 | * @param string $name |
||
| 695 | * @param string $tag |
||
| 696 | * @param array|string $path where the data is located |
||
| 697 | */ |
||
| 698 | private function addRemovedTerm( $language, $name, $tag, $path ) { |
||
| 699 | $value = [ |
||
| 700 | $language => [ |
||
| 701 | 'language' => $language, |
||
| 702 | 'removed' => '', |
||
| 703 | ] |
||
| 704 | ]; |
||
| 705 | if ( $this->addMetaData ) { |
||
| 706 | ApiResult::setArrayType( $value, 'kvp', 'language' ); |
||
| 707 | $value[ApiResult::META_KVP_MERGE] = true; |
||
| 708 | } |
||
| 709 | $this->setList( $path, $name, $value, $tag ); |
||
| 710 | } |
||
| 711 | |||
| 712 | /** |
||
| 713 | * Get serialized AliasGroupList and add it to result |
||
| 714 | * |
||
| 715 | * @param AliasGroupList $aliasGroupList the AliasGroupList to set in the result |
||
| 716 | * @param array|string $path where the data is located |
||
| 717 | */ |
||
| 718 | public function addAliasGroupList( AliasGroupList $aliasGroupList, $path ) { |
||
| 719 | $serializer = $this->serializerFactory->newAliasGroupListSerializer(); |
||
| 720 | $values = $serializer->serialize( $aliasGroupList ); |
||
| 721 | |||
| 722 | if ( $this->addMetaData ) { |
||
| 723 | $values = $this->modifier->modifyUsingCallback( |
||
| 724 | $values, |
||
| 725 | null, |
||
| 726 | $this->callbackFactory->getCallbackToSetArrayType( 'kvp', 'id' ) |
||
| 727 | ); |
||
| 728 | $values = $this->modifier->modifyUsingCallback( |
||
| 729 | $values, |
||
| 730 | '*', |
||
| 731 | $this->callbackFactory->getCallbackToIndexTags( 'alias' ) |
||
| 732 | ); |
||
| 733 | } |
||
| 734 | |||
| 735 | $this->setList( $path, 'aliases', $values, 'language' ); |
||
| 736 | ApiResult::setArrayType( $values, 'kvp', 'id' ); |
||
| 737 | } |
||
| 738 | |||
| 739 | /** |
||
| 740 | * Get serialized sitelinks and add them to result |
||
| 741 | * |
||
| 742 | * @todo use a SiteLinkListSerializer when created in DataModelSerialization here |
||
| 743 | * |
||
| 744 | * @param SiteLinkList $siteLinkList the site links to insert in the result |
||
| 745 | * @param array|string $path where the data is located |
||
| 746 | * @param bool $addUrl |
||
| 747 | */ |
||
| 748 | public function addSiteLinkList( SiteLinkList $siteLinkList, $path, $addUrl = false ) { |
||
| 749 | $serializer = $this->serializerFactory->newSiteLinkSerializer(); |
||
| 750 | |||
| 751 | $values = []; |
||
| 752 | foreach ( $siteLinkList->toArray() as $siteLink ) { |
||
| 753 | $values[$siteLink->getSiteId()] = $serializer->serialize( $siteLink ); |
||
| 754 | } |
||
| 755 | |||
| 756 | if ( $addUrl ) { |
||
| 757 | $values = $this->getSiteLinkListArrayWithUrls( $values ); |
||
| 758 | } |
||
| 759 | |||
| 760 | if ( $this->addMetaData ) { |
||
| 761 | $values = $this->getSiteLinkListArrayWithMetaData( $values ); |
||
| 762 | } |
||
| 763 | |||
| 764 | $this->setList( $path, 'sitelinks', $values, 'sitelink' ); |
||
| 765 | } |
||
| 766 | |||
| 767 | private function getSiteLinkListArrayWithUrls( array $array ) { |
||
| 768 | $siteLookup = $this->siteLookup; |
||
| 769 | $addUrlCallback = function( $array ) use ( $siteLookup ) { |
||
| 770 | $site = $siteLookup->getSite( $array['site'] ); |
||
| 771 | if ( $site !== null ) { |
||
| 772 | $array['url'] = $site->getPageUrl( $array['title'] ); |
||
| 773 | } |
||
| 774 | return $array; |
||
| 775 | }; |
||
| 776 | return $this->modifier->modifyUsingCallback( $array, '*', $addUrlCallback ); |
||
| 777 | } |
||
| 778 | |||
| 779 | private function getSiteLinkListArrayWithMetaData( array $array ) { |
||
| 780 | $array = $this->modifier->modifyUsingCallback( |
||
| 781 | $array, |
||
| 782 | null, |
||
| 783 | $this->callbackFactory->getCallbackToSetArrayType( 'kvp', 'site' ) |
||
| 784 | ); |
||
| 785 | $array[ApiResult::META_KVP_MERGE] = true; |
||
| 786 | $array = $this->modifier->modifyUsingCallback( |
||
| 787 | $array, |
||
| 788 | '*/badges', |
||
| 789 | $this->callbackFactory->getCallbackToIndexTags( 'badge' ) |
||
| 790 | ); |
||
| 791 | return $array; |
||
| 792 | } |
||
| 793 | |||
| 794 | /** |
||
| 795 | * Adds fake serialization to show a sitelink has been removed |
||
| 796 | * |
||
| 797 | * @param SiteLinkList $siteLinkList |
||
| 798 | * @param array|string $path where the data is located |
||
| 799 | */ |
||
| 800 | public function addRemovedSiteLinks( SiteLinkList $siteLinkList, $path ) { |
||
| 801 | $serializer = $this->serializerFactory->newSiteLinkSerializer(); |
||
| 802 | $values = []; |
||
| 803 | foreach ( $siteLinkList->toArray() as $siteLink ) { |
||
| 804 | $value = $serializer->serialize( $siteLink ); |
||
| 805 | $value['removed'] = ''; |
||
| 806 | $values[$siteLink->getSiteId()] = $value; |
||
| 807 | } |
||
| 808 | if ( $this->addMetaData ) { |
||
| 809 | $values = $this->modifier->modifyUsingCallback( |
||
| 810 | $values, |
||
| 811 | null, |
||
| 812 | $this->callbackFactory->getCallbackToSetArrayType( 'kvp', 'site' ) |
||
| 813 | ); |
||
| 814 | $values[ApiResult::META_KVP_MERGE] = true; |
||
| 815 | } |
||
| 816 | $this->setList( $path, 'sitelinks', $values, 'sitelink' ); |
||
| 817 | } |
||
| 818 | |||
| 819 | /** |
||
| 820 | * Get serialized claims and add them to result |
||
| 821 | * |
||
| 822 | * @param StatementList $statements the labels to set in the result |
||
| 823 | * @param array|string $path where the data is located |
||
| 824 | * @param array|string $props a list of fields to include, or "all" |
||
| 825 | */ |
||
| 826 | public function addStatements( StatementList $statements, $path, $props = 'all' ) { |
||
| 827 | $serializer = $this->serializerFactory->newStatementListSerializer(); |
||
| 828 | |||
| 829 | $values = $serializer->serialize( $statements ); |
||
| 830 | |||
| 831 | if ( is_array( $props ) && !in_array( 'references', $props ) ) { |
||
| 832 | $values = $this->modifier->modifyUsingCallback( |
||
| 833 | $values, |
||
| 834 | '*/*', |
||
| 835 | function ( $array ) { |
||
| 836 | unset( $array['references'] ); |
||
| 837 | return $array; |
||
| 838 | } |
||
| 839 | ); |
||
| 840 | } |
||
| 841 | |||
| 842 | $values = $this->getArrayWithAlteredClaims( $values, '*/*/' ); |
||
| 843 | |||
| 844 | if ( $this->addMetaData ) { |
||
| 845 | $values = $this->getClaimsArrayWithMetaData( $values, '*/*/' ); |
||
| 846 | $values = $this->modifier->modifyUsingCallback( |
||
| 847 | $values, |
||
| 848 | null, |
||
| 849 | $this->callbackFactory->getCallbackToSetArrayType( 'kvp', 'id' ) |
||
| 850 | ); |
||
| 851 | $values = $this->modifier->modifyUsingCallback( |
||
| 852 | $values, |
||
| 853 | '*', |
||
| 854 | $this->callbackFactory->getCallbackToIndexTags( 'claim' ) |
||
| 855 | ); |
||
| 856 | } |
||
| 857 | |||
| 858 | $values = $this->dataTypeInjector->getArrayWithDataTypesInSnakAtPath( |
||
| 859 | $values, |
||
| 860 | '*/*/mainsnak' |
||
| 861 | ); |
||
| 862 | |||
| 863 | if ( $this->addMetaData ) { |
||
| 864 | ApiResult::setArrayType( $values, 'kvp', 'id' ); |
||
| 865 | } |
||
| 866 | |||
| 867 | $this->setList( $path, 'claims', $values, 'property' ); |
||
| 868 | } |
||
| 869 | |||
| 870 | /** |
||
| 871 | * Get serialized claim and add it to result |
||
| 872 | * |
||
| 873 | * @param Statement $statement |
||
| 874 | */ |
||
| 875 | public function addStatement( Statement $statement ) { |
||
| 876 | $serializer = $this->serializerFactory->newStatementSerializer(); |
||
| 877 | |||
| 878 | //TODO: this is currently only used to add a Claim as the top level structure, |
||
| 879 | // with a null path and a fixed name. Would be nice to also allow claims |
||
| 880 | // to be added to a list, using a path and a id key or index. |
||
| 881 | |||
| 882 | $value = $serializer->serialize( $statement ); |
||
| 883 | |||
| 884 | $value = $this->getArrayWithAlteredClaims( $value ); |
||
| 885 | |||
| 886 | if ( $this->addMetaData ) { |
||
| 887 | $value = $this->getClaimsArrayWithMetaData( $value ); |
||
| 888 | } |
||
| 889 | |||
| 890 | $value = $this->dataTypeInjector->getArrayWithDataTypesInSnakAtPath( |
||
| 891 | $value, |
||
| 892 | 'mainsnak' |
||
| 893 | ); |
||
| 894 | |||
| 895 | $this->setValue( null, 'claim', $value ); |
||
| 896 | } |
||
| 897 | |||
| 898 | /** |
||
| 899 | * @param array $array |
||
| 900 | * @param string $claimPath to the claim array/arrays with trailing / |
||
| 901 | * |
||
| 902 | * @return array |
||
| 903 | */ |
||
| 904 | private function getArrayWithAlteredClaims( |
||
| 905 | array $array, |
||
| 906 | $claimPath = '' |
||
| 907 | ) { |
||
| 908 | $array = $this->dataTypeInjector->getArrayWithDataTypesInGroupedSnakListAtPath( |
||
| 909 | $array, |
||
| 910 | $claimPath . 'references/*/snaks' |
||
| 911 | ); |
||
| 912 | $array = $this->dataTypeInjector->getArrayWithDataTypesInGroupedSnakListAtPath( |
||
| 913 | $array, |
||
| 914 | $claimPath . 'qualifiers' |
||
| 915 | ); |
||
| 916 | |||
| 917 | $array = $this->dataTypeInjector->getArrayWithDataTypesInSnakAtPath( |
||
| 918 | $array, |
||
| 919 | $claimPath . 'mainsnak' |
||
| 920 | ); |
||
| 921 | |||
| 922 | return $array; |
||
| 923 | } |
||
| 924 | |||
| 925 | /** |
||
| 926 | * @param array $array |
||
| 927 | * @param string $claimPath to the claim array/arrays with trailing / |
||
| 928 | * |
||
| 929 | * @return array |
||
| 930 | */ |
||
| 931 | private function getClaimsArrayWithMetaData( array $array, $claimPath = '' ) { |
||
| 932 | $metaDataModifications = [ |
||
| 933 | 'references/*/snaks/*' => [ |
||
| 934 | $this->callbackFactory->getCallbackToIndexTags( 'snak' ), |
||
| 935 | ], |
||
| 936 | 'references/*/snaks' => [ |
||
| 937 | $this->callbackFactory->getCallbackToSetArrayType( 'kvp', 'id' ), |
||
| 938 | $this->callbackFactory->getCallbackToIndexTags( 'property' ), |
||
| 939 | ], |
||
| 940 | 'references/*/snaks-order' => [ |
||
| 941 | $this->callbackFactory->getCallbackToIndexTags( 'property' ) |
||
| 942 | ], |
||
| 943 | 'references' => [ |
||
| 944 | $this->callbackFactory->getCallbackToIndexTags( 'reference' ), |
||
| 945 | ], |
||
| 946 | 'qualifiers/*' => [ |
||
| 947 | $this->callbackFactory->getCallbackToIndexTags( 'qualifiers' ), |
||
| 948 | ], |
||
| 949 | 'qualifiers' => [ |
||
| 950 | $this->callbackFactory->getCallbackToSetArrayType( 'kvp', 'id' ), |
||
| 951 | $this->callbackFactory->getCallbackToIndexTags( 'property' ), |
||
| 952 | ], |
||
| 953 | 'qualifiers-order' => [ |
||
| 954 | $this->callbackFactory->getCallbackToIndexTags( 'property' ) |
||
| 955 | ], |
||
| 956 | 'mainsnak' => [ |
||
| 957 | $this->callbackFactory->getCallbackToAddDataTypeToSnak( $this->dataTypeLookup ), |
||
| 958 | ], |
||
| 959 | ]; |
||
| 960 | |||
| 961 | foreach ( $metaDataModifications as $path => $callbacks ) { |
||
| 962 | foreach ( $callbacks as $callback ) { |
||
| 963 | $array = $this->modifier->modifyUsingCallback( $array, $claimPath . $path, $callback ); |
||
| 964 | } |
||
| 965 | } |
||
| 966 | |||
| 967 | return $array; |
||
| 968 | } |
||
| 969 | |||
| 970 | /** |
||
| 971 | * Get serialized reference and add it to result |
||
| 972 | * |
||
| 973 | * @param Reference $reference |
||
| 974 | */ |
||
| 975 | public function addReference( Reference $reference ) { |
||
| 976 | $serializer = $this->serializerFactory->newReferenceSerializer(); |
||
| 977 | |||
| 978 | //TODO: this is currently only used to add a Reference as the top level structure, |
||
| 979 | // with a null path and a fixed name. Would be nice to also allow references |
||
| 980 | // to be added to a list, using a path and a id key or index. |
||
| 981 | |||
| 982 | $value = $serializer->serialize( $reference ); |
||
| 983 | |||
| 984 | $value = $this->dataTypeInjector->getArrayWithDataTypesInGroupedSnakListAtPath( $value, 'snaks' ); |
||
| 985 | |||
| 986 | if ( $this->addMetaData ) { |
||
| 987 | $value = $this->getReferenceArrayWithMetaData( $value ); |
||
| 988 | } |
||
| 989 | |||
| 990 | $this->setValue( null, 'reference', $value ); |
||
| 991 | } |
||
| 992 | |||
| 993 | private function getReferenceArrayWithMetaData( array $array ) { |
||
| 994 | $array = $this->modifier->modifyUsingCallback( $array, 'snaks-order', function ( $array ) { |
||
| 995 | ApiResult::setIndexedTagName( $array, 'property' ); |
||
| 996 | return $array; |
||
| 997 | } ); |
||
| 998 | $array = $this->modifier->modifyUsingCallback( $array, 'snaks', function ( $array ) { |
||
| 999 | foreach ( $array as &$snakGroup ) { |
||
| 1000 | if ( is_array( $snakGroup ) ) { |
||
| 1001 | ApiResult::setArrayType( $array, 'array' ); |
||
| 1002 | ApiResult::setIndexedTagName( $snakGroup, 'snak' ); |
||
| 1003 | } |
||
| 1004 | } |
||
| 1005 | ApiResult::setArrayType( $array, 'kvp', 'id' ); |
||
| 1006 | ApiResult::setIndexedTagName( $array, 'property' ); |
||
| 1007 | return $array; |
||
| 1008 | } ); |
||
| 1009 | return $array; |
||
| 1010 | } |
||
| 1011 | |||
| 1012 | /** |
||
| 1013 | * Add an entry for a missing entity... |
||
| 1014 | * |
||
| 1015 | * @param string|null $key The key under which to place the missing entity in the 'entities' |
||
| 1016 | * structure. If null, defaults to the 'id' field in $missingDetails if that is set; |
||
| 1017 | * otherwise, it defaults to using a unique negative number. |
||
| 1018 | * @param array $missingDetails array containing key value pair missing details |
||
| 1019 | */ |
||
| 1020 | public function addMissingEntity( $key, array $missingDetails ) { |
||
| 1021 | if ( $key === null && isset( $missingDetails['id'] ) ) { |
||
| 1022 | $key = $missingDetails['id']; |
||
| 1023 | } |
||
| 1024 | |||
| 1025 | if ( $key === null ) { |
||
| 1026 | $key = $this->missingEntityCounter; |
||
| 1027 | } |
||
| 1028 | |||
| 1029 | $this->appendValue( |
||
| 1030 | 'entities', |
||
| 1031 | $key, |
||
| 1032 | array_merge( $missingDetails, [ 'missing' => "" ] ), |
||
| 1033 | 'entity' |
||
| 1034 | ); |
||
| 1035 | |||
| 1036 | if ( $this->addMetaData ) { |
||
| 1037 | $this->result->addIndexedTagName( 'entities', 'entity' ); |
||
| 1038 | $this->result->addArrayType( [ 'entities' ], 'kvp', 'id' ); |
||
| 1039 | $this->result->addValue( |
||
| 1040 | [ 'entities' ], |
||
| 1041 | ApiResult::META_KVP_MERGE, |
||
| 1042 | true, |
||
| 1043 | ApiResult::OVERRIDE |
||
| 1044 | ); |
||
| 1045 | } |
||
| 1046 | |||
| 1047 | $this->missingEntityCounter--; |
||
| 1048 | } |
||
| 1049 | |||
| 1050 | /** |
||
| 1051 | * @param string $from |
||
| 1052 | * @param string $to |
||
| 1053 | * @param string $name |
||
| 1054 | */ |
||
| 1055 | public function addNormalizedTitle( $from, $to, $name = 'n' ) { |
||
| 1056 | $this->setValue( |
||
| 1057 | 'normalized', |
||
| 1058 | $name, |
||
| 1059 | [ 'from' => $from, 'to' => $to ] |
||
| 1060 | ); |
||
| 1061 | } |
||
| 1062 | |||
| 1063 | /** |
||
| 1064 | * Adds the ID of the new revision from the Status object to the API result structure. |
||
| 1065 | * The status value is expected to be structured in the way that EditEntity::attemptSave() |
||
| 1066 | * resp WikiPage::doEditContent() do it: as an array, with an EntityRevision object in the |
||
| 1067 | * 'revision' field. If $oldRevId is set and the latest edit was null, a 'nochange' flag |
||
| 1068 | * is also added. |
||
| 1069 | * |
||
| 1070 | * If no revision is found in the Status object, this method does nothing. |
||
| 1071 | * |
||
| 1072 | * @see ApiResult::addValue() |
||
| 1073 | * |
||
| 1074 | * @param Status $status The status to get the revision ID from. |
||
| 1075 | * @param string|null|array $path Where in the result to put the revision id |
||
| 1076 | * @param int|null $oldRevId The id of the latest revision of the entity before |
||
| 1077 | * the last (possibly null) edit |
||
| 1078 | */ |
||
| 1079 | public function addRevisionIdFromStatusToResult( Status $status, $path, $oldRevId = null ) { |
||
| 1080 | $value = $status->getValue(); |
||
| 1081 | |||
| 1082 | if ( isset( $value['revision'] ) ) { |
||
| 1083 | if ( $value['revision'] instanceof EntityRevision ) { |
||
| 1084 | // Should always be the case, but sanity check |
||
| 1085 | $revisionId = $value['revision']->getRevisionId(); |
||
| 1086 | } else { |
||
| 1087 | $revisionId = 0; |
||
| 1088 | } |
||
| 1089 | |||
| 1090 | $this->setValue( $path, 'lastrevid', $revisionId ); |
||
| 1091 | |||
| 1092 | if ( $oldRevId && $oldRevId === $revisionId ) { |
||
|
0 ignored issues
–
show
The expression
$oldRevId of type integer|null is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.
In PHP, under loose comparison (like For 0 == false // true
0 == null // true
123 == false // false
123 == null // false
// It is often better to use strict comparison
0 === false // false
0 === null // false
Loading history...
|
|||
| 1093 | // like core's ApiEditPage |
||
| 1094 | $this->setValue( $path, 'nochange', true ); |
||
| 1095 | } |
||
| 1096 | } |
||
| 1097 | } |
||
| 1098 | |||
| 1099 | } |
||
| 1100 |
Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code: