jenkins-bot has submitted this change and it was merged. Change subject: Move WatchedItem::duplicateEntries to WatchedItemStore ......................................................................
Move WatchedItem::duplicateEntries to WatchedItemStore This removes static logic from WatchedItem into a class that we can slowly fill with watchlist database and storage things. This also removes the dual namespace handling in the new implementation. Change-Id: Ia67ab1d200ac393c65013b2091e61acefcb3defb --- M autoload.php M includes/WatchedItem.php A includes/WatchedItemStore.php A tests/phpunit/includes/WatchedItemStoreTest.php 4 files changed, 185 insertions(+), 55 deletions(-) Approvals: Daniel Kinzler: Looks good to me, approved jenkins-bot: Verified diff --git a/autoload.php b/autoload.php index 83ca3b3..87d0c7c 100644 --- a/autoload.php +++ b/autoload.php @@ -1401,6 +1401,7 @@ 'WantedTemplatesPage' => __DIR__ . '/includes/specials/SpecialWantedtemplates.php', 'WatchAction' => __DIR__ . '/includes/actions/WatchAction.php', 'WatchedItem' => __DIR__ . '/includes/WatchedItem.php', + 'WatchedItemStore' => __DIR__ . '/includes/WatchedItemStore.php', 'WatchlistCleanup' => __DIR__ . '/maintenance/cleanupWatchlist.php', 'WebInstaller' => __DIR__ . '/includes/installer/WebInstaller.php', 'WebInstallerComplete' => __DIR__ . '/includes/installer/WebInstallerComplete.php', diff --git a/includes/WatchedItem.php b/includes/WatchedItem.php index 2c400d0..b597f99 100644 --- a/includes/WatchedItem.php +++ b/includes/WatchedItem.php @@ -379,63 +379,15 @@ } /** - * Check if the given title already is watched by the user, and if so - * add watches on a new title. To be used for page renames and such. + * @deprecated since 1.27. See WatchedItemStore::duplicateEntry * - * @param Title $ot Page title to duplicate entries from, if present - * @param Title $nt Page title to add watches on + * @param Title $oldTitle + * @param Title $newTitle */ - public static function duplicateEntries( $ot, $nt ) { - WatchedItem::doDuplicateEntries( $ot->getSubjectPage(), $nt->getSubjectPage() ); - WatchedItem::doDuplicateEntries( $ot->getTalkPage(), $nt->getTalkPage() ); + public static function duplicateEntries( Title $oldTitle, Title $newTitle ) { + $store = WatchedItemStore::getDefaultInstance(); + $store->duplicateEntry( $oldTitle->getSubjectPage(), $newTitle->getSubjectPage() ); + $store->duplicateEntry( $oldTitle->getTalkPage(), $newTitle->getTalkPage() ); } - /** - * Handle duplicate entries. Backend for duplicateEntries(). - * - * @param Title $ot - * @param Title $nt - * - * @return bool - */ - private static function doDuplicateEntries( $ot, $nt ) { - $oldnamespace = $ot->getNamespace(); - $newnamespace = $nt->getNamespace(); - $oldtitle = $ot->getDBkey(); - $newtitle = $nt->getDBkey(); - - $dbw = wfGetDB( DB_MASTER ); - $res = $dbw->select( 'watchlist', - [ 'wl_user', 'wl_notificationtimestamp' ], - [ 'wl_namespace' => $oldnamespace, 'wl_title' => $oldtitle ], - __METHOD__, 'FOR UPDATE' - ); - # Construct array to replace into the watchlist - $values = []; - foreach ( $res as $s ) { - $values[] = [ - 'wl_user' => $s->wl_user, - 'wl_namespace' => $newnamespace, - 'wl_title' => $newtitle, - 'wl_notificationtimestamp' => $s->wl_notificationtimestamp, - ]; - } - - if ( empty( $values ) ) { - // Nothing to do - return true; - } - - # Perform replace - # Note that multi-row replace is very efficient for MySQL but may be inefficient for - # some other DBMSes, mostly due to poor simulation by us - $dbw->replace( - 'watchlist', - [ [ 'wl_user', 'wl_namespace', 'wl_title' ] ], - $values, - __METHOD__ - ); - - return true; - } } diff --git a/includes/WatchedItemStore.php b/includes/WatchedItemStore.php new file mode 100644 index 0000000..83a5856 --- /dev/null +++ b/includes/WatchedItemStore.php @@ -0,0 +1,86 @@ +<?php + +/** + * Storage layer class for WatchedItems. + * Database interaction + * + * @author Addshore + * + * @since 1.27 + */ +class WatchedItemStore { + + /** + * @var LoadBalancer + */ + private $loadBalancer; + + public function __construct( LoadBalancer $loadBalancer ) { + $this->loadBalancer = $loadBalancer; + } + + /** + * @return self + */ + public static function getDefaultInstance() { + static $instance; + if ( !$instance ) { + $instance = new self( wfGetLB() ); + } + return $instance; + } + + /** + * Check if the given title already is watched by the user, and if so + * add a watch for the new title. + * + * To be used for page renames and such. + * This must be called separately for Subject and Talk pages + * + * @param LinkTarget $oldTarget + * @param LinkTarget $newTarget + */ + public function duplicateEntry( LinkTarget $oldTarget, LinkTarget $newTarget ) { + $dbw = $this->loadBalancer->getConnection( DB_MASTER, [ 'watchlist' ] ); + + $result = $dbw->select( + 'watchlist', + [ 'wl_user', 'wl_notificationtimestamp' ], + [ + 'wl_namespace' => $oldTarget->getNamespace(), + 'wl_title' => $oldTarget->getDBkey(), + ], + __METHOD__, + [ 'FOR UPDATE' ] + ); + + $newNamespace = $newTarget->getNamespace(); + $newDBkey = $newTarget->getDBkey(); + + # Construct array to replace into the watchlist + $values = []; + foreach ( $result as $row ) { + $values[] = [ + 'wl_user' => $row->wl_user, + 'wl_namespace' => $newNamespace, + 'wl_title' => $newDBkey, + 'wl_notificationtimestamp' => $row->wl_notificationtimestamp, + ]; + } + + if ( !empty( $values ) ) { + # Perform replace + # Note that multi-row replace is very efficient for MySQL but may be inefficient for + # some other DBMSes, mostly due to poor simulation by us + $dbw->replace( + 'watchlist', + [ [ 'wl_user', 'wl_namespace', 'wl_title' ] ], + $values, + __METHOD__ + ); + } + + $this->loadBalancer->reuseConnection( $dbw ); + } + +} diff --git a/tests/phpunit/includes/WatchedItemStoreTest.php b/tests/phpunit/includes/WatchedItemStoreTest.php new file mode 100644 index 0000000..fc132b0 --- /dev/null +++ b/tests/phpunit/includes/WatchedItemStoreTest.php @@ -0,0 +1,91 @@ +<?php + +/** + * @author Addshore + * + * @covers WatchedItemStore + */ +class WatchedItemStoreTest extends PHPUnit_Framework_TestCase { + + /** + * @return PHPUnit_Framework_MockObject_MockObject|IDatabase + */ + private function getMockDb() { + return $this->getMock( 'IDatabase' ); + } + + /** + * @return PHPUnit_Framework_MockObject_MockObject|LoadBalancer + */ + private function getMockLoadBalancer( $mockDb ) { + $mock = $this->getMockBuilder( 'LoadBalancer' ) + ->disableOriginalConstructor() + ->getMock(); + $mock->expects( $this->any() ) + ->method( 'getConnection' ) + ->will( $this->returnValue( $mockDb ) ); + return $mock; + } + + private function getFakeRow( $userId, $timestamp ) { + $fakeRow = new stdClass(); + $fakeRow->wl_user = $userId; + $fakeRow->wl_notificationtimestamp = $timestamp; + return $fakeRow; + } + + public function testDuplicateEntry_nothingToDuplicate() { + $mockDb = $this->getMockDb(); + $mockDb->expects( $this->exactly( 1 ) ) + ->method( 'select' ) + ->will( $this->returnValue( new FakeResultWrapper( [] ) ) ); + + $store = new WatchedItemStore( $this->getMockLoadBalancer( $mockDb ) ); + + $store->duplicateEntry( + Title::newFromText( 'Old_Title' ), + Title::newFromText( 'New_Title' ) + ); + } + + public function testDuplicateEntry_somethingToDuplicate() { + $fakeRows = [ + $this->getFakeRow( 1, '20151212010101' ), + $this->getFakeRow( 2, null ), + ]; + + $mockDb = $this->getMockDb(); + $mockDb->expects( $this->at( 0 ) ) + ->method( 'select' ) + ->will( $this->returnValue( new FakeResultWrapper( $fakeRows ) ) ); + $mockDb->expects( $this->at( 1 ) ) + ->method( 'replace' ) + ->with( + 'watchlist', + [ [ 'wl_user', 'wl_namespace', 'wl_title' ] ], + [ + [ + 'wl_user' => 1, + 'wl_namespace' => 0, + 'wl_title' => 'New_Title', + 'wl_notificationtimestamp' => '20151212010101', + ], + [ + 'wl_user' => 2, + 'wl_namespace' => 0, + 'wl_title' => 'New_Title', + 'wl_notificationtimestamp' => null, + ], + ], + $this->isType( 'string' ) + ); + + $store = new WatchedItemStore( $this->getMockLoadBalancer( $mockDb ) ); + + $store->duplicateEntry( + Title::newFromText( 'Old_Title' ), + Title::newFromText( 'New_Title' ) + ); + } + +} -- To view, visit https://gerrit.wikimedia.org/r/266524 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: merged Gerrit-Change-Id: Ia67ab1d200ac393c65013b2091e61acefcb3defb Gerrit-PatchSet: 17 Gerrit-Project: mediawiki/core Gerrit-Branch: master Gerrit-Owner: Addshore <addshorew...@gmail.com> Gerrit-Reviewer: Aaron Schulz <asch...@wikimedia.org> Gerrit-Reviewer: Addshore <addshorew...@gmail.com> Gerrit-Reviewer: Anomie <bjor...@wikimedia.org> Gerrit-Reviewer: Aude <aude.w...@gmail.com> Gerrit-Reviewer: Bene <benestar.wikime...@gmail.com> Gerrit-Reviewer: Catrope <roan.katt...@gmail.com> Gerrit-Reviewer: Daniel Kinzler <daniel.kinz...@wikimedia.de> Gerrit-Reviewer: Fhocutt <fhoc...@wikimedia.org> Gerrit-Reviewer: Hoo man <h...@online.de> Gerrit-Reviewer: Kai Nissen (WMDE) <kai.nis...@wikimedia.de> Gerrit-Reviewer: Kaldari <rkald...@wikimedia.org> Gerrit-Reviewer: Legoktm <legoktm.wikipe...@gmail.com> Gerrit-Reviewer: Niharika29 <nihar...@wikimedia.org> Gerrit-Reviewer: Tobias Gritschacher <tobias.gritschac...@wikimedia.de> Gerrit-Reviewer: WMDE-Fisch <christoph.fisc...@wikimedia.de> Gerrit-Reviewer: WMDE-leszek <leszek.mani...@wikimedia.de> Gerrit-Reviewer: jenkins-bot <> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits