28use InvalidArgumentException;
30use Psr\Log\LoggerInterface;
34use Wikimedia\RequestTimeout\TimeoutException;
71 private const STATE_NEW = 1;
73 private const STATE_CHECKED = 2;
75 private const STATE_ATTEMPTED = 3;
90 foreach ( $required as $name ) {
91 if ( isset(
$params[$name] ) ) {
92 $this->params[$name] =
$params[$name];
94 throw new InvalidArgumentException(
"File operation missing parameter '$name'." );
97 foreach ( $optional as $name ) {
98 if ( isset(
$params[$name] ) ) {
99 $this->params[$name] =
$params[$name];
102 foreach ( $paths as $name ) {
103 if ( isset( $this->params[$name] ) ) {
120 return $res ??
$path;
133 return $this->params[$name] ??
null;
151 return [
'read' => [],
'write' => [] ];
175 if ( isset( $deps[
'read'][
$path] ) || isset( $deps[
'write'][
$path] ) ) {
180 if ( isset( $deps[
'write'][
$path] ) ) {
197 if ( $this->state !== self::STATE_NEW ) {
198 return StatusValue::newFatal(
'fileop-fail-state', self::STATE_NEW, $this->state );
200 $this->state = self::STATE_CHECKED;
202 $status = StatusValue::newGood();
204 if ( !$this->backend->isPathUsableInternal(
$path ) ) {
205 $status->fatal(
'backend-fail-usable',
$path );
208 if ( !$status->isOK() ) {
213 $status = $this->
doPrecheck( $opPredicates, $predicates );
214 if ( !$status->isOK() ) {
234 return StatusValue::newGood();
243 if ( $this->state !== self::STATE_CHECKED ) {
244 return StatusValue::newFatal(
'fileop-fail-state', self::STATE_CHECKED, $this->state );
245 } elseif ( $this->
failed ) {
246 return StatusValue::newFatal(
'fileop-fail-attempt-precheck' );
248 $this->state = self::STATE_ATTEMPTED;
250 $status = StatusValue::newGood();
253 if ( !$status->isOK() ) {
266 return StatusValue::newGood();
277 $this->async =
false;
288 $this->state = self::STATE_CHECKED;
299 $this->state = self::STATE_CHECKED;
310 return [ [], [], [] ];
347 return array_values( array_unique(
368 $status = StatusValue::newGood();
372 $this->overwriteSameCase =
false;
373 if ( $this->destExists ) {
374 if ( $this->
getParam(
'overwrite' ) ) {
376 } elseif ( $this->
getParam(
'overwriteSame' ) ) {
378 $sourceSize = ( $sourceSize instanceof Closure ) ? $sourceSize() : $sourceSize;
379 $sourceSha1 = ( $sourceSha1 instanceof Closure ) ? $sourceSha1() : $sourceSha1;
381 $dstSize = $this->
resolveFileSize( $this->params[
'dst'], $opPredicates );
383 if ( !strlen( $sourceSha1 ) || !strlen( $dstSha1 ) ) {
384 $status->fatal(
'backend-fail-hashes' );
385 } elseif ( !is_int( $sourceSize ) || !is_int( $dstSize ) ) {
386 $status->fatal(
'backend-fail-sizes' );
387 } elseif ( $sourceSha1 !== $dstSha1 || $sourceSize !== $dstSize ) {
389 $status->fatal(
'backend-fail-notsame', $this->params[
'dst'] );
391 $this->overwriteSameCase =
true;
394 $status->fatal(
'backend-fail-alreadyexists', $this->params[
'dst'] );
396 } elseif ( $this->destExists === FileBackend::EXISTENCE_ERROR ) {
397 $status->fatal(
'backend-fail-stat', $this->params[
'dst'] );
417 return $this->backend->fileExists( [
'src' =>
$path,
'latest' =>
true ] );
437 return $this->backend->getFileSize( [
'src' =>
$path,
'latest' =>
true ] );
453 return $this->backend->getFileSha1Base36( [
'src' =>
$path,
'latest' =>
true ] );
474 $params[
'failedAction'] = $action;
476 $this->logger->error( static::class .
477 " failed: " . FormatJson::encode(
$params ) );
478 }
catch ( TimeoutException $e ) {
480 }
catch ( Exception $e ) {
487class_alias( FileOp::class,
'FileOp' );
Generic operation result class Has warning/error list, boolean status and arbitrary value.