28use InvalidArgumentException;
29use Psr\Log\LoggerInterface;
33use Wikimedia\RequestTimeout\TimeoutException;
70 private const STATE_NEW = 1;
72 private const STATE_CHECKED = 2;
74 private const STATE_ATTEMPTED = 3;
89 foreach ( $required as $name ) {
90 if ( isset(
$params[$name] ) ) {
91 $this->params[$name] =
$params[$name];
93 throw new InvalidArgumentException(
"File operation missing parameter '$name'." );
96 foreach ( $optional as $name ) {
97 if ( isset(
$params[$name] ) ) {
98 $this->params[$name] =
$params[$name];
101 foreach ( $paths as $name ) {
102 if ( isset( $this->params[$name] ) ) {
119 return $res ??
$path;
132 return $this->params[$name] ??
null;
150 return [
'read' => [],
'write' => [] ];
174 if ( isset( $deps[
'read'][
$path] ) || isset( $deps[
'write'][
$path] ) ) {
179 if ( isset( $deps[
'write'][
$path] ) ) {
196 if ( $this->state !== self::STATE_NEW ) {
197 return StatusValue::newFatal(
'fileop-fail-state', self::STATE_NEW, $this->state );
199 $this->state = self::STATE_CHECKED;
201 $status = StatusValue::newGood();
203 if ( !$this->backend->isPathUsableInternal(
$path ) ) {
204 $status->fatal(
'backend-fail-usable',
$path );
207 if ( !$status->isOK() ) {
212 $status = $this->
doPrecheck( $opPredicates, $predicates );
213 if ( !$status->isOK() ) {
233 return StatusValue::newGood();
242 if ( $this->state !== self::STATE_CHECKED ) {
243 return StatusValue::newFatal(
'fileop-fail-state', self::STATE_CHECKED, $this->state );
244 } elseif ( $this->
failed ) {
245 return StatusValue::newFatal(
'fileop-fail-attempt-precheck' );
247 $this->state = self::STATE_ATTEMPTED;
249 $status = StatusValue::newGood();
252 if ( !$status->isOK() ) {
265 return StatusValue::newGood();
276 $this->async =
false;
287 $this->state = self::STATE_CHECKED;
298 $this->state = self::STATE_CHECKED;
309 return [ [], [], [] ];
346 return array_values( array_unique(
367 $status = StatusValue::newGood();
371 $this->overwriteSameCase =
false;
372 if ( $this->destExists ) {
373 if ( $this->
getParam(
'overwrite' ) ) {
375 } elseif ( $this->
getParam(
'overwriteSame' ) ) {
377 $sourceSize = ( $sourceSize instanceof Closure ) ? $sourceSize() : $sourceSize;
378 $sourceSha1 = ( $sourceSha1 instanceof Closure ) ? $sourceSha1() : $sourceSha1;
380 $dstSize = $this->
resolveFileSize( $this->params[
'dst'], $opPredicates );
382 if ( !strlen( $sourceSha1 ) || !strlen( $dstSha1 ) ) {
383 $status->fatal(
'backend-fail-hashes' );
384 } elseif ( !is_int( $sourceSize ) || !is_int( $dstSize ) ) {
385 $status->fatal(
'backend-fail-sizes' );
386 } elseif ( $sourceSha1 !== $dstSha1 || $sourceSize !== $dstSize ) {
388 $status->fatal(
'backend-fail-notsame', $this->params[
'dst'] );
390 $this->overwriteSameCase =
true;
393 $status->fatal(
'backend-fail-alreadyexists', $this->params[
'dst'] );
395 } elseif ( $this->destExists === FileBackend::EXISTENCE_ERROR ) {
396 $status->fatal(
'backend-fail-stat', $this->params[
'dst'] );
416 return $this->backend->fileExists( [
'src' =>
$path,
'latest' =>
true ] );
436 return $this->backend->getFileSize( [
'src' =>
$path,
'latest' =>
true ] );
452 return $this->backend->getFileSha1Base36( [
'src' =>
$path,
'latest' =>
true ] );
473 $this->logger->error( static::class .
' failed: ' . $action,
474 [
'params' => $this->params ]
476 }
catch ( TimeoutException $e ) {
478 }
catch ( Exception $e ) {
485class_alias( FileOp::class,
'FileOp' );
Generic operation result class Has warning/error list, boolean status and arbitrary value.