File: FileStatePredicates.php

package info (click to toggle)
mediawiki 1%3A1.43.3%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 417,464 kB
  • sloc: php: 1,062,949; javascript: 664,290; sql: 9,714; python: 5,458; xml: 3,489; sh: 1,131; makefile: 64
file content (149 lines) | stat: -rw-r--r-- 5,040 bytes parent folder | download
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' );