MediaWiki master
RepoGroup.php
Go to the documentation of this file.
1<?php
24
30class 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,
72 $foreignInfo,
73 WANObjectCache $wanCache,
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
425 private function splitVirtualUrl( $url ) {
426 if ( !str_starts_with( $url, 'mwrepo://' ) ) {
427 throw new InvalidArgumentException( __METHOD__ . ': unknown protocol' );
428 }
429
430 $bits = explode( '/', substr( $url, 9 ), 3 );
431 if ( count( $bits ) != 3 ) {
432 throw new InvalidArgumentException( __METHOD__ . ": invalid mwrepo URL: $url" );
433 }
434
435 return $bits;
436 }
437
442 public function getFileProps( $fileName ) {
443 if ( FileRepo::isVirtualUrl( $fileName ) ) {
444 [ $repoName, /* $zone */, /* $rel */ ] = $this->splitVirtualUrl( $fileName );
445 if ( $repoName === '' ) {
446 $repoName = 'local';
447 }
448 $repo = $this->getRepo( $repoName );
449
450 return $repo->getFileProps( $fileName );
451 } else {
452 $mwProps = new MWFileProps( $this->mimeAnalyzer );
453
454 return $mwProps->getPropsFromPath( $fileName, true );
455 }
456 }
457
462 public function clearCache( $title = null ) {
463 if ( $title == null ) {
464 $this->cache->clear();
465 } elseif ( is_string( $title ) ) {
466 $this->cache->clear( $title );
467 } else {
468 $this->cache->clear( $title->getDBkey() );
469 }
470 }
471}
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:51
static isVirtualUrl( $url)
Determine if a string is an mwrepo:// URL.
Definition FileRepo.php:287
Implements some public methods and some protected utility functions which are required by multiple ch...
Definition File.php:73
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:30
WANObjectCache $wanCache
Definition RepoGroup.php:38
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: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?
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:41
MapCacheLRU $cache
Definition RepoGroup.php:50
findFile( $title, $options=[])
Search repositories for an image.
getLocalRepo()
Get the local repository, i.e.
FileRepo[] $foreignRepos
Definition RepoGroup.php:35
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:32
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:44
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.