diff options
Diffstat (limited to 'www/wiki/extensions/SemanticMediaWiki/src/ParserData.php')
-rw-r--r-- | www/wiki/extensions/SemanticMediaWiki/src/ParserData.php | 512 |
1 files changed, 512 insertions, 0 deletions
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/ParserData.php b/www/wiki/extensions/SemanticMediaWiki/src/ParserData.php new file mode 100644 index 00000000..03d8d45f --- /dev/null +++ b/www/wiki/extensions/SemanticMediaWiki/src/ParserData.php @@ -0,0 +1,512 @@ +<?php + +namespace SMW; + +use Onoi\Cache\Cache; +use ParserOptions; +use ParserOutput; +use Psr\Log\LoggerAwareTrait; +use SMWDataValue as DataValue; +use Title; + +/** + * Handling semantic data exchange with a ParserOutput object + * + * Provides access to a semantic data container that is generated + * either from the ParserOutput or is a newly created container + * + * @license GNU GPL v2+ + * @since 1.9 + * + * @author mwjames + * @author Markus Krötzsch + */ +class ParserData { + + use LoggerAwareTrait; + + /** + * Identifies the extension data + */ + const DATA_ID = 'smwdata'; + + /** + * Identifies the cache namespace for update markers + */ + const CACHE_NAMESPACE = 'smw:update'; + + /** + * Option that allows to force an update even in cases where an update + * marker exists + */ + const OPT_FORCED_UPDATE = 'smw:opt.forced.update'; + + /** + * Option whether creation of iteratibe update jobs are allowed + */ + const OPT_CREATE_UPDATE_JOB = 'smw:opt.create.update.job'; + + /** + * Indicates that an update was caused by a change propagation request + */ + const OPT_CHANGE_PROP_UPDATE = 'smw:opt.change.prop.update'; + + /** + * Indicates that no #ask dependency tracking should occur + */ + const NO_QUERY_DEPENDENCY_TRACE = 'no.query.dependency.trace'; + + /** + * Indicates that no #ask dependency tracking should occur + */ + const ANNOTATION_BLOCK = 'smw-blockannotation'; + + /** + * @var Title + */ + private $title; + + /** + * @var ParserOutput + */ + private $parserOutput; + + /** + * @var Cache + */ + private $cache; + + /** + * @var ParserOptions + */ + private $parserOptions; + + /** + * @var SemanticData + */ + private $semanticData; + + /** + * @var array + */ + private $errors = []; + + /** + * @var $canCreateUpdateJob + */ + private $canCreateUpdateJob = true; + + /** + * Identifies the origin of a request. + * + * @var string + */ + private $origin = ''; + + /** + * @var Options + */ + private $options = null; + + /** + * @since 1.9 + * + * @param Title $title + * @param ParserOutput $parserOutput + * @param Cache|null $cache + */ + public function __construct( Title $title, ParserOutput $parserOutput, Cache $cache = null ) { + $this->title = $title; + $this->parserOutput = $parserOutput; + $this->cache = $cache; + + if ( $this->cache === null ) { + $this->cache = ApplicationFactory::getInstance()->getCache(); + } + + $this->initSemanticData(); + } + + /** + * @since 2.5 + * + * @param string $key + * @param mixed $default + * + * @return mixed + */ + public function getOption( $key, $default = null ) { + + if ( !$this->options instanceof Options ) { + $this->options = new Options(); + } + + return $this->options->safeGet( $key, $default ); + } + + /** + * @since 2.5 + * + * @param string $key + * @param string $value + */ + public function setOption( $key, $value ) { + + if ( !$this->options instanceof Options ) { + $this->options = new Options(); + } + + return $this->options->set( $key, $value ); + } + + /** + * @since 2.5 + * + * @param string $origin + */ + public function setOrigin( $origin ) { + $this->origin = $origin; + } + + /** + * @since 1.9 + * + * @return Title + */ + public function getTitle() { + return $this->title; + } + + /** + * @since 1.9 + * + * @return DIWikiPage + */ + public function getSubject() { + return $this->getSemanticData()->getSubject(); + } + + /** + * @since 1.9 + * + * @return ParserOutput + */ + public function getOutput() { + return $this->parserOutput; + } + + /** + * @since 3.0 + * + * @param ParserOptions $parserOptions + */ + public function setParserOptions( ParserOptions $parserOptions ) { + $this->parserOptions = $parserOptions; + } + + /** + * @since 3.0 + * + * @return ParserOptions|null + */ + public function addExtraParserKey( $key ) { + // Looks odd in 1.30 "Saved in parser cache ... idhash:19989-0!canonical!userlang!dateformat!userlang!dateformat!userlang!dateformat!userlang!dateformat and ..." + // threfore use the ParserOutput::recordOption instead + if ( $key === 'userlang' || $key === 'dateformat' ) { + $this->parserOutput->recordOption( $key ); + } elseif ( $this->parserOptions !== null ) { + $this->parserOptions->addExtraKey( $key ); + } + } + + /** + * @since 2.4 + * + * @return boolean + */ + public function isBlocked() { + + // ParserOutput::getExtensionData returns null if no value was set for this key + if ( $this->parserOutput->getExtensionData( self::ANNOTATION_BLOCK ) !== null && + $this->parserOutput->getExtensionData( self::ANNOTATION_BLOCK ) ) { + return true; + } + + return false; + } + + /** + * @since 3.0 + * + * @return boolean + */ + public function canUse() { + return !$this->isBlocked(); + } + + /** + * Returns collected errors occurred during processing + * + * @since 1.9 + * + * @return array + */ + public function getErrors() { + return $this->errors; + } + + /** + * @since 1.9 + */ + public function addError( $error ) { + $this->errors = array_merge( $this->errors, (array)$error ); + } + + /** + * @since 1.9 + * + * @param SemanticData $semanticData + */ + public function setSemanticData( SemanticData $semanticData ) { + $this->semanticData = $semanticData; + } + + /** + * @deprecated since 2.0, use setSemanticData + */ + public function setData( SemanticData $semanticData ) { + $this->setSemanticData( $semanticData ); + } + + /** + * @since 1.9 + * + * @return SemanticData + */ + public function getSemanticData() { + return $this->semanticData; + } + + /** + * @deprecated since 2.0, use getSemanticData + */ + public function getData() { + return $this->getSemanticData(); + } + + /** + * @since 2.1 + */ + public function setEmptySemanticData() { + $this->setSemanticData( new SemanticData( DIWikiPage::newFromTitle( $this->title ) ) ); + } + + /** + * @since 2.1 + * + * @param ParserOutput|null + */ + public function importFromParserOutput( ParserOutput $parserOutput = null ) { + + if ( $parserOutput === null ) { + return; + } + + $semanticData = $parserOutput->getExtensionData( self::DATA_ID ); + + // Only import data that is known to be different + if ( $semanticData !== null && + $this->getSubject()->equals( $semanticData->getSubject() ) && + $semanticData->getHash() !== $this->getSemanticData()->getHash() ) { + + $this->getSemanticData()->importDataFrom( $semanticData ); + } + } + + /** + * @since 3.0 + */ + public function copyToParserOutput() { + + // Ensure that errors are reported and recorded + $processingErrorMsgHandler = new ProcessingErrorMsgHandler( + $this->getSubject() + ); + + foreach ( $this->errors as $error ) { + $processingErrorMsgHandler->addToSemanticData( + $this->semanticData, + $processingErrorMsgHandler->newErrorContainerFromMsg( $error ) + ); + } + + $this->markParserOutput(); + $this->parserOutput->setExtensionData( self::DATA_ID, $this->semanticData ); + } + + /** + * @deprecated since 3.0, use copyToParserOutput + */ + public function pushSemanticDataToParserOutput() { + $this->copyToParserOutput(); + } + + /** + * @since 3.0 + */ + public function markParserOutput() { + + $this->parserOutput->setTimestamp( wfTimestampNow() ); + + $this->parserOutput->setProperty( + 'smw-semanticdata-status', + $this->semanticData->getProperties() !== [] + ); + } + + /** + * @deprecated since 3.0, use pushSemanticDataToParserOutput + */ + public function setSemanticDataStateToParserOutputProperty() { + $this->markParserOutput(); + } + + /** + * @since 2.5 + * + * @param ParserOutput $parserOutput + * + * @return boolean + */ + public static function hasSemanticData( ParserOutput $parserOutput ) { + return (bool)$parserOutput->getProperty( 'smw-semanticdata-status' ); + } + + /** + * @see SemanticData::addDataValue + * + * @since 1.9 + * + * @param SMWDataValue $dataValue + */ + public function addDataValue( DataValue $dataValue ) { + $this->semanticData->addDataValue( $dataValue ); + } + + /** + * Persistent marker to identify an update with a revision ID and allow + * to filter successive updates with that very same ID. + * + * @see LinksUpdateConstructed::process + * + * @since 3.0 + * + * @param integer $rev + */ + public function markUpdate( $rev ) { + $this->cache->save( smwfCacheKey( self::CACHE_NAMESPACE, $this->semanticData->getSubject()->getHash() ), $rev, 3600 ); + } + + /** + * @private This method is not for public use + * + * @since 1.9 + * + * @return boolean + */ + public function updateStore( $opts = [] ) { + + $isDeferrableUpdate = false; + + // @legacy + if ( $opts === true ) { + $isDeferrableUpdate = true; + } + + if ( isset( $opts['defer'] ) && $opts['defer'] ) { + $isDeferrableUpdate = true; + } + + $applicationFactory = ApplicationFactory::getInstance(); + $latestRevID = $this->title->getLatestRevID( Title::GAID_FOR_UPDATE ); + + if ( $this->skipUpdate( $latestRevID ) ) { + + $this->logger->info( + [ 'Update', 'Skipping update', 'Found revision', '{revID}' ], + [ 'role' => 'user', 'revID' => $latestRevID ] + ); + + return false; + } + + $this->semanticData->setOption( + Enum::OPT_SUSPEND_PURGE, + $this->getOption( Enum::OPT_SUSPEND_PURGE ) + ); + + $dataUpdater = $applicationFactory->newDataUpdater( + $this->semanticData + ); + + $dataUpdater->canCreateUpdateJob( + $this->getOption( self::OPT_CREATE_UPDATE_JOB, true ) + ); + + $dataUpdater->isChangeProp( + $this->getOption( self::OPT_CHANGE_PROP_UPDATE ) + ); + + $dataUpdater->isDeferrableUpdate( + $isDeferrableUpdate + ); + + $dataUpdater->setOrigin( + $this->origin + ); + + $dataUpdater->doUpdate(); + + return true; + } + + /** + * @note ParserOutput::setLimitReportData + * + * @since 2.4 + * + * @param string $key + * @param string $value + */ + public function addLimitReport( $key, $value ) { + $this->parserOutput->setLimitReportData( 'smw-limitreport-' . $key, $value ); + } + + /** + * Setup the semantic data container either from the ParserOutput or + * if not available create an empty container + */ + private function initSemanticData() { + + $this->semanticData = $this->parserOutput->getExtensionData( self::DATA_ID ); + + if ( !( $this->semanticData instanceof SemanticData ) ) { + $this->setEmptySemanticData(); + } + } + + private function skipUpdate( $rev ) { + + if ( $this->getOption( self::OPT_FORCED_UPDATE, false ) ) { + return false; + } + + $key = smwfCacheKey( + self::CACHE_NAMESPACE, + $this->semanticData->getSubject()->getHash() + ); + + return $this->cache->fetch( $key ) === $rev; + } + +} |