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