]> scripts.mit.edu Git - autoinstalls/mediawiki.git/blob - includes/filerepo/RepoGroup.php
MediaWiki 1.17.0
[autoinstalls/mediawiki.git] / includes / filerepo / RepoGroup.php
1 <?php
2 /**
3  * Prioritized list of file repositories
4  *
5  * @file
6  * @ingroup FileRepo
7  */
8
9 /**
10  * @defgroup FileRepo FileRepo
11  */
12
13 /**
14  * Prioritized list of file repositories
15  *
16  * @ingroup FileRepo
17  */
18 class RepoGroup {
19         var $localRepo, $foreignRepos, $reposInitialised = false;
20         var $localInfo, $foreignInfo;
21         var $cache;
22
23         protected static $instance;
24         const MAX_CACHE_SIZE = 1000;
25
26         /**
27          * Get a RepoGroup instance. At present only one instance of RepoGroup is
28          * needed in a MediaWiki invocation, this may change in the future.
29          */
30         static function singleton() {
31                 if ( self::$instance ) {
32                         return self::$instance;
33                 }
34                 global $wgLocalFileRepo, $wgForeignFileRepos;
35                 self::$instance = new RepoGroup( $wgLocalFileRepo, $wgForeignFileRepos );
36                 return self::$instance;
37         }
38
39         /**
40          * Destroy the singleton instance, so that a new one will be created next
41          * time singleton() is called.
42          */
43         static function destroySingleton() {
44                 self::$instance = null;
45         }
46
47         /**
48          * Set the singleton instance to a given object
49          */
50         static function setSingleton( $instance ) {
51                 self::$instance = $instance;
52         }
53
54         /**
55          * Construct a group of file repositories.
56          *
57          * @param $localInfo Associative array for local repo's info
58          * @param $foreignInfo Array of repository info arrays.
59          *     Each info array is an associative array with the 'class' member
60          *     giving the class name. The entire array is passed to the repository
61          *     constructor as the first parameter.
62          */
63         function __construct( $localInfo, $foreignInfo ) {
64                 $this->localInfo = $localInfo;
65                 $this->foreignInfo = $foreignInfo;
66                 $this->cache = array();
67         }
68
69         /**
70          * Search repositories for an image.
71          * You can also use wfFindFile() to do this.
72          *
73          * @param $title Mixed: Title object or string
74          * @param $options Associative array of options:
75          *     time:           requested time for an archived image, or false for the
76          *                     current version. An image object will be returned which was
77          *                     created at the specified time.
78          *
79          *     ignoreRedirect: If true, do not follow file redirects
80          *
81          *     private:        If true, return restricted (deleted) files if the current
82          *                     user is allowed to view them. Otherwise, such files will not
83          *                     be found.
84          *
85          *     bypassCache:    If true, do not use the process-local cache of File objects
86          * @return File object or false if it is not found
87          */
88         function findFile( $title, $options = array() ) {
89                 if ( !is_array( $options ) ) {
90                         // MW 1.15 compat
91                         $options = array( 'time' => $options );
92                 }
93                 if ( !$this->reposInitialised ) {
94                         $this->initialiseRepos();
95                 }
96                 if ( !($title instanceof Title) ) {
97                         $title = Title::makeTitleSafe( NS_FILE, $title );
98                         if ( !is_object( $title ) ) {
99                                 return false;
100                         }
101                 }
102
103                 if ( $title->getNamespace() != NS_MEDIA && $title->getNamespace() != NS_FILE ) {
104                         throw new MWException( __METHOD__ . ' recieved an Title object with incorrect namespace' );
105                 }
106
107                 # Check the cache
108                 if ( empty( $options['ignoreRedirect'] )
109                         && empty( $options['private'] )
110                         && empty( $options['bypassCache'] )
111                         && $title->getNamespace() == NS_FILE )
112                 {
113                         $useCache = true;
114                         $time = isset( $options['time'] ) ? $options['time'] : '';
115                         $dbkey = $title->getDBkey();
116                         if ( isset( $this->cache[$dbkey][$time] ) ) {
117                                 wfDebug( __METHOD__.": got File:$dbkey from process cache\n" );
118                                 # Move it to the end of the list so that we can delete the LRU entry later
119                                 $tmp = $this->cache[$dbkey];
120                                 unset( $this->cache[$dbkey] );
121                                 $this->cache[$dbkey] = $tmp;
122                                 # Return the entry
123                                 return $this->cache[$dbkey][$time];
124                         } else {
125                                 # Add a negative cache entry, may be overridden
126                                 $this->trimCache();
127                                 $this->cache[$dbkey][$time] = false;
128                                 $cacheEntry =& $this->cache[$dbkey][$time];
129                         }
130                 } else {
131                         $useCache = false;
132                 }
133
134                 # Check the local repo
135                 $image = $this->localRepo->findFile( $title, $options );
136                 if ( $image ) {
137                         if ( $useCache ) {
138                                 $cacheEntry = $image;
139                         }
140                         return $image;
141                 }
142
143                 # Check the foreign repos
144                 foreach ( $this->foreignRepos as $repo ) {
145                         $image = $repo->findFile( $title, $options );
146                         if ( $image ) {
147                                 if ( $useCache ) {
148                                         $cacheEntry = $image;
149                                 }
150                                 return $image;
151                         }
152                 }
153                 # Not found, do not override negative cache
154                 return false;
155         }
156
157         function findFiles( $inputItems ) {
158                 if ( !$this->reposInitialised ) {
159                         $this->initialiseRepos();
160                 }
161
162                 $items = array();
163                 foreach ( $inputItems as $item ) {
164                         if ( !is_array( $item ) ) {
165                                 $item = array( 'title' => $item );
166                         }
167                         if ( !( $item['title'] instanceof Title ) )
168                                 $item['title'] = Title::makeTitleSafe( NS_FILE, $item['title'] );
169                         if ( $item['title'] )
170                                 $items[$item['title']->getDBkey()] = $item;
171                 }
172
173                 $images = $this->localRepo->findFiles( $items );
174
175                 foreach ( $this->foreignRepos as $repo ) {
176                         // Remove found files from $items
177                         foreach ( $images as $name => $image ) {
178                                 unset( $items[$name] );
179                         }
180
181                         $images = array_merge( $images, $repo->findFiles( $items ) );
182                 }
183                 return $images;
184         }
185
186         /**
187          * Interface for FileRepo::checkRedirect()
188          */
189         function checkRedirect( $title ) {
190                 if ( !$this->reposInitialised ) {
191                         $this->initialiseRepos();
192                 }
193
194                 $redir = $this->localRepo->checkRedirect( $title );
195                 if( $redir ) {
196                         return $redir;
197                 }
198                 foreach ( $this->foreignRepos as $repo ) {
199                         $redir = $repo->checkRedirect( $title );
200                         if ( $redir ) {
201                                 return $redir;
202                         }
203                 }
204                 return false;
205         }
206
207         function findBySha1( $hash ) {
208                 if ( !$this->reposInitialised ) {
209                         $this->initialiseRepos();
210                 }
211
212                 $result = $this->localRepo->findBySha1( $hash );
213                 foreach ( $this->foreignRepos as $repo )
214                         $result = array_merge( $result, $repo->findBySha1( $hash ) );
215                 return $result;
216         }
217
218         /**
219          * Get the repo instance with a given key.
220          */
221         function getRepo( $index ) {
222                 if ( !$this->reposInitialised ) {
223                         $this->initialiseRepos();
224                 }
225                 if ( $index === 'local' ) {
226                         return $this->localRepo;
227                 } elseif ( isset( $this->foreignRepos[$index] ) ) {
228                         return $this->foreignRepos[$index];
229                 } else {
230                         return false;
231                 }
232         }
233         /**
234          * Get the repo instance by its name
235          */
236         function getRepoByName( $name ) {
237                 if ( !$this->reposInitialised ) {
238                         $this->initialiseRepos();
239                 }
240                 foreach ( $this->foreignRepos as $repo ) {
241                         if ( $repo->name == $name)
242                                 return $repo;
243                 }
244                 return false;
245         }
246
247         /**
248          * Get the local repository, i.e. the one corresponding to the local image
249          * table. Files are typically uploaded to the local repository.
250          */
251         function getLocalRepo() {
252                 return $this->getRepo( 'local' );
253         }
254
255         /**
256          * Call a function for each foreign repo, with the repo object as the
257          * first parameter.
258          *
259          * @param $callback Callback: the function to call
260          * @param $params Array: optional additional parameters to pass to the function
261          */
262         function forEachForeignRepo( $callback, $params = array() ) {
263                 foreach( $this->foreignRepos as $repo ) {
264                         $args = array_merge( array( $repo ), $params );
265                         if( call_user_func_array( $callback, $args ) ) {
266                                 return true;
267                         }
268                 }
269                 return false;
270         }
271
272         /**
273          * Does the installation have any foreign repos set up?
274          * @return Boolean
275          */
276         function hasForeignRepos() {
277                 return (bool)$this->foreignRepos;
278         }
279
280         /**
281          * Initialise the $repos array
282          */
283         function initialiseRepos() {
284                 if ( $this->reposInitialised ) {
285                         return;
286                 }
287                 $this->reposInitialised = true;
288
289                 $this->localRepo = $this->newRepo( $this->localInfo );
290                 $this->foreignRepos = array();
291                 foreach ( $this->foreignInfo as $key => $info ) {
292                         $this->foreignRepos[$key] = $this->newRepo( $info );
293                 }
294         }
295
296         /**
297          * Create a repo class based on an info structure
298          */
299         protected function newRepo( $info ) {
300                 $class = $info['class'];
301                 return new $class( $info );
302         }
303
304         /**
305          * Split a virtual URL into repo, zone and rel parts
306          * @return an array containing repo, zone and rel
307          */
308         function splitVirtualUrl( $url ) {
309                 if ( substr( $url, 0, 9 ) != 'mwrepo://' ) {
310                         throw new MWException( __METHOD__.': unknown protoocl' );
311                 }
312
313                 $bits = explode( '/', substr( $url, 9 ), 3 );
314                 if ( count( $bits ) != 3 ) {
315                         throw new MWException( __METHOD__.": invalid mwrepo URL: $url" );
316                 }
317                 return $bits;
318         }
319
320         function getFileProps( $fileName ) {
321                 if ( FileRepo::isVirtualUrl( $fileName ) ) {
322                         list( $repoName, /* $zone */, /* $rel */ ) = $this->splitVirtualUrl( $fileName );
323                         if ( $repoName === '' ) {
324                                 $repoName = 'local';
325                         }
326                         $repo = $this->getRepo( $repoName );
327                         return $repo->getFileProps( $fileName );
328                 } else {
329                         return File::getPropsFromPath( $fileName );
330                 }
331         }
332
333         /**
334          * Limit cache memory
335          */
336         function trimCache() {
337                 while ( count( $this->cache ) >= self::MAX_CACHE_SIZE ) {
338                         reset( $this->cache );
339                         $key = key( $this->cache );
340                         wfDebug( __METHOD__.": evicting $key\n" );
341                         unset( $this->cache[$key] );
342                 }
343         }
344 }