Issues (1401)

Security Analysis    no request data  

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

repo/includes/Content/EntityHandler.php (1 issue)

Upgrade to new PHP Analysis Engine

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\Content;
4
5
use Content;
6
use ContentHandler;
7
use Diff\Patcher\PatcherException;
8
use Html;
9
use IContextSource;
10
use InvalidArgumentException;
11
use Language;
12
use MediaWiki\MediaWikiServices;
13
use MWContentSerializationException;
14
use MWException;
15
use ParserCache;
16
use ParserOutput;
17
use Revision;
18
use SearchEngine;
19
use Title;
20
use Wikibase\DataModel\Entity\EntityDocument;
21
use Wikibase\DataModel\Entity\EntityId;
22
use Wikibase\DataModel\Entity\EntityIdParser;
23
use Wikibase\DataModel\Entity\EntityIdParsingException;
24
use Wikibase\DataModel\Entity\EntityRedirect;
25
use Wikibase\Lib\Store\EntityContentDataCodec;
26
use Wikibase\Repo\Diff\EntityContentDiffView;
27
use Wikibase\Repo\Search\Fields\FieldDefinitions;
28
use Wikibase\Repo\Validators\EntityConstraintProvider;
29
use Wikibase\Repo\Validators\EntityValidator;
30
use Wikibase\Repo\Validators\ValidatorErrorLocalizer;
31
use Wikibase\Repo\WikibaseRepo;
32
use Wikimedia\Assert\Assert;
33
use WikiPage;
34
35
/**
36
 * Base handler class for Entity content classes.
37
 * @license GPL-2.0-or-later
38
 * @author Daniel Kinzler
39
 * @author Jeroen De Dauw < [email protected] >
40
 */
41
abstract class EntityHandler extends ContentHandler {
42
43
	/**
44
	 * Added to parser options for EntityContent.
45
	 *
46
	 * Bump the version when making incompatible changes
47
	 * to parser output.
48
	 */
49
	const PARSER_VERSION = 3;
50
51
	/**
52
	 * @var FieldDefinitions
53
	 */
54
	protected $fieldDefinitions;
55
56
	/**
57
	 * @var EntityContentDataCodec
58
	 */
59
	protected $contentCodec;
60
61
	/**
62
	 * @var EntityConstraintProvider
63
	 */
64
	protected $constraintProvider;
65
66
	/**
67
	 * @var ValidatorErrorLocalizer
68
	 */
69
	private $errorLocalizer;
70
71
	/**
72
	 * @var EntityIdParser
73
	 */
74
	private $entityIdParser;
75
76
	/**
77
	 * @var callable|null Callback to determine whether a serialized
78
	 *        blob needs to be re-serialized on export.
79
	 */
80
	private $legacyExportFormatDetector;
81
82
	/**
83
	 * @param string $modelId
84
	 * @param mixed $unused @todo Get rid of me
85
	 * @param EntityContentDataCodec $contentCodec
86
	 * @param EntityConstraintProvider $constraintProvider
87
	 * @param ValidatorErrorLocalizer $errorLocalizer
88
	 * @param EntityIdParser $entityIdParser
89
	 * @param FieldDefinitions $fieldDefinitions
90
	 * @param callable|null $legacyExportFormatDetector Callback to determine whether a serialized
91
	 *        blob needs to be re-serialized on export. The callback must take two parameters,
92
	 *        the blob an the serialization format. It must return true if re-serialization is needed.
93
	 *        False positives are acceptable, false negatives are not.
94
	 *
95
	 */
96
	public function __construct(
97
		$modelId,
98
		$unused,
99
		EntityContentDataCodec $contentCodec,
100
		EntityConstraintProvider $constraintProvider,
101
		ValidatorErrorLocalizer $errorLocalizer,
102
		EntityIdParser $entityIdParser,
103
		FieldDefinitions $fieldDefinitions,
104
		$legacyExportFormatDetector = null
105
	) {
106
		$formats = $contentCodec->getSupportedFormats();
107
108
		parent::__construct( $modelId, $formats );
109
110
		if ( $legacyExportFormatDetector && !is_callable( $legacyExportFormatDetector ) ) {
111
			throw new InvalidArgumentException( '$legacyExportFormatDetector must be a callable (or null)' );
112
		}
113
114
		$this->contentCodec = $contentCodec;
115
		$this->constraintProvider = $constraintProvider;
116
		$this->errorLocalizer = $errorLocalizer;
117
		$this->entityIdParser = $entityIdParser;
118
		$this->legacyExportFormatDetector = $legacyExportFormatDetector;
119
		$this->fieldDefinitions = $fieldDefinitions;
120
	}
121
122
	/**
123
	 * Returns the callback used to determine whether a serialized blob needs
124
	 * to be re-serialized on export (or null of re-serialization is disabled).
125
	 *
126
	 * @return callable|null
127
	 */
128
	public function getLegacyExportFormatDetector() {
129
		return $this->legacyExportFormatDetector;
130
	}
131
132
	/**
133
	 * Handle the fact that a given page does not contain an Entity, even though it could.
134
	 * Per default, this behaves similarly to Article::showMissingArticle: it shows
135
	 * a message to the user.
136
	 *
137
	 * @see Article::showMissingArticle
138
	 *
139
	 * @param Title $title The title of the page that potentially could, but does not,
140
	 *        contain an entity.
141
	 * @param IContextSource $context Context to use for reporting. In particular, output
142
	 *        will be written to $context->getOutput().
143
	 */
144
	public function showMissingEntity( Title $title, IContextSource $context ) {
145
		$text = wfMessage( 'wikibase-noentity' )->setContext( $context )->plain();
146
147
		$dir = $context->getLanguage()->getDir();
148
		$lang = $context->getLanguage()->getHtmlCode();
149
150
		$outputPage = $context->getOutput();
151
		$outputPage->addWikiTextAsInterface( Html::openElement( 'div', [
152
				'class' => "noarticletext mw-content-$dir",
153
				'dir' => $dir,
154
				'lang' => $lang,
155
			] ) . "\n$text\n</div>" );
156
	}
157
158
	/**
159
	 * @see ContentHandler::getDiffEngineClass
160
	 *
161
	 * @return string
162
	 */
163
	protected function getDiffEngineClass() {
164
		return EntityContentDiffView::class;
165
	}
166
167
	/**
168
	 * Get EntityValidators for on-save validation.
169
	 *
170
	 * @see getValidationErrorLocalizer()
171
	 *
172
	 * @param bool $forCreation Whether the entity is created (true) or updated (false).
173
	 *
174
	 * @return EntityValidator[]
175
	 */
176
	public function getOnSaveValidators( $forCreation, EntityId $entityId ) {
177
		if ( $forCreation ) {
178
			$validators = $this->constraintProvider->getCreationValidators( $this->getEntityType(), $entityId );
179
		} else {
180
			$validators = $this->constraintProvider->getUpdateValidators( $this->getEntityType() );
181
		}
182
183
		return $validators;
184
	}
185
186
	/**
187
	 * Error localizer for use together with getOnSaveValidators().
188
	 *
189
	 * @see getOnSaveValidators()
190
	 *
191
	 * @return ValidatorErrorLocalizer
192
	 */
193
	public function getValidationErrorLocalizer() {
194
		return $this->errorLocalizer;
195
	}
196
197
	/**
198
	 * @see ContentHandler::makeEmptyContent
199
	 *
200
	 * @return EntityContent
201
	 */
202
	public function makeEmptyContent() {
203
		return $this->newEntityContent( null );
204
	}
205
206
	/**
207
	 * Returns an empty Entity object of the type supported by this handler.
208
	 * This is intended to provide a baseline for diffing and related operations.
209
	 *
210
	 * @note The Entity returned here will not have an ID set, and is thus not
211
	 * suitable for use in an EntityContent object.
212
	 *
213
	 * @return EntityDocument
214
	 */
215
	abstract public function makeEmptyEntity();
216
217
	/**
218
	 * @param EntityRedirect $redirect Unused in this default implementation.
219
	 *
220
	 * @return EntityContent|null Either a new EntityContent representing the given EntityRedirect,
221
	 *  or null if the entity type does not support redirects. Always null in this default
222
	 *  implementation.
223
	 */
224
	public function makeEntityRedirectContent( EntityRedirect $redirect ) {
225
		return null;
226
	}
227
228
	/**
229
	 * None of the Entity content models support categories.
230
	 *
231
	 * @return bool Always false.
232
	 */
233
	public function supportsCategories() {
234
		return false;
235
	}
236
237
	/**
238
	 * @see ContentHandler::getAutosummary
239
	 *
240
	 * We never want to use MediaWiki's autosummaries, used e.g. for new page creation. Override this
241
	 * to make sure they never overwrite our autosummaries (which look like the automatic summary
242
	 * prefixes with a section title, and so could be overwritten).
243
	 *
244
	 * @param Content|null $oldContent
245
	 * @param Content|null $newContent
246
	 * @param int $flags
247
	 *
248
	 * @return string Empty string
249
	 */
250
	public function getAutosummary(
251
		Content $oldContent = null,
252
		Content $newContent = null,
253
		$flags = 0
254
	) {
255
		return '';
256
	}
257
258
	/**
259
	 * @see ContentHandler::makeRedirectContent
260
	 *
261
	 * @warning Always throws an MWException, since an EntityRedirects needs to know it's own
262
	 * ID in addition to the target ID. We have no way to guess that in makeRedirectContent().
263
	 * Use makeEntityRedirectContent() instead.
264
	 *
265
	 * @see makeEntityRedirectContent()
266
	 *
267
	 * @param Title $title
268
	 * @param string $text
269
	 *
270
	 * @throws MWException Always.
271
	 * @return EntityContent|null
272
	 */
273
	public function makeRedirectContent( Title $title, $text = '' ) {
274
		throw new MWException( 'EntityContent does not support plain title based redirects.'
275
			. ' Use makeEntityRedirectContent() instead.' );
276
	}
277
278
	/**
279
	 * @see ContentHandler::exportTransform
280
	 *
281
	 * @param string $blob
282
	 * @param string|null $format
283
	 *
284
	 * @return string
285
	 */
286
	public function exportTransform( $blob, $format = null ) {
287
		if ( !$this->legacyExportFormatDetector ) {
288
			return $blob;
289
		}
290
291
		$needsTransform = call_user_func( $this->legacyExportFormatDetector, $blob, $format );
292
293
		if ( $needsTransform ) {
294
			$format = ( $format === null ) ? $this->getDefaultFormat() : $format;
295
296
			$content = $this->unserializeContent( $blob, $format );
297
			$blob = $this->serializeContent( $content );
298
		}
299
300
		return $blob;
301
	}
302
303
	/**
304
	 * @param EntityHolder $entityHolder
305
	 *
306
	 * @return EntityContent
307
	 */
308
	public function makeEntityContent( EntityHolder $entityHolder ) {
309
		return $this->newEntityContent( $entityHolder );
310
	}
311
312
	/**
313
	 * @param EntityHolder|null $entityHolder
314
	 *
315
	 * @return EntityContent
316
	 */
317
	abstract protected function newEntityContent( EntityHolder $entityHolder = null );
318
319
	/**
320
	 * Parses the given ID string into an EntityId for the type of entity
321
	 * supported by this EntityHandler. If the string is not a valid
322
	 * serialization of the correct type of entity ID, an exception is thrown.
323
	 *
324
	 * @param string $id String representation the entity ID
325
	 *
326
	 * @return EntityId
327
	 * @throws InvalidArgumentException
328
	 */
329
	abstract public function makeEntityId( $id );
330
331
	/**
332
	 * @return string
333
	 */
334
	public function getDefaultFormat() {
335
		return $this->contentCodec->getDefaultFormat();
336
	}
337
338
	/**
339
	 * @param Content $content
340
	 * @param string|null $format
341
	 *
342
	 * @throws InvalidArgumentException
343
	 * @throws MWContentSerializationException
344
	 * @return string
345
	 */
346
	public function serializeContent( Content $content, $format = null ) {
347
		if ( !( $content instanceof EntityContent ) ) {
348
			throw new InvalidArgumentException( '$content must be an instance of EntityContent' );
349
		}
350
351
		if ( $content->isRedirect() ) {
352
			$redirect = $content->getEntityRedirect();
353
			return $this->contentCodec->encodeRedirect( $redirect, $format );
354
		} else {
355
			// TODO: If we have an un-decoded Entity in a DeferredDecodingEntityHolder, just re-use
356
			// the encoded form.
357
			$entity = $content->getEntity();
358
			return $this->contentCodec->encodeEntity( $entity, $format );
359
		}
360
	}
361
362
	/**
363
	 * @see ContentHandler::unserializeContent
364
	 *
365
	 * @param string $blob
366
	 * @param string|null $format
367
	 *
368
	 * @throws MWContentSerializationException
369
	 * @return EntityContent
370
	 */
371
	public function unserializeContent( $blob, $format = null ) {
372
		$redirect = $this->contentCodec->decodeRedirect( $blob, $format );
373
374
		if ( $redirect !== null ) {
375
			return $this->makeEntityRedirectContent( $redirect );
376
		} else {
377
			$holder = new DeferredDecodingEntityHolder(
378
				$this->contentCodec,
379
				$blob,
380
				$format,
381
				$this->getEntityType()
382
			);
383
			$entityContent = $this->makeEntityContent( $holder );
384
385
			return $entityContent;
386
		}
387
	}
388
389
	/**
390
	 * Returns the ID of the entity contained by the page of the given title.
391
	 *
392
	 * @warning This should not really be needed and may just go away!
393
	 *
394
	 * @param Title $target
395
	 *
396
	 * @throws EntityIdParsingException
397
	 * @return EntityId
398
	 */
399
	public function getIdForTitle( Title $target ) {
400
		return $this->entityIdParser->parse( $target->getText() );
401
	}
402
403
	/**
404
	 * Returns the appropriate page Title for the given EntityId.
405
	 *
406
	 * @warning This should not really be needed and may just go away!
407
	 *
408
	 * @see EntityTitleStoreLookup::getTitleForId
409
	 *
410
	 * @param EntityId $id
411
	 *
412
	 * @throws InvalidArgumentException if $id refers to an entity of the wrong type.
413
	 * @return Title
414
	 */
415
	public function getTitleForId( EntityId $id ) {
416
		if ( $id->getEntityType() !== $this->getEntityType() ) {
417
			throw new InvalidArgumentException( 'The given ID does not refer to an entity of type '
418
				. $this->getEntityType() );
419
		}
420
421
		return Title::makeTitle( $this->getEntityNamespace(), $id->getSerialization() );
422
	}
423
424
	/**
425
	 * Returns the appropriate page Titles for the given EntityIds
426
	 *
427
	 * @param EntityId[] $ids
428
	 * @return Title[] Array of Title objects indexed by the entity id serializations
429
	 */
430
	public function getTitlesForIds( array $ids ) {
431
		$titles = [];
432
		foreach ( $ids as $id ) {
433
			if ( $id->getEntityType() !== $this->getEntityType() ) {
434
				throw new InvalidArgumentException(
435
					'The given ID does not refer to an entity of type ' . $this->getEntityType()
436
				);
437
			}
438
			$titles[ $id->getSerialization() ] =
439
				Title::makeTitle( $this->getEntityNamespace(), $id->getSerialization() );
440
		}
441
442
		return $titles;
443
	}
444
445
	/**
446
	 * Returns the namespace that is to be used for this kind of entities.
447
	 *
448
	 * @return int
449
	 */
450
	final public function getEntityNamespace() {
451
		$entityNamespaceLookup = WikibaseRepo::getDefaultInstance()->getEntityNamespaceLookup();
452
453
		$ns = $entityNamespaceLookup->getEntityNamespace( $this->getEntityType() );
454
455
		Assert::postcondition(
456
			$ns !== null,
457
			'Namespace for entity type ' . $this->getEntityType() . ' must be defined!'
458
		);
459
460
		return $ns;
461
	}
462
463
	/**
464
	 * Returns the slot that is to be used for this kind of entities.
465
	 *
466
	 * @return string the role name of the slot
467
	 */
468
	final public function getEntitySlotRole() {
469
		$entityNamespaceLookup = WikibaseRepo::getDefaultInstance()->getEntityNamespaceLookup();
470
471
		return $entityNamespaceLookup->getEntitySlotRole( $this->getEntityType() );
472
	}
473
474
	/**
475
	 * @see ContentHandler::canBeUsedOn
476
	 *
477
	 * This implementation returns true if and only if the given title's namespace
478
	 * is the same as the one returned by $this->getEntityNamespace().
479
	 *
480
	 * @param Title $title
481
	 *
482
	 * @return bool true if $title represents a page in the appropriate entity namespace.
483
	 */
484
	public function canBeUsedOn( Title $title ) {
485
		if ( !parent::canBeUsedOn( $title ) ) {
486
			return false;
487
		}
488
489
		$namespace = $this->getEntityNamespace();
490
		return $namespace === $title->getNamespace();
491
	}
492
493
	/**
494
	 * Returns true to indicate that the parser cache can be used for data items.
495
	 *
496
	 * @note The html representation of entities depends on the user language, so
497
	 * EntityContent::getParserOutput needs to make sure ParserOutput::recordOption( 'userlang' )
498
	 * is called to split the cache by user language.
499
	 *
500
	 * @see ContentHandler::isParserCacheSupported
501
	 *
502
	 * @return bool Always true in this default implementation.
503
	 */
504
	public function isParserCacheSupported() {
505
		return true;
506
	}
507
508
	/**
509
	 * @see ContentHandler::getPageViewLanguage
510
	 *
511
	 * This implementation returns the user language, because entities get rendered in
512
	 * the user's language. The PageContentLanguage hook is bypassed.
513
	 *
514
	 * @param Title        $title the page to determine the language for.
515
	 * @param Content|null $content the page's content, if you have it handy, to avoid reloading it.
516
	 *
517
	 * @return Language The page's language
518
	 */
519
	public function getPageViewLanguage( Title $title, Content $content = null ) {
520
		global $wgLang;
521
522
		return $wgLang;
523
	}
524
525
	/**
526
	 * @see ContentHandler::getPageLanguage
527
	 *
528
	 * This implementation unconditionally returns the wiki's content language.
529
	 * The PageContentLanguage hook is bypassed.
530
	 *
531
	 * @note Ideally, this would return 'mul' to indicate multilingual content. But MediaWiki
532
	 * currently doesn't support that.
533
	 *
534
	 * @note in several places in mediawiki, most importantly the parser cache, getPageLanguage
535
	 * is used in places where getPageViewLanguage would be more appropriate.
536
	 *
537
	 * @param Title        $title the page to determine the language for.
538
	 * @param Content|null $content the page's content, if you have it handy, to avoid reloading it.
539
	 *
540
	 * @return Language The page's language
541
	 */
542
	public function getPageLanguage( Title $title, Content $content = null ) {
543
		return MediaWikiServices::getInstance()->getContentLanguage();
544
	}
545
546
	/**
547
	 * Returns the name of the special page responsible for creating a page
548
	 * for this type of entity content.
549
	 * Returns null if there is no such special page.
550
	 *
551
	 * @return string|null Always null in this default implementation.
552
	 */
553
	public function getSpecialPageForCreation() {
554
		return null;
555
	}
556
557
	/**
558
	 * @see ContentHandler::getUndoContent
559
	 *
560
	 * @param Revision|Content $latest The current text
561
	 * @param Revision|Content $newer The revision to undo
562
	 * @param Revision|Content $older Must be an earlier revision than $newer
563
	 * @param bool $undoIsLatest Set to true if $newer is from the current revision (since 1.32)
564
	 *
565
	 * @return EntityContent|bool Content on success, false on failure
566
	 */
567
	public function getUndoContent( $latest, $newer, $older, $undoIsLatest = false ) {
568
		$latestContent = ( $latest instanceof Revision ) ? $latest->getContent() : $latest;
569
		$newerContent = ( $newer instanceof Revision ) ? $newer->getContent() : $newer;
570
		$olderContent = ( $older instanceof Revision ) ? $older->getContent() : $older;
571
		if (
572
			!$latestContent instanceof EntityContent
573
			|| !$newerContent instanceof EntityContent
574
			|| !$olderContent instanceof EntityContent
575
		) {
576
			return false;
577
		}
578
		if ( $latest instanceof Revision && $newer instanceof Revision ) {
579
			$undoIsLatest = ( $latest->getId() === $newer->getId() );
580
		}
581
582
		if ( $undoIsLatest ) {
583
			// no patching needed, just roll back
584
			return $olderContent;
585
		}
586
587
		// diff from new to base
588
		$patch = $newerContent->getDiff( $olderContent );
589
590
		try {
591
			// apply the patch( new -> old ) to the current revision.
592
			$patchedCurrent = $latestContent->getPatchedCopy( $patch );
593
		} catch ( PatcherException $ex ) {
594
			return false;
595
		}
596
597
		// detect conflicts against current revision
598
		$cleanPatch = $latestContent->getDiff( $patchedCurrent );
599
		$conflicts = $patch->count() - $cleanPatch->count();
600
601
		if ( $conflicts > 0 ) {
602
			return false;
603
		} else {
604
			return $patchedCurrent;
605
		}
606
	}
607
608
	/**
609
	 * Returns the entity type ID for the kind of entity managed by this EntityContent implementation.
610
	 *
611
	 * @return string
612
	 */
613
	abstract public function getEntityType();
614
615
	/**
616
	 * Whether IDs can automatically be assigned to entities
617
	 * of the kind supported by this EntityHandler.
618
	 *
619
	 * @return bool
620
	 */
621
	public function allowAutomaticIds() {
622
		return true;
623
	}
624
625
	/**
626
	 * Whether the given custom ID is valid for creating a new entity
627
	 * of the kind supported by this EntityHandler.
628
	 *
629
	 * Implementations are not required to check if an entity with the given ID already exists.
630
	 * If this method returns true, this means that an entity with the given ID could be
631
	 * created (or already existed) at the time the method was called. There is no guarantee
632
	 * that this continues to be true after the method call returned. Callers must be careful
633
	 * to handle race conditions.
634
	 *
635
	 * @note For entity types that cannot be created with custom IDs (that is,
636
	 * entity types that are defined to use automatic IDs), this should always
637
	 * return false.
638
	 *
639
	 * @see EntityStore::canCreateWithCustomId()
640
	 *
641
	 * @param EntityId $id
642
	 *
643
	 * @return bool
644
	 */
645
	public function canCreateWithCustomId( EntityId $id ) {
646
		return false;
647
	}
648
649
	/**
650
	 * @param SearchEngine $engine
651
	 * @return \SearchIndexField[] List of fields this content handler can provide.
652
	 */
653
	public function getFieldsForSearchIndex( SearchEngine $engine ) {
654
		$fields = [];
655
656
		foreach ( $this->fieldDefinitions->getFields() as $name => $field ) {
657
			$mappingField = $field->getMappingField( $engine, $name );
0 ignored issues
show
Are you sure the assignment to $mappingField is correct as $field->getMappingField($engine, $name) (which targets Wikibase\Repo\Search\Fie...ield::getMappingField()) seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
658
			if ( $mappingField ) {
659
				$fields[$name] = $mappingField;
660
			}
661
		}
662
663
		return $fields;
664
	}
665
666
	/**
667
	 * @param WikiPage $page
668
	 * @param ParserOutput $output
669
	 * @param SearchEngine $engine
670
	 *
671
	 * @return array Wikibase fields data, map of name=>value for fields
672
	 */
673
	public function getDataForSearchIndex(
674
		WikiPage $page,
675
		ParserOutput $output,
676
		SearchEngine $engine
677
	) {
678
		$fieldsData = parent::getDataForSearchIndex( $page, $output, $engine );
679
680
		$content = $page->getContent();
681
		if ( ( $content instanceof EntityContent ) && !$content->isRedirect() ) {
682
			$entity = $content->getEntity();
683
			$fields = $this->fieldDefinitions->getFields();
684
685
			foreach ( $fields as $fieldName => $field ) {
686
				$fieldsData[$fieldName] = $field->getFieldData( $entity );
687
			}
688
		}
689
690
		return $fieldsData;
691
	}
692
693
	/**
694
	 * Produce page output suitable for indexing.
695
	 * Does not include HTML.
696
	 *
697
	 * @param WikiPage $page
698
	 * @param ParserCache|null $cache
699
	 * @return bool|ParserOutput|null
700
	 */
701
	public function getParserOutputForIndexing( WikiPage $page, ParserCache $cache = null ) {
702
		$parserOptions = $page->makeParserOptions( 'canonical' );
703
		if ( $cache ) {
704
			$parserOutput = $cache->get( $page, $parserOptions );
705
			if ( $parserOutput ) {
706
				return $parserOutput;
707
			}
708
		}
709
710
		$renderer = MediaWikiServices::getInstance()->getRevisionRenderer();
711
		$revisionRecord = $this->latestRevision( $page );
712
		$parserOutput = $renderer->getRenderedRevision( $revisionRecord, $parserOptions )
713
			// this will call EntityContent::getParserOutput() with $generateHtml = false
714
			->getRevisionParserOutput( [
715
				'generate-html' => false,
716
			] );
717
		// since we didn’t generate HTML, don’t call $cache->save()
718
		return $parserOutput;
719
	}
720
721
}
722