use Psr\Log\LoggerAwareInterface;
use Psr\Log\LoggerInterface;
use Wikimedia\ScopedCallback;
+use Psr\Log\NullLogger;
/**
* @brief Base class for all file backend classes (including multi-write backends).
protected $fileJournal;
/** @var LoggerInterface */
protected $logger;
- /** @var object|string Class name or object With profileIn/profileOut methods */
+ /** @var callable|null */
protected $profiler;
/** @var callable */
* - obResetFunc : alternative callback to clear the output buffer
* - streamMimeFunc : alternative method to determine the content type from the path
* - logger : Optional PSR logger object.
- * - profiler : Optional class name or object With profileIn/profileOut methods.
+ * - profiler : Optional callback that takes a section name argument and returns
+ * a ScopedCallback instance that ends the profile section in its destructor.
* @throws InvalidArgumentException
*/
public function __construct( array $config ) {
$this->name = $config['name'];
- $this->domainId = isset( $config['domainId'] )
- ? $config['domainId'] // e.g. "my_wiki-en_"
- : $config['wikiId']; // b/c alias
+ $this->domainId = $config['domainId'] // e.g. "my_wiki-en_"
+ ?? $config['wikiId']; // b/c alias
if ( !preg_match( '!^[a-zA-Z0-9-_]{1,255}$!', $this->name ) ) {
throw new InvalidArgumentException( "Backend name '{$this->name}' is invalid." );
} elseif ( !is_string( $this->domainId ) ) {
$this->statusWrapper = $config['statusWrapper'] ?? null;
$this->profiler = $config['profiler'] ?? null;
- $this->logger = $config['logger'] ?? new \Psr\Log\NullLogger();
+ if ( !is_callable( $this->profiler ) ) {
+ $this->profiler = null;
+ }
+ $this->logger = $config['logger'] ?? new NullLogger();
$this->statusWrapper = $config['statusWrapper'] ?? null;
$this->tmpDirectory = $config['tmpDirectory'] ?? null;
}
if ( empty( $opts['bypassReadOnly'] ) && $this->isReadOnly() ) {
return $this->newStatus( 'backend-fail-readonly', $this->name, $this->readOnly );
}
- if ( !count( $ops ) ) {
+ if ( $ops === [] ) {
return $this->newStatus(); // nothing to do
}
}
/** @noinspection PhpUnusedLocalVariableInspection */
- $scope = $this->getScopedPHPBehaviorForOps(); // try to ignore client aborts
+ $scope = ScopedCallback::newScopedIgnoreUserAbort(); // try to ignore client aborts
return $this->doOperationsInternal( $ops, $opts );
}
* @see FileBackend::doOperations()
* @param array $ops
* @param array $opts
+ * @return StatusValue
*/
abstract protected function doOperationsInternal( array $ops, array $opts );
if ( empty( $opts['bypassReadOnly'] ) && $this->isReadOnly() ) {
return $this->newStatus( 'backend-fail-readonly', $this->name, $this->readOnly );
}
- if ( !count( $ops ) ) {
+ if ( $ops === [] ) {
return $this->newStatus(); // nothing to do
}
}
/** @noinspection PhpUnusedLocalVariableInspection */
- $scope = $this->getScopedPHPBehaviorForOps(); // try to ignore client aborts
+ $scope = ScopedCallback::newScopedIgnoreUserAbort(); // try to ignore client aborts
return $this->doQuickOperationsInternal( $ops );
}
/**
* @see FileBackend::doQuickOperations()
* @param array $ops
+ * @return StatusValue
* @since 1.20
*/
abstract protected function doQuickOperationsInternal( array $ops );
return $this->newStatus( 'backend-fail-readonly', $this->name, $this->readOnly );
}
/** @noinspection PhpUnusedLocalVariableInspection */
- $scope = $this->getScopedPHPBehaviorForOps(); // try to ignore client aborts
+ $scope = ScopedCallback::newScopedIgnoreUserAbort(); // try to ignore client aborts
return $this->doPrepare( $params );
}
/**
* @see FileBackend::prepare()
* @param array $params
+ * @return StatusValue
*/
abstract protected function doPrepare( array $params );
* files whereas key/value store backends might revoke container
* access to the storage user representing end-users in web requests.
*
- * This is not guaranteed to actually make files or listings publically hidden.
+ * This is not guaranteed to actually make files or listings publicly hidden.
* Additional server configuration may be needed to achieve the desired effect.
*
* @param array $params Parameters include:
return $this->newStatus( 'backend-fail-readonly', $this->name, $this->readOnly );
}
/** @noinspection PhpUnusedLocalVariableInspection */
- $scope = $this->getScopedPHPBehaviorForOps(); // try to ignore client aborts
+ $scope = ScopedCallback::newScopedIgnoreUserAbort(); // try to ignore client aborts
return $this->doSecure( $params );
}
/**
* @see FileBackend::secure()
* @param array $params
+ * @return StatusValue
*/
abstract protected function doSecure( array $params );
* access to the storage user representing end-users in web requests.
* This essentially can undo the result of secure() calls.
*
- * This is not guaranteed to actually make files or listings publically viewable.
+ * This is not guaranteed to actually make files or listings publicly viewable.
* Additional server configuration may be needed to achieve the desired effect.
*
* @param array $params Parameters include:
return $this->newStatus( 'backend-fail-readonly', $this->name, $this->readOnly );
}
/** @noinspection PhpUnusedLocalVariableInspection */
- $scope = $this->getScopedPHPBehaviorForOps(); // try to ignore client aborts
+ $scope = ScopedCallback::newScopedIgnoreUserAbort(); // try to ignore client aborts
return $this->doPublish( $params );
}
/**
* @see FileBackend::publish()
* @param array $params
+ * @return StatusValue
*/
abstract protected function doPublish( array $params );
return $this->newStatus( 'backend-fail-readonly', $this->name, $this->readOnly );
}
/** @noinspection PhpUnusedLocalVariableInspection */
- $scope = $this->getScopedPHPBehaviorForOps(); // try to ignore client aborts
+ $scope = ScopedCallback::newScopedIgnoreUserAbort(); // try to ignore client aborts
return $this->doClean( $params );
}
/**
* @see FileBackend::clean()
* @param array $params
+ * @return StatusValue
*/
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 != 'cli' ) { // https://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.
* - StatusValue::newGood() if this method is called without parameters
* - StatusValue::newFatal() with all parameters to this method if passed in
*
- * @param string $args,...
+ * @param string ...$args
* @return StatusValue
*/
- final protected function newStatus() {
- $args = func_get_args();
+ final protected function newStatus( ...$args ) {
if ( count( $args ) ) {
$sv = StatusValue::newFatal( ...$args );
} else {
* @return ScopedCallback|null
*/
protected function scopedProfileSection( $section ) {
- if ( $this->profiler ) {
- call_user_func( [ $this->profiler, 'profileIn' ], $section );
- return new ScopedCallback( [ $this->profiler, 'profileOut' ], [ $section ] );
- }
-
- return null;
+ return $this->profiler ? ( $this->profiler )( $section ) : null;
}
protected function resetOutputBuffer() {