r22470 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r22469‎ | r22470 | r22471 >
Date:06:15, 27 May 2007
Author:tstarling
Status:old
Tags:
Comment:
OldLocalFile concept. Untested.
Modified paths:
  • /branches/filerepo-work/phase3/includes/GlobalFunctions.php (modified) (history)
  • /branches/filerepo-work/phase3/includes/filerepo/FSRepo.php (modified) (history)
  • /branches/filerepo-work/phase3/includes/filerepo/File.php (modified) (history)
  • /branches/filerepo-work/phase3/includes/filerepo/LocalFile.php (modified) (history)
  • /branches/filerepo-work/phase3/includes/filerepo/OldLocalFile.php (added) (history)
  • /branches/filerepo-work/phase3/includes/filerepo/RepoGroup.php (modified) (history)

Diff [purge]

Index: branches/filerepo-work/phase3/includes/GlobalFunctions.php
@@ -2276,11 +2276,14 @@
22772277 /**
22782278 * Find a file.
22792279 * Shortcut for RepoGroup::singleton()->findFile()
2280 - * @param mixed $title Title object or string. May be interwiki.
 2280+ * @param mixed $title Title object or string. May be interwiki.
 2281+ * @param mixed $time Requested time for an archived image, or false for the
 2282+ * current version. An image object will be returned which
 2283+ * existed at or before the specified time.
22812284 * @return File, or false if the file does not exist
22822285 */
2283 -function wfFindFile( $title ) {
2284 - return RepoGroup::singleton()->findFile( $title );
 2286+function wfFindFile( $title, $time = false ) {
 2287+ return RepoGroup::singleton()->findFile( $title, $time );
22852288 }
22862289
22872290 /**
Index: branches/filerepo-work/phase3/includes/filerepo/OldLocalFile.php
@@ -0,0 +1,145 @@
 2+<?php
 3+
 4+/**
 5+ * Class to represent a file in the oldimage table
 6+ *
 7+ * @addtogroup FileRepo
 8+ */
 9+class OldLocalFile extends LocalFile {
 10+ var $requestedTime, $archive_name;
 11+
 12+ const CACHE_VERSION = 1;
 13+ const MAX_CACHE_ROWS = 20;
 14+
 15+ function newFromTitle( $title, $repo, $time ) {
 16+ return new OldLocalFile( $title, $repo, $time );
 17+ }
 18+
 19+ function __construct( $title, $repo, $time ) {
 20+ parent::__construct( $title, $repo );
 21+ $this->requestedTime = $time;
 22+ }
 23+
 24+ function getCacheKey() {
 25+ $hashedName = md5($this->getName());
 26+ return wfMemcKey( 'oldfile', $hashedName );
 27+ }
 28+
 29+ /**
 30+ * Try to load file metadata from memcached. Returns true on success.
 31+ */
 32+ function loadFromCache() {
 33+ global $wgMemc;
 34+ wfProfileIn( __METHOD__ );
 35+ $this->dataLoaded = false;
 36+ $key = $this->getCacheKey();
 37+ if ( !$key ) {
 38+ return false;
 39+ }
 40+ $oldImages = $wgMemc->get( $key );
 41+
 42+ if ( isset( $oldImages['version'] ) && $oldImages['version'] == MW_OLDFILE_VERSION ) {
 43+ unset( $oldImages['version'];
 44+ $more = isset( $oldImages['more'] );
 45+ unset( $oldImages['more'] );
 46+ krsort( $oldImages );
 47+ $found = false;
 48+ foreach ( $oldImages as $timestamp => $info ) {
 49+ if ( $timestamp <= $this->desiredTimestamp ) {
 50+ $found = true;
 51+ break;
 52+ }
 53+ }
 54+ if ( $found ) {
 55+ wfDebug( "Pulling file metadata from cache key {$key}[{$timestamp}]\n" );
 56+ $this->loadFromRow( (object)$cachedValues ) );
 57+ $this->fileExists = true;
 58+ $this->dataLoaded = true;
 59+ } elseif ( $more ) {
 60+ wfDebug( "Cache key was truncated, oldimage row might be found in the database\n" );
 61+ } else {
 62+ wfDebug( "Image did not exist at the specified time.\n" );
 63+ $this->fileExists = false;
 64+ $this->dataLoaded = true;
 65+ }
 66+ }
 67+
 68+ if ( $this->dataLoaded ) {
 69+ wfIncrStats( 'image_cache_hit' );
 70+ } else {
 71+ wfIncrStats( 'image_cache_miss' );
 72+ }
 73+
 74+ wfProfileOut( __METHOD__ );
 75+ return $this->dataLoaded;
 76+ }
 77+
 78+ function saveToCache() {
 79+ // Cache the entire history of the image (up to MAX_CACHE_ROWS).
 80+ // This is expensive, so we only do it if $wgMemc is real
 81+ global $wgMemc;
 82+ if ( $wgMemc instanceof FakeMemcachedClient ) {
 83+ return;
 84+ }
 85+ $key = $this->getCacheKey();
 86+ if ( !$key ) {
 87+ return;
 88+ }
 89+ wfProfileIn( __METHOD__ );
 90+
 91+ $dbr = $this->repo->getSlaveDB();
 92+ $res = $dbr->select( 'oldimage', $this->getCacheFields(),
 93+ array( 'oi_name' => $this->getName() ), __METHOD__,
 94+ array(
 95+ 'LIMIT' => self::MAX_CACHE_ROWS + 1,
 96+ 'ORDER BY' => 'oi_timestamp DESC',
 97+ ));
 98+ $cache = array( 'version' => self::CACHE_VERSION );
 99+ $numRows = $dbr->numRows( $res );
 100+ if ( $numRows > self::MAX_CACHE_ROWS ) {
 101+ $cache['more'] = true;
 102+ $numRows--;
 103+ }
 104+ for ( $i = 0; $i < $numRows; $i++ ) {
 105+ $row = $dbr->fetchObject( $res );
 106+ $this->decodeRow( $row, 'oi_' );
 107+ $cache[$row->oi_timestamp] = $row;
 108+ }
 109+ $dbr->freeResult( $res );
 110+ $wgMemc->set( $key, $cache, 7*86400 /* 1 week */ );
 111+ wfProfileOut( __METHOD__ );
 112+ }
 113+
 114+ function loadFromDB() {
 115+ wfProfileIn( __METHOD__ );
 116+ $dbr = $this->repo->getSlaveDB();
 117+ $row = $dbr->selectRow( 'oldimage', $this->getCacheFields( 'oi_' ),
 118+ array(
 119+ 'oi_name' => $this->getName(),
 120+ 'oi_timestamp <= ' . $this->requestedTimestamp
 121+ ), __METHOD__, array( 'ORDER BY' => 'oi_timestamp DESC' ) );
 122+ if ( $row ) {
 123+ $this->decodeRow( $row, 'oi_' );
 124+ $this->loadFromRow( $row, 'oi_' );
 125+ } else {
 126+ $this->fileExists = false;
 127+ }
 128+ $this->dataLoaded = true;
 129+ }
 130+
 131+ function getCacheFields( $prefix = 'img_' ) {
 132+ $fields = parent::getCacheFields( $prefix );
 133+ $fields[] = $prefix . 'archive_name';
 134+ }
 135+
 136+ function getRel() {
 137+ return 'archive/' . $this->getHashPath() . '/' . $this->archive_name;
 138+ }
 139+
 140+ function getUrlRel() {
 141+ return 'archive/' . $this->getHashPath() . '/' . urlencode( $this->archive_name );
 142+ }
 143+}
 144+
 145+
 146+?>
Index: branches/filerepo-work/phase3/includes/filerepo/LocalFile.php
@@ -5,7 +5,7 @@
66 /**
77 * Bump this number when serialized cache records may be incompatible.
88 */
9 -define( 'MW_FILE_VERSION', 3 );
 9+define( 'MW_FILE_VERSION', 4 );
1010
1111 /**
1212 * Class to represent a local file in the wiki's own database
@@ -27,10 +27,13 @@
2828 $height, # |
2929 $bits, # --- returned by getimagesize (loadFromXxx)
3030 $attr, # /
31 - $type, # MEDIATYPE_xxx (bitmap, drawing, audio...)
 31+ $media_type, # MEDIATYPE_xxx (bitmap, drawing, audio...)
3232 $mime, # MIME type, determined by MimeMagic::guessMimeType
 33+ $major_mime, # Major mime type
 34+ $minor_mine, # Minor mime type
3335 $size, # Size in bytes (loadFromXxx)
3436 $metadata, # Metadata
 37+ $timestamp, # Upload timestamp
3538 $dataLoaded; # Whether or not all this has been loaded from the database (loadFromXxx)
3639
3740 /**#@-*/
@@ -71,20 +74,9 @@
7275 $cachedValues = $wgMemc->get( $key );
7376
7477 // Check if the key existed and belongs to this version of MediaWiki
75 - if (!empty($cachedValues) && is_array($cachedValues)
76 - && isset($cachedValues['version']) && ( $cachedValues['version'] == MW_FILE_VERSION )
77 - && isset( $cachedValues['mime'] ) && isset( $cachedValues['metadata'] ) )
78 - {
 78+ if ( isset($cachedValues['version']) && ( $cachedValues['version'] == MW_FILE_VERSION ) ) {
7979 wfDebug( "Pulling file metadata from cache key $key\n" );
80 - $this->fileExists = $cachedValues['fileExists'];
81 - $this->width = $cachedValues['width'];
82 - $this->height = $cachedValues['height'];
83 - $this->bits = $cachedValues['bits'];
84 - $this->type = $cachedValues['type'];
85 - $this->mime = $cachedValues['mime'];
86 - $this->metadata = $cachedValues['metadata'];
87 - $this->size = $cachedValues['size'];
88 - $this->dataLoaded = true;
 80+ $this->loadFromRow( $cachedValues, '' );
8981 }
9082 if ( $this->dataLoaded ) {
9183 wfIncrStats( 'image_cache_hit' );
@@ -106,25 +98,19 @@
10799 if ( !$key ) {
108100 return;
109101 }
110 - $cachedValues = array(
111 - 'version' => MW_FILE_VERSION,
112 - 'fileExists' => $this->fileExists,
113 - 'width' => $this->width,
114 - 'height' => $this->height,
115 - 'bits' => $this->bits,
116 - 'type' => $this->type,
117 - 'mime' => $this->mime,
118 - 'metadata' => $this->metadata,
119 - 'size' => $this->size );
 102+ $fields = $this->getCacheFields( '' );
 103+ $cache = array( 'version' => MW_FILE_VERSION );
 104+ foreach ( $fields as $field ) {
 105+ $cache[$field] = $this->$field;
 106+ }
120107
121 - $wgMemc->set( $key, $cachedValues, 60 * 60 * 24 * 7 ); // A week
 108+ $wgMemc->set( $key, $cache, 60 * 60 * 24 * 7 ); // A week
122109 }
123110
124111 /**
125112 * Load metadata from the file itself
126113 */
127114 function loadFromFile() {
128 - global $wgContLang;
129115 wfProfileIn( __METHOD__ );
130116 $path = $this->getPath();
131117 $this->fileExists = file_exists( $path );
@@ -134,7 +120,7 @@
135121 $magic=& MimeMagic::singleton();
136122
137123 $this->mime = $magic->guessMimeType($path,true);
138 - $this->type = $magic->getMediaType($path,$this->mime);
 124+ $this->media_type = $magic->getMediaType($path,$this->mime);
139125 $handler = MediaHandler::getHandler( $this->mime );
140126
141127 # Get size in bytes
@@ -152,7 +138,7 @@
153139 wfDebug(__METHOD__.": $path loaded, {$this->size} bytes, {$this->mime}.\n");
154140 } else {
155141 $this->mime = NULL;
156 - $this->type = MEDIATYPE_UNKNOWN;
 142+ $this->media_type = MEDIATYPE_UNKNOWN;
157143 $this->metadata = '';
158144 wfDebug(__METHOD__.": $path NOT FOUND!\n");
159145 }
@@ -178,33 +164,40 @@
179165 wfProfileOut( __METHOD__ );
180166 }
181167
 168+ function getCacheFields( $prefix = 'img_' ) {
 169+ static $fields = array( 'size', 'width', 'height', 'bits', 'media_type',
 170+ 'major_mime', 'minor_mime', 'metadata', 'timestamp' );
 171+ static $results = array();
 172+ if ( $prefix = '' ) {
 173+ return $fields;
 174+ }
 175+ if ( !isset( $results[$prefix] ) ) {
 176+ $prefixedFields = array();
 177+ foreach ( $fields as $field ) {
 178+ $prefixedFields[] = $prefix . $field;
 179+ }
 180+ $fields[$prefix] = $prefixedFields;
 181+ }
 182+ return $fields[$prefix];
 183+ }
 184+
182185 /**
183186 * Load file metadata from the DB
184187 */
185188 function loadFromDB() {
186 - global $wgContLang;
187189 wfProfileIn( __METHOD__ );
188190
189191 $dbr = $this->repo->getSlaveDB();
190192
191 - $row = $dbr->selectRow( 'image',
192 - array( 'img_size', 'img_width', 'img_height', 'img_bits',
193 - 'img_media_type', 'img_major_mime', 'img_minor_mime', 'img_metadata' ),
 193+ $row = $dbr->selectRow( 'image', $this->getCacheFields( 'img_' ),
194194 array( 'img_name' => $this->getName() ), __METHOD__ );
195195 if ( $row ) {
196 - $this->fileExists = true;
 196+ $this->decodeRow( $row );
197197 $this->loadFromRow( $row );
198198 // Check for rows from a previous schema, quietly upgrade them
199199 $this->maybeUpgradeRow();
200200 } else {
201 - $this->size = 0;
202 - $this->width = 0;
203 - $this->height = 0;
204 - $this->bits = 0;
205 - $this->type = 0;
206201 $this->fileExists = false;
207 - $this->metadata = '';
208 - $this->mime = false;
209202 }
210203
211204 # Unconditionally set loaded=true, we don't want the accessors constantly rechecking
@@ -212,26 +205,39 @@
213206 wfProfileOut( __METHOD__ );
214207 }
215208
 209+ function decodeRow( &$row, $prefix = 'img_' ) {
 210+ $tsName = $prefix . 'timestamp';
 211+ $row->$tsName = wfTimestamp( $row->$tsName );
 212+ }
 213+
 214+ function encodeRow( &$row, $db, $prefix = 'img_' ) {
 215+ $tsName = $prefix . 'timestamp';
 216+ $row->$tsName = $db->timestamp( $row->$tsName );
 217+ }
 218+
216219 /*
217220 * Load file metadata from a DB result row
218221 */
219 - function loadFromRow( &$row ) {
220 - $this->size = $row->img_size;
221 - $this->width = $row->img_width;
222 - $this->height = $row->img_height;
223 - $this->bits = $row->img_bits;
224 - $this->type = $row->img_media_type;
225 -
226 - $major= $row->img_major_mime;
227 - $minor= $row->img_minor_mime;
228 -
229 - if (!$major) $this->mime = "unknown/unknown";
230 - else {
231 - if (!$minor) $minor= "unknown";
232 - $this->mime = $major.'/'.$minor;
 222+ function loadFromRow( $row, $prefix = 'img_' ) {
 223+ $array = (array)$row;
 224+ $prefixLength = strlen( $prefix );
 225+ // Sanity check prefix once
 226+ if ( substr( key( $array ), 0, $prefixLength ) !== $prefix ) {
 227+ throw new MWException( __METHOD__. ': incorrect $prefix parameter' );
 228+ }
 229+ foreach ( $array as $name => $value ) {
 230+ $deprefixedName = substr( $name, $prefixLength );
 231+ $this->$deprefixedName = $value;
233232 }
234 - $this->metadata = $row->img_metadata;
235 -
 233+ if ( !$this->major_mime ) {
 234+ $this->mime = "unknown/unknown";
 235+ } else {
 236+ if (!$this->minor_mime) {
 237+ $this->minor_mime = "unknown";
 238+ }
 239+ $this->mime = $this->major_mime.'/'.$this->minor_mime;
 240+ }
 241+ $this->fileExists = true;
236242 $this->dataLoaded = true;
237243 }
238244
@@ -242,12 +248,7 @@
243249 if ( !$this->dataLoaded ) {
244250 if ( !$this->loadFromCache() ) {
245251 $this->loadFromDB();
246 - if ( $this->fileExists ) {
247 - // FIXME: We can do negative caching for local files, because the cache
248 - // will be purged on upload. But we can't do it when shared files
249 - // are enabled, since updates to that won't purge foreign caches.
250 - $this->saveToCache();
251 - }
 252+ $this->saveToCache();
252253 }
253254 $this->dataLoaded = true;
254255 }
@@ -257,7 +258,7 @@
258259 * Upgrade a row if it needs it
259260 */
260261 function maybeUpgradeRow() {
261 - if ( is_null($this->type) || $this->mime == 'image/svg' ) {
 262+ if ( is_null($this->media_type) || $this->mime == 'image/svg' ) {
262263 $this->upgradeRow();
263264 } else {
264265 $handler = $this->getHandler();
@@ -285,7 +286,7 @@
286287 'img_width' => $this->width,
287288 'img_height' => $this->height,
288289 'img_bits' => $this->bits,
289 - 'img_media_type' => $this->type,
 290+ 'img_media_type' => $this->media_type,
290291 'img_major_mime' => $major,
291292 'img_minor_mime' => $minor,
292293 'img_metadata' => $this->metadata,
@@ -372,7 +373,7 @@
373374 */
374375 function getMediaType() {
375376 $this->load();
376 - return $this->type;
 377+ return $this->media_type;
377378 }
378379
379380 /** canRender inherited */
@@ -632,7 +633,7 @@
633634 'img_width' => intval( $this->width ),
634635 'img_height' => intval( $this->height ),
635636 'img_bits' => $this->bits,
636 - 'img_media_type' => $this->type,
 637+ 'img_media_type' => $this->media_type,
637638 'img_major_mime' => $major,
638639 'img_minor_mime' => $minor,
639640 'img_timestamp' => $now,
@@ -670,7 +671,7 @@
671672 'img_width' => intval( $this->width ),
672673 'img_height' => intval( $this->height ),
673674 'img_bits' => $this->bits,
674 - 'img_media_type' => $this->type,
 675+ 'img_media_type' => $this->media_type,
675676 'img_major_mime' => $major,
676677 'img_minor_mime' => $minor,
677678 'img_timestamp' => $now,
@@ -1254,14 +1255,18 @@
12551256 return $html;
12561257 }
12571258
1258 -} //class
 1259+ function getTimestamp() {
 1260+ $this->load();
 1261+ return $this->timestamp;
 1262+ }
 1263+} // LocalFile class
12591264
12601265 /**
12611266 * Backwards compatibility class
12621267 */
12631268 class Image extends LocalFile {
12641269 function __construct( $title ) {
1265 - $repo = FileRepoGroup::singleton()->getRepo( 0 );
 1270+ $repo = FileRepoGroup::singleton()->getLocalRepo();
12661271 parent::__construct( $title, $repo );
12671272 }
12681273 }
Index: branches/filerepo-work/phase3/includes/filerepo/FSRepo.php
@@ -33,21 +33,44 @@
3434 /**
3535 * Create a new File object from the local repository
3636 * @param mixed $title Title object or string
 37+ * @param mixed $time Time at which the image is supposed to have existed.
 38+ * If this is specified, the returned object will be an
 39+ * instance of the repository's old file class instead of
 40+ * a current file. Repositories not supporting version
 41+ * control should return false if this parameter is set.
3742 */
38 -
39 - function newFile( $title ) {
40 - if ( $title instanceof Title ) {
41 - return call_user_func( $this->fileFactory, $title, $this );
42 - } else {
 43+ function newFile( $title, $time = false ) {
 44+ if ( !($title instanceof Title) ) {
4345 $title = Title::makeTitleSafe( NS_IMAGE, $title );
44 - if ( is_object( $title ) ) {
45 - return call_user_func( $this->fileFactory, $title, $this );
46 - } else {
47 - return NULL;
 46+ if ( !is_object( $title ) ) {
 47+ return null;
4848 }
4949 }
 50+ if ( $time ) {
 51+ return call_user_func( $this->oldFileFactor, $title, $this, $time );
 52+ } else {
 53+ return call_user_func( $this->fileFactory, $title, $this );
 54+ }
5055 }
5156
 57+ /**
 58+ * Find an instance of the named file that existed at the specified time
 59+ * Returns false if the file did not exist. Repositories not supporting
 60+ * version control should return false if the time is specified.
 61+ *
 62+ * @param mixed $time 14-character timestamp, or false for the current version
 63+ */
 64+ function findFile( $title, $time = false ) {
 65+ $img = $this->newFile( $title );
 66+ if ( $img->exists() && $img->getTimestamp() <= $time ) {
 67+ return $img;
 68+ }
 69+ $img = $this->newFile( $title, $time );
 70+ if ( $img->exists() ) {
 71+ return $img;
 72+ }
 73+ }
 74+
5275 function getRootDirectory() {
5376 return $this->directory;
5477 }
Index: branches/filerepo-work/phase3/includes/filerepo/File.php
@@ -90,6 +90,7 @@
9191 /**
9292 * Upgrade the database row if there is one
9393 * Called by ImagePage
 94+ * STUB
9495 */
9596 function upgradeRow() {}
9697
@@ -941,6 +942,17 @@
942943 return false;
943944 }
944945 }
 946+
 947+ /**
 948+ * Get the 14-character timestamp of the file upload, or false if
 949+ */
 950+ function getTimestmap() {
 951+ $path = $this->getPath();
 952+ if ( !file_exists( $path ) ) {
 953+ return false;
 954+ }
 955+ return wfTimestamp( filemtime( $path ) );
 956+ }
945957 }
946958
947959 ?>
Index: branches/filerepo-work/phase3/includes/filerepo/RepoGroup.php
@@ -31,20 +31,22 @@
3232 * Search repositories for an image.
3333 * You can also use wfGetFile() to do this.
3434 * @param mixed $title Title object or string
 35+ * @param mixed $time The 14-char timestamp before which the file should
 36+ * have been uploaded, or false for the current version
3537 * @return File object or false if it is not found
3638 */
37 - function findFile( $title ) {
 39+ function findFile( $title, $time = false ) {
3840 if ( !$this->reposInitialised ) {
3941 $this->initialiseRepos();
4042 }
4143
42 - $image = $this->localRepo->newFile( $title );
43 - if ( $image->exists() ) {
 44+ $image = $this->localRepo->findFile( $title, $time );
 45+ if ( $image ) {
4446 return $image;
4547 }
4648 foreach ( $this->foreignRepos as $repo ) {
47 - $image = $repo->newFile( $title );
48 - if ( $image->exists() ) {
 49+ $image = $repo->findFile( $image, $time );
 50+ if ( $image ) {
4951 return $image;
5052 }
5153 }