MediaWiki REL1_37
FileBackend.php
Go to the documentation of this file.
1<?php
31use Psr\Log\LoggerAwareInterface;
32use Psr\Log\LoggerInterface;
33use Psr\Log\NullLogger;
34use Wikimedia\ScopedCallback;
35
98abstract class FileBackend implements LoggerAwareInterface {
100 protected $name;
101
103 protected $domainId;
104
106 protected $readOnly;
107
109 protected $parallelize;
110
112 protected $concurrency;
113
116
118 protected $lockManager;
120 protected $fileJournal;
122 protected $logger;
124 protected $profiler;
125
127 protected $obResetFunc;
131 protected $statusWrapper;
132
134 public const ATTR_HEADERS = 1; // files can be tagged with standard HTTP headers
135 public const ATTR_METADATA = 2; // files can be stored with metadata key/values
136 public const ATTR_UNICODE_PATHS = 4; // files can have Unicode paths (not just ASCII)
137
139 protected const STAT_ABSENT = false;
140
142 public const STAT_ERROR = null;
144 public const LIST_ERROR = null;
146 public const TEMPURL_ERROR = null;
148 public const EXISTENCE_ERROR = null;
149
151 public const TIMESTAMP_FAIL = false;
153 public const CONTENT_FAIL = false;
155 public const XATTRS_FAIL = false;
157 public const SIZE_FAIL = false;
159 public const SHA1_FAIL = false;
160
196 public function __construct( array $config ) {
197 if ( !array_key_exists( 'name', $config ) ) {
198 throw new InvalidArgumentException( 'Backend name not specified.' );
199 }
200 $this->name = $config['name'];
201 $this->domainId = $config['domainId'] // e.g. "my_wiki-en_"
202 ?? $config['wikiId'] // b/c alias
203 ?? null;
204 if ( !preg_match( '!^[a-zA-Z0-9-_]{1,255}$!', $this->name ) ) {
205 throw new InvalidArgumentException( "Backend name '{$this->name}' is invalid." );
206 }
207 if ( !is_string( $this->domainId ) ) {
208 throw new InvalidArgumentException(
209 "Backend domain ID not provided for '{$this->name}'." );
210 }
211 $this->lockManager = $config['lockManager'] ?? new NullLockManager( [] );
212 $this->fileJournal = $config['fileJournal'] ?? new NullFileJournal;
213 $this->readOnly = isset( $config['readOnly'] )
214 ? (string)$config['readOnly']
215 : '';
216 $this->parallelize = isset( $config['parallelize'] )
217 ? (string)$config['parallelize']
218 : 'off';
219 $this->concurrency = isset( $config['concurrency'] )
220 ? (int)$config['concurrency']
221 : 50;
222 $this->obResetFunc = $config['obResetFunc'] ?? [ $this, 'resetOutputBuffer' ];
223 $this->streamMimeFunc = $config['streamMimeFunc'] ?? null;
224
225 $this->profiler = $config['profiler'] ?? null;
226 if ( !is_callable( $this->profiler ) ) {
227 $this->profiler = null;
228 }
229 $this->logger = $config['logger'] ?? new NullLogger();
230 $this->statusWrapper = $config['statusWrapper'] ?? null;
231 // tmpDirectory gets precedence for backward compatibility
232 if ( isset( $config['tmpDirectory'] ) ) {
233 $this->tmpFileFactory = new TempFSFileFactory( $config['tmpDirectory'] );
234 } else {
235 $this->tmpFileFactory = $config['tmpFileFactory'] ?? new TempFSFileFactory();
236 }
237 }
238
239 public function setLogger( LoggerInterface $logger ) {
240 $this->logger = $logger;
241 }
242
251 final public function getName() {
252 return $this->name;
253 }
254
261 final public function getDomainId() {
262 return $this->domainId;
263 }
264
272 final public function getWikiId() {
273 return $this->getDomainId();
274 }
275
281 final public function isReadOnly() {
282 return ( $this->readOnly != '' );
283 }
284
290 final public function getReadOnlyReason() {
291 return ( $this->readOnly != '' ) ? $this->readOnly : false;
292 }
293
301 public function getFeatures() {
303 }
304
312 final public function hasFeatures( $bitfield ) {
313 return ( $this->getFeatures() & $bitfield ) === $bitfield;
314 }
315
467 final public function doOperations( array $ops, array $opts = [] ) {
468 if ( empty( $opts['bypassReadOnly'] ) && $this->isReadOnly() ) {
469 return $this->newStatus( 'backend-fail-readonly', $this->name, $this->readOnly );
470 }
471 if ( $ops === [] ) {
472 return $this->newStatus(); // nothing to do
473 }
474
475 $ops = $this->resolveFSFileObjects( $ops );
476 if ( empty( $opts['force'] ) ) { // sanity
477 unset( $opts['nonLocking'] );
478 }
479
481 $scope = ScopedCallback::newScopedIgnoreUserAbort(); // try to ignore client aborts
482
483 return $this->doOperationsInternal( $ops, $opts );
484 }
485
492 abstract protected function doOperationsInternal( array $ops, array $opts );
493
505 final public function doOperation( array $op, array $opts = [] ) {
506 return $this->doOperations( [ $op ], $opts );
507 }
508
519 final public function create( array $params, array $opts = [] ) {
520 return $this->doOperation( [ 'op' => 'create' ] + $params, $opts );
521 }
522
533 final public function store( array $params, array $opts = [] ) {
534 return $this->doOperation( [ 'op' => 'store' ] + $params, $opts );
535 }
536
547 final public function copy( array $params, array $opts = [] ) {
548 return $this->doOperation( [ 'op' => 'copy' ] + $params, $opts );
549 }
550
561 final public function move( array $params, array $opts = [] ) {
562 return $this->doOperation( [ 'op' => 'move' ] + $params, $opts );
563 }
564
575 final public function delete( array $params, array $opts = [] ) {
576 return $this->doOperation( [ 'op' => 'delete' ] + $params, $opts );
577 }
578
590 final public function describe( array $params, array $opts = [] ) {
591 return $this->doOperation( [ 'op' => 'describe' ] + $params, $opts );
592 }
593
708 final public function doQuickOperations( array $ops, array $opts = [] ) {
709 if ( empty( $opts['bypassReadOnly'] ) && $this->isReadOnly() ) {
710 return $this->newStatus( 'backend-fail-readonly', $this->name, $this->readOnly );
711 }
712 if ( $ops === [] ) {
713 return $this->newStatus(); // nothing to do
714 }
715
716 $ops = $this->resolveFSFileObjects( $ops );
717 foreach ( $ops as &$op ) {
718 $op['overwrite'] = true; // avoids RTTs in key/value stores
719 }
720
722 $scope = ScopedCallback::newScopedIgnoreUserAbort(); // try to ignore client aborts
723
724 return $this->doQuickOperationsInternal( $ops, $opts );
725 }
726
734 abstract protected function doQuickOperationsInternal( array $ops, array $opts );
735
747 final public function doQuickOperation( array $op, array $opts = [] ) {
748 return $this->doQuickOperations( [ $op ], $opts );
749 }
750
762 final public function quickCreate( array $params, array $opts = [] ) {
763 return $this->doQuickOperation( [ 'op' => 'create' ] + $params, $opts );
764 }
765
777 final public function quickStore( array $params, array $opts = [] ) {
778 return $this->doQuickOperation( [ 'op' => 'store' ] + $params, $opts );
779 }
780
792 final public function quickCopy( array $params, array $opts = [] ) {
793 return $this->doQuickOperation( [ 'op' => 'copy' ] + $params, $opts );
794 }
795
807 final public function quickMove( array $params, array $opts = [] ) {
808 return $this->doQuickOperation( [ 'op' => 'move' ] + $params, $opts );
809 }
810
822 final public function quickDelete( array $params, array $opts = [] ) {
823 return $this->doQuickOperation( [ 'op' => 'delete' ] + $params, $opts );
824 }
825
837 final public function quickDescribe( array $params, array $opts = [] ) {
838 return $this->doQuickOperation( [ 'op' => 'describe' ] + $params, $opts );
839 }
840
853 abstract public function concatenate( array $params );
854
873 final public function prepare( array $params ) {
874 if ( empty( $params['bypassReadOnly'] ) && $this->isReadOnly() ) {
875 return $this->newStatus( 'backend-fail-readonly', $this->name, $this->readOnly );
876 }
878 $scope = ScopedCallback::newScopedIgnoreUserAbort(); // try to ignore client aborts
879 return $this->doPrepare( $params );
880 }
881
887 abstract protected function doPrepare( array $params );
888
905 final public function secure( array $params ) {
906 if ( empty( $params['bypassReadOnly'] ) && $this->isReadOnly() ) {
907 return $this->newStatus( 'backend-fail-readonly', $this->name, $this->readOnly );
908 }
910 $scope = ScopedCallback::newScopedIgnoreUserAbort(); // try to ignore client aborts
911 return $this->doSecure( $params );
912 }
913
919 abstract protected function doSecure( array $params );
920
939 final public function publish( array $params ) {
940 if ( empty( $params['bypassReadOnly'] ) && $this->isReadOnly() ) {
941 return $this->newStatus( 'backend-fail-readonly', $this->name, $this->readOnly );
942 }
944 $scope = ScopedCallback::newScopedIgnoreUserAbort(); // try to ignore client aborts
945 return $this->doPublish( $params );
946 }
947
953 abstract protected function doPublish( array $params );
954
966 final public function clean( array $params ) {
967 if ( empty( $params['bypassReadOnly'] ) && $this->isReadOnly() ) {
968 return $this->newStatus( 'backend-fail-readonly', $this->name, $this->readOnly );
969 }
971 $scope = ScopedCallback::newScopedIgnoreUserAbort(); // try to ignore client aborts
972 return $this->doClean( $params );
973 }
974
980 abstract protected function doClean( array $params );
981
998 abstract public function fileExists( array $params );
999
1010 abstract public function getFileTimestamp( array $params );
1011
1023 final public function getFileContents( array $params ) {
1024 $contents = $this->getFileContentsMulti( [ 'srcs' => [ $params['src'] ] ] + $params );
1025
1026 return $contents[$params['src']];
1027 }
1028
1042 abstract public function getFileContentsMulti( array $params );
1043
1064 abstract public function getFileXAttributes( array $params );
1065
1076 abstract public function getFileSize( array $params );
1077
1094 abstract public function getFileStat( array $params );
1095
1106 abstract public function getFileSha1Base36( array $params );
1107
1117 abstract public function getFileProps( array $params );
1118
1138 abstract public function streamFile( array $params );
1139
1158 final public function getLocalReference( array $params ) {
1159 $fsFiles = $this->getLocalReferenceMulti( [ 'srcs' => [ $params['src'] ] ] + $params );
1160
1161 return $fsFiles[$params['src']];
1162 }
1163
1181 abstract public function getLocalReferenceMulti( array $params );
1182
1195 final public function getLocalCopy( array $params ) {
1196 $tmpFiles = $this->getLocalCopyMulti( [ 'srcs' => [ $params['src'] ] ] + $params );
1197
1198 return $tmpFiles[$params['src']];
1199 }
1200
1216 abstract public function getLocalCopyMulti( array $params );
1217
1236 abstract public function getFileHttpUrl( array $params );
1237
1263 abstract public function directoryExists( array $params );
1264
1287 abstract public function getDirectoryList( array $params );
1288
1305 final public function getTopDirectoryList( array $params ) {
1306 return $this->getDirectoryList( [ 'topOnly' => true ] + $params );
1307 }
1308
1329 abstract public function getFileList( array $params );
1330
1347 final public function getTopFileList( array $params ) {
1348 return $this->getFileList( [ 'topOnly' => true ] + $params );
1349 }
1350
1359 abstract public function preloadCache( array $paths );
1360
1369 abstract public function clearCache( array $paths = null );
1370
1385 abstract public function preloadFileStat( array $params );
1386
1398 final public function lockFiles( array $paths, $type, $timeout = 0 ) {
1399 $paths = array_map( [ __CLASS__, 'normalizeStoragePath' ], $paths );
1400
1401 return $this->wrapStatus( $this->lockManager->lock( $paths, $type, $timeout ) );
1402 }
1403
1411 final public function unlockFiles( array $paths, $type ) {
1412 $paths = array_map( [ __CLASS__, 'normalizeStoragePath' ], $paths );
1413
1414 return $this->wrapStatus( $this->lockManager->unlock( $paths, $type ) );
1415 }
1416
1433 final public function getScopedFileLocks(
1434 array $paths, $type, StatusValue $status, $timeout = 0
1435 ) {
1436 if ( $type === 'mixed' ) {
1437 foreach ( $paths as &$typePaths ) {
1438 $typePaths = array_map( [ __CLASS__, 'normalizeStoragePath' ], $typePaths );
1439 }
1440 } else {
1441 $paths = array_map( [ __CLASS__, 'normalizeStoragePath' ], $paths );
1442 }
1443
1444 return ScopedLock::factory( $this->lockManager, $paths, $type, $status, $timeout );
1445 }
1446
1463 abstract public function getScopedLocksForOps( array $ops, StatusValue $status );
1464
1472 final public function getRootStoragePath() {
1473 return "mwstore://{$this->name}";
1474 }
1475
1483 final public function getContainerStoragePath( $container ) {
1484 return $this->getRootStoragePath() . "/{$container}";
1485 }
1486
1492 final public function getJournal() {
1493 return $this->fileJournal;
1494 }
1495
1505 protected function resolveFSFileObjects( array $ops ) {
1506 foreach ( $ops as &$op ) {
1507 $src = $op['src'] ?? null;
1508 if ( $src instanceof FSFile ) {
1509 $op['srcRef'] = $src;
1510 $op['src'] = $src->getPath();
1511 }
1512 }
1513 unset( $op );
1514
1515 return $ops;
1516 }
1517
1525 final public static function isStoragePath( $path ) {
1526 return ( strpos( $path ?? '', 'mwstore://' ) === 0 );
1527 }
1528
1537 final public static function splitStoragePath( $storagePath ) {
1538 if ( self::isStoragePath( $storagePath ) ) {
1539 // Remove the "mwstore://" prefix and split the path
1540 $parts = explode( '/', substr( $storagePath, 10 ), 3 );
1541 if ( count( $parts ) >= 2 && $parts[0] != '' && $parts[1] != '' ) {
1542 if ( count( $parts ) == 3 ) {
1543 return $parts; // e.g. "backend/container/path"
1544 } else {
1545 return [ $parts[0], $parts[1], '' ]; // e.g. "backend/container"
1546 }
1547 }
1548 }
1549
1550 return [ null, null, null ];
1551 }
1552
1560 final public static function normalizeStoragePath( $storagePath ) {
1561 list( $backend, $container, $relPath ) = self::splitStoragePath( $storagePath );
1562 if ( $relPath !== null ) { // must be for this backend
1563 $relPath = self::normalizeContainerPath( $relPath );
1564 if ( $relPath !== null ) {
1565 return ( $relPath != '' )
1566 ? "mwstore://{$backend}/{$container}/{$relPath}"
1567 : "mwstore://{$backend}/{$container}";
1568 }
1569 }
1570
1571 return null;
1572 }
1573
1582 final public static function parentStoragePath( $storagePath ) {
1583 // XXX dirname() depends on platform and locale! If nothing enforces that the storage path
1584 // doesn't contain characters like '\', behavior can vary by platform. We should use
1585 // explode() instead.
1586 $storagePath = dirname( $storagePath );
1587 list( , , $rel ) = self::splitStoragePath( $storagePath );
1588
1589 return ( $rel === null ) ? null : $storagePath;
1590 }
1591
1599 final public static function extensionFromPath( $path, $case = 'lowercase' ) {
1600 // This will treat a string starting with . as not having an extension, but store paths have
1601 // to start with 'mwstore://', so "garbage in, garbage out".
1602 $i = strrpos( $path, '.' );
1603 $ext = $i ? substr( $path, $i + 1 ) : '';
1604
1605 if ( $case === 'lowercase' ) {
1606 $ext = strtolower( $ext );
1607 } elseif ( $case === 'uppercase' ) {
1608 $ext = strtoupper( $ext );
1609 }
1610
1611 return $ext;
1612 }
1613
1621 final public static function isPathTraversalFree( $path ) {
1622 return ( self::normalizeContainerPath( $path ) !== null );
1623 }
1624
1634 final public static function makeContentDisposition( $type, $filename = '' ) {
1635 $parts = [];
1636
1637 $type = strtolower( $type );
1638 if ( !in_array( $type, [ 'inline', 'attachment' ] ) ) {
1639 throw new InvalidArgumentException( "Invalid Content-Disposition type '$type'." );
1640 }
1641 $parts[] = $type;
1642
1643 if ( strlen( $filename ) ) {
1644 $parts[] = "filename*=UTF-8''" . rawurlencode( basename( $filename ) );
1645 }
1646
1647 return implode( ';', $parts );
1648 }
1649
1660 final protected static function normalizeContainerPath( $path ) {
1661 // Normalize directory separators
1662 $path = strtr( $path, '\\', '/' );
1663 // Collapse any consecutive directory separators
1664 $path = preg_replace( '![/]{2,}!', '/', $path );
1665 // Remove any leading directory separator
1666 $path = ltrim( $path, '/' );
1667 // Use the same traversal protection as Title::secureAndSplit()
1668 if ( strpos( $path, '.' ) !== false ) {
1669 if (
1670 $path === '.' ||
1671 $path === '..' ||
1672 strpos( $path, './' ) === 0 ||
1673 strpos( $path, '../' ) === 0 ||
1674 strpos( $path, '/./' ) !== false ||
1675 strpos( $path, '/../' ) !== false
1676 ) {
1677 return null;
1678 }
1679 }
1680
1681 return $path;
1682 }
1683
1692 final protected function newStatus( ...$args ) {
1693 if ( count( $args ) ) {
1694 $sv = StatusValue::newFatal( ...$args );
1695 } else {
1696 $sv = StatusValue::newGood();
1697 }
1698
1699 return $this->wrapStatus( $sv );
1700 }
1701
1706 final protected function wrapStatus( StatusValue $sv ) {
1707 return $this->statusWrapper ? call_user_func( $this->statusWrapper, $sv ) : $sv;
1708 }
1709
1714 protected function scopedProfileSection( $section ) {
1715 return $this->profiler ? ( $this->profiler )( $section ) : null;
1716 }
1717
1721 protected function resetOutputBuffer() {
1722 // XXX According to documentation, ob_get_status() always returns a non-empty array and this
1723 // condition will always be true
1724 while ( ob_get_status() ) {
1725 if ( !ob_end_clean() ) {
1726 // Could not remove output buffer handler; abort now
1727 // to avoid getting in some kind of infinite loop.
1728 break;
1729 }
1730 }
1731 }
1732}
Class representing a non-directory file on the file system.
Definition FSFile.php:32
Base class for all file backend classes (including multi-write backends).
getScopedLocksForOps(array $ops, StatusValue $status)
Get an array of scoped locks needed for a batch of file operations.
concatenate(array $params)
Concatenate a list of storage files into a single file system file.
static parentStoragePath( $storagePath)
Get the parent storage directory of a storage path.
doOperation(array $op, array $opts=[])
Same as doOperations() except it takes a single operation.
create(array $params, array $opts=[])
Performs a single create operation.
quickDescribe(array $params, array $opts=[])
Performs a single quick describe operation.
wrapStatus(StatusValue $sv)
static isStoragePath( $path)
Check if a given path is a "mwstore://" path.
hasFeatures( $bitfield)
Check if the backend medium supports a field of extra features.
getFileXAttributes(array $params)
Get metadata about a file at a storage path in the backend.
getLocalCopy(array $params)
Get a local copy on disk of the file at a storage path in the backend.
clean(array $params)
Delete a storage directory if it is empty.
quickCreate(array $params, array $opts=[])
Performs a single quick create operation.
string $name
Unique backend name.
move(array $params, array $opts=[])
Performs a single move operation.
getDirectoryList(array $params)
Get an iterator to list all directories under a storage directory.
getFileList(array $params)
Get an iterator to list all stored files under a storage directory.
callable null $profiler
lockFiles(array $paths, $type, $timeout=0)
Lock the files at the given storage paths in the backend.
preloadFileStat(array $params)
Preload file stat information (concurrently if possible) into in-process cache.
describe(array $params, array $opts=[])
Performs a single describe operation.
getDomainId()
Get the domain identifier used for this backend (possibly empty).
getFeatures()
Get the a bitfield of extra features supported by the backend medium.
streamFile(array $params)
Stream the content of the file 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.
getReadOnlyReason()
Get an explanatory message if this backend is read-only.
doClean(array $params)
publish(array $params)
Remove measures to block web access to a storage directory and the container it belongs to.
quickDelete(array $params, array $opts=[])
Performs a single quick delete operation.
const ATTR_UNICODE_PATHS
callable $obResetFunc
clearCache(array $paths=null)
Invalidate any in-process file stat and property cache.
getFileStat(array $params)
Get quick information about a file at a storage path in the backend.
fileExists(array $params)
Check if a file exists at a storage path in the backend.
static splitStoragePath( $storagePath)
Split a storage path into a backend name, a container name, and a relative file path.
getFileTimestamp(array $params)
Get the last-modified timestamp of the file at a storage path.
LoggerInterface $logger
static extensionFromPath( $path, $case='lowercase')
Get the final extension from a storage or FS path.
static makeContentDisposition( $type, $filename='')
Build a Content-Disposition header value per RFC 6266.
static normalizeContainerPath( $path)
Validate and normalize a relative storage path.
string $readOnly
Read-only explanation message.
preloadCache(array $paths)
Preload persistent file stat cache and property cache into in-process cache.
string $domainId
Unique domain name.
getScopedFileLocks(array $paths, $type, StatusValue $status, $timeout=0)
Lock the files at the given storage paths in the backend.
store(array $params, array $opts=[])
Performs a single store operation.
isReadOnly()
Check if this backend is read-only.
getRootStoragePath()
Get the root storage path of this backend.
getTopDirectoryList(array $params)
Same as FileBackend::getDirectoryList() except only lists directories that are immediately under the ...
getFileProps(array $params)
Get the properties of the content of the file at a storage path in the backend.
getFileSize(array $params)
Get the size (bytes) of a file at a storage path in the backend.
getWikiId()
Alias to getDomainId()
prepare(array $params)
Prepare a storage directory for usage.
getFileContentsMulti(array $params)
Like getFileContents() except it takes an array of storage paths and returns an order preserved map o...
doQuickOperations(array $ops, array $opts=[])
Perform a set of independent file operations on some files.
unlockFiles(array $paths, $type)
Unlock the files at the given storage paths in the backend.
getContainerStoragePath( $container)
Get the storage path for the given container for this backend.
scopedProfileSection( $section)
const ATTR_METADATA
doOperations(array $ops, array $opts=[])
This is the main entry point into the backend for write operations.
newStatus(... $args)
Yields the result of the status wrapper callback on either:
static normalizeStoragePath( $storagePath)
Normalize a storage path by cleaning up directory separators.
directoryExists(array $params)
Check if a directory exists at a given storage path.
callable $streamMimeFunc
resolveFSFileObjects(array $ops)
Convert FSFile 'src' paths to string paths (with an 'srcRef' field set to the FSFile)
quickMove(array $params, array $opts=[])
Performs a single quick move operation.
string $parallelize
When to do operations in parallel.
doPublish(array $params)
LockManager $lockManager
getLocalCopyMulti(array $params)
Like getLocalCopy() except it takes an array of storage paths and yields an order preserved-map of st...
int $concurrency
How many operations can be done in parallel.
getTopFileList(array $params)
Same as FileBackend::getFileList() except only lists files that are immediately under the given direc...
const ATTR_HEADERS
Bitfield flags for supported features.
doPrepare(array $params)
__construct(array $config)
Create a new backend instance from configuration.
quickCopy(array $params, array $opts=[])
Performs a single quick copy operation.
static isPathTraversalFree( $path)
Check if a relative path has no directory traversals.
quickStore(array $params, array $opts=[])
Performs a single quick store operation.
doQuickOperation(array $op, array $opts=[])
Same as doQuickOperations() except it takes a single operation.
callable $statusWrapper
doSecure(array $params)
setLogger(LoggerInterface $logger)
doQuickOperationsInternal(array $ops, array $opts)
getLocalReferenceMulti(array $params)
Like getLocalReference() except it takes an array of storage paths and yields an order-preserved map ...
copy(array $params, array $opts=[])
Performs a single copy operation.
FileJournal $fileJournal
getFileContents(array $params)
Get the contents of a file at a storage path in the backend.
TempFSFileFactory $tmpFileFactory
getLocalReference(array $params)
Returns a file system file, identical in content to the file at a storage path.
getName()
Get the unique backend name.
getJournal()
Get the file journal object for this backend.
getFileHttpUrl(array $params)
Return an HTTP URL to a given file that requires no authentication to use.
doOperationsInternal(array $ops, array $opts)
secure(array $params)
Take measures to block web access to a storage directory and the container it belongs to.
Class for handling file operation journaling.
Class for handling resource locking.
Simple version of FileJournal that does nothing.
Simple version of LockManager that only does lock reference counting.
static factory(LockManager $manager, array $paths, $type, StatusValue $status, $timeout=0)
Get a ScopedLock object representing a lock on resource paths.
Generic operation result class Has warning/error list, boolean status and arbitrary value.
if( $line===false) $args
Definition mcc.php:124
if(!is_readable( $file)) $ext
Definition router.php:48