wikimedia/mediawiki-extensions-Wikibase

View on GitHub
repo/includes/Dumpers/JsonDumpGenerator.php

Summary

Maintainability
A
50 mins
Test Coverage
<?php

declare( strict_types = 1 );

namespace Wikibase\Repo\Dumpers;

use InvalidArgumentException;
use MWContentSerializationException;
use Serializers\Serializer;
use Wikibase\DataModel\Entity\EntityId;
use Wikibase\DataModel\Entity\EntityIdParser;
use Wikibase\DataModel\Services\Entity\EntityPrefetcher;
use Wikibase\DataModel\Services\Lookup\EntityLookupException;
use Wikibase\DataModel\Services\Lookup\PropertyDataTypeLookup;
use Wikibase\Lib\Serialization\CallbackFactory;
use Wikibase\Lib\Serialization\SerializationModifier;
use Wikibase\Lib\Store\EntityRevisionLookup;
use Wikibase\Lib\Store\RevisionedUnresolvedRedirectException;
use Wikibase\Lib\Store\StorageException;
use Wikibase\Repo\AddPageInfo;
use Wikibase\Repo\Store\EntityTitleStoreLookup;

/**
 * JsonDumpGenerator generates an JSON dump of a given set of entities, excluding
 * redirects.
 *
 * @license GPL-2.0-or-later
 * @author Daniel Kinzler
 */
class JsonDumpGenerator extends DumpGenerator {

    /**
     * @var int flags to use with json_encode as a bit field, see PHP's JSON_XXX constants.
     */
    private int $jsonFlags = 0;
    private Serializer $entitySerializer;
    private EntityRevisionLookup $entityLookup;
    private EntityTitleStoreLookup $entityTitleStoreLookup;
    private bool $useSnippets = false;
    private bool $addPageMetadata = false;
    private JsonDataTypeInjector $dataTypeInjector;
    private AddPageInfo $addPageInfo;

    /**
     * @param resource $out
     * @param EntityRevisionLookup $lookup
     * @param Serializer $entitySerializer
     * @param EntityPrefetcher $entityPrefetcher
     * @param PropertyDataTypeLookup $dataTypeLookup
     * @param EntityIdParser $entityIdParser
     * @param EntityTitleStoreLookup $entityTitleStoreLookup
     *
     * @throws InvalidArgumentException
     */
    public function __construct(
        $out,
        EntityRevisionLookup $lookup,
        Serializer $entitySerializer,
        EntityPrefetcher $entityPrefetcher,
        PropertyDataTypeLookup $dataTypeLookup,
        EntityIdParser $entityIdParser,
        EntityTitleStoreLookup $entityTitleStoreLookup
    ) {
        parent::__construct( $out, $entityPrefetcher );

        $this->entitySerializer = $entitySerializer;
        $this->entityLookup = $lookup;
        $this->entityTitleStoreLookup = $entityTitleStoreLookup;

        $this->dataTypeInjector = new JsonDataTypeInjector(
            new SerializationModifier(),
            new CallbackFactory(),
            $dataTypeLookup,
            $entityIdParser
        );

        $this->addPageInfo = new AddPageInfo( $this->entityTitleStoreLookup );
    }

    /**
     * Do something before dumping data
     */
    protected function preDump(): void {
        if ( !$this->useSnippets ) {
            $this->writeToDump( "[\n" );
        }
    }

    /**
     * Do something after dumping data
     */
    protected function postDump(): void {
        if ( !$this->useSnippets ) {
            $this->writeToDump( "\n]\n" );
        }
    }

    /**
     * Do something before dumping entity
     */
    protected function preEntityDump( int $dumpCount ): void {
        if ( $dumpCount > 0 ) {
            $this->writeToDump( ",\n" );
        }
    }

    /**
     * @throws EntityLookupException
     * @throws StorageException
     */
    protected function generateDumpForEntityId( EntityId $entityId ): ?string {
        try {
            $revision = $this->entityLookup->getEntityRevision( $entityId );

            if ( !$revision ) {
                throw new EntityLookupException( $entityId, 'Entity not found: ' . $entityId->getSerialization() );
            }

        } catch ( MWContentSerializationException $ex ) {
            throw new StorageException( 'Deserialization error for ' . $entityId->getSerialization() );
        } catch ( RevisionedUnresolvedRedirectException $e ) {
            // Redirects aren't supposed to be in the JSON dumps
            return null;
        }

        $entity = $revision->getEntity();

        $data = $this->entitySerializer->serialize( $entity );

        $data = $this->dataTypeInjector->injectEntitySerializationWithDataTypes( $data );

        if ( $this->addPageMetadata ) {
            $data = $this->addPageInfo->add( $data, $revision );
        } else {
            $data['lastrevid'] = $revision->getRevisionId();
        }

        $json = $this->encode( $data );

        return $json;
    }

    /**
     * Encodes the given data as JSON
     *
     * @param mixed $data
     *
     * @throws StorageException
     */
    public function encode( $data ): string {
        $json = json_encode( $data, $this->jsonFlags );

        if ( $json === false ) {
            throw new StorageException( 'Failed to encode data structure.' );
        }

        return $json;
    }

    /**
     * @param bool $useSnippets Whether to output valid json (false) or only comma separated entities
     */
    public function setUseSnippets( bool $useSnippets ): void {
        $this->useSnippets = $useSnippets;
    }

    /**
     * @param bool $addPageMetadata Whether to add page metadata to entities
     */
    public function setAddPageMetadata( bool $addPageMetadata ): void {
        $this->addPageMetadata = $addPageMetadata;
    }

    /**
     * Flags to use with json_encode as a bit field, see PHP's JSON_XXX constants.
     */
    public function setJsonFlags( int $jsonFlags ): void {
        $this->jsonFlags = $jsonFlags;
    }

    /**
     * @see setJsonFlags
     */
    public function getJsonFlags(): int {
        return $this->jsonFlags;
    }

}