MediaWiki
master
|
00001 <?php 00031 class Category { 00033 private $mName = null; 00034 private $mID = null; 00039 private $mTitle = null; 00041 private $mPages = null, $mSubcats = null, $mFiles = null; 00042 00043 private function __construct() { } 00044 00050 protected function initialize() { 00051 if ( $this->mName === null && $this->mID === null ) { 00052 throw new MWException( __METHOD__ . ' has both names and IDs null' ); 00053 } elseif ( $this->mID === null ) { 00054 $where = array( 'cat_title' => $this->mName ); 00055 } elseif ( $this->mName === null ) { 00056 $where = array( 'cat_id' => $this->mID ); 00057 } else { 00058 # Already initialized 00059 return true; 00060 } 00061 00062 wfProfileIn( __METHOD__ ); 00063 00064 $dbr = wfGetDB( DB_SLAVE ); 00065 $row = $dbr->selectRow( 00066 'category', 00067 array( 'cat_id', 'cat_title', 'cat_pages', 'cat_subcats', 'cat_files' ), 00068 $where, 00069 __METHOD__ 00070 ); 00071 00072 wfProfileOut( __METHOD__ ); 00073 00074 if ( !$row ) { 00075 # Okay, there were no contents. Nothing to initialize. 00076 if ( $this->mTitle ) { 00077 # If there is a title object but no record in the category table, treat this as an empty category 00078 $this->mID = false; 00079 $this->mName = $this->mTitle->getDBkey(); 00080 $this->mPages = 0; 00081 $this->mSubcats = 0; 00082 $this->mFiles = 0; 00083 00084 return true; 00085 } else { 00086 return false; # Fail 00087 } 00088 } 00089 00090 $this->mID = $row->cat_id; 00091 $this->mName = $row->cat_title; 00092 $this->mPages = $row->cat_pages; 00093 $this->mSubcats = $row->cat_subcats; 00094 $this->mFiles = $row->cat_files; 00095 00096 # (bug 13683) If the count is negative, then 1) it's obviously wrong 00097 # and should not be kept, and 2) we *probably* don't have to scan many 00098 # rows to obtain the correct figure, so let's risk a one-time recount. 00099 if ( $this->mPages < 0 || $this->mSubcats < 0 || $this->mFiles < 0 ) { 00100 $this->refreshCounts(); 00101 } 00102 00103 return true; 00104 } 00105 00113 public static function newFromName( $name ) { 00114 $cat = new self(); 00115 $title = Title::makeTitleSafe( NS_CATEGORY, $name ); 00116 00117 if ( !is_object( $title ) ) { 00118 return false; 00119 } 00120 00121 $cat->mTitle = $title; 00122 $cat->mName = $title->getDBkey(); 00123 00124 return $cat; 00125 } 00126 00133 public static function newFromTitle( $title ) { 00134 $cat = new self(); 00135 00136 $cat->mTitle = $title; 00137 $cat->mName = $title->getDBkey(); 00138 00139 return $cat; 00140 } 00141 00148 public static function newFromID( $id ) { 00149 $cat = new self(); 00150 $cat->mID = intval( $id ); 00151 return $cat; 00152 } 00153 00164 public static function newFromRow( $row, $title = null ) { 00165 $cat = new self(); 00166 $cat->mTitle = $title; 00167 00168 # NOTE: the row often results from a LEFT JOIN on categorylinks. This may result in 00169 # all the cat_xxx fields being null, if the category page exists, but nothing 00170 # was ever added to the category. This case should be treated linke an empty 00171 # category, if possible. 00172 00173 if ( $row->cat_title === null ) { 00174 if ( $title === null ) { 00175 # the name is probably somewhere in the row, for example as page_title, 00176 # but we can't know that here... 00177 return false; 00178 } else { 00179 $cat->mName = $title->getDBkey(); # if we have a title object, fetch the category name from there 00180 } 00181 00182 $cat->mID = false; 00183 $cat->mSubcats = 0; 00184 $cat->mPages = 0; 00185 $cat->mFiles = 0; 00186 } else { 00187 $cat->mName = $row->cat_title; 00188 $cat->mID = $row->cat_id; 00189 $cat->mSubcats = $row->cat_subcats; 00190 $cat->mPages = $row->cat_pages; 00191 $cat->mFiles = $row->cat_files; 00192 } 00193 00194 return $cat; 00195 } 00196 00198 public function getName() { 00199 return $this->getX( 'mName' ); 00200 } 00201 00203 public function getID() { 00204 return $this->getX( 'mID' ); 00205 } 00206 00208 public function getPageCount() { 00209 return $this->getX( 'mPages' ); 00210 } 00211 00213 public function getSubcatCount() { 00214 return $this->getX( 'mSubcats' ); 00215 } 00216 00218 public function getFileCount() { 00219 return $this->getX( 'mFiles' ); 00220 } 00221 00225 public function getTitle() { 00226 if ( $this->mTitle ) { 00227 return $this->mTitle; 00228 } 00229 00230 if ( !$this->initialize() ) { 00231 return false; 00232 } 00233 00234 $this->mTitle = Title::makeTitleSafe( NS_CATEGORY, $this->mName ); 00235 return $this->mTitle; 00236 } 00237 00245 public function getMembers( $limit = false, $offset = '' ) { 00246 wfProfileIn( __METHOD__ ); 00247 00248 $dbr = wfGetDB( DB_SLAVE ); 00249 00250 $conds = array( 'cl_to' => $this->getName(), 'cl_from = page_id' ); 00251 $options = array( 'ORDER BY' => 'cl_sortkey' ); 00252 00253 if ( $limit ) { 00254 $options['LIMIT'] = $limit; 00255 } 00256 00257 if ( $offset !== '' ) { 00258 $conds[] = 'cl_sortkey > ' . $dbr->addQuotes( $offset ); 00259 } 00260 00261 $result = TitleArray::newFromResult( 00262 $dbr->select( 00263 array( 'page', 'categorylinks' ), 00264 array( 'page_id', 'page_namespace', 'page_title', 'page_len', 00265 'page_is_redirect', 'page_latest' ), 00266 $conds, 00267 __METHOD__, 00268 $options 00269 ) 00270 ); 00271 00272 wfProfileOut( __METHOD__ ); 00273 00274 return $result; 00275 } 00276 00281 private function getX( $key ) { 00282 if ( !$this->initialize() ) { 00283 return false; 00284 } 00285 return $this->{$key}; 00286 } 00287 00293 public function refreshCounts() { 00294 if ( wfReadOnly() ) { 00295 return false; 00296 } 00297 00298 # Note, we must use names for this, since categorylinks does. 00299 if ( $this->mName === null ) { 00300 if ( !$this->initialize() ) { 00301 return false; 00302 } 00303 } 00304 00305 wfProfileIn( __METHOD__ ); 00306 00307 $dbw = wfGetDB( DB_MASTER ); 00308 $dbw->begin( __METHOD__ ); 00309 00310 # Insert the row if it doesn't exist yet (e.g., this is being run via 00311 # update.php from a pre-1.16 schema). TODO: This will cause lots and 00312 # lots of gaps on some non-MySQL DBMSes if you run populateCategory.php 00313 # repeatedly. Plus it's an extra query that's unneeded almost all the 00314 # time. This should be rewritten somehow, probably. 00315 $seqVal = $dbw->nextSequenceValue( 'category_cat_id_seq' ); 00316 $dbw->insert( 00317 'category', 00318 array( 00319 'cat_id' => $seqVal, 00320 'cat_title' => $this->mName 00321 ), 00322 __METHOD__, 00323 'IGNORE' 00324 ); 00325 00326 $cond1 = $dbw->conditional( array( 'page_namespace' => NS_CATEGORY ), 1, 'NULL' ); 00327 $cond2 = $dbw->conditional( array( 'page_namespace' => NS_FILE ), 1, 'NULL' ); 00328 $result = $dbw->selectRow( 00329 array( 'categorylinks', 'page' ), 00330 array( 'pages' => 'COUNT(*)', 00331 'subcats' => "COUNT($cond1)", 00332 'files' => "COUNT($cond2)" 00333 ), 00334 array( 'cl_to' => $this->mName, 'page_id = cl_from' ), 00335 __METHOD__, 00336 'LOCK IN SHARE MODE' 00337 ); 00338 $ret = $dbw->update( 00339 'category', 00340 array( 00341 'cat_pages' => $result->pages, 00342 'cat_subcats' => $result->subcats, 00343 'cat_files' => $result->files 00344 ), 00345 array( 'cat_title' => $this->mName ), 00346 __METHOD__ 00347 ); 00348 $dbw->commit( __METHOD__ ); 00349 00350 wfProfileOut( __METHOD__ ); 00351 00352 # Now we should update our local counts. 00353 $this->mPages = $result->pages; 00354 $this->mSubcats = $result->subcats; 00355 $this->mFiles = $result->files; 00356 00357 return $ret; 00358 } 00359 }