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;
188 $status = $this->
doPrecheck( $opPredicates, $predicates );
189 if ( !$status->isOK() ) {
209 return StatusValue::newGood();
218 if ( $this->state !== self::STATE_CHECKED ) {
219 return StatusValue::newFatal(
'fileop-fail-state', self::STATE_CHECKED, $this->state );
220 } elseif ( $this->
failed ) {
221 return StatusValue::newFatal(
'fileop-fail-attempt-precheck' );
223 $this->state = self::STATE_ATTEMPTED;
225 $status = StatusValue::newGood();
228 if ( !$status->isOK() ) {
241 return StatusValue::newGood();
252 $this->async =
false;
263 $this->state = self::STATE_CHECKED;
274 $this->state = self::STATE_CHECKED;
285 return [ [], [], [] ];
322 return array_values( array_unique(
343 $status = StatusValue::newGood();
347 $this->overwriteSameCase =
false;
348 if ( $this->destExists ) {
349 if ( $this->
getParam(
'overwrite' ) ) {
351 } elseif ( $this->
getParam(
'overwriteSame' ) ) {
353 $sourceSize = ( $sourceSize instanceof Closure ) ? $sourceSize() : $sourceSize;
354 $sourceSha1 = ( $sourceSha1 instanceof Closure ) ? $sourceSha1() : $sourceSha1;
356 $dstSize = $this->
resolveFileSize( $this->params[
'dst'], $opPredicates );
358 if ( !strlen( $sourceSha1 ) || !strlen( $dstSha1 ) ) {
359 $status->fatal(
'backend-fail-hashes' );
360 } elseif ( !is_int( $sourceSize ) || !is_int( $dstSize ) ) {
361 $status->fatal(
'backend-fail-sizes' );
362 } elseif ( $sourceSha1 !== $dstSha1 || $sourceSize !== $dstSize ) {
364 $status->fatal(
'backend-fail-notsame', $this->params[
'dst'] );
366 $this->overwriteSameCase =
true;
369 $status->fatal(
'backend-fail-alreadyexists', $this->params[
'dst'] );
371 } elseif ( $this->destExists === FileBackend::EXISTENCE_ERROR ) {
372 $status->fatal(
'backend-fail-stat', $this->params[
'dst'] );
392 return $this->backend->fileExists( [
'src' =>
$path,
'latest' =>
true ] );
412 return $this->backend->getFileSize( [
'src' =>
$path,
'latest' =>
true ] );
428 return $this->backend->getFileSha1Base36( [
'src' =>
$path,
'latest' =>
true ] );
449 $this->logger->error( static::class .
' failed: ' . $action,
450 [
'params' => $this->params ]
452 }
catch ( TimeoutException $e ) {
454 }
catch ( Exception ) {
461class_alias( FileOp::class,
'FileOp' );
Generic operation result class Has warning/error list, boolean status and arbitrary value.