3 * Local repository that stores files in the local filesystem and registers them
4 * in the wiki's own database.
11 * A repository that stores files in the local filesystem and registers them
12 * in the wiki's own database. This is the most commonly used repository class.
16 class LocalRepo
extends FileRepo
{
17 var $fileFactory = array( 'LocalFile' , 'newFromTitle' );
18 var $fileFactoryKey = array( 'LocalFile' , 'newFromKey' );
19 var $fileFromRowFactory = array( 'LocalFile' , 'newFromRow' );
20 var $oldFileFactory = array( 'OldLocalFile', 'newFromTitle' );
21 var $oldFileFactoryKey = array( 'OldLocalFile', 'newFromKey' );
22 var $oldFileFromRowFactory = array( 'OldLocalFile', 'newFromRow' );
29 function newFileFromRow( $row ) {
30 if ( isset( $row->img_name
) ) {
31 return call_user_func( $this->fileFromRowFactory
, $row, $this );
32 } elseif ( isset( $row->oi_name
) ) {
33 return call_user_func( $this->oldFileFromRowFactory
, $row, $this );
35 throw new MWException( __METHOD__
.': invalid row' );
42 * @return OldLocalFile
44 function newFromArchiveName( $title, $archiveName ) {
45 return OldLocalFile
::newFromArchiveName( $title, $this, $archiveName );
49 * Delete files in the deleted directory if they are not referenced in the
50 * filearchive table. This needs to be done in the repo because it needs to
51 * interleave database locks with file operations, which is potentially a
54 * @param $storageKeys array
56 * @return FileRepoStatus
58 function cleanupDeletedBatch( array $storageKeys ) {
59 $backend = $this->backend
; // convenience
60 $root = $this->getZonePath( 'deleted' );
61 $dbw = $this->getMasterDB();
62 $status = $this->newGood();
63 $storageKeys = array_unique( $storageKeys );
64 foreach ( $storageKeys as $key ) {
65 $hashPath = $this->getDeletedHashPath( $key );
66 $path = "$root/$hashPath$key";
67 $dbw->begin( __METHOD__
);
68 // Check for usage in deleted/hidden files and pre-emptively
69 // lock the key to avoid any future use until we are finished.
70 $deleted = $this->deletedFileHasKey( $key, 'lock' );
71 $hidden = $this->hiddenFileHasKey( $key, 'lock' );
72 if ( !$deleted && !$hidden ) { // not in use now
73 wfDebug( __METHOD__
. ": deleting $key\n" );
74 $op = array( 'op' => 'delete', 'src' => $path );
75 if ( !$backend->doOperation( $op )->isOK() ) {
76 $status->error( 'undelete-cleanup-error', $path );
80 wfDebug( __METHOD__
. ": $key still in use\n" );
81 $status->successCount++
;
83 $dbw->commit( __METHOD__
);
89 * Check if a deleted (filearchive) file has this sha1 key
91 * @param $key String File storage key (base-36 sha1 key with file extension)
92 * @param $lock String|null Use "lock" to lock the row via FOR UPDATE
93 * @return bool File with this key is in use
95 protected function deletedFileHasKey( $key, $lock = null ) {
96 $options = ( $lock === 'lock' ) ?
array( 'FOR UPDATE' ) : array();
98 $dbw = $this->getMasterDB();
99 return (bool)$dbw->selectField( 'filearchive', '1',
100 array( 'fa_storage_group' => 'deleted', 'fa_storage_key' => $key ),
106 * Check if a hidden (revision delete) file has this sha1 key
108 * @param $key String File storage key (base-36 sha1 key with file extension)
109 * @param $lock String|null Use "lock" to lock the row via FOR UPDATE
110 * @return bool File with this key is in use
112 protected function hiddenFileHasKey( $key, $lock = null ) {
113 $options = ( $lock === 'lock' ) ?
array( 'FOR UPDATE' ) : array();
115 $sha1 = self
::getHashFromKey( $key );
116 $ext = File
::normalizeExtension( substr( $key, strcspn( $key, '.' ) +
1 ) );
118 $dbw = $this->getMasterDB();
119 return (bool)$dbw->selectField( 'oldimage', '1',
120 array( 'oi_sha1' => $sha1,
121 'oi_archive_name ' . $dbw->buildLike( $dbw->anyString(), ".$ext" ),
122 $dbw->bitAnd( 'oi_deleted', File
::DELETED_FILE
) => File
::DELETED_FILE
),
128 * Gets the SHA1 hash from a storage key
133 public static function getHashFromKey( $key ) {
134 return strtok( $key, '.' );
138 * Checks if there is a redirect named as $title
140 * @param $title Title of file
143 function checkRedirect( Title
$title ) {
146 $title = File
::normalizeTitle( $title, 'exception' );
148 $memcKey = $this->getSharedCacheKey( 'image_redirect', md5( $title->getDBkey() ) );
149 if ( $memcKey === false ) {
150 $memcKey = $this->getLocalCacheKey( 'image_redirect', md5( $title->getDBkey() ) );
151 $expiry = 300; // no invalidation, 5 minutes
153 $expiry = 86400; // has invalidation, 1 day
155 $cachedValue = $wgMemc->get( $memcKey );
156 if ( $cachedValue === ' ' ||
$cachedValue === '' ) {
159 } elseif ( strval( $cachedValue ) !== '' ) {
160 return Title
::newFromText( $cachedValue, NS_FILE
);
161 } // else $cachedValue is false or null: cache miss
163 $id = $this->getArticleID( $title );
165 $wgMemc->set( $memcKey, " ", $expiry );
168 $dbr = $this->getSlaveDB();
169 $row = $dbr->selectRow(
171 array( 'rd_title', 'rd_namespace' ),
172 array( 'rd_from' => $id ),
176 if( $row && $row->rd_namespace
== NS_FILE
) {
177 $targetTitle = Title
::makeTitle( $row->rd_namespace
, $row->rd_title
);
178 $wgMemc->set( $memcKey, $targetTitle->getDBkey(), $expiry );
181 $wgMemc->set( $memcKey, '', $expiry );
188 * Function link Title::getArticleID().
189 * We can't say Title object, what database it should use, so we duplicate that function here.
191 * @param $title Title
192 * @return bool|int|mixed
194 protected function getArticleID( $title ) {
195 if( !$title instanceof Title
) {
198 $dbr = $this->getSlaveDB();
199 $id = $dbr->selectField(
203 'page_namespace' => $title->getNamespace(),
204 'page_title' => $title->getDBkey(),
206 __METHOD__
//Function name
212 * Get an array or iterator of file objects for files that have a given
213 * SHA-1 content hash.
215 * @param $hash String a sha1 hash to look for
218 function findBySha1( $hash ) {
219 $dbr = $this->getSlaveDB();
222 LocalFile
::selectFields(),
223 array( 'img_sha1' => $hash )
227 foreach ( $res as $row ) {
228 $result[] = $this->newFileFromRow( $row );
236 * Get a connection to the slave DB
237 * @return DatabaseBase
239 function getSlaveDB() {
240 return wfGetDB( DB_SLAVE
);
244 * Get a connection to the master DB
245 * @return DatabaseBase
247 function getMasterDB() {
248 return wfGetDB( DB_MASTER
);
252 * Get a key on the primary cache for this repository.
253 * Returns false if the repository's cache is not accessible at this site.
254 * The parameters are the parts of the key, as for wfMemcKey().
258 function getSharedCacheKey( /*...*/ ) {
259 $args = func_get_args();
260 return call_user_func_array( 'wfMemcKey', $args );
264 * Invalidates image redirect cache related to that image
266 * @param $title Title of page
269 function invalidateImageRedirect( Title
$title ) {
271 $memcKey = $this->getSharedCacheKey( 'image_redirect', md5( $title->getDBkey() ) );
273 $wgMemc->delete( $memcKey );