store = $store; $this->serializerFactory = $serializerFactory; } /** * @since 2.5 * * @param array $propertyList */ public function setPropertyList( array $propertyList ) { $this->propertyList = $propertyList; } /** * @see https://www.mediawiki.org/wiki/Manual:$wgCommandLineMode * Indicates whether MW is running in command-line mode. * * @since 3.0 * * @param boolean $isCommandLineMode */ public function isCommandLineMode( $isCommandLineMode ) { $this->isCommandLineMode = $isCommandLineMode; } /** * @since 1.9 * * @return boolean */ public function hasDiff() { return $this->hasDiff; } /** * @since 2.5 * * @param DIWikiPage $subject */ public function notify( DIWikiPage $subject ) { $namespace = $subject->getNamespace(); if ( !$this->hasDiff() || ( $namespace !== SMW_NS_PROPERTY && $namespace !== NS_CATEGORY ) ) { return false; } $params = []; if ( $this->isTypePropagation ) { $params['isTypePropagation'] = true; } return ChangePropagationDispatchJob::planAsJob( $subject, $params ); } /** * Compare and detect differences between the invoked semantic data * and the current stored data * * @note Compare on extra properties from `smwgChangePropagationWatchlist` * (e.g '_PLIST') to find a possible specification change * * @since 1.9 */ public function checkAndNotify( SemanticData &$semanticData ) { $namespace = $semanticData->getSubject()->getNamespace(); if ( $namespace !== SMW_NS_PROPERTY && $namespace !== NS_CATEGORY ) { return; } $this->hasDiff = false; // Check the type first $propertyList = array_merge( [ '_TYPE', '_CONV', '_UNIT', '_REDI' ], $this->propertyList ); foreach ( $propertyList as $key ) { // No need to keep comparing once a diff has been // detected if ( $this->hasDiff() ) { break; } $this->doCompare( $semanticData, $key ); } $this->doNotifyAndPostpone( $semanticData ); } private function doCompare( $semanticData, $key ) { $property = new DIProperty( $key ); $newValues = $semanticData->getPropertyValues( $property ); $oldValues = $this->store->getPropertyValues( $semanticData->getSubject(), $property ); $this->setDiff( !$this->isEqual( $oldValues, $newValues ), $key ); } private function setDiff( $hasDiff = true, $key ) { if ( !$hasDiff || $this->hasDiff ) { return; } $this->hasDiff = true; $this->isTypePropagation = $key === '_TYPE'; } /** * Helper function that compares two arrays of data values to check whether * they contain the same content. Returns true if the two arrays contain the * same data values (irrespective of their order), false otherwise. * * @param SMWDataItem[] $oldDataValue * @param SMWDataItem[] $newDataValue * * @return boolean */ private function isEqual( array $oldDataValue, array $newDataValue ) { // The hashes of all values of both arrays are taken, then sorted // and finally concatenated, thus creating one long hash out of each // of the data value arrays. These are compared. $values = []; foreach ( $oldDataValue as $v ) { $values[] = $v->getHash(); } sort( $values ); $oldDataValueHash = implode( '___', $values ); $values = []; foreach ( $newDataValue as $v ) { $values[] = $v->getHash(); } sort( $values ); $newDataValueHash = implode( '___', $values ); return $oldDataValueHash == $newDataValueHash; } private function doNotifyAndPostpone( SemanticData &$semanticData ) { if ( !$this->hasDiff() ) { return; } $this->notify( $semanticData->getSubject() ); // If executed from the commandLine (cronJob etc.), do not // suspend the update if ( $this->isCommandLineMode === true ) { return; } $previous = $this->store->getSemanticData( $semanticData->getSubject() ); $semanticDataSerializer = $this->serializerFactory->newSemanticDataSerializer(); $new = $semanticDataSerializer->serialize( $semanticData ); // Encode and store the new version of the SemanticData and suspend // the update until ChangePropagationDispatchJob was able to select // all connected entities $previous->addPropertyObjectValue( new DIProperty( DIProperty::TYPE_CHANGE_PROP ), new DIBlob( json_encode( $new ) ) ); $semanticData = $previous; } }