14use InvalidArgumentException;
15use Psr\Log\LoggerInterface;
19use Wikimedia\RequestTimeout\TimeoutException;
56 private const STATE_NEW = 1;
58 private const STATE_CHECKED = 2;
60 private const STATE_ATTEMPTED = 3;
75 foreach ( $required as $name ) {
76 if ( isset(
$params[$name] ) ) {
77 $this->params[$name] =
$params[$name];
79 throw new InvalidArgumentException(
"File operation missing parameter '$name'." );
82 foreach ( $optional as $name ) {
83 if ( isset(
$params[$name] ) ) {
84 $this->params[$name] =
$params[$name];
87 foreach ( $paths as $name ) {
88 if ( isset( $this->params[$name] ) ) {
105 return $res ??
$path;
118 return $this->params[$name] ??
null;
136 return [
'read' => [],
'write' => [] ];
160 if ( isset( $deps[
'read'][
$path] ) || isset( $deps[
'write'][
$path] ) ) {
165 if ( isset( $deps[
'write'][
$path] ) ) {
182 if ( $this->state !== self::STATE_NEW ) {
183 return StatusValue::newFatal(
'fileop-fail-state', self::STATE_NEW, $this->state );
185 $this->state = self::STATE_CHECKED;
187 $status = StatusValue::newGood();
189 if ( !$this->backend->isPathUsableInternal(
$path ) ) {
190 $status->fatal(
'backend-fail-usable',
$path );
193 if ( !$status->isOK() ) {
198 $status = $this->
doPrecheck( $opPredicates, $predicates );
199 if ( !$status->isOK() ) {
219 return StatusValue::newGood();
228 if ( $this->state !== self::STATE_CHECKED ) {
229 return StatusValue::newFatal(
'fileop-fail-state', self::STATE_CHECKED, $this->state );
230 } elseif ( $this->
failed ) {
231 return StatusValue::newFatal(
'fileop-fail-attempt-precheck' );
233 $this->state = self::STATE_ATTEMPTED;
235 $status = StatusValue::newGood();
238 if ( !$status->isOK() ) {
251 return StatusValue::newGood();
262 $this->async =
false;
273 $this->state = self::STATE_CHECKED;
284 $this->state = self::STATE_CHECKED;
295 return [ [], [], [] ];
332 return array_values( array_unique(
353 $status = StatusValue::newGood();
357 $this->overwriteSameCase =
false;
358 if ( $this->destExists ) {
359 if ( $this->
getParam(
'overwrite' ) ) {
361 } elseif ( $this->
getParam(
'overwriteSame' ) ) {
363 $sourceSize = ( $sourceSize instanceof Closure ) ? $sourceSize() : $sourceSize;
364 $sourceSha1 = ( $sourceSha1 instanceof Closure ) ? $sourceSha1() : $sourceSha1;
366 $dstSize = $this->
resolveFileSize( $this->params[
'dst'], $opPredicates );
368 if ( !strlen( $sourceSha1 ) || !strlen( $dstSha1 ) ) {
369 $status->fatal(
'backend-fail-hashes' );
370 } elseif ( !is_int( $sourceSize ) || !is_int( $dstSize ) ) {
371 $status->fatal(
'backend-fail-sizes' );
372 } elseif ( $sourceSha1 !== $dstSha1 || $sourceSize !== $dstSize ) {
374 $status->fatal(
'backend-fail-notsame', $this->params[
'dst'] );
376 $this->overwriteSameCase =
true;
379 $status->fatal(
'backend-fail-alreadyexists', $this->params[
'dst'] );
381 } elseif ( $this->destExists === FileBackend::EXISTENCE_ERROR ) {
382 $status->fatal(
'backend-fail-stat', $this->params[
'dst'] );
402 return $this->backend->fileExists( [
'src' =>
$path,
'latest' =>
true ] );
422 return $this->backend->getFileSize( [
'src' =>
$path,
'latest' =>
true ] );
438 return $this->backend->getFileSha1Base36( [
'src' =>
$path,
'latest' =>
true ] );
459 $this->logger->error( static::class .
' failed: ' . $action,
460 [
'params' => $this->params ]
462 }
catch ( TimeoutException $e ) {
464 }
catch ( Exception ) {
471class_alias( FileOp::class,
'FileOp' );
Generic operation result class Has warning/error list, boolean status and arbitrary value.