[ Index ] |
PHP Cross Reference of MediaWiki-1.24.0 |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * Prioritized list of file repositories. 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 * @ingroup FileRepo 22 */ 23 24 /** 25 * Prioritized list of file repositories 26 * 27 * @ingroup FileRepo 28 */ 29 class RepoGroup { 30 /** @var LocalRepo */ 31 protected $localRepo; 32 33 /** @var FileRepo[] */ 34 protected $foreignRepos; 35 36 /** @var bool */ 37 protected $reposInitialised = false; 38 39 /** @var array */ 40 protected $localInfo; 41 42 /** @var array */ 43 protected $foreignInfo; 44 45 /** @var ProcessCacheLRU */ 46 protected $cache; 47 48 /** @var RepoGroup */ 49 protected static $instance; 50 51 /** Maximum number of cache items */ 52 const MAX_CACHE_SIZE = 500; 53 54 /** 55 * Get a RepoGroup instance. At present only one instance of RepoGroup is 56 * needed in a MediaWiki invocation, this may change in the future. 57 * @return RepoGroup 58 */ 59 static function singleton() { 60 if ( self::$instance ) { 61 return self::$instance; 62 } 63 global $wgLocalFileRepo, $wgForeignFileRepos; 64 self::$instance = new RepoGroup( $wgLocalFileRepo, $wgForeignFileRepos ); 65 66 return self::$instance; 67 } 68 69 /** 70 * Destroy the singleton instance, so that a new one will be created next 71 * time singleton() is called. 72 */ 73 static function destroySingleton() { 74 self::$instance = null; 75 } 76 77 /** 78 * Set the singleton instance to a given object 79 * Used by extensions which hook into the Repo chain. 80 * It's not enough to just create a superclass ... you have 81 * to get people to call into it even though all they know is RepoGroup::singleton() 82 * 83 * @param RepoGroup $instance 84 */ 85 static function setSingleton( $instance ) { 86 self::$instance = $instance; 87 } 88 89 /** 90 * Construct a group of file repositories. 91 * 92 * @param array $localInfo Associative array for local repo's info 93 * @param array $foreignInfo Array of repository info arrays. 94 * Each info array is an associative array with the 'class' member 95 * giving the class name. The entire array is passed to the repository 96 * constructor as the first parameter. 97 */ 98 function __construct( $localInfo, $foreignInfo ) { 99 $this->localInfo = $localInfo; 100 $this->foreignInfo = $foreignInfo; 101 $this->cache = new ProcessCacheLRU( self::MAX_CACHE_SIZE ); 102 } 103 104 /** 105 * Search repositories for an image. 106 * You can also use wfFindFile() to do this. 107 * 108 * @param Title|string $title Title object or string 109 * @param array $options Associative array of options: 110 * time: requested time for an archived image, or false for the 111 * current version. An image object will be returned which was 112 * created at the specified time. 113 * ignoreRedirect: If true, do not follow file redirects 114 * private: If true, return restricted (deleted) files if the current 115 * user is allowed to view them. Otherwise, such files will not 116 * be found. 117 * bypassCache: If true, do not use the process-local cache of File objects 118 * @return File|bool False if title is not found 119 */ 120 function findFile( $title, $options = array() ) { 121 if ( !is_array( $options ) ) { 122 // MW 1.15 compat 123 $options = array( 'time' => $options ); 124 } 125 if ( !$this->reposInitialised ) { 126 $this->initialiseRepos(); 127 } 128 $title = File::normalizeTitle( $title ); 129 if ( !$title ) { 130 return false; 131 } 132 133 # Check the cache 134 if ( empty( $options['ignoreRedirect'] ) 135 && empty( $options['private'] ) 136 && empty( $options['bypassCache'] ) 137 ) { 138 $time = isset( $options['time'] ) ? $options['time'] : ''; 139 $dbkey = $title->getDBkey(); 140 if ( $this->cache->has( $dbkey, $time, 60 ) ) { 141 return $this->cache->get( $dbkey, $time ); 142 } 143 $useCache = true; 144 } else { 145 $useCache = false; 146 } 147 148 # Check the local repo 149 $image = $this->localRepo->findFile( $title, $options ); 150 151 # Check the foreign repos 152 if ( !$image ) { 153 foreach ( $this->foreignRepos as $repo ) { 154 $image = $repo->findFile( $title, $options ); 155 if ( $image ) { 156 break; 157 } 158 } 159 } 160 161 $image = $image ? $image : false; // type sanity 162 # Cache file existence or non-existence 163 if ( $useCache && ( !$image || $image->isCacheable() ) ) { 164 $this->cache->set( $dbkey, $time, $image ); 165 } 166 167 return $image; 168 } 169 170 /** 171 * Search repositories for many files at once. 172 * 173 * @param array $inputItems An array of titles, or an array of findFile() options with 174 * the "title" option giving the title. Example: 175 * 176 * $findItem = array( 'title' => $title, 'private' => true ); 177 * $findBatch = array( $findItem ); 178 * $repo->findFiles( $findBatch ); 179 * 180 * No title should appear in $items twice, as the result use titles as keys 181 * @param int $flags Supports: 182 * - FileRepo::NAME_AND_TIME_ONLY : return a (search title => (title,timestamp)) map. 183 * The search title uses the input titles; the other is the final post-redirect title. 184 * All titles are returned as string DB keys and the inner array is associative. 185 * @return array Map of (file name => File objects) for matches 186 */ 187 function findFiles( array $inputItems, $flags = 0 ) { 188 if ( !$this->reposInitialised ) { 189 $this->initialiseRepos(); 190 } 191 192 $items = array(); 193 foreach ( $inputItems as $item ) { 194 if ( !is_array( $item ) ) { 195 $item = array( 'title' => $item ); 196 } 197 $item['title'] = File::normalizeTitle( $item['title'] ); 198 if ( $item['title'] ) { 199 $items[$item['title']->getDBkey()] = $item; 200 } 201 } 202 203 $images = $this->localRepo->findFiles( $items, $flags ); 204 205 foreach ( $this->foreignRepos as $repo ) { 206 // Remove found files from $items 207 foreach ( $images as $name => $image ) { 208 unset( $items[$name] ); 209 } 210 211 $images = array_merge( $images, $repo->findFiles( $items, $flags ) ); 212 } 213 214 return $images; 215 } 216 217 /** 218 * Interface for FileRepo::checkRedirect() 219 * @param Title $title 220 * @return bool|Title 221 */ 222 function checkRedirect( Title $title ) { 223 if ( !$this->reposInitialised ) { 224 $this->initialiseRepos(); 225 } 226 227 $redir = $this->localRepo->checkRedirect( $title ); 228 if ( $redir ) { 229 return $redir; 230 } 231 232 foreach ( $this->foreignRepos as $repo ) { 233 $redir = $repo->checkRedirect( $title ); 234 if ( $redir ) { 235 return $redir; 236 } 237 } 238 239 return false; 240 } 241 242 /** 243 * Find an instance of the file with this key, created at the specified time 244 * Returns false if the file does not exist. 245 * 246 * @param string $hash Base 36 SHA-1 hash 247 * @param array $options Option array, same as findFile() 248 * @return File|bool File object or false if it is not found 249 */ 250 function findFileFromKey( $hash, $options = array() ) { 251 if ( !$this->reposInitialised ) { 252 $this->initialiseRepos(); 253 } 254 255 $file = $this->localRepo->findFileFromKey( $hash, $options ); 256 if ( !$file ) { 257 foreach ( $this->foreignRepos as $repo ) { 258 $file = $repo->findFileFromKey( $hash, $options ); 259 if ( $file ) { 260 break; 261 } 262 } 263 } 264 265 return $file; 266 } 267 268 /** 269 * Find all instances of files with this key 270 * 271 * @param string $hash Base 36 SHA-1 hash 272 * @return File[] 273 */ 274 function findBySha1( $hash ) { 275 if ( !$this->reposInitialised ) { 276 $this->initialiseRepos(); 277 } 278 279 $result = $this->localRepo->findBySha1( $hash ); 280 foreach ( $this->foreignRepos as $repo ) { 281 $result = array_merge( $result, $repo->findBySha1( $hash ) ); 282 } 283 usort( $result, 'File::compare' ); 284 285 return $result; 286 } 287 288 /** 289 * Find all instances of files with this keys 290 * 291 * @param array $hashes Base 36 SHA-1 hashes 292 * @return array Array of array of File objects 293 */ 294 function findBySha1s( array $hashes ) { 295 if ( !$this->reposInitialised ) { 296 $this->initialiseRepos(); 297 } 298 299 $result = $this->localRepo->findBySha1s( $hashes ); 300 foreach ( $this->foreignRepos as $repo ) { 301 $result = array_merge_recursive( $result, $repo->findBySha1s( $hashes ) ); 302 } 303 //sort the merged (and presorted) sublist of each hash 304 foreach ( $result as $hash => $files ) { 305 usort( $result[$hash], 'File::compare' ); 306 } 307 308 return $result; 309 } 310 311 /** 312 * Get the repo instance with a given key. 313 * @param string|int $index 314 * @return bool|LocalRepo 315 */ 316 function getRepo( $index ) { 317 if ( !$this->reposInitialised ) { 318 $this->initialiseRepos(); 319 } 320 if ( $index === 'local' ) { 321 return $this->localRepo; 322 } elseif ( isset( $this->foreignRepos[$index] ) ) { 323 return $this->foreignRepos[$index]; 324 } else { 325 return false; 326 } 327 } 328 329 /** 330 * Get the repo instance by its name 331 * @param string $name 332 * @return bool 333 */ 334 function getRepoByName( $name ) { 335 if ( !$this->reposInitialised ) { 336 $this->initialiseRepos(); 337 } 338 foreach ( $this->foreignRepos as $repo ) { 339 if ( $repo->name == $name ) { 340 return $repo; 341 } 342 } 343 344 return false; 345 } 346 347 /** 348 * Get the local repository, i.e. the one corresponding to the local image 349 * table. Files are typically uploaded to the local repository. 350 * 351 * @return LocalRepo 352 */ 353 function getLocalRepo() { 354 return $this->getRepo( 'local' ); 355 } 356 357 /** 358 * Call a function for each foreign repo, with the repo object as the 359 * first parameter. 360 * 361 * @param callable $callback The function to call 362 * @param array $params Optional additional parameters to pass to the function 363 * @return bool 364 */ 365 function forEachForeignRepo( $callback, $params = array() ) { 366 if ( !$this->reposInitialised ) { 367 $this->initialiseRepos(); 368 } 369 foreach ( $this->foreignRepos as $repo ) { 370 $args = array_merge( array( $repo ), $params ); 371 if ( call_user_func_array( $callback, $args ) ) { 372 return true; 373 } 374 } 375 376 return false; 377 } 378 379 /** 380 * Does the installation have any foreign repos set up? 381 * @return bool 382 */ 383 function hasForeignRepos() { 384 if ( !$this->reposInitialised ) { 385 $this->initialiseRepos(); 386 } 387 return (bool)$this->foreignRepos; 388 } 389 390 /** 391 * Initialise the $repos array 392 */ 393 function initialiseRepos() { 394 if ( $this->reposInitialised ) { 395 return; 396 } 397 $this->reposInitialised = true; 398 399 $this->localRepo = $this->newRepo( $this->localInfo ); 400 $this->foreignRepos = array(); 401 foreach ( $this->foreignInfo as $key => $info ) { 402 $this->foreignRepos[$key] = $this->newRepo( $info ); 403 } 404 } 405 406 /** 407 * Create a repo class based on an info structure 408 * @param array $info 409 * @return FileRepo 410 */ 411 protected function newRepo( $info ) { 412 $class = $info['class']; 413 414 return new $class( $info ); 415 } 416 417 /** 418 * Split a virtual URL into repo, zone and rel parts 419 * @param string $url 420 * @throws MWException 421 * @return array Containing repo, zone and rel 422 */ 423 function splitVirtualUrl( $url ) { 424 if ( substr( $url, 0, 9 ) != 'mwrepo://' ) { 425 throw new MWException( __METHOD__ . ': unknown protocol' ); 426 } 427 428 $bits = explode( '/', substr( $url, 9 ), 3 ); 429 if ( count( $bits ) != 3 ) { 430 throw new MWException( __METHOD__ . ": invalid mwrepo URL: $url" ); 431 } 432 433 return $bits; 434 } 435 436 /** 437 * @param string $fileName 438 * @return array 439 */ 440 function getFileProps( $fileName ) { 441 if ( FileRepo::isVirtualUrl( $fileName ) ) { 442 list( $repoName, /* $zone */, /* $rel */ ) = $this->splitVirtualUrl( $fileName ); 443 if ( $repoName === '' ) { 444 $repoName = 'local'; 445 } 446 $repo = $this->getRepo( $repoName ); 447 448 return $repo->getFileProps( $fileName ); 449 } else { 450 return FSFile::getPropsFromPath( $fileName ); 451 } 452 } 453 454 /** 455 * Clear RepoGroup process cache used for finding a file 456 * @param Title|null $title Title of the file or null to clear all files 457 */ 458 public function clearCache( Title $title = null ) { 459 if ( $title == null ) { 460 $this->cache->clear(); 461 } else { 462 $this->cache->clear( $title->getDBkey() ); 463 } 464 } 465 }
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 |