1<?php 2 3namespace MediaWiki\Page; 4 5use DBAccessObjectUtils; 6use InvalidArgumentException; 7use MediaWiki\Linker\LinkTarget; 8use MediaWiki\Page\Hook\WikiPageFactoryHook; 9use stdClass; 10use Title; 11use TitleFactory; 12use WikiCategoryPage; 13use WikiFilePage; 14use Wikimedia\Rdbms\ILoadBalancer; 15use WikiPage; 16 17/** 18 * @since 1.36 19 */ 20class WikiPageFactory { 21 22 /** @var TitleFactory */ 23 private $titleFactory; 24 25 /** @var WikiPageFactoryHook */ 26 private $wikiPageFactoryHookRunner; 27 28 /** @var ILoadBalancer */ 29 private $loadBalancer; 30 31 /** 32 * @param TitleFactory $titleFactory 33 * @param WikiPageFactoryHook $wikiPageFactoryHookRunner 34 * @param ILoadBalancer $loadBalancer 35 */ 36 public function __construct( 37 TitleFactory $titleFactory, 38 WikiPageFactoryHook $wikiPageFactoryHookRunner, 39 ILoadBalancer $loadBalancer 40 ) { 41 $this->titleFactory = $titleFactory; 42 $this->wikiPageFactoryHookRunner = $wikiPageFactoryHookRunner; 43 $this->loadBalancer = $loadBalancer; 44 } 45 46 /** 47 * Create a WikiPage object from a title. 48 * 49 * @param PageIdentity $pageIdentity 50 * 51 * @return WikiPage 52 */ 53 public function newFromTitle( PageIdentity $pageIdentity ) { 54 if ( $pageIdentity instanceof WikiPage ) { 55 return $pageIdentity; 56 } 57 58 if ( !$pageIdentity->canExist() ) { 59 throw new InvalidArgumentException( 60 "The given PageIdentity does not represent a proper page" 61 ); 62 } 63 64 $ns = $pageIdentity->getNamespace(); 65 66 // TODO: remove the need for casting to Title. We'll have to create a new hook to 67 // replace the WikiPageFactory hook. 68 $title = Title::castFromPageIdentity( $pageIdentity ); 69 70 $page = null; 71 if ( !$this->wikiPageFactoryHookRunner->onWikiPageFactory( $title, $page ) ) { 72 return $page; 73 } 74 75 switch ( $ns ) { 76 case NS_FILE: 77 $page = new WikiFilePage( $title ); 78 break; 79 case NS_CATEGORY: 80 $page = new WikiCategoryPage( $title ); 81 break; 82 default: 83 $page = new WikiPage( $title ); 84 } 85 86 return $page; 87 } 88 89 /** 90 * Create a WikiPage object from a link target. 91 * 92 * @param LinkTarget $title 93 * 94 * @return WikiPage 95 */ 96 public function newFromLinkTarget( LinkTarget $title ) { 97 return $this->newFromTitle( $this->titleFactory->newFromLinkTarget( $title ) ); 98 } 99 100 /** 101 * Create a WikiPage object from a database row 102 * 103 * @param stdClass $row Database row containing at least fields returned by getQueryInfo(). 104 * @param string|int $from Source of $data: 105 * - "fromdb" or WikiPage::READ_NORMAL: from a replica DB 106 * - "fromdbmaster" or WikiPage::READ_LATEST: from the master DB 107 * - "forupdate" or WikiPage::READ_LOCKING: from the master DB using SELECT FOR UPDATE 108 * 109 * @return WikiPage 110 */ 111 public function newFromRow( $row, $from = 'fromdb' ) { 112 $page = $this->newFromTitle( $this->titleFactory->newFromRow( $row ) ); 113 $page->loadFromRow( $row, $from ); 114 return $page; 115 } 116 117 /** 118 * Create a WikiPage object from a page ID 119 * 120 * @param int $id Article ID to load 121 * @param string|int $from One of the following values: 122 * - "fromdb" or WikiPage::READ_NORMAL to select from a replica DB 123 * - "fromdbmaster" or WikiPage::READ_LATEST to select from the master database 124 * 125 * @return WikiPage|null Null when no page exists with that ID 126 */ 127 public function newFromID( $id, $from = 'fromdb' ) { 128 // page ids are never 0 or negative, see T63166 129 if ( $id < 1 ) { 130 return null; 131 } 132 133 $from = WikiPage::convertSelectType( $from ); 134 [ $index ] = DBAccessObjectUtils::getDBOptions( $from ); 135 $db = $this->loadBalancer->getMaintenanceConnectionRef( $index ); 136 $pageQuery = WikiPage::getQueryInfo(); 137 $row = $db->selectRow( 138 $pageQuery['tables'], $pageQuery['fields'], [ 'page_id' => $id ], __METHOD__, 139 [], $pageQuery['joins'] 140 ); 141 if ( !$row ) { 142 return null; 143 } 144 return $this->newFromRow( $row, $from ); 145 } 146 147} 148