MediaWiki master
MemoryFileBackend.php
Go to the documentation of this file.
1<?php
24use Wikimedia\AtEase\AtEase;
25use Wikimedia\Timestamp\ConvertibleTimestamp;
26
38 protected $files = [];
39
40 public function getFeatures() {
42 }
43
44 public function isPathUsableInternal( $storagePath ) {
45 return ( $this->resolveHashKey( $storagePath ) !== null );
46 }
47
48 protected function doCreateInternal( array $params ) {
49 $status = $this->newStatus();
50
51 $dst = $this->resolveHashKey( $params['dst'] );
52 if ( $dst === null ) {
53 $status->fatal( 'backend-fail-invalidpath', $params['dst'] );
54
55 return $status;
56 }
57
58 $this->files[$dst] = [
59 'data' => $params['content'],
60 'mtime' => ConvertibleTimestamp::convert( TS_MW, time() )
61 ];
62
63 return $status;
64 }
65
66 protected function doStoreInternal( array $params ) {
67 $status = $this->newStatus();
68
69 $dst = $this->resolveHashKey( $params['dst'] );
70 if ( $dst === null ) {
71 $status->fatal( 'backend-fail-invalidpath', $params['dst'] );
72
73 return $status;
74 }
75
76 AtEase::suppressWarnings();
77 $data = file_get_contents( $params['src'] );
78 AtEase::restoreWarnings();
79 if ( $data === false ) { // source doesn't exist?
80 $status->fatal( 'backend-fail-store', $params['src'], $params['dst'] );
81
82 return $status;
83 }
84
85 $this->files[$dst] = [
86 'data' => $data,
87 'mtime' => ConvertibleTimestamp::convert( TS_MW, time() )
88 ];
89
90 return $status;
91 }
92
93 protected function doCopyInternal( array $params ) {
94 $status = $this->newStatus();
95
96 $src = $this->resolveHashKey( $params['src'] );
97 if ( $src === null ) {
98 $status->fatal( 'backend-fail-invalidpath', $params['src'] );
99
100 return $status;
101 }
102
103 $dst = $this->resolveHashKey( $params['dst'] );
104 if ( $dst === null ) {
105 $status->fatal( 'backend-fail-invalidpath', $params['dst'] );
106
107 return $status;
108 }
109
110 if ( !isset( $this->files[$src] ) ) {
111 if ( empty( $params['ignoreMissingSource'] ) ) {
112 $status->fatal( 'backend-fail-copy', $params['src'], $params['dst'] );
113 }
114
115 return $status;
116 }
117
118 $this->files[$dst] = [
119 'data' => $this->files[$src]['data'],
120 'mtime' => ConvertibleTimestamp::convert( TS_MW, time() )
121 ];
122
123 return $status;
124 }
125
126 protected function doMoveInternal( array $params ) {
127 $status = $this->newStatus();
128
129 $src = $this->resolveHashKey( $params['src'] );
130 if ( $src === null ) {
131 $status->fatal( 'backend-fail-invalidpath', $params['src'] );
132
133 return $status;
134 }
135
136 $dst = $this->resolveHashKey( $params['dst'] );
137 if ( $dst === null ) {
138 $status->fatal( 'backend-fail-invalidpath', $params['dst'] );
139
140 return $status;
141 }
142
143 if ( !isset( $this->files[$src] ) ) {
144 if ( empty( $params['ignoreMissingSource'] ) ) {
145 $status->fatal( 'backend-fail-move', $params['src'], $params['dst'] );
146 }
147
148 return $status;
149 }
150
151 $this->files[$dst] = $this->files[$src];
152 unset( $this->files[$src] );
153 $this->files[$dst]['mtime'] = ConvertibleTimestamp::convert( TS_MW, time() );
154
155 return $status;
156 }
157
158 protected function doDeleteInternal( array $params ) {
159 $status = $this->newStatus();
160
161 $src = $this->resolveHashKey( $params['src'] );
162 if ( $src === null ) {
163 $status->fatal( 'backend-fail-invalidpath', $params['src'] );
164
165 return $status;
166 }
167
168 if ( !isset( $this->files[$src] ) ) {
169 if ( empty( $params['ignoreMissingSource'] ) ) {
170 $status->fatal( 'backend-fail-delete', $params['src'] );
171 }
172
173 return $status;
174 }
175
176 unset( $this->files[$src] );
177
178 return $status;
179 }
180
181 protected function doGetFileStat( array $params ) {
182 $src = $this->resolveHashKey( $params['src'] );
183 if ( $src === null ) {
184 return self::RES_ERROR; // invalid path
185 }
186
187 if ( isset( $this->files[$src] ) ) {
188 return [
189 'mtime' => $this->files[$src]['mtime'],
190 'size' => strlen( $this->files[$src]['data'] ),
191 ];
192 }
193
194 return self::RES_ABSENT;
195 }
196
197 protected function doGetLocalCopyMulti( array $params ) {
198 $tmpFiles = []; // (path => TempFSFile)
199 foreach ( $params['srcs'] as $srcPath ) {
200 $src = $this->resolveHashKey( $srcPath );
201 if ( $src === null ) {
202 $fsFile = self::RES_ERROR;
203 } elseif ( !isset( $this->files[$src] ) ) {
204 $fsFile = self::RES_ABSENT;
205 } else {
206 // Create a new temporary file with the same extension...
207 $ext = FileBackend::extensionFromPath( $src );
208 $fsFile = $this->tmpFileFactory->newTempFSFile( 'localcopy_', $ext );
209 if ( $fsFile ) {
210 $bytes = file_put_contents( $fsFile->getPath(), $this->files[$src]['data'] );
211 if ( $bytes !== strlen( $this->files[$src]['data'] ) ) {
212 $fsFile = self::RES_ERROR;
213 }
214 }
215 }
216 $tmpFiles[$srcPath] = $fsFile;
217 }
218
219 return $tmpFiles;
220 }
221
222 protected function doDirectoryExists( $container, $dir, array $params ) {
223 $prefix = rtrim( "$container/$dir", '/' ) . '/';
224 foreach ( $this->files as $path => $data ) {
225 if ( strpos( $path, $prefix ) === 0 ) {
226 return true;
227 }
228 }
229
230 return false;
231 }
232
233 public function getDirectoryListInternal( $container, $dir, array $params ) {
234 $dirs = [];
235 $prefix = rtrim( "$container/$dir", '/' ) . '/';
236 $prefixLen = strlen( $prefix );
237 foreach ( $this->files as $path => $data ) {
238 if ( strpos( $path, $prefix ) === 0 ) {
239 $relPath = substr( $path, $prefixLen );
240 if ( $relPath === false ) {
241 continue;
242 } elseif ( strpos( $relPath, '/' ) === false ) {
243 continue; // just a file
244 }
245 $parts = array_slice( explode( '/', $relPath ), 0, -1 ); // last part is file name
246 if ( !empty( $params['topOnly'] ) ) {
247 $dirs[$parts[0]] = 1; // top directory
248 } else {
249 $current = '';
250 foreach ( $parts as $part ) { // all directories
251 $dir = ( $current === '' ) ? $part : "$current/$part";
252 $dirs[$dir] = 1;
253 $current = $dir;
254 }
255 }
256 }
257 }
258
259 return array_keys( $dirs );
260 }
261
262 public function getFileListInternal( $container, $dir, array $params ) {
263 $files = [];
264 $prefix = rtrim( "$container/$dir", '/' ) . '/';
265 $prefixLen = strlen( $prefix );
266 foreach ( $this->files as $path => $data ) {
267 if ( strpos( $path, $prefix ) === 0 ) {
268 $relPath = substr( $path, $prefixLen );
269 if ( $relPath === false ) {
270 continue;
271 } elseif ( !empty( $params['topOnly'] ) && strpos( $relPath, '/' ) !== false ) {
272 continue;
273 }
274 $files[] = $relPath;
275 }
276 }
277
278 return $files;
279 }
280
281 protected function directoriesAreVirtual() {
282 return true;
283 }
284
291 protected function resolveHashKey( $storagePath ) {
292 [ $fullCont, $relPath ] = $this->resolveStoragePathReal( $storagePath );
293 if ( $relPath === null ) {
294 return null; // invalid
295 }
296
297 return ( $relPath !== '' ) ? "$fullCont/$relPath" : $fullCont;
298 }
299}
array $params
The job parameters.
Base class for all backends using particular storage medium.
resolveStoragePathReal( $storagePath)
Like resolveStoragePath() except null values are returned if the container is sharded and the shard c...
const ATTR_UNICODE_PATHS
static extensionFromPath( $path, $case='lowercase')
Get the final extension from a storage or FS path.
newStatus(... $args)
Yields the result of the status wrapper callback on either:
Simulation of a backend storage in memory.
doGetLocalCopyMulti(array $params)
doDirectoryExists( $container, $dir, array $params)
doCreateInternal(array $params)
getFileListInternal( $container, $dir, array $params)
Do not call this function from places outside FileBackend.
directoriesAreVirtual()
Is this a key/value store where directories are just virtual? Virtual directories exists in so much a...
getFeatures()
Get the a bitfield of extra features supported by the backend medium.
getDirectoryListInternal( $container, $dir, array $params)
Do not call this function from places outside FileBackend.
doStoreInternal(array $params)
doDeleteInternal(array $params)
doMoveInternal(array $params)
array $files
Map of (file path => (data,mtime)
doCopyInternal(array $params)
doGetFileStat(array $params)
isPathUsableInternal( $storagePath)
Check if a file can be created or changed at a given storage path in the backend.
resolveHashKey( $storagePath)
Get the absolute file system path for a storage path.