protected $expensiveCache;
/** @var array Map of container names to sharding config */
- protected $shardViaHashLevels = array();
+ protected $shardViaHashLevels = [];
/** @var callable Method to get the MIME type of files */
protected $mimeCallback;
/**
* @see FileBackend::__construct()
* Additional $config params include:
- * - wanCache : WANOBjectCache object to use for persistent caching.
+ * - wanCache : WANObjectCache object to use for persistent caching.
* - mimeCallback : Callback that takes (storage path, content, file system path) and
* returns the MIME type of the file or 'unknown/unknown'. The file
* system path parameter should be used if the content one is null.
parent::__construct( $config );
$this->mimeCallback = isset( $config['mimeCallback'] )
? $config['mimeCallback']
- : function ( $storagePath, $content, $fsPath ) {
- // @todo handle the case of extension-less files using the contents
- return StreamFile::contentTypeFromPath( $storagePath ) ?: 'unknown/unknown';
- };
+ : null;
$this->memCache = WANObjectCache::newEmpty(); // disabled by default
$this->cheapCache = new ProcessCacheLRU( self::CACHE_CHEAP_SIZE );
$this->expensiveCache = new ProcessCacheLRU( self::CACHE_EXPENSIVE_SIZE );
$params['dst'], $this->maxFileSizeInternal() );
} else {
$status = $this->doCreateInternal( $params );
- $this->clearCache( array( $params['dst'] ) );
+ $this->clearCache( [ $params['dst'] ] );
if ( !isset( $params['dstExists'] ) || $params['dstExists'] ) {
$this->deleteFileCache( $params['dst'] ); // persistent cache
}
$params['dst'], $this->maxFileSizeInternal() );
} else {
$status = $this->doStoreInternal( $params );
- $this->clearCache( array( $params['dst'] ) );
+ $this->clearCache( [ $params['dst'] ] );
if ( !isset( $params['dstExists'] ) || $params['dstExists'] ) {
$this->deleteFileCache( $params['dst'] ); // persistent cache
}
final public function copyInternal( array $params ) {
$ps = Profiler::instance()->scopedProfileIn( __METHOD__ . "-{$this->name}" );
$status = $this->doCopyInternal( $params );
- $this->clearCache( array( $params['dst'] ) );
+ $this->clearCache( [ $params['dst'] ] );
if ( !isset( $params['dstExists'] ) || $params['dstExists'] ) {
$this->deleteFileCache( $params['dst'] ); // persistent cache
}
final public function deleteInternal( array $params ) {
$ps = Profiler::instance()->scopedProfileIn( __METHOD__ . "-{$this->name}" );
$status = $this->doDeleteInternal( $params );
- $this->clearCache( array( $params['src'] ) );
+ $this->clearCache( [ $params['src'] ] );
$this->deleteFileCache( $params['src'] ); // persistent cache
return $status;
}
final public function moveInternal( array $params ) {
$ps = Profiler::instance()->scopedProfileIn( __METHOD__ . "-{$this->name}" );
$status = $this->doMoveInternal( $params );
- $this->clearCache( array( $params['src'], $params['dst'] ) );
+ $this->clearCache( [ $params['src'], $params['dst'] ] );
$this->deleteFileCache( $params['src'] ); // persistent cache
if ( !isset( $params['dstExists'] ) || $params['dstExists'] ) {
$this->deleteFileCache( $params['dst'] ); // persistent cache
$status = $this->copyInternal( $params );
if ( $nsrc !== $ndst && $status->isOK() ) {
// Delete source (only fails due to races or network problems)
- $status->merge( $this->deleteInternal( array( 'src' => $params['src'] ) ) );
+ $status->merge( $this->deleteInternal( [ 'src' => $params['src'] ] ) );
$status->setResult( true, $status->value ); // ignore delete() errors
}
$ps = Profiler::instance()->scopedProfileIn( __METHOD__ . "-{$this->name}" );
if ( count( $params['headers'] ) ) {
$status = $this->doDescribeInternal( $params );
- $this->clearCache( array( $params['src'] ) );
+ $this->clearCache( [ $params['src'] ] );
$this->deleteFileCache( $params['src'] ); // persistent cache
} else {
$status = Status::newGood(); // nothing to do
$fsFiles = $this->getLocalReferenceMulti( $params );
foreach ( $fsFiles as $path => &$fsFile ) {
if ( !$fsFile ) { // chunk failed to download?
- $fsFile = $this->getLocalReference( array( 'src' => $path ) );
+ $fsFile = $this->getLocalReference( [ 'src' => $path ] );
if ( !$fsFile ) { // retry failed?
$status->fatal( 'backend-fail-read', $path );
// Recursive: first delete all empty subdirs recursively
if ( !empty( $params['recursive'] ) && !$this->directoriesAreVirtual() ) {
- $subDirsRel = $this->getTopDirectoryList( array( 'dir' => $params['dir'] ) );
+ $subDirsRel = $this->getTopDirectoryList( [ 'dir' => $params['dir'] ] );
if ( $subDirsRel !== null ) { // no errors
foreach ( $subDirsRel as $subDirRel ) {
$subDir = $params['dir'] . "/{$subDirRel}"; // full path
- $status->merge( $this->doClean( array( 'dir' => $subDir ) + $params ) );
+ $status->merge( $this->doClean( [ 'dir' => $subDir ] + $params ) );
}
unset( $subDirsRel ); // free directory for rmdir() on Windows (for FS backends)
}
}
// Attempt to lock this directory...
- $filesLockEx = array( $params['dir'] );
+ $filesLockEx = [ $params['dir'] ];
$scopedLockE = $this->getScopedFileLocks( $filesLockEx, LockManager::LOCK_EX, $status );
if ( !$status->isOK() ) {
return $status; // abort
$ps = Profiler::instance()->scopedProfileIn( __METHOD__ . "-{$this->name}" );
$latest = !empty( $params['latest'] ); // use latest data?
if ( !$latest && !$this->cheapCache->has( $path, 'stat', self::CACHE_TTL ) ) {
- $this->primeFileCache( array( $path ) ); // check persistent cache
+ $this->primeFileCache( [ $path ] ); // check persistent cache
}
if ( $this->cheapCache->has( $path, 'stat', self::CACHE_TTL ) ) {
$stat = $this->cheapCache->get( $path, 'stat' );
if ( !$latest || $stat['latest'] ) {
return $stat;
}
- } elseif ( in_array( $stat, array( 'NOT_EXIST', 'NOT_EXIST_LATEST' ) ) ) {
+ } elseif ( in_array( $stat, [ 'NOT_EXIST', 'NOT_EXIST_LATEST' ] ) ) {
if ( !$latest || $stat === 'NOT_EXIST_LATEST' ) {
return false;
}
$this->setFileCache( $path, $stat ); // update persistent cache
if ( isset( $stat['sha1'] ) ) { // some backends store SHA-1 as metadata
$this->cheapCache->set( $path, 'sha1',
- array( 'hash' => $stat['sha1'], 'latest' => $latest ) );
+ [ 'hash' => $stat['sha1'], 'latest' => $latest ] );
}
if ( isset( $stat['xattr'] ) ) { // some backends store headers/metadata
$stat['xattr'] = self::normalizeXAttributes( $stat['xattr'] );
$this->cheapCache->set( $path, 'xattr',
- array( 'map' => $stat['xattr'], 'latest' => $latest ) );
+ [ 'map' => $stat['xattr'], 'latest' => $latest ] );
}
} elseif ( $stat === false ) { // file does not exist
$this->cheapCache->set( $path, 'stat', $latest ? 'NOT_EXIST_LATEST' : 'NOT_EXIST' );
- $this->cheapCache->set( $path, 'xattr', array( 'map' => false, 'latest' => $latest ) );
- $this->cheapCache->set( $path, 'sha1', array( 'hash' => false, 'latest' => $latest ) );
+ $this->cheapCache->set( $path, 'xattr', [ 'map' => false, 'latest' => $latest ] );
+ $this->cheapCache->set( $path, 'sha1', [ 'hash' => false, 'latest' => $latest ] );
wfDebug( __METHOD__ . ": File $path does not exist.\n" );
} else { // an error occurred
wfDebug( __METHOD__ . ": Could not stat file $path.\n" );
* @return array
*/
protected function doGetFileContentsMulti( array $params ) {
- $contents = array();
+ $contents = [];
foreach ( $this->doGetLocalReferenceMulti( $params ) as $path => $fsFile ) {
MediaWiki\suppressWarnings();
$contents[$path] = $fsFile ? file_get_contents( $fsFile->getPath() ) : false;
}
$fields = $this->doGetFileXAttributes( $params );
$fields = is_array( $fields ) ? self::normalizeXAttributes( $fields ) : false;
- $this->cheapCache->set( $path, 'xattr', array( 'map' => $fields, 'latest' => $latest ) );
+ $this->cheapCache->set( $path, 'xattr', [ 'map' => $fields, 'latest' => $latest ] );
return $fields;
}
* @return bool|string
*/
protected function doGetFileXAttributes( array $params ) {
- return array( 'headers' => array(), 'metadata' => array() ); // not supported
+ return [ 'headers' => [], 'metadata' => [] ]; // not supported
}
final public function getFileSha1Base36( array $params ) {
}
}
$hash = $this->doGetFileSha1Base36( $params );
- $this->cheapCache->set( $path, 'sha1', array( 'hash' => $hash, 'latest' => $latest ) );
+ $this->cheapCache->set( $path, 'sha1', [ 'hash' => $hash, 'latest' => $latest ] );
return $hash;
}
$params = $this->setConcurrencyFlags( $params );
- $fsFiles = array(); // (path => FSFile)
+ $fsFiles = []; // (path => FSFile)
$latest = !empty( $params['latest'] ); // use latest data?
// Reuse any files already in process cache...
foreach ( $params['srcs'] as $src ) {
$fsFiles[$path] = $fsFile;
if ( $fsFile ) { // update the process cache...
$this->expensiveCache->set( $path, 'localRef',
- array( 'object' => $fsFile, 'latest' => $latest ) );
+ [ 'object' => $fsFile, 'latest' => $latest ] );
}
}
}
// Set output buffer and HTTP headers for stream
- $extraHeaders = isset( $params['headers'] ) ? $params['headers'] : array();
+ $extraHeaders = isset( $params['headers'] ) ? $params['headers'] : [];
$res = StreamFile::prepareForStream( $params['src'], $info, $extraHeaders );
if ( $res == StreamFile::NOT_MODIFIED ) {
// do nothing; client cache is up to date
// Per bug 41113, nasty things can happen if bad cache entries get
// stuck in cache. It's also possible that this error can come up
// with simple race conditions. Clear out the stat cache to be safe.
- $this->clearCache( array( $params['src'] ) );
+ $this->clearCache( [ $params['src'] ] );
$this->deleteFileCache( $params['src'] );
trigger_error( "Bad stat cache or race condition for file {$params['src']}." );
}
* @throws FileBackendError
*/
final public function getOperationsInternal( array $ops ) {
- $supportedOps = array(
+ $supportedOps = [
'store' => 'StoreFileOp',
'copy' => 'CopyFileOp',
'move' => 'MoveFileOp',
'create' => 'CreateFileOp',
'describe' => 'DescribeFileOp',
'null' => 'NullFileOp'
- );
+ ];
- $performOps = array(); // array of FileOp objects
+ $performOps = []; // array of FileOp objects
// Build up ordered array of FileOps...
foreach ( $ops as $operation ) {
$opName = $operation['op'];
*/
final public function getPathsToLockForOpsInternal( array $performOps ) {
// Build up a list of files to lock...
- $paths = array( 'sh' => array(), 'ex' => array() );
+ $paths = [ 'sh' => [], 'ex' => [] ];
foreach ( $performOps as $fileOp ) {
$paths['sh'] = array_merge( $paths['sh'], $fileOp->storagePathsRead() );
$paths['ex'] = array_merge( $paths['ex'], $fileOp->storagePathsChanged() );
// Get a shared lock on the parent directory of each path changed
$paths['sh'] = array_merge( $paths['sh'], array_map( 'dirname', $paths['ex'] ) );
- return array(
+ return [
LockManager::LOCK_UW => $paths['sh'],
LockManager::LOCK_EX => $paths['ex']
- );
+ ];
}
public function getScopedLocksForOps( array $ops, Status $status ) {
$status = Status::newGood();
// Fix up custom header name/value pairs...
- $ops = array_map( array( $this, 'sanitizeOpHeaders' ), $ops );
+ $ops = array_map( [ $this, 'sanitizeOpHeaders' ], $ops );
// Build up a list of FileOps...
$performOps = $this->getOperationsInternal( $ops );
}
// Build the list of paths involved
- $paths = array();
+ $paths = [];
foreach ( $performOps as $op ) {
$paths = array_merge( $paths, $op->storagePathsRead() );
$paths = array_merge( $paths, $op->storagePathsChanged() );
// Load from the persistent container caches
$this->primeContainerCache( $paths );
// Get the latest stat info for all the files (having locked them)
- $ok = $this->preloadFileStat( array( 'srcs' => $paths, 'latest' => true ) );
+ $ok = $this->preloadFileStat( [ 'srcs' => $paths, 'latest' => true ] );
if ( $ok ) {
// Actually attempt the operation batch...
$status = Status::newGood();
// Fix up custom header name/value pairs...
- $ops = array_map( array( $this, 'sanitizeOpHeaders' ), $ops );
+ $ops = array_map( [ $this, 'sanitizeOpHeaders' ], $ops );
// Clear any file cache entries
$this->clearCache();
- $supportedOps = array( 'create', 'store', 'copy', 'move', 'delete', 'describe', 'null' );
+ $supportedOps = [ 'create', 'store', 'copy', 'move', 'delete', 'describe', 'null' ];
// Parallel ops may be disabled in config due to dependencies (e.g. needing popen())
$async = ( $this->parallelize === 'implicit' && count( $ops ) > 1 );
$maxConcurrency = $this->concurrency; // throttle
- $statuses = array(); // array of (index => Status)
- $fileOpHandles = array(); // list of (index => handle) arrays
- $curFileOpHandles = array(); // current handle batch
+ $statuses = []; // array of (index => Status)
+ $fileOpHandles = []; // list of (index => handle) arrays
+ $curFileOpHandles = []; // current handle batch
// Perform the sync-only ops and build up op handles for the async ops...
foreach ( $ops as $index => $params ) {
if ( !in_array( $params['op'], $supportedOps ) ) {
throw new FileBackendError( "Operation '{$params['op']}' is not supported." );
}
$method = $params['op'] . 'Internal'; // e.g. "storeInternal"
- $subStatus = $this->$method( array( 'async' => $async ) + $params );
+ $subStatus = $this->$method( [ 'async' => $async ] + $params );
if ( $subStatus->value instanceof FileBackendStoreOpHandle ) { // async
if ( count( $curFileOpHandles ) >= $maxConcurrency ) {
$fileOpHandles[] = $curFileOpHandles; // push this batch
- $curFileOpHandles = array();
+ $curFileOpHandles = [];
}
$curFileOpHandles[$index] = $subStatus->value; // keep index
} else { // error or completed
throw new FileBackendError( "This backend supports no asynchronous operations." );
}
- return array();
+ return [];
}
/**
* @return array
*/
protected function sanitizeOpHeaders( array $op ) {
- static $longs = array( 'content-disposition' );
+ static $longs = [ 'content-disposition' ];
if ( isset( $op['headers'] ) ) { // op sets HTTP headers
- $newHeaders = array();
+ $newHeaders = [];
foreach ( $op['headers'] as $name => $value ) {
$name = strtolower( $name );
$maxHVLen = in_array( $name, $longs ) ? INF : 255;
}
final public function preloadCache( array $paths ) {
- $fullConts = array(); // full container names
+ $fullConts = []; // full container names
foreach ( $paths as $path ) {
list( $fullCont, , ) = $this->resolveStoragePath( $path );
$fullConts[] = $fullCont;
$this->setFileCache( $path, $stat ); // update persistent cache
if ( isset( $stat['sha1'] ) ) { // some backends store SHA-1 as metadata
$this->cheapCache->set( $path, 'sha1',
- array( 'hash' => $stat['sha1'], 'latest' => $latest ) );
+ [ 'hash' => $stat['sha1'], 'latest' => $latest ] );
}
if ( isset( $stat['xattr'] ) ) { // some backends store headers/metadata
$stat['xattr'] = self::normalizeXAttributes( $stat['xattr'] );
$this->cheapCache->set( $path, 'xattr',
- array( 'map' => $stat['xattr'], 'latest' => $latest ) );
+ [ 'map' => $stat['xattr'], 'latest' => $latest ] );
}
} elseif ( $stat === false ) { // file does not exist
$this->cheapCache->set( $path, 'stat',
$latest ? 'NOT_EXIST_LATEST' : 'NOT_EXIST' );
$this->cheapCache->set( $path, 'xattr',
- array( 'map' => false, 'latest' => $latest ) );
+ [ 'map' => false, 'latest' => $latest ] );
$this->cheapCache->set( $path, 'sha1',
- array( 'hash' => false, 'latest' => $latest ) );
+ [ 'hash' => false, 'latest' => $latest ] );
wfDebug( __METHOD__ . ": File $path does not exist.\n" );
} else { // an error occurred
$success = false;
// Validate and sanitize the container name (backend-specific)
$container = $this->resolveContainerName( "{$container}{$cShard}" );
if ( $container !== null ) {
- return array( $container, $relPath, $cShard );
+ return [ $container, $relPath, $cShard ];
}
}
}
}
}
- return array( null, null, null );
+ return [ null, null, null ];
}
/**
final protected function resolveStoragePathReal( $storagePath ) {
list( $container, $relPath, $cShard ) = $this->resolveStoragePath( $storagePath );
if ( $cShard !== null && substr( $relPath, -1 ) !== '/' ) {
- return array( $container, $relPath );
+ return [ $container, $relPath ];
}
- return array( null, null );
+ return [ null, null ];
}
/**
// Allow certain directories to be above the hash dirs so as
// to work with FileRepo (e.g. "archive/a/ab" or "temp/a/ab").
// They must be 2+ chars to avoid any hash directory ambiguity.
- $m = array();
+ $m = [];
if ( preg_match( "!^(?:[^/]{2,}/)*$hashDirRegex(?:/|$)!", $relPath, $m ) ) {
return '.' . implode( '', array_slice( $m, 1 ) );
}
if ( $hashLevels == 1 || $hashLevels == 2 ) {
$hashBase = (int)$config['base'];
if ( $hashBase == 16 || $hashBase == 36 ) {
- return array( $hashLevels, $hashBase, $config['repeat'] );
+ return [ $hashLevels, $hashBase, $config['repeat'] ];
}
}
}
- return array( 0, 0, false ); // no sharding
+ return [ 0, 0, false ]; // no sharding
}
/**
* @return array
*/
final protected function getContainerSuffixes( $container ) {
- $shards = array();
+ $shards = [];
list( $digits, $base ) = $this->getContainerHashLevels( $container );
if ( $digits > 0 ) {
$numShards = pow( $base, $digits );
for ( $index = 0; $index < $numShards; $index++ ) {
- $shards[] = '.' . wfBaseConvert( $index, 10, $base, $digits );
+ $shards[] = '.' . Wikimedia\base_convert( $index, 10, $base, $digits );
}
}
final protected function primeContainerCache( array $items ) {
$ps = Profiler::instance()->scopedProfileIn( __METHOD__ . "-{$this->name}" );
- $paths = array(); // list of storage paths
- $contNames = array(); // (cache key => resolved container name)
+ $paths = []; // list of storage paths
+ $contNames = []; // (cache key => resolved container name)
// Get all the paths/containers from the items...
foreach ( $items as $item ) {
if ( self::isStoragePath( $item ) ) {
}
}
- $contInfo = array(); // (resolved container name => cache value)
+ $contInfo = []; // (resolved container name => cache value)
// Get all cache entries for these container cache keys...
$values = $this->memCache->getMulti( array_keys( $contNames ) );
foreach ( $values as $cacheKey => $val ) {
final protected function primeFileCache( array $items ) {
$ps = Profiler::instance()->scopedProfileIn( __METHOD__ . "-{$this->name}" );
- $paths = array(); // list of storage paths
- $pathNames = array(); // (cache key => storage path)
+ $paths = []; // list of storage paths
+ $pathNames = []; // (cache key => storage path)
// Get all the paths/containers from the items...
foreach ( $items as $item ) {
if ( self::isStoragePath( $item ) ) {
$this->cheapCache->set( $path, 'stat', $val );
if ( isset( $val['sha1'] ) ) { // some backends store SHA-1 as metadata
$this->cheapCache->set( $path, 'sha1',
- array( 'hash' => $val['sha1'], 'latest' => false ) );
+ [ 'hash' => $val['sha1'], 'latest' => false ] );
}
if ( isset( $val['xattr'] ) ) { // some backends store headers/metadata
$val['xattr'] = self::normalizeXAttributes( $val['xattr'] );
$this->cheapCache->set( $path, 'xattr',
- array( 'map' => $val['xattr'], 'latest' => false ) );
+ [ 'map' => $val['xattr'], 'latest' => false ] );
}
}
}
* @since 1.22
*/
final protected static function normalizeXAttributes( array $xattr ) {
- $newXAttr = array( 'headers' => array(), 'metadata' => array() );
+ $newXAttr = [ 'headers' => [], 'metadata' => [] ];
foreach ( $xattr['headers'] as $name => $value ) {
$newXAttr['headers'][strtolower( $name )] = $value;
* @return string MIME type
*/
protected function getContentType( $storagePath, $content, $fsPath ) {
- return call_user_func_array( $this->mimeCallback, func_get_args() );
+ if ( $this->mimeCallback ) {
+ return call_user_func_array( $this->mimeCallback, func_get_args() );
+ }
+
+ $mime = null;
+ if ( $fsPath !== null && function_exists( 'finfo_file' ) ) {
+ $finfo = finfo_open( FILEINFO_MIME_TYPE );
+ $mime = finfo_file( $finfo, $fsPath );
+ finfo_close( $finfo );
+ }
+
+ return is_string( $mime ) ? $mime : 'unknown/unknown';
}
}
*/
abstract class FileBackendStoreOpHandle {
/** @var array */
- public $params = array(); // params to caller functions
+ public $params = []; // params to caller functions
/** @var FileBackendStore */
public $backend;
/** @var array */
- public $resourcesToClose = array();
+ public $resourcesToClose = [];
public $call; // string; name that identifies the function called
protected $directory;
/** @var array */
- protected $multiShardPaths = array(); // (rel path => 1)
+ protected $multiShardPaths = []; // (rel path => 1)
/**
* @param FileBackendStore $backend
public function rewind() {
parent::rewind();
- $this->multiShardPaths = array();
+ $this->multiShardPaths = [];
}
/**
$list = $this->backend->getDirectoryListInternal(
$container, $this->directory, $this->params );
if ( $list === null ) {
- return new ArrayIterator( array() );
+ return new ArrayIterator( [] );
} else {
return is_array( $list ) ? new ArrayIterator( $list ) : $list;
}
$list = $this->backend->getFileListInternal(
$container, $this->directory, $this->params );
if ( $list === null ) {
- return new ArrayIterator( array() );
+ return new ArrayIterator( [] );
} else {
return is_array( $list ) ? new ArrayIterator( $list ) : $list;
}