MediaWiki master
RepoGroup.php
Go to the documentation of this file.
1<?php
21namespace MediaWiki\FileRepo;
22
23use InvalidArgumentException;
28use MWFileProps;
30use Wikimedia\Mime\MimeAnalyzer;
32
38class RepoGroup {
40 protected $localRepo;
41
43 protected $foreignRepos;
44
46 protected $wanCache;
47
49 protected $reposInitialised = false;
50
52 protected $localInfo;
53
55 protected $foreignInfo;
56
58 protected $cache;
59
61 private const MAX_CACHE_SIZE = 500;
62
64 private $mimeAnalyzer;
65
78 public function __construct(
82 MimeAnalyzer $mimeAnalyzer
83 ) {
84 $this->localInfo = $localInfo;
85 $this->foreignInfo = $foreignInfo;
86 $this->cache = new MapCacheLRU( self::MAX_CACHE_SIZE );
87 $this->wanCache = $wanCache;
88 $this->mimeAnalyzer = $mimeAnalyzer;
89 }
90
109 public function findFile( $title, $options = [] ) {
110 if ( !is_array( $options ) ) {
111 // MW 1.15 compat
112 $options = [ 'time' => $options ];
113 }
114 if ( isset( $options['bypassCache'] ) ) {
115 $options['latest'] = $options['bypassCache']; // b/c
116 }
117 if ( isset( $options['time'] ) && $options['time'] !== false ) {
118 $options['time'] = wfTimestamp( TS_MW, $options['time'] );
119 } else {
120 $options['time'] = false;
121 }
122
123 if ( !$this->reposInitialised ) {
124 $this->initialiseRepos();
125 }
126
127 $title = File::normalizeTitle( $title );
128 if ( !$title ) {
129 return false;
130 }
131
132 # Check the cache
133 $dbkey = $title->getDBkey();
134 $timeKey = is_string( $options['time'] ) ? $options['time'] : '';
135 if ( empty( $options['ignoreRedirect'] )
136 && empty( $options['private'] )
137 && empty( $options['latest'] )
138 ) {
139 if ( $this->cache->hasField( $dbkey, $timeKey, 60 ) ) {
140 return $this->cache->getField( $dbkey, $timeKey );
141 }
142 $useCache = true;
143 } else {
144 $useCache = false;
145 }
146
147 # Check the local repo
148 $image = $this->localRepo->findFile( $title, $options );
149
150 # Check the foreign repos
151 if ( !$image ) {
152 foreach ( $this->foreignRepos as $repo ) {
153 $image = $repo->findFile( $title, $options );
154 if ( $image ) {
155 break;
156 }
157 }
158 }
159
160 $image = $image instanceof File ? $image : false; // type check
161 # Cache file existence or non-existence
162 if ( $useCache && ( !$image || $image->isCacheable() ) ) {
163 $this->cache->setField( $dbkey, $timeKey, $image );
164 }
165
166 return $image;
167 }
168
186 public function findFiles( array $inputItems, $flags = 0 ) {
187 if ( !$this->reposInitialised ) {
188 $this->initialiseRepos();
189 }
190
191 $items = [];
192 foreach ( $inputItems as $item ) {
193 if ( !is_array( $item ) ) {
194 $item = [ 'title' => $item ];
195 }
196 $item['title'] = File::normalizeTitle( $item['title'] );
197 if ( $item['title'] ) {
198 $items[$item['title']->getDBkey()] = $item;
199 }
200 }
201
202 $images = $this->localRepo->findFiles( $items, $flags );
203
204 foreach ( $this->foreignRepos as $repo ) {
205 // Remove found files from $items
206 $items = array_diff_key( $items, $images );
207 $images = array_merge( $images, $repo->findFiles( $items, $flags ) );
208 }
209
210 return $images;
211 }
212
218 public function checkRedirect( $title ) {
219 if ( !$this->reposInitialised ) {
220 $this->initialiseRepos();
221 }
222
223 $title = File::normalizeTitle( $title );
224
225 $redir = $this->localRepo->checkRedirect( $title );
226 if ( $redir ) {
227 return $redir;
228 }
229
230 foreach ( $this->foreignRepos as $repo ) {
231 $redir = $repo->checkRedirect( $title );
232 if ( $redir ) {
233 return $redir;
234 }
235 }
236
237 return false;
238 }
239
248 public function findFileFromKey( $hash, $options = [] ) {
249 if ( !$this->reposInitialised ) {
250 $this->initialiseRepos();
251 }
252
253 $file = $this->localRepo->findFileFromKey( $hash, $options );
254 if ( !$file ) {
255 foreach ( $this->foreignRepos as $repo ) {
256 $file = $repo->findFileFromKey( $hash, $options );
257 if ( $file ) {
258 break;
259 }
260 }
261 }
262
263 return $file;
264 }
265
272 public function findBySha1( $hash ) {
273 if ( !$this->reposInitialised ) {
274 $this->initialiseRepos();
275 }
276
277 $result = $this->localRepo->findBySha1( $hash );
278 foreach ( $this->foreignRepos as $repo ) {
279 $result = array_merge( $result, $repo->findBySha1( $hash ) );
280 }
281 usort( $result, [ File::class, 'compare' ] );
282
283 return $result;
284 }
285
292 public function findBySha1s( array $hashes ) {
293 if ( !$this->reposInitialised ) {
294 $this->initialiseRepos();
295 }
296
297 $result = $this->localRepo->findBySha1s( $hashes );
298 foreach ( $this->foreignRepos as $repo ) {
299 $result = array_merge_recursive( $result, $repo->findBySha1s( $hashes ) );
300 }
301 // sort the merged (and presorted) sublist of each hash
302 foreach ( $result as $hash => $files ) {
303 usort( $result[$hash], [ File::class, 'compare' ] );
304 }
305
306 return $result;
307 }
308
314 public function getRepo( $index ) {
315 if ( !$this->reposInitialised ) {
316 $this->initialiseRepos();
317 }
318 if ( $index === 'local' ) {
319 return $this->localRepo;
320 }
321 return $this->foreignRepos[$index] ?? false;
322 }
323
329 public function getRepoByName( $name ) {
330 if ( !$this->reposInitialised ) {
331 $this->initialiseRepos();
332 }
333 foreach ( $this->foreignRepos as $repo ) {
334 if ( $repo->name == $name ) {
335 return $repo;
336 }
337 }
338
339 return false;
340 }
341
348 public function getLocalRepo() {
349 // @phan-suppress-next-line PhanTypeMismatchReturnSuperType
350 return $this->getRepo( 'local' );
351 }
352
361 public function forEachForeignRepo( $callback, $params = [] ) {
362 if ( !$this->reposInitialised ) {
363 $this->initialiseRepos();
364 }
365 foreach ( $this->foreignRepos as $repo ) {
366 if ( $callback( $repo, ...$params ) ) {
367 return true;
368 }
369 }
370
371 return false;
372 }
373
378 public function hasForeignRepos() {
379 if ( !$this->reposInitialised ) {
380 $this->initialiseRepos();
381 }
382 return (bool)$this->foreignRepos;
383 }
384
388 public function initialiseRepos() {
389 if ( $this->reposInitialised ) {
390 return;
391 }
392 $this->reposInitialised = true;
393
394 $this->localRepo = $this->newRepo( $this->localInfo );
395 $this->foreignRepos = [];
396 foreach ( $this->foreignInfo as $key => $info ) {
397 $this->foreignRepos[$key] = $this->newRepo( $info );
398 }
399 }
400
407 public function newCustomLocalRepo( $info = [] ) {
408 // @phan-suppress-next-line PhanTypeMismatchReturnSuperType
409 return $this->newRepo( $info + $this->localInfo );
410 }
411
417 protected function newRepo( $info ) {
418 $class = $info['class'];
419
420 $info['wanCache'] = $this->wanCache;
421
422 return new $class( $info );
423 }
424
430 private function splitVirtualUrl( $url ) {
431 if ( !str_starts_with( $url, 'mwrepo://' ) ) {
432 throw new InvalidArgumentException( __METHOD__ . ': unknown protocol' );
433 }
434
435 $bits = explode( '/', substr( $url, 9 ), 3 );
436 if ( count( $bits ) != 3 ) {
437 throw new InvalidArgumentException( __METHOD__ . ": invalid mwrepo URL: $url" );
438 }
439
440 return $bits;
441 }
442
447 public function getFileProps( $fileName ) {
448 if ( FileRepo::isVirtualUrl( $fileName ) ) {
449 [ $repoName, /* $zone */, /* $rel */ ] = $this->splitVirtualUrl( $fileName );
450 if ( $repoName === '' ) {
451 $repoName = 'local';
452 }
453 $repo = $this->getRepo( $repoName );
454
455 return $repo->getFileProps( $fileName );
456 } else {
457 $mwProps = new MWFileProps( $this->mimeAnalyzer );
458
459 return $mwProps->getPropsFromPath( $fileName, true );
460 }
461 }
462
467 public function clearCache( $title = null ) {
468 if ( $title == null ) {
469 $this->cache->clear();
470 } elseif ( is_string( $title ) ) {
471 $this->cache->clear( $title );
472 } else {
473 $this->cache->clear( $title->getDBkey() );
474 }
475 }
476}
477
479class_alias( RepoGroup::class, 'RepoGroup' );
wfTimestamp( $outputtype=TS_UNIX, $ts=0)
Get a timestamp string in one of various formats.
MimeMagic helper wrapper.
Base class for file repositories.
Definition FileRepo.php:68
static isVirtualUrl( $url)
Determine if a string is an mwrepo:// URL.
Definition FileRepo.php:304
Implements some public methods and some protected utility functions which are required by multiple ch...
Definition File.php:93
Local repository that stores files in the local filesystem and registers them in the wiki's own datab...
Definition LocalRepo.php:57
Prioritized list of file repositories.
Definition RepoGroup.php:38
getRepo( $index)
Get the repo instance with a given key.
forEachForeignRepo( $callback, $params=[])
Call a function for each foreign repo, with the repo object as the first parameter.
clearCache( $title=null)
Clear RepoGroup process cache used for finding a file.
findBySha1s(array $hashes)
Find all instances of files with this keys.
newRepo( $info)
Create a repo class based on an info structure.
findFiles(array $inputItems, $flags=0)
Search repositories for many files at once.
checkRedirect( $title)
Interface for FileRepo::checkRedirect()
findFileFromKey( $hash, $options=[])
Find an instance of the file with this key, created at the specified time Returns false if the file d...
newCustomLocalRepo( $info=[])
Create a local repo with the specified option overrides.
findFile( $title, $options=[])
Search repositories for an image.
initialiseRepos()
Initialise the $repos array.
hasForeignRepos()
Does the installation have any foreign repos set up?
getRepoByName( $name)
Get the repo instance by its name.
getLocalRepo()
Get the local repository, i.e.
findBySha1( $hash)
Find all instances of files with this key.
__construct( $localInfo, $foreignInfo, WANObjectCache $wanCache, MimeAnalyzer $mimeAnalyzer)
Construct a group of file repositories.
Definition RepoGroup.php:78
Represents a title within MediaWiki.
Definition Title.php:78
Store key-value entries in a size-limited in-memory LRU cache.
Multi-datacenter aware caching interface.
Represents the target of a wiki link.
Interface for objects (potentially) representing an editable wiki page.