Fix up r51559: Linker::link() expects Title objects, not strings
[mediawiki.git] / includes / filerepo / FileCache.php
blobe7f254e39510f9f9f17c8fe682fe5793ee806dcc
1 <?php
2 /**
3 * Cache of file objects, wrapping some RepoGroup functions to avoid redundant
4 * queries. Loosely inspired by the LinkCache / LinkBatch classes for titles.
6 * ISSUE: Merge with RepoGroup?
8 * @ingroup FileRepo
9 */
10 class FileCache {
11 var $repoGroup;
12 var $cache = array(), $oldCache = array(), $notFound = array();
14 protected static $instance;
16 /**
17 * Get a FileCache instance. Typically, only one instance of FileCache
18 * is needed in a MediaWiki invocation.
20 static function singleton() {
21 if ( self::$instance ) {
22 return self::$instance;
24 self::$instance = new FileCache( RepoGroup::singleton() );
25 return self::$instance;
28 /**
29 * Destroy the singleton instance, so that a new one will be created next
30 * time singleton() is called.
32 static function destroySingleton() {
33 self::$instance = null;
36 /**
37 * Set the singleton instance to a given object
39 static function setSingleton( $instance ) {
40 self::$instance = $instance;
43 /**
44 * Construct a group of file repositories.
45 * @param RepoGroup $repoGroup
47 function __construct( $repoGroup ) {
48 $this->repoGroup = $repoGroup;
52 /**
53 * Add some files to the cache. This is a fairly low-level function,
54 * which most users should not need to call. Note that any existing
55 * entries for the same keys will not be replaced. Call clearFiles()
56 * first if you need that.
57 * @param array $files array of File objects, indexed by DB key
59 function addFiles( $files ) {
60 wfDebug( "FileCache adding ".count( $files )." files\n" );
61 $this->cache += $files;
64 /**
65 * Remove some files from the cache, so that their existence will be
66 * rechecked. This is a fairly low-level function, which most users
67 * should not need to call.
68 * @param array $remove array indexed by DB keys to remove (the values are ignored)
70 function clearFiles( $remove ) {
71 wfDebug( "FileCache clearing data for ".count( $remove )." files\n" );
72 $this->cache = array_diff_key( $this->cache, $remove );
73 $this->notFound = array_diff_key( $this->notFound, $remove );
76 /**
77 * Mark some DB keys as nonexistent. This is a fairly low-level
78 * function, which most users should not need to call.
79 * @param array $dbkeys array of DB keys
81 function markNotFound( $dbkeys ) {
82 wfDebug( "FileCache marking ".count( $dbkeys )." files as not found\n" );
83 $this->notFound += array_fill_key( $dbkeys, true );
87 /**
88 * Search the cache for a file.
89 * @param mixed $title Title object or string
90 * @param string or false $time, old version time
91 * @return File object or false if it is not found
93 function findFile( $title, $time = false ) {
94 if( !( $title instanceof Title ) ) {
95 $title = Title::makeTitleSafe( NS_FILE, $title );
97 if( !$title ) {
98 return false; // invalid title?
101 $dbkey = $title->getDBkey();
102 # Is there a current version cached?
103 if( array_key_exists( $dbkey, $this->cache ) ) {
104 if( !$time || $this->cache[$dbkey]->getTimestamp() === $time ) {
105 wfDebug( "FileCache HIT for $dbkey\n" );
106 return $this->cache[$dbkey];
109 # Is there no current version? Then assume no old versions too.
110 if( array_key_exists( $dbkey, $this->notFound ) ) {
111 wfDebug( "FileCache negative HIT for $dbkey\n" );
112 return false;
114 # Is this old version cached?
115 if( $time && array_key_exists( $dbkey, $this->oldCache ) &&
116 array_key_exists( $time, $this->oldCache[$dbkey] ) )
118 wfDebug( "FileCache HIT for $dbkey on $time\n" );
119 return $this->oldCache[$dbkey][$time];
122 // Not in cache, fall back to a direct query
123 $file = $this->repoGroup->findFile( $title, $time );
124 if( $file ) {
125 wfDebug( "FileCache MISS for $dbkey\n" );
126 if( !$file->isOld() ) {
127 $this->cache[$dbkey] = $file; // cache the current version
129 if( !array_key_exists( $dbkey, $this->oldCache ) ) {
130 $this->oldCache[$dbkey] = array();
132 $this->oldCache[$dbkey][$file->getTimestamp()] = $file; // cache this version
133 } else {
134 wfDebug( "FileCache negative MISS for $dbkey\n" );
135 $this->notFound[$dbkey] = true;
137 return $file;
141 * Search the cache for multiple files.
142 * @param array $titles Title objects or strings to search for
143 * @return array of File objects, indexed by DB key
145 function findFiles( $titles ) {
146 $titleObjs = array();
147 foreach ( $titles as $title ) {
148 if ( !( $title instanceof Title ) ) {
149 $title = Title::makeTitleSafe( NS_FILE, $title );
151 if ( $title ) {
152 $titleObjs[$title->getDBkey()] = $title;
156 $result = array_intersect_key( $this->cache, $titleObjs );
158 $unsure = array_diff_key( $titleObjs, $result, $this->notFound );
159 if( $unsure ) {
160 wfDebug( "FileCache MISS for ".count( $unsure )." files out of ".count( $titleObjs )."...\n" );
161 // XXX: We assume the array returned by findFiles() is
162 // indexed by DBkey; this appears to be true, but should
163 // be explicitly documented.
164 $found = $this->repoGroup->findFiles( $unsure );
165 $result += $found;
166 $this->addFiles( $found );
167 $this->markNotFound( array_keys( array_diff_key( $unsure, $found ) ) );
170 wfDebug( "FileCache found ".count( $result )." files out of ".count( $titleObjs )."\n" );
171 return $result;