MediaWiki  REL1_22
LocalRepo.php
Go to the documentation of this file.
00001 <?php
00031 class LocalRepo extends FileRepo {
00032     var $fileFactory           = array( 'LocalFile'   , 'newFromTitle' );
00033     var $fileFactoryKey        = array( 'LocalFile'   , 'newFromKey'   );
00034     var $fileFromRowFactory    = array( 'LocalFile'   , 'newFromRow'   );
00035     var $oldFileFactory        = array( 'OldLocalFile', 'newFromTitle' );
00036     var $oldFileFactoryKey     = array( 'OldLocalFile', 'newFromKey'   );
00037     var $oldFileFromRowFactory = array( 'OldLocalFile', 'newFromRow'   );
00038 
00044     function newFileFromRow( $row ) {
00045         if ( isset( $row->img_name ) ) {
00046             return call_user_func( $this->fileFromRowFactory, $row, $this );
00047         } elseif ( isset( $row->oi_name ) ) {
00048             return call_user_func( $this->oldFileFromRowFactory, $row, $this );
00049         } else {
00050             throw new MWException( __METHOD__ . ': invalid row' );
00051         }
00052     }
00053 
00059     function newFromArchiveName( $title, $archiveName ) {
00060         return OldLocalFile::newFromArchiveName( $title, $this, $archiveName );
00061     }
00062 
00073     function cleanupDeletedBatch( array $storageKeys ) {
00074         $backend = $this->backend; // convenience
00075         $root = $this->getZonePath( 'deleted' );
00076         $dbw = $this->getMasterDB();
00077         $status = $this->newGood();
00078         $storageKeys = array_unique( $storageKeys );
00079         foreach ( $storageKeys as $key ) {
00080             $hashPath = $this->getDeletedHashPath( $key );
00081             $path = "$root/$hashPath$key";
00082             $dbw->begin( __METHOD__ );
00083             // Check for usage in deleted/hidden files and pre-emptively
00084             // lock the key to avoid any future use until we are finished.
00085             $deleted = $this->deletedFileHasKey( $key, 'lock' );
00086             $hidden = $this->hiddenFileHasKey( $key, 'lock' );
00087             if ( !$deleted && !$hidden ) { // not in use now
00088                 wfDebug( __METHOD__ . ": deleting $key\n" );
00089                 $op = array( 'op' => 'delete', 'src' => $path );
00090                 if ( !$backend->doOperation( $op )->isOK() ) {
00091                     $status->error( 'undelete-cleanup-error', $path );
00092                     $status->failCount++;
00093                 }
00094             } else {
00095                 wfDebug( __METHOD__ . ": $key still in use\n" );
00096                 $status->successCount++;
00097             }
00098             $dbw->commit( __METHOD__ );
00099         }
00100         return $status;
00101     }
00102 
00110     protected function deletedFileHasKey( $key, $lock = null ) {
00111         $options = ( $lock === 'lock' ) ? array( 'FOR UPDATE' ) : array();
00112 
00113         $dbw = $this->getMasterDB();
00114         return (bool)$dbw->selectField( 'filearchive', '1',
00115             array( 'fa_storage_group' => 'deleted', 'fa_storage_key' => $key ),
00116             __METHOD__, $options
00117         );
00118     }
00119 
00127     protected function hiddenFileHasKey( $key, $lock = null ) {
00128         $options = ( $lock === 'lock' ) ? array( 'FOR UPDATE' ) : array();
00129 
00130         $sha1 = self::getHashFromKey( $key );
00131         $ext = File::normalizeExtension( substr( $key, strcspn( $key, '.' ) + 1 ) );
00132 
00133         $dbw = $this->getMasterDB();
00134         return (bool)$dbw->selectField( 'oldimage', '1',
00135             array( 'oi_sha1' => $sha1,
00136                 'oi_archive_name ' . $dbw->buildLike( $dbw->anyString(), ".$ext" ),
00137                 $dbw->bitAnd( 'oi_deleted', File::DELETED_FILE ) => File::DELETED_FILE ),
00138             __METHOD__, $options
00139         );
00140     }
00141 
00148     public static function getHashFromKey( $key ) {
00149         return strtok( $key, '.' );
00150     }
00151 
00158     function checkRedirect( Title $title ) {
00159         global $wgMemc;
00160 
00161         $title = File::normalizeTitle( $title, 'exception' );
00162 
00163         $memcKey = $this->getSharedCacheKey( 'image_redirect', md5( $title->getDBkey() ) );
00164         if ( $memcKey === false ) {
00165             $memcKey = $this->getLocalCacheKey( 'image_redirect', md5( $title->getDBkey() ) );
00166             $expiry = 300; // no invalidation, 5 minutes
00167         } else {
00168             $expiry = 86400; // has invalidation, 1 day
00169         }
00170         $cachedValue = $wgMemc->get( $memcKey );
00171         if ( $cachedValue === ' ' || $cachedValue === '' ) {
00172             // Does not exist
00173             return false;
00174         } elseif ( strval( $cachedValue ) !== '' && $cachedValue !== ' PURGED' ) {
00175             return Title::newFromText( $cachedValue, NS_FILE );
00176         } // else $cachedValue is false or null: cache miss
00177 
00178         $id = $this->getArticleID( $title );
00179         if ( !$id ) {
00180             $wgMemc->add( $memcKey, " ", $expiry );
00181             return false;
00182         }
00183         $dbr = $this->getSlaveDB();
00184         $row = $dbr->selectRow(
00185             'redirect',
00186             array( 'rd_title', 'rd_namespace' ),
00187             array( 'rd_from' => $id ),
00188             __METHOD__
00189         );
00190 
00191         if ( $row && $row->rd_namespace == NS_FILE ) {
00192             $targetTitle = Title::makeTitle( $row->rd_namespace, $row->rd_title );
00193             $wgMemc->add( $memcKey, $targetTitle->getDBkey(), $expiry );
00194             return $targetTitle;
00195         } else {
00196             $wgMemc->add( $memcKey, '', $expiry );
00197             return false;
00198         }
00199     }
00200 
00208     protected function getArticleID( $title ) {
00209         if ( !$title instanceof Title ) {
00210             return 0;
00211         }
00212         $dbr = $this->getSlaveDB();
00213         $id = $dbr->selectField(
00214             'page', // Table
00215             'page_id', //Field
00216             array( //Conditions
00217                 'page_namespace' => $title->getNamespace(),
00218                 'page_title' => $title->getDBkey(),
00219             ),
00220             __METHOD__ //Function name
00221         );
00222         return $id;
00223     }
00224 
00232     function findBySha1( $hash ) {
00233         $dbr = $this->getSlaveDB();
00234         $res = $dbr->select(
00235             'image',
00236             LocalFile::selectFields(),
00237             array( 'img_sha1' => $hash ),
00238             __METHOD__,
00239             array( 'ORDER BY' => 'img_name' )
00240         );
00241 
00242         $result = array();
00243         foreach ( $res as $row ) {
00244             $result[] = $this->newFileFromRow( $row );
00245         }
00246         $res->free();
00247 
00248         return $result;
00249     }
00250 
00260     function findBySha1s( array $hashes ) {
00261         if ( !count( $hashes ) ) {
00262             return array(); //empty parameter
00263         }
00264 
00265         $dbr = $this->getSlaveDB();
00266         $res = $dbr->select(
00267             'image',
00268             LocalFile::selectFields(),
00269             array( 'img_sha1' => $hashes ),
00270             __METHOD__,
00271             array( 'ORDER BY' => 'img_name' )
00272         );
00273 
00274         $result = array();
00275         foreach ( $res as $row ) {
00276             $file = $this->newFileFromRow( $row );
00277             $result[$file->getSha1()][] = $file;
00278         }
00279         $res->free();
00280 
00281         return $result;
00282     }
00283 
00291     public function findFilesByPrefix( $prefix, $limit ) {
00292         $selectOptions = array( 'ORDER BY' => 'img_name', 'LIMIT' => intval( $limit ) );
00293 
00294         // Query database
00295         $dbr = $this->getSlaveDB();
00296         $res = $dbr->select(
00297             'image',
00298             LocalFile::selectFields(),
00299             'img_name ' . $dbr->buildLike( $prefix, $dbr->anyString() ),
00300             __METHOD__,
00301             $selectOptions
00302             );
00303 
00304         // Build file objects
00305         $files = array();
00306         foreach ( $res as $row ) {
00307             $files[] = $this->newFileFromRow( $row );
00308         }
00309         return $files;
00310     }
00311 
00316     function getSlaveDB() {
00317         return wfGetDB( DB_SLAVE );
00318     }
00319 
00324     function getMasterDB() {
00325         return wfGetDB( DB_MASTER );
00326     }
00327 
00335     function getSharedCacheKey( /*...*/ ) {
00336         $args = func_get_args();
00337         return call_user_func_array( 'wfMemcKey', $args );
00338     }
00339 
00346     function invalidateImageRedirect( Title $title ) {
00347         global $wgMemc;
00348         $memcKey = $this->getSharedCacheKey( 'image_redirect', md5( $title->getDBkey() ) );
00349         if ( $memcKey ) {
00350             // Set a temporary value for the cache key, to ensure
00351             // that this value stays purged long enough so that
00352             // it isn't refreshed with a stale value due to a
00353             // lagged slave.
00354             $wgMemc->set( $memcKey, ' PURGED', 12 );
00355         }
00356     }
00357 }