Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 26 |
|
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 | /** |
23 | * Helper class for tracking counterfactual file states when pre-checking file operation batches |
24 | * |
25 | * The file states are represented with (existence,size,sha1) triples. When combined with the |
26 | * current state of files in the backend, this can be used to simulate how a batch of operations |
27 | * would play out as a "dry run". This is used in FileBackend::doOperations() to bail out if any |
28 | * failure can be predicted before modifying any data. This includes file operation batches where |
29 | * the same file gets modified by different operations within the batch. |
30 | * |
31 | * @internal Only for use within FileBackend |
32 | */ |
33 | class FileStatePredicates { |
34 | protected const EXISTS = 'exists'; |
35 | protected const SIZE = 'size'; |
36 | protected const SHA1 = 'sha1'; |
37 | |
38 | /** @var array<string,array> Map of (storage path => file state map) */ |
39 | private $fileStateByPath; |
40 | |
41 | public function __construct() { |
42 | $this->fileStateByPath = []; |
43 | } |
44 | |
45 | /** |
46 | * Predicate that a file exists at the path |
47 | * |
48 | * @param string $path Storage path |
49 | * @param int|false|Closure $size File size or idempotent function yielding the size |
50 | * @param string|Closure $sha1Base36 File hash, or, idempotent function yielding the hash |
51 | */ |
52 | public function assumeFileExists( string $path, $size, $sha1Base36 ) { |
53 | $this->fileStateByPath[$path] = [ |
54 | self::EXISTS => true, |
55 | self::SIZE => $size, |
56 | self::SHA1 => $sha1Base36 |
57 | ]; |
58 | } |
59 | |
60 | /** |
61 | * Predicate that no file exists at the path |
62 | * |
63 | * @param string $path Storage path |
64 | */ |
65 | public function assumeFileDoesNotExist( string $path ) { |
66 | $this->fileStateByPath[$path] = [ |
67 | self::EXISTS => false, |
68 | self::SIZE => false, |
69 | self::SHA1 => false |
70 | ]; |
71 | } |
72 | |
73 | /** |
74 | * Get the hypothetical existance a file given predicated and current state of files |
75 | * |
76 | * @param string $path Storage path |
77 | * @param Closure $curExistenceFunc Function to compute the current existence for a given path |
78 | * @return bool|null Whether the file exists; null on error |
79 | */ |
80 | public function resolveFileExistence( string $path, $curExistenceFunc ) { |
81 | return self::resolveFileProperty( $path, self::EXISTS, $curExistenceFunc ); |
82 | } |
83 | |
84 | /** |
85 | * Get the hypothetical size of a file given predicated and current state of files |
86 | * |
87 | * @param string $path Storage path |
88 | * @param Closure $curSizeFunc Function to compute the current size for a given path |
89 | * @return int|false Bytes; false on error |
90 | */ |
91 | public function resolveFileSize( string $path, $curSizeFunc ) { |
92 | return self::resolveFileProperty( $path, self::SIZE, $curSizeFunc ); |
93 | } |
94 | |
95 | /** |
96 | * Get the hypothetical SHA-1 hash of a file given predicated and current state of files |
97 | * |
98 | * @param string $path Storage path |
99 | * @param Closure $curSha1Func Function to compute the current SHA-1 hash for a given path |
100 | * @return string|false Base 36 SHA-1 hash; false on error |
101 | */ |
102 | public function resolveFileSha1Base36( string $path, $curSha1Func ) { |
103 | return self::resolveFileProperty( $path, self::SHA1, $curSha1Func ); |
104 | } |
105 | |
106 | /** |
107 | * @param string $path Storage path |
108 | * @param string $property One of (self::EXISTS, self::SIZE, self::SHA1) |
109 | * @param Closure $curValueFunc Function to compute the current value for a given path |
110 | * @return mixed |
111 | */ |
112 | private function resolveFileProperty( $path, $property, $curValueFunc ) { |
113 | if ( isset( $this->fileStateByPath[$path] ) ) { |
114 | // File is predicated to have a counterfactual state |
115 | $value = $this->fileStateByPath[$path][$property]; |
116 | if ( $value instanceof Closure ) { |
117 | $value = $value(); |
118 | $this->fileStateByPath[$path][$property] = $value; |
119 | } |
120 | } else { |
121 | // File is not predicated to have a counterfactual state; use the current state |
122 | $value = $curValueFunc( $path ); |
123 | } |
124 | |
125 | return $value; |
126 | } |
127 | |
128 | /** |
129 | * @param string[] $paths List of storage paths |
130 | * @return self Clone predicates limited to the given paths |
131 | */ |
132 | public function snapshot( array $paths ) { |
133 | $snapshot = new self(); |
134 | foreach ( $paths as $path ) { |
135 | if ( isset( $this->fileStateByPath[$path] ) ) { |
136 | $snapshot->fileStateByPath[$path] = $this->fileStateByPath[$path]; |
137 | } |
138 | } |
139 | |
140 | return $snapshot; |
141 | } |
142 | } |