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