cachedPropertyValuesPrefetcher = $cachedPropertyValuesPrefetcher; $this->intermediaryMemoryCache = $intermediaryMemoryCache; $this->languageCode = Localizer::getInstance()->getContentLanguage()->getCode(); } /** * @since 2.4 * * @param DIWikiPage $subject */ public function resetCacheBy( DIWikiPage $subject ) { $this->cachedPropertyValuesPrefetcher->resetCacheBy( $subject ); $this->intermediaryMemoryCache->delete( $subject->getHash() ); } /** * @since 2.5 * * @param DIProperty|DIWikiPage $source * @param DIProperty $target * * @return []|DataItem[] */ public function getSpecification( $source, DIProperty $target ) { if ( $source instanceof DIProperty ) { $dataItem = $source->getCanonicalDiWikiPage(); } elseif( $source instanceof DIWikiPage ) { $dataItem = $source; } else { throw new RuntimeException( "Invalid request instance type" ); } $hash = $dataItem->getHash(); $key = $target->getKey(); $definition = $this->intermediaryMemoryCache->fetch( $hash ); if ( $definition === false ) { $definition = []; } if ( isset( $definition[$key] ) ) { return $definition[$key]; } $dataItems = $this->cachedPropertyValuesPrefetcher->getPropertyValues( $dataItem, $target ); if ( !is_array( $dataItems ) ) { $dataItems = []; } $definition[$key] = $dataItems; $this->intermediaryMemoryCache->save( $hash, $definition ); return $dataItems; } /** * @since 2.5 * * @param DIProperty $property * * @return false|DataItem */ public function getFieldListBy( DIProperty $property ) { $fieldList = false; $dataItems = $this->getSpecification( $property, new DIProperty( '_LIST' ) ); if ( is_array( $dataItems ) && $dataItems !== [] ) { $fieldList = end( $dataItems ); } return $fieldList; } /** * @since 2.5 * * @param DIProperty $property * @param string $languageCode * * @return string */ public function getPreferredPropertyLabelBy( DIProperty $property, $languageCode = '' ) { $languageCode = $languageCode === '' ? $this->languageCode : $languageCode; $key = 'ppl:' . $languageCode . ':'. $property->getKey(); // Guard against high frequency lookup if ( ( $preferredPropertyLabel = $this->intermediaryMemoryCache->fetch( $key ) ) !== false ) { return $preferredPropertyLabel; } $preferredPropertyLabel = $this->findPreferredPropertyLabel( $property, $languageCode ); $this->intermediaryMemoryCache->save( $key, $preferredPropertyLabel ); return $preferredPropertyLabel; } /** * @since 2.4 * * @param string $displayTitle * * @return DIProperty|false */ public function getPropertyFromDisplayTitle( $displayTitle ) { $descriptionFactory = new DescriptionFactory(); $description = $descriptionFactory->newSomeProperty( new DIProperty( '_DTITLE' ), $descriptionFactory->newValueDescription( new DIBlob( $displayTitle ) ) ); $query = new Query( $description ); $query->setLimit( 1 ); $query->setOption( Query::PROC_CONTEXT, 'PropertySpecificationLookup' ); $dataItems = $this->cachedPropertyValuesPrefetcher->queryPropertyValuesFor( $query ); if ( is_array( $dataItems ) && $dataItems !== [] ) { $dataItem = end( $dataItems ); // Cache results as a linked list attached to // the property so that it can be purged all together return new DIProperty( $dataItem->getDBKey() ); } return false; } /** * @since 2.4 * * @param DIProperty $property * * @return boolean */ public function hasUniquenessConstraint( DIProperty $property ) { $hasUniquenessConstraint = false; $dataItems = $this->getSpecification( $property, new DIProperty( '_PVUC' ) ); if ( is_array( $dataItems ) && $dataItems !== [] ) { $hasUniquenessConstraint = end( $dataItems )->getBoolean(); } return $hasUniquenessConstraint; } /** * @since 3.0 * * @param DIProperty $property * * @return DataItem|null */ public function getPropertyGroup( DIProperty $property ) { $dataItem = null; $dataItems = $this->getSpecification( $property, new DIProperty( '_INST' ) ); if ( is_array( $dataItems ) && $dataItems !== [] ) { foreach ( $dataItems as $dataItem ) { $pv = $this->cachedPropertyValuesPrefetcher->getPropertyValues( $dataItem, new DIProperty( '_PPGR' ) ); $di = end( $pv ); if ( $di instanceof DIBoolean && $di->getBoolean() ) { return $dataItem; } } } return null; } /** * @since 2.5 * * @param DIProperty $property * * @return DataItem|null */ public function getExternalFormatterUri( DIProperty $property ) { $dataItem = null; $dataItems = $this->getSpecification( $property, new DIProperty( '_PEFU' ) ); if ( is_array( $dataItems ) && $dataItems !== [] ) { $dataItem = end( $dataItems ); } return $dataItem; } /** * @since 2.4 * * @param DIProperty $property * * @return string */ public function getAllowedPatternBy( DIProperty $property ) { $allowsPattern = ''; $dataItems = $this->getSpecification( $property, new DIProperty( '_PVAP' ) ); if ( is_array( $dataItems ) && $dataItems !== [] ) { $allowsPattern = end( $dataItems )->getString(); } return $allowsPattern; } /** * @since 2.4 * * @param DIProperty $property * * @return array */ public function getAllowedValues( DIProperty $property ) { $allowsValues = []; $dataItems = $this->getSpecification( $property, new DIProperty( '_PVAL' ) ); if ( is_array( $dataItems ) && $dataItems !== [] ) { $allowsValues = $dataItems; } return $allowsValues; } /** * @since 2.5 * * @param DIProperty $property * * @return array */ public function getAllowedListValues( DIProperty $property ) { $allowsListValue = []; $dataItems = $this->getSpecification( $property, new DIProperty( '_PVALI' ) ); if ( is_array( $dataItems ) && $dataItems !== [] ) { $allowsListValue = $dataItems; } return $allowsListValue; } /** * @since 2.4 * * @param DIProperty $property * * @return integer|false */ public function getDisplayPrecision( DIProperty $property ) { $displayPrecision = false; $dataItems = $this->getSpecification( $property, new DIProperty( '_PREC' ) ); if ( $dataItems !== false && $dataItems !== [] ) { $dataItem = end( $dataItems ); $displayPrecision = abs( (int)$dataItem->getNumber() ); } return $displayPrecision; } /** * @since 2.4 * * @param DIProperty $property * * @return array */ public function getDisplayUnits( DIProperty $property ) { $units = []; $dataItems = $this->cachedPropertyValuesPrefetcher->getPropertyValues( $property->getCanonicalDiWikiPage(), new DIProperty( '_UNIT' ) ); if ( $dataItems !== false && $dataItems !== [] ) { foreach ( $dataItems as $dataItem ) { $units = array_merge( $units, preg_split( '/\s*,\s*/u', $dataItem->getString() ) ); } } return $units; } /** * We try to cache anything to avoid unnecessary store connections or DB * lookups. For cases where a property was changed, the EventDipatcher will * receive a 'property.specification.change' event (emitted as soon as the content of * a property page was altered) with PropertySpecificationLookup::resetCacheBy * being invoked to remove the cache entry for that specific property. * * @since 2.4 * * @param DIProperty $property * @param string $languageCode * @param mixed|null $linker * * @return string */ public function getPropertyDescriptionByLanguageCode( DIProperty $property, $languageCode = '', $linker = null ) { // Take the linker into account (Special vs. in page rendering etc.) $languageCode = $languageCode === '' ? $this->languageCode : $languageCode; $key = '--pdesc:' . $languageCode . ':' . ( $linker === null ? '0' : '1' ); $blobStore = $this->cachedPropertyValuesPrefetcher->getBlobStore(); $container = $blobStore->read( $this->cachedPropertyValuesPrefetcher->getRootHashFrom( $property->getCanonicalDiWikiPage() ) ); if ( $container->has( $key ) ) { return $container->get( $key ); } $localPropertyDescription = $this->findLocalPropertyDescription( $property, $linker, $languageCode ); // If a local property description wasn't available for a predefined property // the try to find a system translation if ( trim( $localPropertyDescription ) === '' && !$property->isUserDefined() ) { $localPropertyDescription = $this->getPredefinedPropertyDescription( $property, $linker, $languageCode ); } $container->set( $key, $localPropertyDescription ); $blobStore->save( $container ); return $localPropertyDescription; } private function getPredefinedPropertyDescription( $property, $linker, $languageCode ) { $description = ''; $key = $property->getKey(); if ( ( $msgKey = PropertyRegistry::getInstance()->findPropertyDescriptionMsgKeyById( $key ) ) === '' ) { $msgKey = 'smw-property-predefined' . str_replace( '_', '-', strtolower( $key ) ); } if ( !Message::exists( $msgKey ) ) { return $description; } $dataValue = DataValueFactory::getInstance()->newDataValueByItem( $property ); $label = $dataValue->getFormattedLabel(); $message = Message::get( [ $msgKey, $label ], $linker === null ? Message::ESCAPED : Message::PARSE, $languageCode ); return $message; } private function findLocalPropertyDescription( $property, $linker, $languageCode ) { $text = ''; $descriptionProperty = new DIProperty( '_PDESC' ); $dataItems = $this->cachedPropertyValuesPrefetcher->getPropertyValues( $property->getCanonicalDiWikiPage(), $descriptionProperty ); if ( ( $dataValue = $this->findTextValueByLanguage( $dataItems, $descriptionProperty, $languageCode ) ) !== null ) { $text = $dataValue->getShortWikiText( $linker ); } return $text; } private function findPreferredPropertyLabel( $property, $languageCode ) { $text = ''; $preferredProperty = new DIProperty( '_PPLB' ); $dataItems = $this->cachedPropertyValuesPrefetcher->getPropertyValues( $property->getCanonicalDiWikiPage(), $preferredProperty ); if ( ( $dataValue = $this->findTextValueByLanguage( $dataItems, $preferredProperty, $languageCode ) ) !== null ) { $text = $dataValue->getShortWikiText(); } return $text; } private function findTextValueByLanguage( $dataItems, $property, $languageCode ) { if ( $dataItems === null || $dataItems === [] ) { return null; } foreach ( $dataItems as $dataItem ) { $dataValue = DataValueFactory::getInstance()->newDataValueByItem( $dataItem, $property ); // Here a MonolingualTextValue was retunred therefore the method // can be called without validation $dv = $dataValue->getTextValueByLanguage( $languageCode ); if ( $dv !== null ) { return $dv; } } return null; } }