[ Index ] |
PHP Cross Reference of MediaWiki-1.24.0 |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * Representation for a category. 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 2 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License along 16 * with this program; if not, write to the Free Software Foundation, Inc., 17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 * http://www.gnu.org/copyleft/gpl.html 19 * 20 * @file 21 * @author Simetrical 22 */ 23 24 /** 25 * Category objects are immutable, strictly speaking. If you call methods that change the database, 26 * like to refresh link counts, the objects will be appropriately reinitialized. 27 * Member variables are lazy-initialized. 28 * 29 * @todo Move some stuff from CategoryPage.php to here, and use that. 30 */ 31 class Category { 32 /** Name of the category, normalized to DB-key form */ 33 private $mName = null; 34 private $mID = null; 35 /** 36 * Category page title 37 * @var Title 38 */ 39 private $mTitle = null; 40 /** Counts of membership (cat_pages, cat_subcats, cat_files) */ 41 private $mPages = null, $mSubcats = null, $mFiles = null; 42 43 private function __construct() { 44 } 45 46 /** 47 * Set up all member variables using a database query. 48 * @throws MWException 49 * @return bool True on success, false on failure. 50 */ 51 protected function initialize() { 52 if ( $this->mName === null && $this->mID === null ) { 53 throw new MWException( __METHOD__ . ' has both names and IDs null' ); 54 } elseif ( $this->mID === null ) { 55 $where = array( 'cat_title' => $this->mName ); 56 } elseif ( $this->mName === null ) { 57 $where = array( 'cat_id' => $this->mID ); 58 } else { 59 # Already initialized 60 return true; 61 } 62 63 wfProfileIn( __METHOD__ ); 64 65 $dbr = wfGetDB( DB_SLAVE ); 66 $row = $dbr->selectRow( 67 'category', 68 array( 'cat_id', 'cat_title', 'cat_pages', 'cat_subcats', 'cat_files' ), 69 $where, 70 __METHOD__ 71 ); 72 73 wfProfileOut( __METHOD__ ); 74 75 if ( !$row ) { 76 # Okay, there were no contents. Nothing to initialize. 77 if ( $this->mTitle ) { 78 # If there is a title object but no record in the category table, 79 # treat this as an empty category. 80 $this->mID = false; 81 $this->mName = $this->mTitle->getDBkey(); 82 $this->mPages = 0; 83 $this->mSubcats = 0; 84 $this->mFiles = 0; 85 86 return true; 87 } else { 88 return false; # Fail 89 } 90 } 91 92 $this->mID = $row->cat_id; 93 $this->mName = $row->cat_title; 94 $this->mPages = $row->cat_pages; 95 $this->mSubcats = $row->cat_subcats; 96 $this->mFiles = $row->cat_files; 97 98 # (bug 13683) If the count is negative, then 1) it's obviously wrong 99 # and should not be kept, and 2) we *probably* don't have to scan many 100 # rows to obtain the correct figure, so let's risk a one-time recount. 101 if ( $this->mPages < 0 || $this->mSubcats < 0 || $this->mFiles < 0 ) { 102 $this->refreshCounts(); 103 } 104 105 return true; 106 } 107 108 /** 109 * Factory function. 110 * 111 * @param array $name A category name (no "Category:" prefix). It need 112 * not be normalized, with spaces replaced by underscores. 113 * @return mixed Category, or false on a totally invalid name 114 */ 115 public static function newFromName( $name ) { 116 $cat = new self(); 117 $title = Title::makeTitleSafe( NS_CATEGORY, $name ); 118 119 if ( !is_object( $title ) ) { 120 return false; 121 } 122 123 $cat->mTitle = $title; 124 $cat->mName = $title->getDBkey(); 125 126 return $cat; 127 } 128 129 /** 130 * Factory function. 131 * 132 * @param Title $title Title for the category page 133 * @return Category|bool On a totally invalid name 134 */ 135 public static function newFromTitle( $title ) { 136 $cat = new self(); 137 138 $cat->mTitle = $title; 139 $cat->mName = $title->getDBkey(); 140 141 return $cat; 142 } 143 144 /** 145 * Factory function. 146 * 147 * @param int $id A category id 148 * @return Category 149 */ 150 public static function newFromID( $id ) { 151 $cat = new self(); 152 $cat->mID = intval( $id ); 153 return $cat; 154 } 155 156 /** 157 * Factory function, for constructing a Category object from a result set 158 * 159 * @param object $row Result set row, must contain the cat_xxx fields. If the 160 * fields are null, the resulting Category object will represent an empty 161 * category if a title object was given. If the fields are null and no 162 * title was given, this method fails and returns false. 163 * @param Title $title Optional title object for the category represented by 164 * the given row. May be provided if it is already known, to avoid having 165 * to re-create a title object later. 166 * @return Category 167 */ 168 public static function newFromRow( $row, $title = null ) { 169 $cat = new self(); 170 $cat->mTitle = $title; 171 172 # NOTE: the row often results from a LEFT JOIN on categorylinks. This may result in 173 # all the cat_xxx fields being null, if the category page exists, but nothing 174 # was ever added to the category. This case should be treated link an empty 175 # category, if possible. 176 177 if ( $row->cat_title === null ) { 178 if ( $title === null ) { 179 # the name is probably somewhere in the row, for example as page_title, 180 # but we can't know that here... 181 return false; 182 } else { 183 # if we have a title object, fetch the category name from there 184 $cat->mName = $title->getDBkey(); 185 } 186 187 $cat->mID = false; 188 $cat->mSubcats = 0; 189 $cat->mPages = 0; 190 $cat->mFiles = 0; 191 } else { 192 $cat->mName = $row->cat_title; 193 $cat->mID = $row->cat_id; 194 $cat->mSubcats = $row->cat_subcats; 195 $cat->mPages = $row->cat_pages; 196 $cat->mFiles = $row->cat_files; 197 } 198 199 return $cat; 200 } 201 202 /** 203 * @return mixed DB key name, or false on failure 204 */ 205 public function getName() { 206 return $this->getX( 'mName' ); 207 } 208 209 /** 210 * @return mixed Category ID, or false on failure 211 */ 212 public function getID() { 213 return $this->getX( 'mID' ); 214 } 215 216 /** 217 * @return mixed Total number of member pages, or false on failure 218 */ 219 public function getPageCount() { 220 return $this->getX( 'mPages' ); 221 } 222 223 /** 224 * @return mixed Number of subcategories, or false on failure 225 */ 226 public function getSubcatCount() { 227 return $this->getX( 'mSubcats' ); 228 } 229 230 /** 231 * @return mixed Number of member files, or false on failure 232 */ 233 public function getFileCount() { 234 return $this->getX( 'mFiles' ); 235 } 236 237 /** 238 * @return Title|bool Title for this category, or false on failure. 239 */ 240 public function getTitle() { 241 if ( $this->mTitle ) { 242 return $this->mTitle; 243 } 244 245 if ( !$this->initialize() ) { 246 return false; 247 } 248 249 $this->mTitle = Title::makeTitleSafe( NS_CATEGORY, $this->mName ); 250 return $this->mTitle; 251 } 252 253 /** 254 * Fetch a TitleArray of up to $limit category members, beginning after the 255 * category sort key $offset. 256 * @param int $limit 257 * @param string $offset 258 * @return TitleArray TitleArray object for category members. 259 */ 260 public function getMembers( $limit = false, $offset = '' ) { 261 wfProfileIn( __METHOD__ ); 262 263 $dbr = wfGetDB( DB_SLAVE ); 264 265 $conds = array( 'cl_to' => $this->getName(), 'cl_from = page_id' ); 266 $options = array( 'ORDER BY' => 'cl_sortkey' ); 267 268 if ( $limit ) { 269 $options['LIMIT'] = $limit; 270 } 271 272 if ( $offset !== '' ) { 273 $conds[] = 'cl_sortkey > ' . $dbr->addQuotes( $offset ); 274 } 275 276 $result = TitleArray::newFromResult( 277 $dbr->select( 278 array( 'page', 'categorylinks' ), 279 array( 'page_id', 'page_namespace', 'page_title', 'page_len', 280 'page_is_redirect', 'page_latest' ), 281 $conds, 282 __METHOD__, 283 $options 284 ) 285 ); 286 287 wfProfileOut( __METHOD__ ); 288 289 return $result; 290 } 291 292 /** 293 * Generic accessor 294 * @param string $key 295 * @return bool 296 */ 297 private function getX( $key ) { 298 if ( !$this->initialize() ) { 299 return false; 300 } 301 return $this->{$key}; 302 } 303 304 /** 305 * Refresh the counts for this category. 306 * 307 * @return bool True on success, false on failure 308 */ 309 public function refreshCounts() { 310 if ( wfReadOnly() ) { 311 return false; 312 } 313 314 # Note, we must use names for this, since categorylinks does. 315 if ( $this->mName === null ) { 316 if ( !$this->initialize() ) { 317 return false; 318 } 319 } 320 321 wfProfileIn( __METHOD__ ); 322 323 $dbw = wfGetDB( DB_MASTER ); 324 $dbw->startAtomic( __METHOD__ ); 325 326 # Insert the row if it doesn't exist yet (e.g., this is being run via 327 # update.php from a pre-1.16 schema). TODO: This will cause lots and 328 # lots of gaps on some non-MySQL DBMSes if you run populateCategory.php 329 # repeatedly. Plus it's an extra query that's unneeded almost all the 330 # time. This should be rewritten somehow, probably. 331 $seqVal = $dbw->nextSequenceValue( 'category_cat_id_seq' ); 332 $dbw->insert( 333 'category', 334 array( 335 'cat_id' => $seqVal, 336 'cat_title' => $this->mName 337 ), 338 __METHOD__, 339 'IGNORE' 340 ); 341 342 $cond1 = $dbw->conditional( array( 'page_namespace' => NS_CATEGORY ), 1, 'NULL' ); 343 $cond2 = $dbw->conditional( array( 'page_namespace' => NS_FILE ), 1, 'NULL' ); 344 $result = $dbw->selectRow( 345 array( 'categorylinks', 'page' ), 346 array( 'pages' => 'COUNT(*)', 347 'subcats' => "COUNT($cond1)", 348 'files' => "COUNT($cond2)" 349 ), 350 array( 'cl_to' => $this->mName, 'page_id = cl_from' ), 351 __METHOD__, 352 array( 'LOCK IN SHARE MODE' ) 353 ); 354 $ret = $dbw->update( 355 'category', 356 array( 357 'cat_pages' => $result->pages, 358 'cat_subcats' => $result->subcats, 359 'cat_files' => $result->files 360 ), 361 array( 'cat_title' => $this->mName ), 362 __METHOD__ 363 ); 364 $dbw->endAtomic( __METHOD__ ); 365 366 wfProfileOut( __METHOD__ ); 367 368 # Now we should update our local counts. 369 $this->mPages = $result->pages; 370 $this->mSubcats = $result->subcats; 371 $this->mFiles = $result->files; 372 373 return $ret; 374 } 375 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Fri Nov 28 14:03:12 2014 | Cross-referenced by PHPXref 0.7.1 |