From f871f7b93ebe7040df2d91e03637290d3558d9c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gerg=C5=91=20Tisza?= Date: Mon, 27 Jul 2020 15:24:05 +0200 Subject: [PATCH] Add WikiPageFactory Replace WikiPage::factory with a proper factory object with dependency injection (only for dependencies needed by the factory methods, not WikiPage itself). Change-Id: Ie7d6e40d8387d8bc4f8592a31fdd70d0aad510ae --- includes/MediaWikiServices.php | 9 ++ includes/ServiceWiring.php | 9 ++ includes/page/WikiPage.php | 52 ++--------- includes/page/WikiPageFactory.php | 142 ++++++++++++++++++++++++++++++ 4 files changed, 167 insertions(+), 45 deletions(-) create mode 100644 includes/page/WikiPageFactory.php diff --git a/includes/MediaWikiServices.php b/includes/MediaWikiServices.php index 302b5a38663..79e4de0d1c9 100644 --- a/includes/MediaWikiServices.php +++ b/includes/MediaWikiServices.php @@ -50,6 +50,7 @@ use MediaWiki\Mail\IEmailer; use MediaWiki\Page\ContentModelChangeFactory; use MediaWiki\Page\MergeHistoryFactory; use MediaWiki\Page\MovePageFactory; +use MediaWiki\Page\WikiPageFactory; use MediaWiki\Permissions\PermissionManager; use MediaWiki\Preferences\PreferencesFactory; use MediaWiki\Revision\ContributionsLookup; @@ -1361,6 +1362,14 @@ class MediaWikiServices extends ServiceContainer { return $this->getService( 'WatchlistNotificationManager' ); } + /** + * @since 1.36 + * @return WikiPageFactory + */ + public function getWikiPageFactory() : WikiPageFactory { + return $this->getService( 'WikiPageFactory' ); + } + /** * @since 1.31 * @return OldRevisionImporter diff --git a/includes/ServiceWiring.php b/includes/ServiceWiring.php index 96ace904606..09d7d434228 100644 --- a/includes/ServiceWiring.php +++ b/includes/ServiceWiring.php @@ -81,6 +81,7 @@ use MediaWiki\Page\ContentModelChangeFactory; use MediaWiki\Page\MergeHistoryFactory; use MediaWiki\Page\MovePageFactory; use MediaWiki\Page\PageCommandFactory; +use MediaWiki\Page\WikiPageFactory; use MediaWiki\Permissions\PermissionManager; use MediaWiki\Preferences\DefaultPreferencesFactory; use MediaWiki\Preferences\PreferencesFactory; @@ -1363,6 +1364,14 @@ return [ ); }, + 'WikiPageFactory' => function ( MediaWikiServices $services ) : WikiPageFactory { + return new WikiPageFactory( + $services->getTitleFactory(), + new HookRunner( $services->getHookContainer() ), + $services->getDBLoadBalancer() + ); + }, + 'WikiRevisionOldRevisionImporterNoUpdates' => function ( MediaWikiServices $services ) : ImportableOldRevisionImporter { return new ImportableOldRevisionImporter( diff --git a/includes/page/WikiPage.php b/includes/page/WikiPage.php index e51fc91dc9d..756c3b303a0 100644 --- a/includes/page/WikiPage.php +++ b/includes/page/WikiPage.php @@ -151,33 +151,10 @@ class WikiPage implements Page, IDBAccessObject { * * @throws MWException * @return WikiPage|WikiCategoryPage|WikiFilePage + * @deprecated since 1.36, use WikiPageFactory::newFromTitle instead */ public static function factory( Title $title ) { - $ns = $title->getNamespace(); - - if ( $ns == NS_MEDIA ) { - throw new MWException( "NS_MEDIA is a virtual namespace; use NS_FILE." ); - } elseif ( $ns < 0 ) { - throw new MWException( "Invalid or virtual namespace $ns given." ); - } - - $page = null; - if ( !Hooks::runner()->onWikiPageFactory( $title, $page ) ) { - return $page; - } - - switch ( $ns ) { - case NS_FILE: - $page = new WikiFilePage( $title ); - break; - case NS_CATEGORY: - $page = new WikiCategoryPage( $title ); - break; - default: - $page = new WikiPage( $title ); - } - - return $page; + return MediaWikiServices::getInstance()->getWikiPageFactory()->newFromTitle( $title ); } /** @@ -189,24 +166,10 @@ class WikiPage implements Page, IDBAccessObject { * - "fromdbmaster" or WikiPage::READ_LATEST to select from the master database * * @return WikiPage|null + * @deprecated since 1.36, use WikiPageFactory::newFromID instead */ public static function newFromID( $id, $from = 'fromdb' ) { - // page ids are never 0 or negative, see T63166 - if ( $id < 1 ) { - return null; - } - - $from = self::convertSelectType( $from ); - $db = wfGetDB( $from === self::READ_LATEST ? DB_MASTER : DB_REPLICA ); - $pageQuery = self::getQueryInfo(); - $row = $db->selectRow( - $pageQuery['tables'], $pageQuery['fields'], [ 'page_id' => $id ], __METHOD__, - [], $pageQuery['joins'] - ); - if ( !$row ) { - return null; - } - return self::newFromRow( $row, $from ); + return MediaWikiServices::getInstance()->getWikiPageFactory()->newFromID( $id, $from ); } /** @@ -219,11 +182,10 @@ class WikiPage implements Page, IDBAccessObject { * - "fromdbmaster" or WikiPage::READ_LATEST: from the master DB * - "forupdate" or WikiPage::READ_LOCKING: from the master DB using SELECT FOR UPDATE * @return WikiPage + * @deprecated since 1.36, use WikiPageFactory::newFromRow instead */ public static function newFromRow( $row, $from = 'fromdb' ) { - $page = self::factory( Title::newFromRow( $row ) ); - $page->loadFromRow( $row, $from ); - return $page; + return MediaWikiServices::getInstance()->getWikiPageFactory()->newFromRow( $row, $from ); } /** @@ -232,7 +194,7 @@ class WikiPage implements Page, IDBAccessObject { * @param object|string|int $type * @return mixed */ - protected static function convertSelectType( $type ) { + public static function convertSelectType( $type ) { switch ( $type ) { case 'fromdb': return self::READ_NORMAL; diff --git a/includes/page/WikiPageFactory.php b/includes/page/WikiPageFactory.php new file mode 100644 index 00000000000..b37bb86fa61 --- /dev/null +++ b/includes/page/WikiPageFactory.php @@ -0,0 +1,142 @@ +titleFactory = $titleFactory; + $this->wikiPageFactoryHookRunner = $wikiPageFactoryHookRunner; + $this->loadBalancer = $loadBalancer; + } + + /** + * Create a WikiPage object from a title. + * + * @param Title $title + * + * @return WikiPage + * @throws MWException + */ + public function newFromTitle( Title $title ) { + $ns = $title->getNamespace(); + + if ( $ns == NS_MEDIA ) { + throw new MWException( "NS_MEDIA is a virtual namespace; use NS_FILE." ); + } elseif ( $ns < 0 ) { + throw new MWException( "Invalid or virtual namespace $ns given." ); + } + + $page = null; + if ( !$this->wikiPageFactoryHookRunner->onWikiPageFactory( $title, $page ) ) { + return $page; + } + + switch ( $ns ) { + case NS_FILE: + $page = new WikiFilePage( $title ); + break; + case NS_CATEGORY: + $page = new WikiCategoryPage( $title ); + break; + default: + $page = new WikiPage( $title ); + } + + return $page; + } + + /** + * Create a WikiPage object from a link target. + * + * @param LinkTarget $title + * + * @throws MWException + * @return WikiPage + */ + public function newFromLinkTarget( LinkTarget $title ) { + return $this->newFromTitle( $this->titleFactory->newFromLinkTarget( $title ) ); + } + + /** + * Create a WikiPage object from a database row + * + * @param object $row Database row containing at least fields returned by getQueryInfo(). + * @param string|int $from Source of $data: + * - "fromdb" or WikiPage::READ_NORMAL: from a replica DB + * - "fromdbmaster" or WikiPage::READ_LATEST: from the master DB + * - "forupdate" or WikiPage::READ_LOCKING: from the master DB using SELECT FOR UPDATE + * + * @return WikiPage + * @throws MWException + */ + public function newFromRow( $row, $from = 'fromdb' ) { + $page = $this->newFromTitle( $this->titleFactory->newFromRow( $row ) ); + $page->loadFromRow( $row, $from ); + return $page; + } + + /** + * Create a WikiPage object from a page ID + * + * @param int $id Article ID to load + * @param string|int $from One of the following values: + * - "fromdb" or WikiPage::READ_NORMAL to select from a replica DB + * - "fromdbmaster" or WikiPage::READ_LATEST to select from the master database + * + * @return WikiPage|null Null when no page exists with that ID + * @throws MWException + */ + public function newFromID( $id, $from = 'fromdb' ) { + // page ids are never 0 or negative, see T63166 + if ( $id < 1 ) { + return null; + } + + $from = WikiPage::convertSelectType( $from ); + [ $index ] = DBAccessObjectUtils::getDBOptions( $from ); + $db = $this->loadBalancer->getMaintenanceConnectionRef( $index ); + $pageQuery = WikiPage::getQueryInfo(); + $row = $db->selectRow( + $pageQuery['tables'], $pageQuery['fields'], [ 'page_id' => $id ], __METHOD__, + [], $pageQuery['joins'] + ); + if ( !$row ) { + return null; + } + return $this->newFromRow( $row, $from ); + } + +}