MediaWiki  master
FileBackend.php
Go to the documentation of this file.
1 <?php
35 
96 abstract class FileBackend implements LoggerAwareInterface {
98  protected $name;
99 
101  protected $domainId;
102 
104  protected $readOnly;
105 
107  protected $parallelize;
108 
110  protected $concurrency;
111 
113  protected $tmpFileFactory;
114 
116  protected $lockManager;
118  protected $fileJournal;
120  protected $logger;
122  protected $profiler;
123 
125  protected $obResetFunc;
127  protected $streamMimeFunc;
129  protected $statusWrapper;
130 
132  const ATTR_HEADERS = 1; // files can be tagged with standard HTTP headers
133  const ATTR_METADATA = 2; // files can be stored with metadata key/values
134  const ATTR_UNICODE_PATHS = 4; // files can have Unicode paths (not just ASCII)
135 
137  const STAT_ABSENT = false;
138 
140  const STAT_ERROR = null;
142  const LIST_ERROR = null;
144  const TEMPURL_ERROR = null;
146  const EXISTENCE_ERROR = null;
147 
149  const TIMESTAMP_FAIL = false;
151  const CONTENT_FAIL = false;
153  const XATTRS_FAIL = false;
155  const SIZE_FAIL = false;
157  const SHA1_FAIL = false;
158 
193  public function __construct( array $config ) {
194  $this->name = $config['name'];
195  $this->domainId = $config['domainId'] // e.g. "my_wiki-en_"
196  ?? $config['wikiId']; // b/c alias
197  if ( !preg_match( '!^[a-zA-Z0-9-_]{1,255}$!', $this->name ) ) {
198  throw new InvalidArgumentException( "Backend name '{$this->name}' is invalid." );
199  } elseif ( !is_string( $this->domainId ) ) {
200  throw new InvalidArgumentException(
201  "Backend domain ID not provided for '{$this->name}'." );
202  }
203  $this->lockManager = $config['lockManager'] ?? new NullLockManager( [] );
204  $this->fileJournal = $config['fileJournal'] ?? new NullFileJournal;
205  $this->readOnly = isset( $config['readOnly'] )
206  ? (string)$config['readOnly']
207  : '';
208  $this->parallelize = isset( $config['parallelize'] )
209  ? (string)$config['parallelize']
210  : 'off';
211  $this->concurrency = isset( $config['concurrency'] )
212  ? (int)$config['concurrency']
213  : 50;
214  $this->obResetFunc = $config['obResetFunc'] ?? [ $this, 'resetOutputBuffer' ];
215  $this->streamMimeFunc = $config['streamMimeFunc'] ?? null;
216 
217  $this->profiler = $config['profiler'] ?? null;
218  if ( !is_callable( $this->profiler ) ) {
219  $this->profiler = null;
220  }
221  $this->logger = $config['logger'] ?? new NullLogger();
222  $this->statusWrapper = $config['statusWrapper'] ?? null;
223  // tmpDirectory gets precedence for backward compatibility
224  if ( isset( $config['tmpDirectory'] ) ) {
225  $this->tmpFileFactory = new TempFSFileFactory( $config['tmpDirectory'] );
226  } else {
227  $this->tmpFileFactory = $config['tmpFileFactory'] ?? new TempFSFileFactory();
228  }
229  }
230 
231  public function setLogger( LoggerInterface $logger ) {
232  $this->logger = $logger;
233  }
234 
243  final public function getName() {
244  return $this->name;
245  }
246 
253  final public function getDomainId() {
254  return $this->domainId;
255  }
256 
264  final public function getWikiId() {
265  return $this->getDomainId();
266  }
267 
273  final public function isReadOnly() {
274  return ( $this->readOnly != '' );
275  }
276 
282  final public function getReadOnlyReason() {
283  return ( $this->readOnly != '' ) ? $this->readOnly : false;
284  }
285 
292  public function getFeatures() {
293  return self::ATTR_UNICODE_PATHS;
294  }
295 
303  final public function hasFeatures( $bitfield ) {
304  return ( $this->getFeatures() & $bitfield ) === $bitfield;
305  }
306 
459  final public function doOperations( array $ops, array $opts = [] ) {
460  if ( empty( $opts['bypassReadOnly'] ) && $this->isReadOnly() ) {
461  return $this->newStatus( 'backend-fail-readonly', $this->name, $this->readOnly );
462  }
463  if ( $ops === [] ) {
464  return $this->newStatus(); // nothing to do
465  }
466 
467  $ops = $this->resolveFSFileObjects( $ops );
468  if ( empty( $opts['force'] ) ) { // sanity
469  unset( $opts['nonLocking'] );
470  }
471 
473  $scope = ScopedCallback::newScopedIgnoreUserAbort(); // try to ignore client aborts
474 
475  return $this->doOperationsInternal( $ops, $opts );
476  }
477 
484  abstract protected function doOperationsInternal( array $ops, array $opts );
485 
497  final public function doOperation( array $op, array $opts = [] ) {
498  return $this->doOperations( [ $op ], $opts );
499  }
500 
511  final public function create( array $params, array $opts = [] ) {
512  return $this->doOperation( [ 'op' => 'create' ] + $params, $opts );
513  }
514 
525  final public function store( array $params, array $opts = [] ) {
526  return $this->doOperation( [ 'op' => 'store' ] + $params, $opts );
527  }
528 
539  final public function copy( array $params, array $opts = [] ) {
540  return $this->doOperation( [ 'op' => 'copy' ] + $params, $opts );
541  }
542 
553  final public function move( array $params, array $opts = [] ) {
554  return $this->doOperation( [ 'op' => 'move' ] + $params, $opts );
555  }
556 
567  final public function delete( array $params, array $opts = [] ) {
568  return $this->doOperation( [ 'op' => 'delete' ] + $params, $opts );
569  }
570 
582  final public function describe( array $params, array $opts = [] ) {
583  return $this->doOperation( [ 'op' => 'describe' ] + $params, $opts );
584  }
585 
700  final public function doQuickOperations( array $ops, array $opts = [] ) {
701  if ( empty( $opts['bypassReadOnly'] ) && $this->isReadOnly() ) {
702  return $this->newStatus( 'backend-fail-readonly', $this->name, $this->readOnly );
703  }
704  if ( $ops === [] ) {
705  return $this->newStatus(); // nothing to do
706  }
707 
708  $ops = $this->resolveFSFileObjects( $ops );
709  foreach ( $ops as &$op ) {
710  $op['overwrite'] = true; // avoids RTTs in key/value stores
711  }
712 
714  $scope = ScopedCallback::newScopedIgnoreUserAbort(); // try to ignore client aborts
715 
716  return $this->doQuickOperationsInternal( $ops, $opts );
717  }
718 
726  abstract protected function doQuickOperationsInternal( array $ops, array $opts );
727 
739  final public function doQuickOperation( array $op, array $opts = [] ) {
740  return $this->doQuickOperations( [ $op ], $opts );
741  }
742 
754  final public function quickCreate( array $params, array $opts = [] ) {
755  return $this->doQuickOperation( [ 'op' => 'create' ] + $params, $opts );
756  }
757 
769  final public function quickStore( array $params, array $opts = [] ) {
770  return $this->doQuickOperation( [ 'op' => 'store' ] + $params, $opts );
771  }
772 
784  final public function quickCopy( array $params, array $opts = [] ) {
785  return $this->doQuickOperation( [ 'op' => 'copy' ] + $params, $opts );
786  }
787 
799  final public function quickMove( array $params, array $opts = [] ) {
800  return $this->doQuickOperation( [ 'op' => 'move' ] + $params, $opts );
801  }
802 
814  final public function quickDelete( array $params, array $opts = [] ) {
815  return $this->doQuickOperation( [ 'op' => 'delete' ] + $params, $opts );
816  }
817 
829  final public function quickDescribe( array $params, array $opts = [] ) {
830  return $this->doQuickOperation( [ 'op' => 'describe' ] + $params, $opts );
831  }
832 
845  abstract public function concatenate( array $params );
846 
865  final public function prepare( array $params ) {
866  if ( empty( $params['bypassReadOnly'] ) && $this->isReadOnly() ) {
867  return $this->newStatus( 'backend-fail-readonly', $this->name, $this->readOnly );
868  }
870  $scope = ScopedCallback::newScopedIgnoreUserAbort(); // try to ignore client aborts
871  return $this->doPrepare( $params );
872  }
873 
879  abstract protected function doPrepare( array $params );
880 
897  final public function secure( array $params ) {
898  if ( empty( $params['bypassReadOnly'] ) && $this->isReadOnly() ) {
899  return $this->newStatus( 'backend-fail-readonly', $this->name, $this->readOnly );
900  }
902  $scope = ScopedCallback::newScopedIgnoreUserAbort(); // try to ignore client aborts
903  return $this->doSecure( $params );
904  }
905 
911  abstract protected function doSecure( array $params );
912 
931  final public function publish( array $params ) {
932  if ( empty( $params['bypassReadOnly'] ) && $this->isReadOnly() ) {
933  return $this->newStatus( 'backend-fail-readonly', $this->name, $this->readOnly );
934  }
936  $scope = ScopedCallback::newScopedIgnoreUserAbort(); // try to ignore client aborts
937  return $this->doPublish( $params );
938  }
939 
945  abstract protected function doPublish( array $params );
946 
958  final public function clean( array $params ) {
959  if ( empty( $params['bypassReadOnly'] ) && $this->isReadOnly() ) {
960  return $this->newStatus( 'backend-fail-readonly', $this->name, $this->readOnly );
961  }
963  $scope = ScopedCallback::newScopedIgnoreUserAbort(); // try to ignore client aborts
964  return $this->doClean( $params );
965  }
966 
972  abstract protected function doClean( array $params );
973 
990  abstract public function fileExists( array $params );
991 
1002  abstract public function getFileTimestamp( array $params );
1003 
1015  final public function getFileContents( array $params ) {
1016  $contents = $this->getFileContentsMulti( [ 'srcs' => [ $params['src'] ] ] + $params );
1017 
1018  return $contents[$params['src']];
1019  }
1020 
1034  abstract public function getFileContentsMulti( array $params );
1035 
1056  abstract public function getFileXAttributes( array $params );
1057 
1068  abstract public function getFileSize( array $params );
1069 
1086  abstract public function getFileStat( array $params );
1087 
1098  abstract public function getFileSha1Base36( array $params );
1099 
1109  abstract public function getFileProps( array $params );
1110 
1130  abstract public function streamFile( array $params );
1131 
1150  final public function getLocalReference( array $params ) {
1151  $fsFiles = $this->getLocalReferenceMulti( [ 'srcs' => [ $params['src'] ] ] + $params );
1152 
1153  return $fsFiles[$params['src']];
1154  }
1155 
1173  abstract public function getLocalReferenceMulti( array $params );
1174 
1187  final public function getLocalCopy( array $params ) {
1188  $tmpFiles = $this->getLocalCopyMulti( [ 'srcs' => [ $params['src'] ] ] + $params );
1189 
1190  return $tmpFiles[$params['src']];
1191  }
1192 
1208  abstract public function getLocalCopyMulti( array $params );
1209 
1228  abstract public function getFileHttpUrl( array $params );
1229 
1255  abstract public function directoryExists( array $params );
1256 
1279  abstract public function getDirectoryList( array $params );
1280 
1297  final public function getTopDirectoryList( array $params ) {
1298  return $this->getDirectoryList( [ 'topOnly' => true ] + $params );
1299  }
1300 
1321  abstract public function getFileList( array $params );
1322 
1339  final public function getTopFileList( array $params ) {
1340  return $this->getFileList( [ 'topOnly' => true ] + $params );
1341  }
1342 
1351  abstract public function preloadCache( array $paths );
1352 
1361  abstract public function clearCache( array $paths = null );
1362 
1377  abstract public function preloadFileStat( array $params );
1378 
1390  final public function lockFiles( array $paths, $type, $timeout = 0 ) {
1391  $paths = array_map( 'FileBackend::normalizeStoragePath', $paths );
1392 
1393  return $this->wrapStatus( $this->lockManager->lock( $paths, $type, $timeout ) );
1394  }
1395 
1403  final public function unlockFiles( array $paths, $type ) {
1404  $paths = array_map( 'FileBackend::normalizeStoragePath', $paths );
1405 
1406  return $this->wrapStatus( $this->lockManager->unlock( $paths, $type ) );
1407  }
1408 
1425  final public function getScopedFileLocks(
1426  array $paths, $type, StatusValue $status, $timeout = 0
1427  ) {
1428  if ( $type === 'mixed' ) {
1429  foreach ( $paths as &$typePaths ) {
1430  $typePaths = array_map( 'FileBackend::normalizeStoragePath', $typePaths );
1431  }
1432  } else {
1433  $paths = array_map( 'FileBackend::normalizeStoragePath', $paths );
1434  }
1435 
1436  return ScopedLock::factory( $this->lockManager, $paths, $type, $status, $timeout );
1437  }
1438 
1455  abstract public function getScopedLocksForOps( array $ops, StatusValue $status );
1456 
1464  final public function getRootStoragePath() {
1465  return "mwstore://{$this->name}";
1466  }
1467 
1475  final public function getContainerStoragePath( $container ) {
1476  return $this->getRootStoragePath() . "/{$container}";
1477  }
1478 
1484  final public function getJournal() {
1485  return $this->fileJournal;
1486  }
1487 
1497  protected function resolveFSFileObjects( array $ops ) {
1498  foreach ( $ops as &$op ) {
1499  $src = $op['src'] ?? null;
1500  if ( $src instanceof FSFile ) {
1501  $op['srcRef'] = $src;
1502  $op['src'] = $src->getPath();
1503  }
1504  }
1505  unset( $op );
1506 
1507  return $ops;
1508  }
1509 
1517  final public static function isStoragePath( $path ) {
1518  return ( strpos( $path, 'mwstore://' ) === 0 );
1519  }
1520 
1529  final public static function splitStoragePath( $storagePath ) {
1530  if ( self::isStoragePath( $storagePath ) ) {
1531  // Remove the "mwstore://" prefix and split the path
1532  $parts = explode( '/', substr( $storagePath, 10 ), 3 );
1533  if ( count( $parts ) >= 2 && $parts[0] != '' && $parts[1] != '' ) {
1534  if ( count( $parts ) == 3 ) {
1535  return $parts; // e.g. "backend/container/path"
1536  } else {
1537  return [ $parts[0], $parts[1], '' ]; // e.g. "backend/container"
1538  }
1539  }
1540  }
1541 
1542  return [ null, null, null ];
1543  }
1544 
1552  final public static function normalizeStoragePath( $storagePath ) {
1553  list( $backend, $container, $relPath ) = self::splitStoragePath( $storagePath );
1554  if ( $relPath !== null ) { // must be for this backend
1555  $relPath = self::normalizeContainerPath( $relPath );
1556  if ( $relPath !== null ) {
1557  return ( $relPath != '' )
1558  ? "mwstore://{$backend}/{$container}/{$relPath}"
1559  : "mwstore://{$backend}/{$container}";
1560  }
1561  }
1562 
1563  return null;
1564  }
1565 
1574  final public static function parentStoragePath( $storagePath ) {
1575  // XXX dirname() depends on platform and locale! If nothing enforces that the storage path
1576  // doesn't contain characters like '\', behavior can vary by platform. We should use
1577  // explode() instead.
1578  $storagePath = dirname( $storagePath );
1579  list( , , $rel ) = self::splitStoragePath( $storagePath );
1580 
1581  return ( $rel === null ) ? null : $storagePath;
1582  }
1583 
1591  final public static function extensionFromPath( $path, $case = 'lowercase' ) {
1592  // This will treat a string starting with . as not having an extension, but store paths have
1593  // to start with 'mwstore://', so "garbage in, garbage out".
1594  $i = strrpos( $path, '.' );
1595  $ext = $i ? substr( $path, $i + 1 ) : '';
1596 
1597  if ( $case === 'lowercase' ) {
1598  $ext = strtolower( $ext );
1599  } elseif ( $case === 'uppercase' ) {
1600  $ext = strtoupper( $ext );
1601  }
1602 
1603  return $ext;
1604  }
1605 
1613  final public static function isPathTraversalFree( $path ) {
1614  return ( self::normalizeContainerPath( $path ) !== null );
1615  }
1616 
1626  final public static function makeContentDisposition( $type, $filename = '' ) {
1627  $parts = [];
1628 
1629  $type = strtolower( $type );
1630  if ( !in_array( $type, [ 'inline', 'attachment' ] ) ) {
1631  throw new InvalidArgumentException( "Invalid Content-Disposition type '$type'." );
1632  }
1633  $parts[] = $type;
1634 
1635  if ( strlen( $filename ) ) {
1636  $parts[] = "filename*=UTF-8''" . rawurlencode( basename( $filename ) );
1637  }
1638 
1639  return implode( ';', $parts );
1640  }
1641 
1652  final protected static function normalizeContainerPath( $path ) {
1653  // Normalize directory separators
1654  $path = strtr( $path, '\\', '/' );
1655  // Collapse any consecutive directory separators
1656  $path = preg_replace( '![/]{2,}!', '/', $path );
1657  // Remove any leading directory separator
1658  $path = ltrim( $path, '/' );
1659  // Use the same traversal protection as Title::secureAndSplit()
1660  if ( strpos( $path, '.' ) !== false ) {
1661  if (
1662  $path === '.' ||
1663  $path === '..' ||
1664  strpos( $path, './' ) === 0 ||
1665  strpos( $path, '../' ) === 0 ||
1666  strpos( $path, '/./' ) !== false ||
1667  strpos( $path, '/../' ) !== false
1668  ) {
1669  return null;
1670  }
1671  }
1672 
1673  return $path;
1674  }
1675 
1684  final protected function newStatus( ...$args ) {
1685  if ( count( $args ) ) {
1686  $sv = StatusValue::newFatal( ...$args );
1687  } else {
1688  $sv = StatusValue::newGood();
1689  }
1690 
1691  return $this->wrapStatus( $sv );
1692  }
1693 
1698  final protected function wrapStatus( StatusValue $sv ) {
1699  return $this->statusWrapper ? call_user_func( $this->statusWrapper, $sv ) : $sv;
1700  }
1701 
1706  protected function scopedProfileSection( $section ) {
1707  return $this->profiler ? ( $this->profiler )( $section ) : null;
1708  }
1709 
1713  protected function resetOutputBuffer() {
1714  // XXX According to documentation, ob_get_status() always returns a non-empty array and this
1715  // condition will always be true
1716  while ( ob_get_status() ) {
1717  if ( !ob_end_clean() ) {
1718  // Could not remove output buffer handler; abort now
1719  // to avoid getting in some kind of infinite loop.
1720  break;
1721  }
1722  }
1723  }
1724 }
static isPathTraversalFree( $path)
Check if a relative path has no directory traversals.
static newFatal( $message,... $parameters)
Factory function for fatal errors.
Definition: StatusValue.php:69
FileJournal $fileJournal
describe(array $params, array $opts=[])
Performs a single describe operation.
unlockFiles(array $paths, $type)
Unlock the files at the given storage paths in the backend.
static splitStoragePath( $storagePath)
Split a storage path into a backend name, a container name, and a relative file path.
doOperations(array $ops, array $opts=[])
This is the main entry point into the backend for write operations.
TempFSFileFactory $tmpFileFactory
doPrepare(array $params)
getFileStat(array $params)
Get quick information about a file at a storage path in the backend.
getWikiId()
Alias to getDomainId()
publish(array $params)
Remove measures to block web access to a storage directory and the container it belongs to...
Simple version of FileJournal that does nothing.
secure(array $params)
Take measures to block web access to a storage directory and the container it belongs to...
static parentStoragePath( $storagePath)
Get the parent storage directory of a storage path.
setLogger(LoggerInterface $logger)
string $domainId
Unique domain name.
scopedProfileSection( $section)
const ATTR_HEADERS
Bitfield flags for supported features.
getFileHttpUrl(array $params)
Return an HTTP URL to a given file that requires no authentication to use.
getFileXAttributes(array $params)
Get metadata about a file at a storage path in the backend.
getName()
Get the unique backend name.
static normalizeContainerPath( $path)
Validate and normalize a relative storage path.
quickStore(array $params, array $opts=[])
Performs a single quick store operation.
getScopedFileLocks(array $paths, $type, StatusValue $status, $timeout=0)
Lock the files at the given storage paths in the backend.
getFileContentsMulti(array $params)
Like getFileContents() except it takes an array of storage paths and returns an order preserved map o...
getDirectoryList(array $params)
Get an iterator to list all directories under a storage directory.
getDomainId()
Get the domain identifier used for this backend (possibly empty).
doOperation(array $op, array $opts=[])
Same as doOperations() except it takes a single operation.
static factory(LockManager $manager, array $paths, $type, StatusValue $status, $timeout=0)
Get a ScopedLock object representing a lock on resource paths.
Definition: ScopedLock.php:70
doClean(array $params)
static extensionFromPath( $path, $case='lowercase')
Get the final extension from a storage or FS path.
getFileTimestamp(array $params)
Get the last-modified timestamp of the file at a storage path.
static isStoragePath( $path)
Check if a given path is a "mwstore://" path.
static normalizeStoragePath( $storagePath)
Normalize a storage path by cleaning up directory separators.
int $concurrency
How many operations can be done in parallel.
getLocalReferenceMulti(array $params)
Like getLocalReference() except it takes an array of storage paths and yields an order-preserved map ...
directoryExists(array $params)
Check if a directory exists at a given storage path.
wrapStatus(StatusValue $sv)
callable $statusWrapper
resolveFSFileObjects(array $ops)
Convert FSFile &#39;src&#39; paths to string paths (with an &#39;srcRef&#39; field set to the FSFile) ...
doQuickOperationsInternal(array $ops, array $opts)
string $readOnly
Read-only explanation message.
create(array $params, array $opts=[])
Performs a single create operation.
if( $line===false) $args
Definition: mcc.php:124
move(array $params, array $opts=[])
Performs a single move operation.
quickCopy(array $params, array $opts=[])
Performs a single quick copy operation.
getReadOnlyReason()
Get an explanatory message if this backend is read-only.
getTopFileList(array $params)
Same as FileBackend::getFileList() except only lists files that are immediately under the given direc...
doQuickOperation(array $op, array $opts=[])
Same as doQuickOperations() except it takes a single operation.
lockFiles(array $paths, $type, $timeout=0)
Lock the files at the given storage paths in the backend.
__construct(array $config)
Create a new backend instance from configuration.
hasFeatures( $bitfield)
Check if the backend medium supports a field of extra features.
static newGood( $value=null)
Factory function for good results.
Definition: StatusValue.php:81
fileExists(array $params)
Check if a file exists at a storage path in the backend.
getFileSha1Base36(array $params)
Get a SHA-1 hash of the content of the file at a storage path in the backend.
callable $obResetFunc
store(array $params, array $opts=[])
Performs a single store operation.
resetOutputBuffer()
Let&#39;s not reset output buffering during tests.
const ATTR_UNICODE_PATHS
doSecure(array $params)
const ATTR_METADATA
callable $streamMimeFunc
newStatus(... $args)
Yields the result of the status wrapper callback on either:
getFileSize(array $params)
Get the size (bytes) of a file at a storage path in the backend.
doQuickOperations(array $ops, array $opts=[])
Perform a set of independent file operations on some files.
prepare(array $params)
Prepare a storage directory for usage.
string $name
Unique backend name.
Definition: FileBackend.php:98
getLocalCopy(array $params)
Get a local copy on disk of the file at a storage path in the backend.
LoggerInterface $logger
quickDescribe(array $params, array $opts=[])
Performs a single quick describe operation.
getJournal()
Get the file journal object for this backend.
concatenate(array $params)
Concatenate a list of storage files into a single file system file.
getRootStoragePath()
Get the root storage path of this backend.
quickMove(array $params, array $opts=[])
Performs a single quick move operation.
Class representing a non-directory file on the file system.
Definition: FSFile.php:32
preloadCache(array $paths)
Preload persistent file stat cache and property cache into in-process cache.
copy(array $params, array $opts=[])
Performs a single copy operation.
doOperationsInternal(array $ops, array $opts)
Base class for all file backend classes (including multi-write backends).
Definition: FileBackend.php:96
getFileList(array $params)
Get an iterator to list all stored files under a storage directory.
if(!is_readable( $file)) $ext
Definition: router.php:48
quickDelete(array $params, array $opts=[])
Performs a single quick delete operation.
getScopedLocksForOps(array $ops, StatusValue $status)
Get an array of scoped locks needed for a batch of file operations.
getTopDirectoryList(array $params)
Same as FileBackend::getDirectoryList() except only lists directories that are immediately under the ...
getLocalCopyMulti(array $params)
Like getLocalCopy() except it takes an array of storage paths and yields an order preserved-map of st...
quickCreate(array $params, array $opts=[])
Performs a single quick create operation.
string $parallelize
When to do operations in parallel.
streamFile(array $params)
Stream the content of the file at a storage path in the backend.
clean(array $params)
Delete a storage directory if it is empty.
getFileProps(array $params)
Get the properties of the content of the file at a storage path in the backend.
clearCache(array $paths=null)
Invalidate any in-process file stat and property cache.
LockManager $lockManager
callable null $profiler
isReadOnly()
Check if this backend is read-only.
preloadFileStat(array $params)
Preload file stat information (concurrently if possible) into in-process cache.
getLocalReference(array $params)
Returns a file system file, identical in content to the file at a storage path.
getFeatures()
Get the a bitfield of extra features supported by the backend medium.
static makeContentDisposition( $type, $filename='')
Build a Content-Disposition header value per RFC 6266.
getContainerStoragePath( $container)
Get the storage path for the given container for this backend.
doPublish(array $params)
getFileContents(array $params)
Get the contents of a file at a storage path in the backend.