3 * Simulation of a backend storage in memory.
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.
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.
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
21 * @ingroup FileBackend
24 use Wikimedia\AtEase\AtEase
;
27 * Simulation of a backend storage in memory.
29 * All data in the backend is automatically deleted at the end of PHP execution.
30 * Since the data stored here is volatile, this is only useful for staging or testing.
32 * @ingroup FileBackend
35 class MemoryFileBackend
extends FileBackendStore
{
36 /** @var array Map of (file path => (data,mtime) */
37 protected $files = [];
39 public function getFeatures() {
40 return self
::ATTR_UNICODE_PATHS
;
43 public function isPathUsableInternal( $storagePath ) {
47 protected function doCreateInternal( array $params ) {
48 $status = $this->newStatus();
50 $dst = $this->resolveHashKey( $params['dst'] );
51 if ( $dst === null ) {
52 $status->fatal( 'backend-fail-invalidpath', $params['dst'] );
57 $this->files
[$dst] = [
58 'data' => $params['content'],
59 'mtime' => wfTimestamp( TS_MW
, time() )
65 protected function doStoreInternal( array $params ) {
66 $status = $this->newStatus();
68 $dst = $this->resolveHashKey( $params['dst'] );
69 if ( $dst === null ) {
70 $status->fatal( 'backend-fail-invalidpath', $params['dst'] );
75 AtEase
::suppressWarnings();
76 $data = file_get_contents( $params['src'] );
77 AtEase
::restoreWarnings();
78 if ( $data === false ) { // source doesn't exist?
79 $status->fatal( 'backend-fail-store', $params['src'], $params['dst'] );
84 $this->files
[$dst] = [
86 'mtime' => wfTimestamp( TS_MW
, time() )
92 protected function doCopyInternal( array $params ) {
93 $status = $this->newStatus();
95 $src = $this->resolveHashKey( $params['src'] );
96 if ( $src === null ) {
97 $status->fatal( 'backend-fail-invalidpath', $params['src'] );
102 $dst = $this->resolveHashKey( $params['dst'] );
103 if ( $dst === null ) {
104 $status->fatal( 'backend-fail-invalidpath', $params['dst'] );
109 if ( !isset( $this->files
[$src] ) ) {
110 if ( empty( $params['ignoreMissingSource'] ) ) {
111 $status->fatal( 'backend-fail-copy', $params['src'], $params['dst'] );
117 $this->files
[$dst] = [
118 'data' => $this->files
[$src]['data'],
119 'mtime' => wfTimestamp( TS_MW
, time() )
125 protected function doDeleteInternal( array $params ) {
126 $status = $this->newStatus();
128 $src = $this->resolveHashKey( $params['src'] );
129 if ( $src === null ) {
130 $status->fatal( 'backend-fail-invalidpath', $params['src'] );
135 if ( !isset( $this->files
[$src] ) ) {
136 if ( empty( $params['ignoreMissingSource'] ) ) {
137 $status->fatal( 'backend-fail-delete', $params['src'] );
143 unset( $this->files
[$src] );
148 protected function doGetFileStat( array $params ) {
149 $src = $this->resolveHashKey( $params['src'] );
150 if ( $src === null ) {
154 if ( isset( $this->files
[$src] ) ) {
156 'mtime' => $this->files
[$src]['mtime'],
157 'size' => strlen( $this->files
[$src]['data'] ),
164 protected function doGetLocalCopyMulti( array $params ) {
165 $tmpFiles = []; // (path => TempFSFile)
166 foreach ( $params['srcs'] as $srcPath ) {
167 $src = $this->resolveHashKey( $srcPath );
168 if ( $src === null ||
!isset( $this->files
[$src] ) ) {
171 // Create a new temporary file with the same extension...
172 $ext = FileBackend
::extensionFromPath( $src );
173 $fsFile = $this->tmpFileFactory
->newTempFSFile( 'localcopy_', $ext );
175 $bytes = file_put_contents( $fsFile->getPath(), $this->files
[$src]['data'] );
176 if ( $bytes !== strlen( $this->files
[$src]['data'] ) ) {
181 $tmpFiles[$srcPath] = $fsFile;
187 protected function doDirectoryExists( $container, $dir, array $params ) {
188 $prefix = rtrim( "$container/$dir", '/' ) . '/';
189 foreach ( $this->files
as $path => $data ) {
190 if ( strpos( $path, $prefix ) === 0 ) {
198 public function getDirectoryListInternal( $container, $dir, array $params ) {
200 $prefix = rtrim( "$container/$dir", '/' ) . '/';
201 $prefixLen = strlen( $prefix );
202 foreach ( $this->files
as $path => $data ) {
203 if ( strpos( $path, $prefix ) === 0 ) {
204 $relPath = substr( $path, $prefixLen );
205 if ( $relPath === false ) {
207 } elseif ( strpos( $relPath, '/' ) === false ) {
208 continue; // just a file
210 $parts = array_slice( explode( '/', $relPath ), 0, -1 ); // last part is file name
211 if ( !empty( $params['topOnly'] ) ) {
212 $dirs[$parts[0]] = 1; // top directory
215 foreach ( $parts as $part ) { // all directories
216 $dir = ( $current === '' ) ?
$part : "$current/$part";
224 return array_keys( $dirs );
227 public function getFileListInternal( $container, $dir, array $params ) {
229 $prefix = rtrim( "$container/$dir", '/' ) . '/';
230 $prefixLen = strlen( $prefix );
231 foreach ( $this->files
as $path => $data ) {
232 if ( strpos( $path, $prefix ) === 0 ) {
233 $relPath = substr( $path, $prefixLen );
234 if ( $relPath === false ) {
236 } elseif ( !empty( $params['topOnly'] ) && strpos( $relPath, '/' ) !== false ) {
246 protected function directoriesAreVirtual() {
251 * Get the absolute file system path for a storage path
253 * @param string $storagePath Storage path
254 * @return string|null
256 protected function resolveHashKey( $storagePath ) {
257 list( $fullCont, $relPath ) = $this->resolveStoragePathReal( $storagePath );
258 if ( $relPath === null ) {
259 return null; // invalid
262 return ( $relPath !== '' ) ?
"$fullCont/$relPath" : $fullCont;