[ Index ]

PHP Cross Reference of MediaWiki-1.24.0

title

Body

[close]

/includes/filerepo/ -> RepoGroup.php (source)

   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  }


Generated: Fri Nov 28 14:03:12 2014 Cross-referenced by PHPXref 0.7.1