* Outside callers can assume that all backends will have these functions.
*
* All "storage paths" are of the format "mwstore://<backend>/<container>/<path>".
- * The "<path>" portion is a relative path that uses UNIX file system (FS)
- * notation, though any particular backend may not actually be using a local
- * filesystem. Therefore, the relative paths are only virtual.
+ * The "backend" portion is unique name for MediaWiki to refer to a backend, while
+ * the "container" portion is a top-level directory of the backend. The "path" portion
+ * is a relative path that uses UNIX file system (FS) notation, though any particular
+ * backend may not actually be using a local filesystem. Therefore, the relative paths
+ * are only virtual.
*
* Backend contents are stored under wiki-specific container names by default.
- * For legacy reasons, this has no effect for the FS backend class, and per-wiki
- * segregation must be done by setting the container paths appropriately.
+ * Global (qualified) backends are achieved by configuring the "wiki ID" to a constant.
+ * For legacy reasons, the FSFileBackend class allows manually setting the paths of
+ * containers to ones that do not respect the "wiki ID".
*
+ * In key/value stores, the container is the only hierarchy (the rest is emulated).
* FS-based backends are somewhat more restrictive due to the existence of real
* directory files; a regular file cannot have the same name as a directory. Other
* backends with virtual directories may not have this limitation. Callers should
* $config includes:
* - name : The unique name of this backend.
* This should consist of alphanumberic, '-', and '_' characters.
- * This name should not be changed after use.
- * - wikiId : Prefix to container names that is unique to this wiki.
+ * This name should not be changed after use (e.g. with journaling).
+ * Note that the name is *not* used in actual container names.
+ * - wikiId : Prefix to container names that is unique to this backend.
* If not provided, this defaults to the current wiki ID.
* It should only consist of alphanumberic, '-', and '_' characters.
+ * This ID is what avoids collisions if multiple logical backends
+ * use the same storage system, so this should be set carefully.
* - lockManager : Registered name of a file lock manager to use.
* - fileJournal : File journal configuration; see FileJournal::factory().
* Journals simply log changes to files stored in the backend.
* $opts is an associative of boolean flags, including:
* - force : Operation precondition errors no longer trigger an abort.
* Any remaining operations are still attempted. Unexpected
- * failures may still cause remaning operations to be aborted.
+ * failures may still cause remaining operations to be aborted.
* - nonLocking : No locks are acquired for the operations.
* This can increase performance for non-critical writes.
* This has no effect unless the 'force' flag is set.
* - a) unexpected operation errors occurred (network partitions, disk full...)
* - b) significant operation errors occurred and 'force' was not set
*
- * @param $ops Array List of operations to execute in order
- * @param $opts Array Batch operation options
+ * @param array $ops List of operations to execute in order
+ * @param array $opts Batch operation options
* @return Status
*/
final public function doOperations( array $ops, array $opts = array() ) {
if ( empty( $opts['force'] ) ) { // sanity
unset( $opts['nonLocking'] );
}
+ $scope = $this->getScopedPHPBehaviorForOps(); // try to ignore client aborts
return $this->doOperationsInternal( $ops, $opts );
}
*
* @see FileBackend::doOperations()
*
- * @param $op Array Operation
- * @param $opts Array Operation options
+ * @param array $op Operation
+ * @param array $opts Operation options
* @return Status
*/
final public function doOperation( array $op, array $opts = array() ) {
*
* @see FileBackend::doOperation()
*
- * @param $params Array Operation parameters
- * @param $opts Array Operation options
+ * @param array $params Operation parameters
+ * @param array $opts Operation options
* @return Status
*/
final public function create( array $params, array $opts = array() ) {
*
* @see FileBackend::doOperation()
*
- * @param $params Array Operation parameters
- * @param $opts Array Operation options
+ * @param array $params Operation parameters
+ * @param array $opts Operation options
* @return Status
*/
final public function store( array $params, array $opts = array() ) {
*
* @see FileBackend::doOperation()
*
- * @param $params Array Operation parameters
- * @param $opts Array Operation options
+ * @param array $params Operation parameters
+ * @param array $opts Operation options
* @return Status
*/
final public function copy( array $params, array $opts = array() ) {
*
* @see FileBackend::doOperation()
*
- * @param $params Array Operation parameters
- * @param $opts Array Operation options
+ * @param array $params Operation parameters
+ * @param array $opts Operation options
* @return Status
*/
final public function move( array $params, array $opts = array() ) {
*
* @see FileBackend::doOperation()
*
- * @param $params Array Operation parameters
- * @param $opts Array Operation options
+ * @param array $params Operation parameters
+ * @param array $opts Operation options
* @return Status
*/
final public function delete( array $params, array $opts = array() ) {
*
* @see FileBackend::doOperation()
*
- * @param $params Array Operation parameters
- * @param $opts Array Operation options
+ * @param array $params Operation parameters
+ * @param array $opts Operation options
* @return Status
* @since 1.21
*/
* will reflect each operation attempted for the given files. The status will be
* considered "OK" as long as no fatal errors occurred.
*
- * @param $ops Array Set of operations to execute
- * @param $opts Array Batch operation options
+ * @param array $ops Set of operations to execute
+ * @param array $opts Batch operation options
* @return Status
* @since 1.20
*/
foreach ( $ops as &$op ) {
$op['overwrite'] = true; // avoids RTTs in key/value stores
}
+ $scope = $this->getScopedPHPBehaviorForOps(); // try to ignore client aborts
return $this->doQuickOperationsInternal( $ops );
}
*
* @see FileBackend::doQuickOperations()
*
- * @param $op Array Operation
+ * @param array $op Operation
* @return Status
* @since 1.20
*/
*
* @see FileBackend::doQuickOperation()
*
- * @param $params Array Operation parameters
+ * @param array $params Operation parameters
* @return Status
* @since 1.20
*/
*
* @see FileBackend::doQuickOperation()
*
- * @param $params Array Operation parameters
+ * @param array $params Operation parameters
* @return Status
* @since 1.20
*/
*
* @see FileBackend::doQuickOperation()
*
- * @param $params Array Operation parameters
+ * @param array $params Operation parameters
* @return Status
* @since 1.20
*/
*
* @see FileBackend::doQuickOperation()
*
- * @param $params Array Operation parameters
+ * @param array $params Operation parameters
* @return Status
* @since 1.20
*/
*
* @see FileBackend::doQuickOperation()
*
- * @param $params Array Operation parameters
+ * @param array $params Operation parameters
* @return Status
* @since 1.20
*/
*
* @see FileBackend::doQuickOperation()
*
- * @param $params Array Operation parameters
+ * @param array $params Operation parameters
* @return Status
* @since 1.21
*/
* otherwise safe from modification from other processes. Normally,
* the file will be a new temp file, which should be adequate.
*
- * @param $params Array Operation parameters
+ * @param array $params Operation parameters
* $params include:
* - srcs : ordered source storage paths (e.g. chunk1, chunk2, ...)
* - dst : file system path to 0-byte temp file
if ( empty( $params['bypassReadOnly'] ) && $this->isReadOnly() ) {
return Status::newFatal( 'backend-fail-readonly', $this->name, $this->readOnly );
}
+ $scope = $this->getScopedPHPBehaviorForOps(); // try to ignore client aborts
return $this->doPrepare( $params );
}
if ( empty( $params['bypassReadOnly'] ) && $this->isReadOnly() ) {
return Status::newFatal( 'backend-fail-readonly', $this->name, $this->readOnly );
}
+ $scope = $this->getScopedPHPBehaviorForOps(); // try to ignore client aborts
return $this->doSecure( $params );
}
if ( empty( $params['bypassReadOnly'] ) && $this->isReadOnly() ) {
return Status::newFatal( 'backend-fail-readonly', $this->name, $this->readOnly );
}
+ $scope = $this->getScopedPHPBehaviorForOps(); // try to ignore client aborts
return $this->doPublish( $params );
}
if ( empty( $params['bypassReadOnly'] ) && $this->isReadOnly() ) {
return Status::newFatal( 'backend-fail-readonly', $this->name, $this->readOnly );
}
+ $scope = $this->getScopedPHPBehaviorForOps(); // try to ignore client aborts
return $this->doClean( $params );
}
*/
abstract protected function doClean( array $params );
+ /**
+ * Enter file operation scope.
+ * This just makes PHP ignore user aborts/disconnects until the return
+ * value leaves scope. This returns null and does nothing in CLI mode.
+ *
+ * @return ScopedCallback|null
+ */
+ final protected function getScopedPHPBehaviorForOps() {
+ if ( php_sapi_name() != 'cli' ) { // http://bugs.php.net/bug.php?id=47540
+ $old = ignore_user_abort( true ); // avoid half-finished operations
+ return new ScopedCallback( function() use ( $old ) { ignore_user_abort( $old ); } );
+ }
+ return null;
+ }
+
/**
* Check if a file exists at a storage path in the backend.
* This returns false if only a directory exists at the path.
* @param $params Array
* $params include:
* - src : source storage path
+ * - ttl : lifetime (seconds) if pre-authenticated; default is 1 day
* @return string|null
* @since 1.21
*/
* Preload persistent file stat and property cache into in-process cache.
* This should be used when stat calls will be made on a known list of a many files.
*
- * @param $paths Array Storage paths
+ * @param array $paths Storage paths
* @return void
*/
public function preloadCache( array $paths ) {}
* Invalidate any in-process file stat and property cache.
* If $paths is given, then only the cache for those files will be cleared.
*
- * @param $paths Array Storage paths (optional)
+ * @param array $paths Storage paths (optional)
* @return void
*/
public function clearCache( array $paths = null ) {}
*
* Callers should consider using getScopedFileLocks() instead.
*
- * @param $paths Array Storage paths
+ * @param array $paths Storage paths
* @param $type integer LockManager::LOCK_* constant
* @return Status
*/
/**
* Unlock the files at the given storage paths in the backend.
*
- * @param $paths Array Storage paths
+ * @param array $paths Storage paths
* @param $type integer LockManager::LOCK_* constant
* @return Status
*/
* Once the return value goes out scope, the locks will be released and
* the status updated. Unlock fatals will not change the status "OK" value.
*
- * @param $paths Array Storage paths
+ * @param array $paths Storage paths
* @param $type integer LockManager::LOCK_* constant
* @param $status Status Status to update on lock/unlock
* @return ScopedLock|null Returns null on failure
*
* @see FileBackend::doOperations()
*
- * @param $ops Array List of file operations to FileBackend::doOperations()
+ * @param array $ops List of file operations to FileBackend::doOperations()
* @param $status Status Status to update on lock/unlock
* @return Array List of ScopedFileLocks or null values
* @since 1.20
/**
* Get the storage path for the given container for this backend
*
- * @param $container string Container name
+ * @param string $container Container name
* @return string Storage path
* @since 1.21
*/
*/
final public static function parentStoragePath( $storagePath ) {
$storagePath = dirname( $storagePath );
- list( $b, $cont, $rel ) = self::splitStoragePath( $storagePath );
+ list( , , $rel ) = self::splitStoragePath( $storagePath );
return ( $rel === null ) ? null : $storagePath;
}
/**
* Build a Content-Disposition header value per RFC 6266.
*
- * @param $type string One of (attachment, inline)
- * @param $filename string Suggested file name (should not contain slashes)
+ * @param string $type One of (attachment, inline)
+ * @param string $filename Suggested file name (should not contain slashes)
* @throws MWException
* @return string
* @since 1.20
*
* This uses the same traversal protection as Title::secureAndSplit().
*
- * @param $path string Storage path relative to a container
+ * @param string $path Storage path relative to a container
* @return string|null
*/
final protected static function normalizeContainerPath( $path ) {