diff options
Diffstat (limited to 'www/wiki/extensions/SemanticMediaWiki/src/Store.php')
-rw-r--r-- | www/wiki/extensions/SemanticMediaWiki/src/Store.php | 582 |
1 files changed, 582 insertions, 0 deletions
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Store.php b/www/wiki/extensions/SemanticMediaWiki/src/Store.php new file mode 100644 index 00000000..4f1598f6 --- /dev/null +++ b/www/wiki/extensions/SemanticMediaWiki/src/Store.php @@ -0,0 +1,582 @@ +<?php + +namespace SMW; + +use InvalidArgumentException; +use Onoi\MessageReporter\MessageReporterAwareTrait; +use Psr\Log\LoggerAwareTrait; +use SMW\Connection\ConnectionManager; +use SMW\Utils\Timer; +use SMWDataItem as DataItem; +use SMWQuery; +use SMWQueryResult; +use SMWRequestOptions; +use SMWSemanticData; +use SMW\Services\Exception\ServiceNotFoundException; +use Title; + +/** + * This group contains all parts of SMW that relate to storing and retrieving + * semantic data. SMW components that relate to semantic querying only have + * their own group. + * + * @defgroup SMWStore SMWStore + * @ingroup SMW + */ + +/** + * The abstract base class for all classes that implement access to some + * semantic store. Besides the relevant interface, this class provides default + * implementations for some optional methods, which inform the caller that + * these methods are not implemented. + * + * @ingroup SMWStore + * + * @author Markus Krötzsch + */ +abstract class Store implements QueryEngine { + + use MessageReporterAwareTrait; + use LoggerAwareTrait; + + /** + * Option to define whether creating updates jobs is allowed for a request + * or not. + */ + const OPT_CREATE_UPDATE_JOB = 'opt.create.update.job'; + + /** + * @var ConnectionManager + */ + protected $connectionManager = null; + + /** + * @var Options + */ + protected $options = null; + +///// Reading methods ///// + + /** + * @see EntityLookup::getSemanticData + * + * @param DIWikiPage $subject + * @param string[]|bool $filter + */ + public abstract function getSemanticData( DIWikiPage $subject, $filter = false ); + + /** + * @see EntityLookup::getPropertyValues + * + * @param $subject mixed SMWDIWikiPage or null + * @param $property DIProperty + * @param $requestoptions SMWRequestOptions + * + * @return array of DataItem + */ + public abstract function getPropertyValues( $subject, DIProperty $property, $requestoptions = null ); + + /** + * @see EntityLookup::getPropertySubjects + * + * @return DIWikiPage[] + */ + public abstract function getPropertySubjects( DIProperty $property, $value, $requestoptions = null ); + + /** + * Get an array of all subjects that have some value for the given + * property. The result is an array of DIWikiPage objects. + * + * @return DIWikiPage[] + */ + public abstract function getAllPropertySubjects( DIProperty $property, $requestoptions = null ); + + /** + * @see EntityLookup::getProperties + * + * @param DIWikiPage $subject denoting the subject + * @param SMWRequestOptions|null $requestOptions optionally defining further options + * + * @return DataItem + */ + public abstract function getProperties( DIWikiPage $subject, $requestOptions = null ); + + /** + * @see EntityLookup::getInProperties + * + * @param DataItem $object + * @param RequestOptions|null $requestOptions + * + * @return DataItem[]|[] + */ + public abstract function getInProperties( DataItem $object, $requestoptions = null ); + + /** + * Convenience method to find the sortkey of an SMWDIWikiPage. The + * result is based on the contents of this store, and may differ from + * the MediaWiki database entry about a Title objects sortkey. If no + * sortkey is stored, the default sortkey (title string) is returned. + * + * @param DIWikiPage $dataItem + * + * @return string sortkey + */ + public function getWikiPageSortKey( DIWikiPage $dataItem ) { + + $dataItems = $this->getPropertyValues( $dataItem, new DIProperty( '_SKEY' ) ); + + if ( is_array( $dataItems ) && count( $dataItems ) > 0 ) { + return end( $dataItems )->getString(); + } + + return str_replace( '_', ' ', $dataItem->getDBkey() ); + } + + /** + * Convenience method to find the redirect target of a DIWikiPage + * or DIProperty object. Returns a dataitem of the same type that + * the input redirects to, or the input itself if there is no redirect. + * + * @param DataItem $dataItem + * + * @return DataItem + */ + public function getRedirectTarget( DataItem $dataItem ) { + + $type = $dataItem->getDIType(); + + if ( $type !== DataItem::TYPE_WIKIPAGE && $type !== DataItem::TYPE_PROPERTY ) { + throw new InvalidArgumentException( 'Store::getRedirectTarget expects a DIProperty or DIWikiPage object.' ); + } + + if ( $type === DataItem::TYPE_PROPERTY ) { + + if ( !$dataItem->isUserDefined() ) { + return $dataItem; + } + + $wikipage = $dataItem->getDiWikiPage(); + } elseif ( $type === DataItem::TYPE_WIKIPAGE ) { + $wikipage = $dataItem; + } + + $dataItems = $this->getPropertyValues( $wikipage, new DIProperty( '_REDI' ) ); + + if ( is_array( $dataItems ) && count( $dataItems ) > 0 ) { + + $redirectDataItem = end( $dataItems ); + + if ( $type == DataItem::TYPE_PROPERTY && $redirectDataItem instanceof DIWikiPage ) { + $dataItem = DIProperty::newFromUserLabel( $redirectDataItem->getDBkey() ); + } else { + $dataItem = $redirectDataItem; + } + } + + return $dataItem; + } + +///// Writing methods ///// + + /** + * Delete all semantic properties that the given subject has. This + * includes relations, attributes, and special properties. This does + * not delete the respective text from the wiki, but only clears the + * stored data. + * + * @param Title $subject + */ + public abstract function deleteSubject( Title $subject ); + + /** + * Update the semantic data stored for some individual. The data is + * given as a SemanticData object, which contains all semantic data + * for one particular subject. + * + * @param SemanticData $data + */ + protected abstract function doDataUpdate( SemanticData $data ); + + /** + * Update the semantic data stored for some individual. The data is + * given as a SemanticData object, which contains all semantic data + * for one particular subject. + * + * @param SemanticData $semanticData + */ + public function updateData( SemanticData $semanticData ) { + + if ( !$this->getOption( 'smwgSemanticsEnabled' ) ) { + return; + } + + Timer::start( __METHOD__ ); + + $applicationFactory = ApplicationFactory::getInstance(); + + $subject = $semanticData->getSubject(); + $hash = $subject->getHash(); + + /** + * @since 1.6 + */ + \Hooks::run( 'SMWStore::updateDataBefore', [ $this, $semanticData ] ); + + $this->doDataUpdate( $semanticData ); + + /** + * @since 1.6 + */ + \Hooks::run( 'SMWStore::updateDataAfter', [ $this, $semanticData ] ); + + $context = [ + 'method' => __METHOD__, + 'role' => 'production', + 'origin' => $hash, + 'procTime' => Timer::getElapsedTime( __METHOD__, 5 ), + ]; + + $this->logger->info( '[Store] Update completed: {origin} (procTime in sec: {procTime})', $context ); + + if ( !$this->getOption( 'smwgAutoRefreshSubject' ) || $semanticData->getOption( Enum::OPT_SUSPEND_PURGE ) ) { + return $this->logger->info( '[Store] Skipping html, parser cache purge', [ 'role' => 'user' ] ); + } + + $pageUpdater = $applicationFactory->newPageUpdater(); + + $pageUpdater->addPage( $subject->getTitle() ); + $pageUpdater->waitOnTransactionIdle(); + $pageUpdater->markAsPending(); + $pageUpdater->setOrigin( __METHOD__ ); + + $pageUpdater->doPurgeParserCache(); + $pageUpdater->doPurgeHtmlCache(); + $pageUpdater->pushUpdate(); + } + + /** + * Clear all semantic data specified for some page. + * + * @param DIWikiPage $di + */ + public function clearData( DIWikiPage $di ) { + $this->updateData( new SMWSemanticData( $di ) ); + } + + /** + * Update the store to reflect a renaming of some article. Normally + * this happens when moving pages in the wiki, and in this case there + * is also a new redirect page generated at the old position. The title + * objects given are only used to specify the name of the title before + * and after the move -- do not use their IDs for anything! The ID of + * the moved page is given in $pageid, and the ID of the newly created + * redirect, if any, is given by $redirid. If no new page was created, + * $redirid will be 0. + */ + public abstract function changeTitle( Title $oldtitle, Title $newtitle, $pageid, $redirid = 0 ); + +///// Query answering ///// + + /** + * @note Change the signature in 3.* to avoid for subclasses to manage the + * hooks; keep the current signature to adhere semver for the 2.* branch + * + * Execute the provided query and return the result as an + * SMWQueryResult if the query was a usual instance retrieval query. In + * the case that the query asked for a plain string (querymode + * MODE_COUNT or MODE_DEBUG) a plain wiki and HTML-compatible string is + * returned. + * + * @param SMWQuery $query + * + * @return SMWQueryResult + */ + public abstract function getQueryResult( SMWQuery $query ); + + /** + * @note Change the signature to abstract for the 3.* branch + * + * @since 2.1 + * + * @param SMWQuery $query + * + * @return SMWQueryResult + */ + protected function fetchQueryResult( SMWQuery $query ) { + } + +///// Special page functions ///// + + /** + * Return all properties that have been used on pages in the wiki. The + * result is an array of arrays, each containing a property data item + * and a count. The expected order is alphabetical w.r.t. to property + * names. + * + * If there is an error on creating some property object, then a + * suitable SMWDIError object might be returned in its place. Even if + * there are errors, the function should always return the number of + * results requested (otherwise callers might assume that there are no + * further results to ask for). + * + * @param SMWRequestOptions $requestoptions + * + * @return array of array( DIProperty|SMWDIError, integer ) + */ + public abstract function getPropertiesSpecial( $requestoptions = null ); + + /** + * Return all properties that have been declared in the wiki but that + * are not used on any page. Stores might restrict here to those + * properties that have been given a type if they have no efficient + * means of accessing the set of all pages in the property namespace. + * + * If there is an error on creating some property object, then a + * suitable SMWDIError object might be returned in its place. Even if + * there are errors, the function should always return the number of + * results requested (otherwise callers might assume that there are no + * further results to ask for). + * + * @param SMWRequestOptions $requestoptions + * + * @return array of DIProperty|SMWDIError + */ + public abstract function getUnusedPropertiesSpecial( $requestoptions = null ); + + /** + * Return all properties that are used on some page but that do not + * have any page describing them. Stores that have no efficient way of + * accessing the set of all existing pages can extend this list to all + * properties that are used but do not have a type assigned to them. + * + * @param SMWRequestOptions $requestoptions + * + * @return array of array( DIProperty, int ) + */ + public abstract function getWantedPropertiesSpecial( $requestoptions = null ); + + /** + * Return statistical information as an associative array with the + * following keys: + * - 'PROPUSES': Number of property instances (value assignments) in the datatbase + * - 'USEDPROPS': Number of properties that are used with at least one value + * - 'DECLPROPS': Number of properties that have been declared (i.e. assigned a type) + * - 'OWNPAGE': Number of properties with their own page + * - 'QUERY': Number of inline queries + * - 'QUERYSIZE': Represents collective query size + * - 'CONCEPTS': Number of declared concepts + * - 'SUBOBJECTS': Number of declared subobjects + * + * @return array + */ + public abstract function getStatistics(); + + /** + * Store administration + */ + + /** + * @private + * + * Returns store specific services. Services are registered with the store + * implementation and may provide different services that are only available + * for a particular store. + * + * @since 3.0 + * + * @param string $service + * + * @return mixed + * @throws ServiceNotFoundException + */ + public function service( $service, ...$args ) { + throw new ServiceNotFoundException( $service ); + } + + /** + * Setup all storage structures properly for using the store. This + * function performs tasks like creation of database tables. It is + * called upon installation as well as on upgrade: hence it must be + * able to upgrade existing storage structures if needed. It should + * return "true" if successful and return a meaningful string error + * message otherwise. + * + * The parameter $verbose determines whether the procedure is allowed + * to report on its progress. This is doen by just using print and + * possibly ob_flush/flush. This is also relevant for preventing + * timeouts during long operations. All output must be valid in an HTML + * context, but should preferably be plain text, possibly with some + * linebreaks and weak markup. + * + * @param boolean $verbose + * + * @return boolean Success indicator + */ + public abstract function setup( $verbose = true ); + + /** + * Drop (delete) all storage structures created by setup(). This will + * delete all semantic data and possibly leave the wiki uninitialised. + * + * @param boolean $verbose + */ + public abstract function drop( $verbose = true ); + + /** + * Refresh some objects in the store, addressed by numerical ids. The + * meaning of the ids is private to the store, and does not need to + * reflect the use of IDs elsewhere (e.g. page ids). The store is to + * refresh $count objects starting from the given $index. Typically, + * updates are achieved by generating update jobs. After the operation, + * $index is set to the next index that should be used for continuing + * refreshing, or to -1 for signaling that no objects of higher index + * require refresh. The method returns a decimal number between 0 and 1 + * to indicate the overall progress of the refreshing (e.g. 0.7 if 70% + * of all objects were refreshed). + * + * The optional parameter $namespaces may contain an array of namespace + * constants. If given, only objects from those namespaces will be + * refreshed. The default value FALSE disables this feature. + * + * The optional parameter $usejobs indicates whether updates should be + * processed later using MediaWiki jobs, instead of doing all updates + * immediately. The default is TRUE. + * + * @param $index integer + * @param $count integer + * @param $namespaces mixed array or false + * @param $usejobs boolean + * + * @return float between 0 and 1 to indicate the overall progress of the refreshing + */ + public abstract function refreshData( &$index, $count, $namespaces = false, $usejobs = true ); + + /** + * Setup the store. + * + * @since 1.8 + * + * @param bool $verbose + * @param Options|null $options + * + * @return boolean Success indicator + */ + public static function setupStore( $verbose = true, $options = null ) { + + // See notes in ExtensionSchemaUpdates + if ( is_bool( $verbose ) ) { + $verbose = $verbose; + } + + if ( isset( $options['verbose'] ) ) { + $verbose = $options['verbose']; + } + + if ( isset( $options['options'] ) ) { + $options = $options['options']; + } + + $store = StoreFactory::getStore(); + + if ( $options instanceof Options ) { + foreach ( $options->getOptions() as $key => $value ) { + $store->getOptions()->set( $key, $value ); + } + } + + return $store->setup( $verbose ); + } + + /** + * @since 2.5 + * + * @return Options + */ + public function getOptions() { + + if ( $this->options === null ) { + $this->options = new Options(); + } + + return $this->options; + } + + /** + * @since 3.0 + * + * @param string $key + * @param mixed $value + */ + public function setOption( $key, $value ) { + + if ( $this->options === null ) { + $this->options = new Options(); + } + + return $this->options->set( $key, $value ); + } + + /** + * @since 3.0 + * + * @param string $key + * @param mixed $default + * + * @return mixed + */ + public function getOption( $key, $default = null ) { + + if ( $this->options === null ) { + $this->options = new Options(); + } + + return $this->options->safeGet( $key, $default ); + } + + /** + * @since 2.0 + */ + public function clear() { + + if ( $this->connectionManager !== null ) { + $this->connectionManager->releaseConnections(); + } + } + + /** + * @since 3.0 + * + * @param string|null $type + * + * @return array + */ + public function getInfo( $type = null ) { + return []; + } + + /** + * @since 2.1 + * + * @param ConnectionManager $connectionManager + */ + public function setConnectionManager( ConnectionManager $connectionManager ) { + $this->connectionManager = $connectionManager; + } + + /** + * @since 2.1 + * + * @param string $type + * + * @return mixed + */ + public function getConnection( $type ) { + + if ( $this->connectionManager === null ) { + $this->connectionManager = ApplicationFactory::getInstance()->getConnectionManager(); + } + + return $this->connectionManager->getConnection( $type ); + } + +} |