Mwjames has uploaded a new change for review.

  https://gerrit.wikimedia.org/r/117168

Change subject: Add SG\CacheInvalidator + unit tests
......................................................................

Add SG\CacheInvalidator + unit tests

- Split responsibilities of the former SemanticGlossaryCacheHandling
- Moved value inpsection into PropertyValueInspector
- Removed static behaviour
- Added unit test

Change-Id: Ic898185210adeafb932bb763cf166770dcf20e3a
---
M SemanticGlossary.php
A src/CacheInvalidator.php
A src/PropertyValueInspector.php
A tests/phpunit/CacheInvalidatorTest.php
A tests/phpunit/PropertyValueInspectorTest.php
5 files changed, 444 insertions(+), 10 deletions(-)


  git pull 
ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/SemanticGlossary 
refs/changes/68/117168/1

diff --git a/SemanticGlossary.php b/SemanticGlossary.php
index 8ac90eb..ba932c7 100644
--- a/SemanticGlossary.php
+++ b/SemanticGlossary.php
@@ -59,20 +59,12 @@
        // register class files with the Autoloader
        $autoloadClasses = array(
                'SemanticGlossaryBackend' => $dir . 
'/SemanticGlossaryBackend.php',
-               'SemanticGlossaryCacheHandling' => $dir . 
'/SemanticGlossaryCacheHandling.php',
                'SG\PropertyRegistry' => $dir . '/src/PropertyRegistry.php',
+               'SG\CacheInvalidator' => $dir . '/src/CacheInvalidator.php',
+               'SG\PropertyValueInspector' => $dir . 
'/src/PropertyValueInspector.php',
        );
 
        $GLOBALS[ 'wgAutoloadClasses' ] = array_merge( $GLOBALS[ 
'wgAutoloadClasses' ], $autoloadClasses );
-
-       // register hook handlers
-       $hooks = array(
-               'SMWStore::updateDataBefore' => array( 
'SemanticGlossaryCacheHandling::purgeCacheForData' ), // invalidate on update
-               'smwDeleteSemanticData' => array( 
'SemanticGlossaryCacheHandling::purgeCacheForSubject' ), // invalidate on delete
-               'TitleMoveComplete' => array( 
'SemanticGlossaryCacheHandling::purgeCacheForTitle' ), // move annotations
-       );
-
-       $GLOBALS[ 'wgHooks' ] = array_merge_recursive( $GLOBALS[ 'wgHooks' ], 
$hooks );
 
        define( 'SG_PROP_GLT', 'Glossary-Term' );
        define( 'SG_PROP_GLD', 'Glossary-Definition' );
@@ -88,4 +80,31 @@
                return 
\SG\PropertyRegistry::getInstance()->registerPropertiesAndAliases();
        };
 
+       /**
+        * Invalidate on update
+        *
+        * @since 1.0
+        */
+       $GLOBALS['wgHooks']['SMWStore::updateDataBefore'][] = function ( 
SMWStore $store, SMWSemanticData $semanticData ) {
+               return \SG\CacheInvalidator::getInstance()->invalidateOnUpdate( 
$store, $semanticData );
+       };
+
+       /**
+        * Invalidate on delete
+        *
+        * @since 1.0
+        */
+       $GLOBALS['wgHooks']['smwDeleteSemanticData'][] = function ( 
SMWDIWikiPage $subject ) {
+               return \SG\CacheInvalidator::getInstance()->invalidateOnDelete( 
smwfGetStore(), $subject );
+       };
+
+       /**
+        * Invalidate on title move
+        *
+        * @since 1.0
+        */
+       $GLOBALS['wgHooks']['TitleMoveComplete'][] = function ( &$old_title, 
&$new_title, &$user, $pageid, $redirid ) {
+               return \SG\CacheInvalidator::getInstance()->invalidateOnMove( 
$old_title );
+       };
+
 } );
diff --git a/src/CacheInvalidator.php b/src/CacheInvalidator.php
new file mode 100644
index 0000000..1a4f161
--- /dev/null
+++ b/src/CacheInvalidator.php
@@ -0,0 +1,170 @@
+<?php
+
+namespace SG;
+
+use LingoParser;
+use BagOStuff;
+use Title;
+
+/**
+ * @ingroup SG
+ *
+ * @licence GNU GPL v2+
+ * @since 1.0
+ *
+ * @author Stephan Gambke
+ * @author mwjames
+ */
+class CacheInvalidator {
+
+       protected static $instance = null;
+       protected $cache = null;
+
+       /**
+        * @since 1.0
+        *
+        * @return PropertyRegistry
+        */
+       public static function getInstance() {
+
+               if ( self::$instance === null ) {
+
+                       $instance = new self();
+                       $instance->setCache( $instance->acquireCache() );
+
+                       self::$instance = $instance;
+               }
+
+               return self::$instance;
+       }
+
+       /**
+        * @since 1.0
+        */
+       public static function clear() {
+               self::$instance = null;
+       }
+
+       /**
+        * @since 1.0
+        *
+        * @param BagOStuff $cache
+        */
+       public function setCache( BagOStuff $cache ) {
+               $this->cache = $cache;
+       }
+
+       /**
+        * @since 1.0
+        *
+        * @param Store $store
+        * @param SemanticData $semanticData
+        *
+        * @return boolean
+        */
+       public function invalidateOnUpdate( \SMWStore $store, \SMWSemanticData 
$semanticData ) {
+
+               wfProfileIn( __METHOD__ );
+
+               $this->findAllSubobjects( $store, $semanticData );
+
+               if ( $this->hasModifiedData( $store, $semanticData ) ) {
+                       $this->purgeCache( $semanticData->getSubject() );
+                       LingoParser::purgeCache();
+               }
+
+               wfProfileOut( __METHOD__ );
+               return true;
+       }
+
+       /**
+        * @since 1.0
+        *
+        * @param SMWStore $store
+        * @param SMWDIWikiPage $subject
+        * @param boolean|true $purgeLingo
+        *
+        * @return boolean
+        */
+       public function invalidateOnDelete( \SMWStore $store, \SMWDIWikiPage 
$subject, $purgeLingo = true ) {
+
+               wfProfileIn( __METHOD__ );
+
+               $this->findSubobjectsToSubject( $store, $subject );
+               $this->purgeCache( $subject );
+
+               if ( $purgeLingo ) {
+                       LingoParser::purgeCache();
+               }
+
+               wfProfileOut( __METHOD__ );
+               return true;
+       }
+
+       /**
+        * @since 1.0
+        *
+        * @param Title $title
+        *
+        * @return boolean
+        */
+       public function invalidateOnMove( Title $title ) {
+               $this->purgeCache( \SMWDIWikiPage::newFromTitle( $title ) );
+               return true;
+       }
+
+       protected function findAllSubobjects( \SMWStore $store, 
\SMWSemanticData $semanticData ) {
+
+               $properties = $semanticData->getProperties();
+
+               if ( array_key_exists( '_SOBJ', $properties ) ) {
+                       foreach ( $semanticData->getPropertyValues( 
$properties['_SOBJ'] ) as $so ) {
+                               $this->invalidateOnUpdate( $store, 
$store->getSemanticData( $so ), false );
+                       }
+               }
+       }
+
+       protected function findSubobjectsToSubject( \SMWStore $store, 
\SMWDIWikiPage $subject ) {
+
+               $properties = $store->getProperties( $subject );
+
+               if ( array_key_exists( '_SOBJ', $properties ) ) {
+                       foreach ( $store->getPropertyValues( $subject, 
$properties['_SOBJ'] ) as $so ) {
+                               $this->invalidateOnDelete( $store, 
$so->getSubject(), false );
+                       }
+               }
+       }
+
+       protected function hasModifiedData( \SMWStore $store, \SMWSemanticData 
$semanticData ) {
+
+               $propertyValueInspector = new PropertyValueInspector( $store, 
$semanticData );
+
+               return $propertyValueInspector->inspect( 
PropertyRegistry::SG_TERM ) ||
+                       $propertyValueInspector->inspect( 
PropertyRegistry::SG_DEFINITION ) ||
+                       $propertyValueInspector->inspect( 
PropertyRegistry::SG_LINK ) ||
+                       $propertyValueInspector->inspect( 
PropertyRegistry::SG_STYLE );
+       }
+
+       protected function purgeCache( \SMWDIWikiPage $subject ) {
+               wfProfileIn( __METHOD__ );
+
+               $this->cache->delete( $this->getCacheId( 
$subject->getSerialization() ) );
+
+               wfProfileOut( __METHOD__ );
+               return true;
+       }
+
+       private function getCacheId( $identifier ) {
+               return wfMemcKey( 'ext', 'semanticglossary', $identifier );
+       }
+
+       private function acquireCache() {
+
+               if ( isset( $GLOBAL['wgexLingoCacheType'] ) && 
$GLOBAL['wgexLingoCacheType'] !== null ) {
+                       return wfGetCache( $GLOBAL['wgexLingoCacheType'] );
+               }
+
+               return wfGetMainCache();
+       }
+
+}
diff --git a/src/PropertyValueInspector.php b/src/PropertyValueInspector.php
new file mode 100644
index 0000000..ecf11ad
--- /dev/null
+++ b/src/PropertyValueInspector.php
@@ -0,0 +1,88 @@
+<?php
+
+namespace SG;
+
+/**
+ * @ingroup SG
+ *
+ * @licence GNU GPL v2+
+ * @since 1.0
+ *
+ * @author Stephan Gambke
+ */
+class PropertyValueInspector {
+
+       protected $store = null;
+       protected $semanticData = null;
+
+       /**
+        * @since 1.0
+        *
+        * @param SMWStore $store
+        * @param SMWSemanticData $semanticData
+        */
+       public function __construct( \SMWStore $store, \SMWSemanticData 
$semanticData ) {
+               $this->store = $store;
+               $this->semanticData = $semanticData;
+       }
+
+       /**
+        * @since 1.0
+        *
+        * @param string $propertId
+        *
+        * @return boolean
+        */
+       public function inspect( $propertId ) {
+
+               $properties = $this->semanticData->getProperties();
+
+               // check if property changed
+               if ( array_key_exists( $propertId, $properties ) ) {
+
+                       $newEntries = $this->semanticData->getPropertyValues( 
$properties[$propertId] );
+                       $oldEntries = $this->store->getPropertyValues(
+                               $this->semanticData->getSubject(),
+                               $properties[$propertId]
+                       );
+               } else {
+
+                       $newEntries = array();
+                       $oldEntries = $this->store->getPropertyValues(
+                               $this->semanticData->getSubject(),
+                               new \SMWDIProperty( $propertId )
+                       );
+               }
+
+               // Did the number of entries change?
+               if ( count( $newEntries ) !== count( $oldEntries ) ) {
+                       return true;
+               }
+
+               // Match each new entry against an old entry
+               foreach ( $newEntries as $newDi ) {
+                       $found = false;
+                       foreach ( $oldEntries as $oldKey => $oldDi ) {
+                               if ( $newDi->getHash() === $oldDi->getHash() ) {
+                                       $found = true;
+                                       unset( $oldEntries[$oldKey] );
+                                       break;
+                               }
+                       }
+
+                       // If no match was possible...
+                       if ( !$found ) {
+                               return true;
+                       }
+               }
+
+               // Are there unmatched old entries left?
+               if ( count( $oldEntries ) > 0 ) {
+                       return true;
+               }
+
+               // Every new entry matched to exaclty one old entry and vice 
versa
+               return false;
+       }
+
+}
diff --git a/tests/phpunit/CacheInvalidatorTest.php 
b/tests/phpunit/CacheInvalidatorTest.php
new file mode 100644
index 0000000..dd15e0c
--- /dev/null
+++ b/tests/phpunit/CacheInvalidatorTest.php
@@ -0,0 +1,105 @@
+<?php
+
+namespace SG\Tests;
+
+use SG\PropertyRegistry;
+use SG\CacheInvalidator;
+
+use HashBagOStuff;
+
+/**
+ * @covers \SG\CacheInvalidator
+ *
+ * @ingroup Test
+ *
+ * @group SG
+ * @group SGExtension
+ *
+ * @licence GNU GPL v2+
+ * @since 1.0
+ *
+ * @author mwjames
+ */
+class CacheInvalidatorTest extends \PHPUnit_Framework_TestCase {
+
+       public function testCanConstruct() {
+               $this->assertInstanceOf(
+                       '\SG\CacheInvalidator',
+                       CacheInvalidator::getInstance()
+               );
+       }
+
+       public function testInvalidateOnUpdateWithEmptyData() {
+
+               $store = $this->getMockBuilder( 'SMWStore' )
+                       ->disableOriginalConstructor()
+                       ->getMockForAbstractClass();
+
+               $semanticData = $this->getMockBuilder( 'SMWSemanticData' )
+                       ->disableOriginalConstructor()
+                       ->getMockForAbstractClass();
+
+               $instance = new CacheInvalidator;
+               $instance->setCache( new HashBagOStuff );
+
+               $this->assertTrue( $instance->invalidateOnUpdate( $store, 
$semanticData ) );
+       }
+
+       public function testInvalidateOnDeleteWithEmptyData() {
+
+               $subject = \SMWDIWikiPage::newFromTitle( \Title::newFromText( 
__METHOD__ ) );
+
+               $store = $this->getMockBuilder( 'SMWStore' )
+                       ->disableOriginalConstructor()
+                       ->getMockForAbstractClass();
+
+               $store->expects( $this->once() )
+                       ->method( 'getProperties' )
+                       ->with( $this->equalTo( $subject ) )
+                       ->will( $this->returnValue( array() ) );
+
+               $instance = new CacheInvalidator;
+               $instance->setCache( new HashBagOStuff );
+
+               $this->assertTrue( $instance->invalidateOnDelete( $store, 
$subject ) );
+       }
+
+       public function testInvalidateOnDeleteWithSubobject() {
+
+               $subobject  = new \SMWDIProperty( '_SOBJ' );
+               $subject    = \SMWDIWikiPage::newFromTitle( 
\Title::newFromText( __METHOD__ ) );
+               $newSubject = \SMWDIWikiPage::newFromTitle( 
\Title::newFromText( 'Subobject' ) );
+
+               $store = $this->getMockBuilder( 'SMWStore' )
+                       ->disableOriginalConstructor()
+                       ->getMockForAbstractClass();
+
+               $store->expects( $this->once() )
+                       ->method( 'getProperties' )
+                       ->with( $this->equalTo( $subject ) )
+                       ->will( $this->returnValue( array( '_SOBJ' => 
$subobject ) ) );
+
+               $store->expects( $this->once() )
+                       ->method( 'getPropertyValues' )
+                       ->with(
+                               $this->equalTo( $subject ),
+                               $this->equalTo( $subobject ) )
+                       ->will( $this->returnValue( $newSubject ) );
+
+               $instance = new CacheInvalidator;
+               $instance->setCache( new HashBagOStuff );
+
+               $this->assertTrue( $instance->invalidateOnDelete( $store, 
$subject ) );
+       }
+
+       public function testInvalidateOnMove() {
+
+               $title = \Title::newFromText( __METHOD__ );
+
+               $instance = new CacheInvalidator;
+               $instance->setCache( new HashBagOStuff );
+
+               $this->assertTrue( $instance->invalidateOnMove( $title ) );
+       }
+
+}
diff --git a/tests/phpunit/PropertyValueInspectorTest.php 
b/tests/phpunit/PropertyValueInspectorTest.php
new file mode 100644
index 0000000..2f23ceb
--- /dev/null
+++ b/tests/phpunit/PropertyValueInspectorTest.php
@@ -0,0 +1,52 @@
+<?php
+
+namespace SG\Tests;
+
+use SG\PropertyValueInspector;
+
+/**
+ * @covers \SG\PropertyValueInspector
+ *
+ * @ingroup Test
+ *
+ * @group SG
+ * @group SGExtension
+ *
+ * @licence GNU GPL v2+
+ * @since 1.0
+ *
+ * @author mwjames
+ */
+class PropertyValueInspectorTest extends \PHPUnit_Framework_TestCase {
+
+       public function testCanConstruct() {
+
+               $store = $this->getMockBuilder( 'SMWStore' )
+                       ->disableOriginalConstructor()
+                       ->getMockForAbstractClass();
+
+               $semanticData = $this->getMockBuilder( 'SMWSemanticData' )
+                       ->disableOriginalConstructor()
+                       ->getMockForAbstractClass();
+
+               $instance = new PropertyValueInspector( $store, $semanticData );
+
+               $this->assertInstanceOf( '\SG\PropertyValueInspector', 
$instance );
+       }
+
+       public function testInspectWithoutData() {
+
+               $store = $this->getMockBuilder( 'SMWStore' )
+                       ->disableOriginalConstructor()
+                       ->getMockForAbstractClass();
+
+               $semanticData = $this->getMockBuilder( 'SMWSemanticData' )
+                       ->disableOriginalConstructor()
+                       ->getMockForAbstractClass();
+
+               $instance = new PropertyValueInspector( $store, $semanticData );
+
+               $this->assertFalse( $instance->inspect( 'foo' ) );
+       }
+
+}

-- 
To view, visit https://gerrit.wikimedia.org/r/117168
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: Ic898185210adeafb932bb763cf166770dcf20e3a
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/SemanticGlossary
Gerrit-Branch: master
Gerrit-Owner: Mwjames <jamesin.hongkon...@gmail.com>

_______________________________________________
MediaWiki-commits mailing list
MediaWiki-commits@lists.wikimedia.org
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to