Completed
Branch master (5998bb)
by
unknown
29:17
created

MemoryFileBackend::doStreamFile()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 14
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 8
nc 2
nop 1
dl 0
loc 14
rs 9.4285
c 0
b 0
f 0
1
<?php
2
/**
3
 * Simulation of a backend storage in memory.
4
 *
5
 * This program is free software; you can redistribute it and/or modify
6
 * it under the terms of the GNU General Public License as published by
7
 * the Free Software Foundation; either version 2 of the License, or
8
 * (at your option) any later version.
9
 *
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
 * GNU General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU General Public License along
16
 * with this program; if not, write to the Free Software Foundation, Inc.,
17
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18
 * http://www.gnu.org/copyleft/gpl.html
19
 *
20
 * @file
21
 * @ingroup FileBackend
22
 * @author Aaron Schulz
23
 */
24
25
/**
26
 * Simulation of a backend storage in memory.
27
 *
28
 * All data in the backend is automatically deleted at the end of PHP execution.
29
 * Since the data stored here is volatile, this is only useful for staging or testing.
30
 *
31
 * @ingroup FileBackend
32
 * @since 1.23
33
 */
34
class MemoryFileBackend extends FileBackendStore {
35
	/** @var array Map of (file path => (data,mtime) */
36
	protected $files = [];
37
38
	public function getFeatures() {
39
		return self::ATTR_UNICODE_PATHS;
40
	}
41
42
	public function isPathUsableInternal( $storagePath ) {
43
		return true;
44
	}
45
46
	protected function doCreateInternal( array $params ) {
47
		$status = Status::newGood();
48
49
		$dst = $this->resolveHashKey( $params['dst'] );
50
		if ( $dst === null ) {
51
			$status->fatal( 'backend-fail-invalidpath', $params['dst'] );
52
53
			return $status;
54
		}
55
56
		$this->files[$dst] = [
57
			'data' => $params['content'],
58
			'mtime' => wfTimestamp( TS_MW, time() )
59
		];
60
61
		return $status;
62
	}
63
64
	protected function doStoreInternal( array $params ) {
65
		$status = Status::newGood();
66
67
		$dst = $this->resolveHashKey( $params['dst'] );
68
		if ( $dst === null ) {
69
			$status->fatal( 'backend-fail-invalidpath', $params['dst'] );
70
71
			return $status;
72
		}
73
74
		MediaWiki\suppressWarnings();
75
		$data = file_get_contents( $params['src'] );
76
		MediaWiki\restoreWarnings();
77 View Code Duplication
		if ( $data === false ) { // source doesn't exist?
78
			$status->fatal( 'backend-fail-store', $params['src'], $params['dst'] );
79
80
			return $status;
81
		}
82
83
		$this->files[$dst] = [
84
			'data' => $data,
85
			'mtime' => wfTimestamp( TS_MW, time() )
86
		];
87
88
		return $status;
89
	}
90
91
	protected function doCopyInternal( array $params ) {
92
		$status = Status::newGood();
93
94
		$src = $this->resolveHashKey( $params['src'] );
95
		if ( $src === null ) {
96
			$status->fatal( 'backend-fail-invalidpath', $params['src'] );
97
98
			return $status;
99
		}
100
101
		$dst = $this->resolveHashKey( $params['dst'] );
102
		if ( $dst === null ) {
103
			$status->fatal( 'backend-fail-invalidpath', $params['dst'] );
104
105
			return $status;
106
		}
107
108 View Code Duplication
		if ( !isset( $this->files[$src] ) ) {
109
			if ( empty( $params['ignoreMissingSource'] ) ) {
110
				$status->fatal( 'backend-fail-copy', $params['src'], $params['dst'] );
111
			}
112
113
			return $status;
114
		}
115
116
		$this->files[$dst] = [
117
			'data' => $this->files[$src]['data'],
118
			'mtime' => wfTimestamp( TS_MW, time() )
119
		];
120
121
		return $status;
122
	}
123
124
	protected function doDeleteInternal( array $params ) {
125
		$status = Status::newGood();
126
127
		$src = $this->resolveHashKey( $params['src'] );
128
		if ( $src === null ) {
129
			$status->fatal( 'backend-fail-invalidpath', $params['src'] );
130
131
			return $status;
132
		}
133
134 View Code Duplication
		if ( !isset( $this->files[$src] ) ) {
135
			if ( empty( $params['ignoreMissingSource'] ) ) {
136
				$status->fatal( 'backend-fail-delete', $params['src'] );
137
			}
138
139
			return $status;
140
		}
141
142
		unset( $this->files[$src] );
143
144
		return $status;
145
	}
146
147
	protected function doGetFileStat( array $params ) {
148
		$src = $this->resolveHashKey( $params['src'] );
149
		if ( $src === null ) {
150
			return null;
151
		}
152
153
		if ( isset( $this->files[$src] ) ) {
154
			return [
155
				'mtime' => $this->files[$src]['mtime'],
156
				'size' => strlen( $this->files[$src]['data'] ),
157
			];
158
		}
159
160
		return false;
161
	}
162
163
	protected function doGetLocalCopyMulti( array $params ) {
164
		$tmpFiles = []; // (path => TempFSFile)
165
		foreach ( $params['srcs'] as $srcPath ) {
166
			$src = $this->resolveHashKey( $srcPath );
167
			if ( $src === null || !isset( $this->files[$src] ) ) {
168
				$fsFile = null;
169
			} else {
170
				// Create a new temporary file with the same extension...
171
				$ext = FileBackend::extensionFromPath( $src );
172
				$fsFile = TempFSFile::factory( 'localcopy_', $ext );
173
				if ( $fsFile ) {
174
					$bytes = file_put_contents( $fsFile->getPath(), $this->files[$src]['data'] );
175
					if ( $bytes !== strlen( $this->files[$src]['data'] ) ) {
176
						$fsFile = null;
177
					}
178
				}
179
			}
180
			$tmpFiles[$srcPath] = $fsFile;
181
		}
182
183
		return $tmpFiles;
184
	}
185
186
	protected function doDirectoryExists( $container, $dir, array $params ) {
187
		$prefix = rtrim( "$container/$dir", '/' ) . '/';
188
		foreach ( $this->files as $path => $data ) {
189
			if ( strpos( $path, $prefix ) === 0 ) {
190
				return true;
191
			}
192
		}
193
194
		return false;
195
	}
196
197
	public function getDirectoryListInternal( $container, $dir, array $params ) {
198
		$dirs = [];
199
		$prefix = rtrim( "$container/$dir", '/' ) . '/';
200
		$prefixLen = strlen( $prefix );
201
		foreach ( $this->files as $path => $data ) {
202
			if ( strpos( $path, $prefix ) === 0 ) {
203
				$relPath = substr( $path, $prefixLen );
204
				if ( $relPath === false ) {
205
					continue;
206
				} elseif ( strpos( $relPath, '/' ) === false ) {
207
					continue; // just a file
208
				}
209
				$parts = array_slice( explode( '/', $relPath ), 0, -1 ); // last part is file name
210
				if ( !empty( $params['topOnly'] ) ) {
211
					$dirs[$parts[0]] = 1; // top directory
212
				} else {
213
					$current = '';
214
					foreach ( $parts as $part ) { // all directories
215
						$dir = ( $current === '' ) ? $part : "$current/$part";
216
						$dirs[$dir] = 1;
217
						$current = $dir;
218
					}
219
				}
220
			}
221
		}
222
223
		return array_keys( $dirs );
224
	}
225
226
	public function getFileListInternal( $container, $dir, array $params ) {
227
		$files = [];
228
		$prefix = rtrim( "$container/$dir", '/' ) . '/';
229
		$prefixLen = strlen( $prefix );
230
		foreach ( $this->files as $path => $data ) {
231
			if ( strpos( $path, $prefix ) === 0 ) {
232
				$relPath = substr( $path, $prefixLen );
233
				if ( $relPath === false ) {
234
					continue;
235
				} elseif ( !empty( $params['topOnly'] ) && strpos( $relPath, '/' ) !== false ) {
236
					continue;
237
				}
238
				$files[] = $relPath;
239
			}
240
		}
241
242
		return $files;
243
	}
244
245
	protected function directoriesAreVirtual() {
246
		return true;
247
	}
248
249
	/**
250
	 * Get the absolute file system path for a storage path
251
	 *
252
	 * @param string $storagePath Storage path
253
	 * @return string|null
254
	 */
255
	protected function resolveHashKey( $storagePath ) {
256
		list( $fullCont, $relPath ) = $this->resolveStoragePathReal( $storagePath );
257
		if ( $relPath === null ) {
258
			return null; // invalid
259
		}
260
261
		return ( $relPath !== '' ) ? "$fullCont/$relPath" : $fullCont;
262
	}
263
}
264