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