wikimedia/mediawiki-extensions-Wikibase

View on GitHub
lib/includes/Store/Sql/WikiPageEntityDataLoader.php

Summary

Maintainability
A
2 hrs
Test Coverage
<?php

namespace Wikibase\Lib\Store\Sql;

use MediaWiki\Revision\RevisionAccessException;
use MediaWiki\Revision\RevisionRecord;
use MediaWiki\Storage\BlobAccessException;
use MediaWiki\Storage\BlobStore;
use Wikibase\Lib\Store\EntityContentDataCodec;
use Wikibase\Lib\Store\EntityRevision;
use Wikibase\Lib\Store\StorageException;

/**
 * @license GPL-2.0-or-later
 */
class WikiPageEntityDataLoader {

    /**
     * @var EntityContentDataCodec
     */
    private $contentCodec;

    /**
     * @var BlobStore
     *
     * @todo remove this once we no longer need to be compatible to the pre-MCR database schema
     */
    private $blobStore;

    /** @var string|false */
    private $wikiId;

    public function __construct(
        EntityContentDataCodec $contentCodec,
        BlobStore $blobStore,
        $wikiId = false
    ) {
        $this->contentCodec = $contentCodec;
        $this->blobStore = $blobStore;
        $this->wikiId = $wikiId;
    }

    /**
     * @param RevisionRecord $revision
     * @param string $slotRole
     * @param int $revStoreFlags See IDBAccessObject.
     *
     * @throws StorageException
     * @return array list( EntityRevision|null $entityRevision, EntityRedirect|null $entityRedirect )
     * with either $entityRevision or $entityRedirect or both being null (but not both being non-null).
     */
    public function loadEntityDataFromWikiPageRevision( RevisionRecord $revision, string $slotRole, int $revStoreFlags ) {
        // NOTE: Support for cross-wiki content access in RevisionStore is incomplete when,
        // reading from the pre-MCR database schema, see T201194.
        // For that reason, we have to load and decode the content blob directly,
        // instead of using RevisionRecord::getContent() or SlotRecord::getContent().
        // TODO Once we can rely on the new MCR enabled DB schema, use getContent() directly!

        if ( !$revision->hasSlot( $slotRole ) ) {
            return [ null, null ];
        }

        try {
            $slot = $revision->getSlot( $slotRole );
        } catch ( RevisionAccessException $e ) {
            throw new StorageException( 'Failed to load slot', 0, $e );
        }

        // WARNING: This will make it look like suppressed revisions don't exist at all.
        // Wikibase should handle old revisions with suppressed content gracefully.
        // @see https://phabricator.wikimedia.org/T198467
        if ( !$revision->audienceCan( RevisionRecord::DELETED_TEXT, RevisionRecord::FOR_PUBLIC ) ) {
            return [ null, null ];
        }

        try {
            $blob = $this->blobStore->getBlob( $slot->getAddress(), $revStoreFlags );
        } catch ( BlobAccessException $e ) {
            throw new StorageException( 'Failed to load blob', 0, $e );
        }

        $entity = $this->contentCodec->decodeEntity( $blob, $slot->getFormat() );

        if ( $entity ) {
            $entityRevision = new EntityRevision(
                $entity,
                $revision->getId( $this->wikiId ),
                $revision->getTimestamp()
            );

            return [ $entityRevision, null ];
        } else {
            $redirect = $this->contentCodec->decodeRedirect( $blob, $slot->getFormat() );

            if ( !$redirect ) {
                throw new StorageException(
                    'The serialized data of revision ' . $revision->getId( $this->wikiId )
                    . ' contains neither an Entity nor an EntityRedirect!'
                );
            }

            return [ null, $redirect ];
        }
    }

}