Merge "selenium: Remove "RunJobs" wait from specialrecentchanges test"
[lhc/web/wiklou.git] / includes / filerepo / FileRepo.php
index 60f1607..f095066 100644 (file)
@@ -142,6 +142,12 @@ class FileRepo {
        /** @var WANObjectCache */
        protected $wanCache;
 
+       /**
+        * @var string
+        * @protected Use $this->getName(). Public for back-compat only
+        */
+       public $name;
+
        /**
         * @param array|null $info
         * @throws MWException
@@ -832,7 +838,11 @@ class FileRepo {
        /**
         * Store a file to a given destination.
         *
-        * @param string $srcPath Source file system path, storage path, or virtual URL
+        * Using FSFile/TempFSFile can improve performance via caching.
+        * Using TempFSFile can further improve performance by signalling that it is safe
+        * to touch the source file or write extended attribute metadata to it directly.
+        *
+        * @param string|FSFile $srcPath Source file system path, storage path, or virtual URL
         * @param string $dstZone Destination zone
         * @param string $dstRel Destination relative path
         * @param int $flags Bitwise combination of the following flags:
@@ -856,6 +866,8 @@ class FileRepo {
        /**
         * Store a batch of files
         *
+        * @see FileRepo::store()
+        *
         * @param array $triplets (src, dest zone, dest rel) triplets as per store()
         * @param int $flags Bitwise combination of the following flags:
         *   self::OVERWRITE         Overwrite an existing destination file instead of failing
@@ -878,11 +890,18 @@ class FileRepo {
                $operations = [];
                // Validate each triplet and get the store operation...
                foreach ( $triplets as $triplet ) {
-                       list( $srcPath, $dstZone, $dstRel ) = $triplet;
+                       list( $src, $dstZone, $dstRel ) = $triplet;
+                       $srcPath = ( $src instanceof FSFile ) ? $src->getPath() : $src;
                        wfDebug( __METHOD__
                                . "( \$src='$srcPath', \$dstZone='$dstZone', \$dstRel='$dstRel' )\n"
                        );
-
+                       // Resolve source path
+                       if ( $src instanceof FSFile ) {
+                               $op = 'store';
+                       } else {
+                               $src = $this->resolveToStoragePathIfVirtual( $src );
+                               $op = FileBackend::isStoragePath( $src ) ? 'copy' : 'store';
+                       }
                        // Resolve destination path
                        $root = $this->getZonePath( $dstZone );
                        if ( !$root ) {
@@ -898,21 +917,13 @@ class FileRepo {
                                return $this->newFatal( 'directorycreateerror', $dstDir );
                        }
 
-                       // Resolve source to a storage path if virtual
-                       $srcPath = $this->resolveToStoragePath( $srcPath );
-
-                       // Get the appropriate file operation
-                       if ( FileBackend::isStoragePath( $srcPath ) ) {
-                               $opName = 'copy';
-                       } else {
-                               $opName = 'store';
-                       }
+                       // Copy the source file to the destination
                        $operations[] = [
-                               'op' => $opName,
-                               'src' => $srcPath,
+                               'op' => $op,
+                               'src' => $src, // storage path (copy) or local file path (store)
                                'dst' => $dstPath,
-                               'overwrite' => $flags & self::OVERWRITE,
-                               'overwriteSame' => $flags & self::OVERWRITE_SAME,
+                               'overwrite' => ( $flags & self::OVERWRITE ) ? true : false,
+                               'overwriteSame' => ( $flags & self::OVERWRITE_SAME ) ? true : false,
                        ];
                }
 
@@ -949,7 +960,7 @@ class FileRepo {
                                $path = $this->getZonePath( $zone ) . "/$rel";
                        } else {
                                // Resolve source to a storage path if virtual
-                               $path = $this->resolveToStoragePath( $path );
+                               $path = $this->resolveToStoragePathIfVirtual( $path );
                        }
                        $operations[] = [ 'op' => 'delete', 'src' => $path ];
                }
@@ -969,6 +980,10 @@ class FileRepo {
         * This function can be used to write to otherwise read-only foreign repos.
         * This is intended for copying generated thumbnails into the repo.
         *
+        * Using FSFile/TempFSFile can improve performance via caching.
+        * Using TempFSFile can further improve performance by signalling that it is safe
+        * to touch the source file or write extended attribute metadata to it directly.
+        *
         * @param string|FSFile $src Source file system path, storage path, or virtual URL
         * @param string $dst Virtual URL or storage path
         * @param array|string|null $options An array consisting of a key named headers
@@ -980,39 +995,14 @@ class FileRepo {
                return $this->quickImportBatch( [ [ $src, $dst, $options ] ] );
        }
 
-       /**
-        * Purge a file from the repo. This does no locking nor journaling.
-        * This function can be used to write to otherwise read-only foreign repos.
-        * This is intended for purging thumbnails.
-        *
-        * @param string $path Virtual URL or storage path
-        * @return Status
-        */
-       final public function quickPurge( $path ) {
-               return $this->quickPurgeBatch( [ $path ] );
-       }
-
-       /**
-        * Deletes a directory if empty.
-        * This function can be used to write to otherwise read-only foreign repos.
-        *
-        * @param string $dir Virtual URL (or storage path) of directory to clean
-        * @return Status
-        */
-       public function quickCleanDir( $dir ) {
-               $status = $this->newGood();
-               $status->merge( $this->backend->clean(
-                       [ 'dir' => $this->resolveToStoragePath( $dir ) ] ) );
-
-               return $status;
-       }
-
        /**
         * Import a batch of files from the local file system into the repo.
         * This does no locking nor journaling and overrides existing files.
         * This function can be used to write to otherwise read-only foreign repos.
         * This is intended for copying generated thumbnails into the repo.
         *
+        * @see FileRepo::quickImport()
+        *
         * All path parameters may be a file system path, storage path, or virtual URL.
         * When "headers" are given they are used as HTTP headers if supported.
         *
@@ -1027,10 +1017,10 @@ class FileRepo {
                        if ( $src instanceof FSFile ) {
                                $op = 'store';
                        } else {
-                               $src = $this->resolveToStoragePath( $src );
+                               $src = $this->resolveToStoragePathIfVirtual( $src );
                                $op = FileBackend::isStoragePath( $src ) ? 'copy' : 'store';
                        }
-                       $dst = $this->resolveToStoragePath( $dst );
+                       $dst = $this->resolveToStoragePathIfVirtual( $dst );
 
                        if ( !isset( $triple[2] ) ) {
                                $headers = [];
@@ -1045,7 +1035,7 @@ class FileRepo {
 
                        $operations[] = [
                                'op' => $op,
-                               'src' => $src,
+                               'src' => $src, // storage path (copy) or local path/FSFile (store)
                                'dst' => $dst,
                                'headers' => $headers
                        ];
@@ -1056,6 +1046,33 @@ class FileRepo {
                return $status;
        }
 
+       /**
+        * Purge a file from the repo. This does no locking nor journaling.
+        * This function can be used to write to otherwise read-only foreign repos.
+        * This is intended for purging thumbnails.
+        *
+        * @param string $path Virtual URL or storage path
+        * @return Status
+        */
+       final public function quickPurge( $path ) {
+               return $this->quickPurgeBatch( [ $path ] );
+       }
+
+       /**
+        * Deletes a directory if empty.
+        * This function can be used to write to otherwise read-only foreign repos.
+        *
+        * @param string $dir Virtual URL (or storage path) of directory to clean
+        * @return Status
+        */
+       public function quickCleanDir( $dir ) {
+               $status = $this->newGood();
+               $status->merge( $this->backend->clean(
+                       [ 'dir' => $this->resolveToStoragePathIfVirtual( $dir ) ] ) );
+
+               return $status;
+       }
+
        /**
         * Purge a batch of files from the repo.
         * This function can be used to write to otherwise read-only foreign repos.
@@ -1070,7 +1087,7 @@ class FileRepo {
                foreach ( $paths as $path ) {
                        $operations[] = [
                                'op' => 'delete',
-                               'src' => $this->resolveToStoragePath( $path ),
+                               'src' => $this->resolveToStoragePathIfVirtual( $path ),
                                'ignoreMissingSource' => true
                        ];
                }
@@ -1139,7 +1156,7 @@ class FileRepo {
                $sources = [];
                foreach ( $srcPaths as $srcPath ) {
                        // Resolve source to a storage path if virtual
-                       $source = $this->resolveToStoragePath( $srcPath );
+                       $source = $this->resolveToStoragePathIfVirtual( $srcPath );
                        $sources[] = $source; // chunk to merge
                }
 
@@ -1168,6 +1185,10 @@ class FileRepo {
         * Returns a Status object. On success, the value contains "new" or
         * "archived", to indicate whether the file was new with that name.
         *
+        * Using FSFile/TempFSFile can improve performance via caching.
+        * Using TempFSFile can further improve performance by signalling that it is safe
+        * to touch the source file or write extended attribute metadata to it directly.
+        *
         * Options to $options include:
         *   - headers : name/value map of HTTP headers to use in response to GET/HEAD requests
         *
@@ -1198,6 +1219,8 @@ class FileRepo {
        /**
         * Publish a batch of files
         *
+        * @see FileRepo::publish()
+        *
         * @param array $ntuples (source, dest, archive) triplets or
         *   (source, dest, archive, options) 4-tuples as per publish().
         * @param int $flags Bitfield, may be FileRepo::DELETE_SOURCE to indicate
@@ -1226,7 +1249,7 @@ class FileRepo {
 
                        $options = $ntuple[3] ?? [];
                        // Resolve source to a storage path if virtual
-                       $srcPath = $this->resolveToStoragePath( $srcPath );
+                       $srcPath = $this->resolveToStoragePathIfVirtual( $srcPath );
                        if ( !$this->validateFilename( $dstRel ) ) {
                                throw new MWException( 'Validation error in $dstRel' );
                        }
@@ -1266,27 +1289,17 @@ class FileRepo {
 
                        // Copy (or move) the source file to the destination
                        if ( FileBackend::isStoragePath( $srcPath ) ) {
-                               if ( $flags & self::DELETE_SOURCE ) {
-                                       $operations[] = [
-                                               'op' => 'move',
-                                               'src' => $srcPath,
-                                               'dst' => $dstPath,
-                                               'overwrite' => true, // replace current
-                                               'headers' => $headers
-                                       ];
-                               } else {
-                                       $operations[] = [
-                                               'op' => 'copy',
-                                               'src' => $srcPath,
-                                               'dst' => $dstPath,
-                                               'overwrite' => true, // replace current
-                                               'headers' => $headers
-                                       ];
-                               }
-                       } else { // FS source path
+                               $operations[] = [
+                                       'op' => ( $flags & self::DELETE_SOURCE ) ? 'move' : 'copy',
+                                       'src' => $srcPath,
+                                       'dst' => $dstPath,
+                                       'overwrite' => true, // replace current
+                                       'headers' => $headers
+                               ];
+                       } else {
                                $operations[] = [
                                        'op' => 'store',
-                                       'src' => $src, // prefer FSFile objects
+                                       'src' => $src, // storage path (copy) or local path/FSFile (store)
                                        'dst' => $dstPath,
                                        'overwrite' => true, // replace current
                                        'headers' => $headers
@@ -1327,7 +1340,7 @@ class FileRepo {
         * @return Status
         */
        protected function initDirectory( $dir ) {
-               $path = $this->resolveToStoragePath( $dir );
+               $path = $this->resolveToStoragePathIfVirtual( $dir );
                list( , $container, ) = FileBackend::splitStoragePath( $path );
 
                $params = [ 'dir' => $path ];
@@ -1357,7 +1370,7 @@ class FileRepo {
 
                $status = $this->newGood();
                $status->merge( $this->backend->clean(
-                       [ 'dir' => $this->resolveToStoragePath( $dir ) ] ) );
+                       [ 'dir' => $this->resolveToStoragePathIfVirtual( $dir ) ] ) );
 
                return $status;
        }
@@ -1381,12 +1394,12 @@ class FileRepo {
         * @return array Map of files and existence flags, or false
         */
        public function fileExistsBatch( array $files ) {
-               $paths = array_map( [ $this, 'resolveToStoragePath' ], $files );
+               $paths = array_map( [ $this, 'resolveToStoragePathIfVirtual' ], $files );
                $this->backend->preloadFileStat( [ 'srcs' => $paths ] );
 
                $result = [];
                foreach ( $files as $key => $file ) {
-                       $path = $this->resolveToStoragePath( $file );
+                       $path = $this->resolveToStoragePathIfVirtual( $file );
                        $result[$key] = $this->backend->fileExists( [ 'src' => $path ] );
                }
 
@@ -1517,7 +1530,7 @@ class FileRepo {
         * @return string
         * @throws MWException
         */
-       protected function resolveToStoragePath( $path ) {
+       protected function resolveToStoragePathIfVirtual( $path ) {
                if ( self::isVirtualUrl( $path ) ) {
                        return $this->resolveVirtualUrl( $path );
                }
@@ -1533,7 +1546,7 @@ class FileRepo {
         * @return TempFSFile|null Returns null on failure
         */
        public function getLocalCopy( $virtualUrl ) {
-               $path = $this->resolveToStoragePath( $virtualUrl );
+               $path = $this->resolveToStoragePathIfVirtual( $virtualUrl );
 
                return $this->backend->getLocalCopy( [ 'src' => $path ] );
        }
@@ -1547,7 +1560,7 @@ class FileRepo {
         * @return FSFile|null Returns null on failure.
         */
        public function getLocalReference( $virtualUrl ) {
-               $path = $this->resolveToStoragePath( $virtualUrl );
+               $path = $this->resolveToStoragePathIfVirtual( $virtualUrl );
 
                return $this->backend->getLocalReference( [ 'src' => $path ] );
        }
@@ -1578,7 +1591,7 @@ class FileRepo {
         * @return string|bool False on failure
         */
        public function getFileTimestamp( $virtualUrl ) {
-               $path = $this->resolveToStoragePath( $virtualUrl );
+               $path = $this->resolveToStoragePathIfVirtual( $virtualUrl );
 
                return $this->backend->getFileTimestamp( [ 'src' => $path ] );
        }
@@ -1590,7 +1603,7 @@ class FileRepo {
         * @return int|bool False on failure
         */
        public function getFileSize( $virtualUrl ) {
-               $path = $this->resolveToStoragePath( $virtualUrl );
+               $path = $this->resolveToStoragePathIfVirtual( $virtualUrl );
 
                return $this->backend->getFileSize( [ 'src' => $path ] );
        }
@@ -1602,7 +1615,7 @@ class FileRepo {
         * @return string|bool
         */
        public function getFileSha1( $virtualUrl ) {
-               $path = $this->resolveToStoragePath( $virtualUrl );
+               $path = $this->resolveToStoragePathIfVirtual( $virtualUrl );
 
                return $this->backend->getFileSha1Base36( [ 'src' => $path ] );
        }
@@ -1617,7 +1630,7 @@ class FileRepo {
         * @since 1.27
         */
        public function streamFileWithStatus( $virtualUrl, $headers = [], $optHeaders = [] ) {
-               $path = $this->resolveToStoragePath( $virtualUrl );
+               $path = $this->resolveToStoragePathIfVirtual( $virtualUrl );
                $params = [ 'src' => $path, 'headers' => $headers, 'options' => $optHeaders ];
 
                // T172851: HHVM does not flush the output properly, causing OOM