1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
|
<?php
/**
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
* @file
* @ingroup FileBackend
*/
namespace Wikimedia\FileBackend\FileOps;
use Closure;
/**
* Helper class for tracking counterfactual file states when pre-checking file operation batches
*
* The file states are represented with (existence,size,sha1) triples. When combined with the
* current state of files in the backend, this can be used to simulate how a batch of operations
* would play out as a "dry run". This is used in FileBackend::doOperations() to bail out if any
* failure can be predicted before modifying any data. This includes file operation batches where
* the same file gets modified by different operations within the batch.
*
* @internal Only for use within FileBackend
*/
class FileStatePredicates {
protected const EXISTS = 'exists';
protected const SIZE = 'size';
protected const SHA1 = 'sha1';
/** @var array<string,array> Map of (storage path => file state map) */
private $fileStateByPath;
public function __construct() {
$this->fileStateByPath = [];
}
/**
* Predicate that a file exists at the path
*
* @param string $path Storage path
* @param int|false|Closure $size File size or idempotent function yielding the size
* @param string|Closure $sha1Base36 File hash, or, idempotent function yielding the hash
*/
public function assumeFileExists( string $path, $size, $sha1Base36 ) {
$this->fileStateByPath[$path] = [
self::EXISTS => true,
self::SIZE => $size,
self::SHA1 => $sha1Base36
];
}
/**
* Predicate that no file exists at the path
*
* @param string $path Storage path
*/
public function assumeFileDoesNotExist( string $path ) {
$this->fileStateByPath[$path] = [
self::EXISTS => false,
self::SIZE => false,
self::SHA1 => false
];
}
/**
* Get the hypothetical existance a file given predicated and current state of files
*
* @param string $path Storage path
* @param Closure $curExistenceFunc Function to compute the current existence for a given path
* @return bool|null Whether the file exists; null on error
*/
public function resolveFileExistence( string $path, $curExistenceFunc ) {
return self::resolveFileProperty( $path, self::EXISTS, $curExistenceFunc );
}
/**
* Get the hypothetical size of a file given predicated and current state of files
*
* @param string $path Storage path
* @param Closure $curSizeFunc Function to compute the current size for a given path
* @return int|false Bytes; false on error
*/
public function resolveFileSize( string $path, $curSizeFunc ) {
return self::resolveFileProperty( $path, self::SIZE, $curSizeFunc );
}
/**
* Get the hypothetical SHA-1 hash of a file given predicated and current state of files
*
* @param string $path Storage path
* @param Closure $curSha1Func Function to compute the current SHA-1 hash for a given path
* @return string|false Base 36 SHA-1 hash; false on error
*/
public function resolveFileSha1Base36( string $path, $curSha1Func ) {
return self::resolveFileProperty( $path, self::SHA1, $curSha1Func );
}
/**
* @param string $path Storage path
* @param string $property One of (self::EXISTS, self::SIZE, self::SHA1)
* @param Closure $curValueFunc Function to compute the current value for a given path
* @return mixed
*/
private function resolveFileProperty( $path, $property, $curValueFunc ) {
if ( isset( $this->fileStateByPath[$path] ) ) {
// File is predicated to have a counterfactual state
$value = $this->fileStateByPath[$path][$property];
if ( $value instanceof Closure ) {
$value = $value();
$this->fileStateByPath[$path][$property] = $value;
}
} else {
// File is not predicated to have a counterfactual state; use the current state
$value = $curValueFunc( $path );
}
return $value;
}
/**
* @param string[] $paths List of storage paths
* @return self Clone predicates limited to the given paths
*/
public function snapshot( array $paths ) {
$snapshot = new self();
foreach ( $paths as $path ) {
if ( isset( $this->fileStateByPath[$path] ) ) {
$snapshot->fileStateByPath[$path] = $this->fileStateByPath[$path];
}
}
return $snapshot;
}
}
/** @deprecated class alias since 1.43 */
class_alias( FileStatePredicates::class, 'FileStatePredicates' );
|