[ Index ] |
PHP Cross Reference of MediaWiki-1.24.0 |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * Recent changes filtering by 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 */ 22 23 /** 24 * The "CategoryFinder" class takes a list of articles, creates an internal 25 * representation of all their parent categories (as well as parents of 26 * parents etc.). From this representation, it determines which of these 27 * articles are in one or all of a given subset of categories. 28 * 29 * Example use : 30 * <code> 31 * # Determines whether the article with the page_id 12345 is in both 32 * # "Category 1" and "Category 2" or their subcategories, respectively 33 * 34 * $cf = new CategoryFinder; 35 * $cf->seed( 36 * array( 12345 ), 37 * array( 'Category 1', 'Category 2' ), 38 * 'AND' 39 * ); 40 * $a = $cf->run(); 41 * print implode( ',' , $a ); 42 * </code> 43 * 44 */ 45 class CategoryFinder { 46 /** @var int[] The original article IDs passed to the seed function */ 47 protected $articles = array(); 48 49 /** @var array Array of DBKEY category names for categories that don't have a page */ 50 protected $deadend = array(); 51 52 /** @var array Array of [ID => array()] */ 53 protected $parents = array(); 54 55 /** @var array Array of article/category IDs */ 56 protected $next = array(); 57 58 /** @var array Array of DBKEY category names */ 59 protected $targets = array(); 60 61 /** @var array */ 62 protected $name2id = array(); 63 64 /** @var string "AND" or "OR" */ 65 protected $mode; 66 67 /** @var DatabaseBase Read-DB slave */ 68 protected $dbr; 69 70 /** 71 * Initializes the instance. Do this prior to calling run(). 72 * @param array $articleIds Array of article IDs 73 * @param array $categories FIXME 74 * @param string $mode FIXME, default 'AND'. 75 * @todo FIXME: $categories/$mode 76 */ 77 public function seed( $articleIds, $categories, $mode = 'AND' ) { 78 $this->articles = $articleIds; 79 $this->next = $articleIds; 80 $this->mode = $mode; 81 82 # Set the list of target categories; convert them to DBKEY form first 83 $this->targets = array(); 84 foreach ( $categories as $c ) { 85 $ct = Title::makeTitleSafe( NS_CATEGORY, $c ); 86 if ( $ct ) { 87 $c = $ct->getDBkey(); 88 $this->targets[$c] = $c; 89 } 90 } 91 } 92 93 /** 94 * Iterates through the parent tree starting with the seed values, 95 * then checks the articles if they match the conditions 96 * @return array Array of page_ids (those given to seed() that match the conditions) 97 */ 98 public function run() { 99 $this->dbr = wfGetDB( DB_SLAVE ); 100 while ( count( $this->next ) > 0 ) { 101 $this->scanNextLayer(); 102 } 103 104 # Now check if this applies to the individual articles 105 $ret = array(); 106 107 foreach ( $this->articles as $article ) { 108 $conds = $this->targets; 109 if ( $this->check( $article, $conds ) ) { 110 # Matches the conditions 111 $ret[] = $article; 112 } 113 } 114 return $ret; 115 } 116 117 /** 118 * Get the parents. Only really useful if run() has been called already 119 * @return array 120 */ 121 public function getParents() { 122 return $this->parents; 123 } 124 125 /** 126 * This functions recurses through the parent representation, trying to match the conditions 127 * @param int $id The article/category to check 128 * @param array $conds The array of categories to match 129 * @param array $path Used to check for recursion loops 130 * @return bool Does this match the conditions? 131 */ 132 private function check( $id, &$conds, $path = array() ) { 133 // Check for loops and stop! 134 if ( in_array( $id, $path ) ) { 135 return false; 136 } 137 138 $path[] = $id; 139 140 # Shortcut (runtime paranoia): No conditions=all matched 141 if ( count( $conds ) == 0 ) { 142 return true; 143 } 144 145 if ( !isset( $this->parents[$id] ) ) { 146 return false; 147 } 148 149 # iterate through the parents 150 foreach ( $this->parents[$id] as $p ) { 151 $pname = $p->cl_to; 152 153 # Is this a condition? 154 if ( isset( $conds[$pname] ) ) { 155 # This key is in the category list! 156 if ( $this->mode == 'OR' ) { 157 # One found, that's enough! 158 $conds = array(); 159 return true; 160 } else { 161 # Assuming "AND" as default 162 unset( $conds[$pname] ); 163 if ( count( $conds ) == 0 ) { 164 # All conditions met, done 165 return true; 166 } 167 } 168 } 169 170 # Not done yet, try sub-parents 171 if ( !isset( $this->name2id[$pname] ) ) { 172 # No sub-parent 173 continue; 174 } 175 $done = $this->check( $this->name2id[$pname], $conds, $path ); 176 if ( $done || count( $conds ) == 0 ) { 177 # Subparents have done it! 178 return true; 179 } 180 } 181 return false; 182 } 183 184 /** 185 * Scans a "parent layer" of the articles/categories in $this->next 186 */ 187 private function scanNextLayer() { 188 $profiler = new ProfileSection( __METHOD__ ); 189 190 # Find all parents of the article currently in $this->next 191 $layer = array(); 192 $res = $this->dbr->select( 193 /* FROM */ 'categorylinks', 194 /* SELECT */ '*', 195 /* WHERE */ array( 'cl_from' => $this->next ), 196 __METHOD__ . '-1' 197 ); 198 foreach ( $res as $o ) { 199 $k = $o->cl_to; 200 201 # Update parent tree 202 if ( !isset( $this->parents[$o->cl_from] ) ) { 203 $this->parents[$o->cl_from] = array(); 204 } 205 $this->parents[$o->cl_from][$k] = $o; 206 207 # Ignore those we already have 208 if ( in_array( $k, $this->deadend ) ) { 209 continue; 210 } 211 212 if ( isset( $this->name2id[$k] ) ) { 213 continue; 214 } 215 216 # Hey, new category! 217 $layer[$k] = $k; 218 } 219 220 $this->next = array(); 221 222 # Find the IDs of all category pages in $layer, if they exist 223 if ( count( $layer ) > 0 ) { 224 $res = $this->dbr->select( 225 /* FROM */ 'page', 226 /* SELECT */ array( 'page_id', 'page_title' ), 227 /* WHERE */ array( 'page_namespace' => NS_CATEGORY, 'page_title' => $layer ), 228 __METHOD__ . '-2' 229 ); 230 foreach ( $res as $o ) { 231 $id = $o->page_id; 232 $name = $o->page_title; 233 $this->name2id[$name] = $id; 234 $this->next[] = $id; 235 unset( $layer[$name] ); 236 } 237 } 238 239 # Mark dead ends 240 foreach ( $layer as $v ) { 241 $this->deadend[$v] = $v; 242 } 243 } 244 }
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 |