MediaWiki  master
RepoGroup.php
Go to the documentation of this file.
1 <?php
24 
30 class RepoGroup {
32  protected $localRepo;
33 
35  protected $foreignRepos;
36 
38  protected $wanCache;
39 
41  protected $reposInitialised = false;
42 
44  protected $localInfo;
45 
47  protected $foreignInfo;
48 
50  protected $cache;
51 
53  private const MAX_CACHE_SIZE = 500;
54 
56  private $mimeAnalyzer;
57 
70  public function __construct(
71  $localInfo,
74  MimeAnalyzer $mimeAnalyzer
75  ) {
76  $this->localInfo = $localInfo;
77  $this->foreignInfo = $foreignInfo;
78  $this->cache = new MapCacheLRU( self::MAX_CACHE_SIZE );
79  $this->wanCache = $wanCache;
80  $this->mimeAnalyzer = $mimeAnalyzer;
81  }
82 
101  public function findFile( $title, $options = [] ) {
102  if ( !is_array( $options ) ) {
103  // MW 1.15 compat
104  $options = [ 'time' => $options ];
105  }
106  if ( isset( $options['bypassCache'] ) ) {
107  $options['latest'] = $options['bypassCache']; // b/c
108  }
109  if ( isset( $options['time'] ) && $options['time'] !== false ) {
110  $options['time'] = wfTimestamp( TS_MW, $options['time'] );
111  } else {
112  $options['time'] = false;
113  }
114 
115  if ( !$this->reposInitialised ) {
116  $this->initialiseRepos();
117  }
118 
119  $title = File::normalizeTitle( $title );
120  if ( !$title ) {
121  return false;
122  }
123 
124  # Check the cache
125  $dbkey = $title->getDBkey();
126  $timeKey = is_string( $options['time'] ) ? $options['time'] : '';
127  if ( empty( $options['ignoreRedirect'] )
128  && empty( $options['private'] )
129  && empty( $options['latest'] )
130  ) {
131  if ( $this->cache->hasField( $dbkey, $timeKey, 60 ) ) {
132  return $this->cache->getField( $dbkey, $timeKey );
133  }
134  $useCache = true;
135  } else {
136  $useCache = false;
137  }
138 
139  # Check the local repo
140  $image = $this->localRepo->findFile( $title, $options );
141 
142  # Check the foreign repos
143  if ( !$image ) {
144  foreach ( $this->foreignRepos as $repo ) {
145  $image = $repo->findFile( $title, $options );
146  if ( $image ) {
147  break;
148  }
149  }
150  }
151 
152  $image = $image instanceof File ? $image : false; // type check
153  # Cache file existence or non-existence
154  if ( $useCache && ( !$image || $image->isCacheable() ) ) {
155  $this->cache->setField( $dbkey, $timeKey, $image );
156  }
157 
158  return $image;
159  }
160 
178  public function findFiles( array $inputItems, $flags = 0 ) {
179  if ( !$this->reposInitialised ) {
180  $this->initialiseRepos();
181  }
182 
183  $items = [];
184  foreach ( $inputItems as $item ) {
185  if ( !is_array( $item ) ) {
186  $item = [ 'title' => $item ];
187  }
188  $item['title'] = File::normalizeTitle( $item['title'] );
189  if ( $item['title'] ) {
190  $items[$item['title']->getDBkey()] = $item;
191  }
192  }
193 
194  $images = $this->localRepo->findFiles( $items, $flags );
195 
196  foreach ( $this->foreignRepos as $repo ) {
197  // Remove found files from $items
198  foreach ( $images as $name => $image ) {
199  unset( $items[$name] );
200  }
201 
202  $images = array_merge( $images, $repo->findFiles( $items, $flags ) );
203  }
204 
205  return $images;
206  }
207 
213  public function checkRedirect( $title ) {
214  if ( !$this->reposInitialised ) {
215  $this->initialiseRepos();
216  }
217 
218  $title = File::normalizeTitle( $title );
219 
220  $redir = $this->localRepo->checkRedirect( $title );
221  if ( $redir ) {
222  return $redir;
223  }
224 
225  foreach ( $this->foreignRepos as $repo ) {
226  $redir = $repo->checkRedirect( $title );
227  if ( $redir ) {
228  return $redir;
229  }
230  }
231 
232  return false;
233  }
234 
243  public function findFileFromKey( $hash, $options = [] ) {
244  if ( !$this->reposInitialised ) {
245  $this->initialiseRepos();
246  }
247 
248  $file = $this->localRepo->findFileFromKey( $hash, $options );
249  if ( !$file ) {
250  foreach ( $this->foreignRepos as $repo ) {
251  $file = $repo->findFileFromKey( $hash, $options );
252  if ( $file ) {
253  break;
254  }
255  }
256  }
257 
258  return $file;
259  }
260 
267  public function findBySha1( $hash ) {
268  if ( !$this->reposInitialised ) {
269  $this->initialiseRepos();
270  }
271 
272  $result = $this->localRepo->findBySha1( $hash );
273  foreach ( $this->foreignRepos as $repo ) {
274  $result = array_merge( $result, $repo->findBySha1( $hash ) );
275  }
276  usort( $result, [ File::class, 'compare' ] );
277 
278  return $result;
279  }
280 
287  public function findBySha1s( array $hashes ) {
288  if ( !$this->reposInitialised ) {
289  $this->initialiseRepos();
290  }
291 
292  $result = $this->localRepo->findBySha1s( $hashes );
293  foreach ( $this->foreignRepos as $repo ) {
294  $result = array_merge_recursive( $result, $repo->findBySha1s( $hashes ) );
295  }
296  // sort the merged (and presorted) sublist of each hash
297  foreach ( $result as $hash => $files ) {
298  usort( $result[$hash], [ File::class, 'compare' ] );
299  }
300 
301  return $result;
302  }
303 
309  public function getRepo( $index ) {
310  if ( !$this->reposInitialised ) {
311  $this->initialiseRepos();
312  }
313  if ( $index === 'local' ) {
314  return $this->localRepo;
315  }
316  return $this->foreignRepos[$index] ?? false;
317  }
318 
324  public function getRepoByName( $name ) {
325  if ( !$this->reposInitialised ) {
326  $this->initialiseRepos();
327  }
328  foreach ( $this->foreignRepos as $repo ) {
329  if ( $repo->name == $name ) {
330  return $repo;
331  }
332  }
333 
334  return false;
335  }
336 
343  public function getLocalRepo() {
344  // @phan-suppress-next-line PhanTypeMismatchReturnSuperType
345  return $this->getRepo( 'local' );
346  }
347 
356  public function forEachForeignRepo( $callback, $params = [] ) {
357  if ( !$this->reposInitialised ) {
358  $this->initialiseRepos();
359  }
360  foreach ( $this->foreignRepos as $repo ) {
361  if ( $callback( $repo, ...$params ) ) {
362  return true;
363  }
364  }
365 
366  return false;
367  }
368 
373  public function hasForeignRepos() {
374  if ( !$this->reposInitialised ) {
375  $this->initialiseRepos();
376  }
377  return (bool)$this->foreignRepos;
378  }
379 
383  public function initialiseRepos() {
384  if ( $this->reposInitialised ) {
385  return;
386  }
387  $this->reposInitialised = true;
388 
389  $this->localRepo = $this->newRepo( $this->localInfo );
390  $this->foreignRepos = [];
391  foreach ( $this->foreignInfo as $key => $info ) {
392  $this->foreignRepos[$key] = $this->newRepo( $info );
393  }
394  }
395 
402  public function newCustomLocalRepo( $info = [] ) {
403  // @phan-suppress-next-line PhanTypeMismatchReturnSuperType
404  return $this->newRepo( $info + $this->localInfo );
405  }
406 
412  protected function newRepo( $info ) {
413  $class = $info['class'];
414 
415  $info['wanCache'] = $this->wanCache;
416 
417  return new $class( $info );
418  }
419 
426  private function splitVirtualUrl( $url ) {
427  if ( !str_starts_with( $url, 'mwrepo://' ) ) {
428  throw new MWException( __METHOD__ . ': unknown protocol' );
429  }
430 
431  $bits = explode( '/', substr( $url, 9 ), 3 );
432  if ( count( $bits ) != 3 ) {
433  throw new MWException( __METHOD__ . ": invalid mwrepo URL: $url" );
434  }
435 
436  return $bits;
437  }
438 
443  public function getFileProps( $fileName ) {
444  if ( FileRepo::isVirtualUrl( $fileName ) ) {
445  [ $repoName, /* $zone */, /* $rel */ ] = $this->splitVirtualUrl( $fileName );
446  if ( $repoName === '' ) {
447  $repoName = 'local';
448  }
449  $repo = $this->getRepo( $repoName );
450 
451  return $repo->getFileProps( $fileName );
452  } else {
453  $mwProps = new MWFileProps( $this->mimeAnalyzer );
454 
455  return $mwProps->getPropsFromPath( $fileName, true );
456  }
457  }
458 
463  public function clearCache( $title = null ) {
464  if ( $title == null ) {
465  $this->cache->clear();
466  } elseif ( is_string( $title ) ) {
467  $this->cache->clear( $title );
468  } else {
469  $this->cache->clear( $title->getDBkey() );
470  }
471  }
472 }
wfTimestamp( $outputtype=TS_UNIX, $ts=0)
Get a timestamp string in one of various formats.
static isVirtualUrl( $url)
Determine if a string is an mwrepo:// URL.
Definition: FileRepo.php:288
Implements some public methods and some protected utility functions which are required by multiple ch...
Definition: File.php:70
static normalizeTitle( $title, $exception=false)
Given a string or Title object return either a valid Title object with namespace NS_FILE or null.
Definition: File.php:209
MediaWiki exception.
Definition: MWException.php:33
MimeMagic helper wrapper.
Definition: MWFileProps.php:28
Store key-value entries in a size-limited in-memory LRU cache.
Definition: MapCacheLRU.php:34
Represents a title within MediaWiki.
Definition: Title.php:76
Implements functions related to MIME types such as detection and mapping to file extension.
Prioritized list of file repositories.
Definition: RepoGroup.php:30
WANObjectCache $wanCache
Definition: RepoGroup.php:38
newCustomLocalRepo( $info=[])
Create a local repo with the specified option overrides.
Definition: RepoGroup.php:402
initialiseRepos()
Initialise the $repos array.
Definition: RepoGroup.php:383
checkRedirect( $title)
Interface for FileRepo::checkRedirect()
Definition: RepoGroup.php:213
getRepoByName( $name)
Get the repo instance by its name.
Definition: RepoGroup.php:324
array $foreignInfo
Definition: RepoGroup.php:47
__construct( $localInfo, $foreignInfo, WANObjectCache $wanCache, MimeAnalyzer $mimeAnalyzer)
Construct a group of file repositories.
Definition: RepoGroup.php:70
hasForeignRepos()
Does the installation have any foreign repos set up?
Definition: RepoGroup.php:373
findFileFromKey( $hash, $options=[])
Find an instance of the file with this key, created at the specified time Returns false if the file d...
Definition: RepoGroup.php:243
bool $reposInitialised
Definition: RepoGroup.php:41
MapCacheLRU $cache
Definition: RepoGroup.php:50
findFile( $title, $options=[])
Search repositories for an image.
Definition: RepoGroup.php:101
getLocalRepo()
Get the local repository, i.e.
Definition: RepoGroup.php:343
FileRepo[] $foreignRepos
Definition: RepoGroup.php:35
findBySha1s(array $hashes)
Find all instances of files with this keys.
Definition: RepoGroup.php:287
clearCache( $title=null)
Clear RepoGroup process cache used for finding a file.
Definition: RepoGroup.php:463
getRepo( $index)
Get the repo instance with a given key.
Definition: RepoGroup.php:309
LocalRepo $localRepo
Definition: RepoGroup.php:32
findBySha1( $hash)
Find all instances of files with this key.
Definition: RepoGroup.php:267
findFiles(array $inputItems, $flags=0)
Search repositories for many files at once.
Definition: RepoGroup.php:178
newRepo( $info)
Create a repo class based on an info structure.
Definition: RepoGroup.php:412
array $localInfo
Definition: RepoGroup.php:44
getFileProps( $fileName)
Definition: RepoGroup.php:443
forEachForeignRepo( $callback, $params=[])
Call a function for each foreign repo, with the repo object as the first parameter.
Definition: RepoGroup.php:356
Multi-datacenter aware caching interface.
Represents the target of a wiki link.
Definition: LinkTarget.php:30
Interface for objects (potentially) representing an editable wiki page.
if(PHP_SAPI !='cli-server') if(!isset( $_SERVER['SCRIPT_FILENAME'])) $file
Item class for a filearchive table row.
Definition: router.php:42