12use Wikimedia\AtEase\AtEase;
13use Wikimedia\Timestamp\ConvertibleTimestamp;
14use Wikimedia\Timestamp\TimestampFormat as TS;
44 if ( $dst ===
null ) {
45 $status->fatal(
'backend-fail-invalidpath', $params[
'dst'] );
50 $this->files[$dst] = [
51 'data' => $params[
'content'],
52 'mtime' => ConvertibleTimestamp::convert( TS::MW, time() )
63 if ( $dst ===
null ) {
64 $status->fatal(
'backend-fail-invalidpath', $params[
'dst'] );
69 AtEase::suppressWarnings();
70 $data = file_get_contents( $params[
'src'] );
71 AtEase::restoreWarnings();
72 if ( $data ===
false ) {
73 $status->fatal(
'backend-fail-store', $params[
'src'], $params[
'dst'] );
78 $this->files[$dst] = [
80 'mtime' => ConvertibleTimestamp::convert( TS::MW, time() )
88 return $this->copyInMemory( $params,
'copy' );
93 return $this->copyInMemory( $params,
'move' );
101 private function copyInMemory( array $params,
string $action ) {
105 if ( $src ===
null ) {
106 $status->fatal(
'backend-fail-invalidpath', $params[
'src'] );
112 if ( $dst ===
null ) {
113 $status->fatal(
'backend-fail-invalidpath', $params[
'dst'] );
118 if ( !isset( $this->files[$src] ) ) {
119 if ( empty( $params[
'ignoreMissingSource'] ) ) {
121 $status->fatal(
'backend-fail-' . $action, $params[
'src'], $params[
'dst'] );
127 $this->files[$dst] = [
128 'data' => $this->files[$src][
'data'],
129 'mtime' => ConvertibleTimestamp::convert( TS::MW, time() )
132 if ( $action ===
'move' ) {
133 unset( $this->files[$src] );
143 if ( $src ===
null ) {
144 $status->fatal(
'backend-fail-invalidpath', $params[
'src'] );
149 if ( !isset( $this->files[$src] ) ) {
150 if ( empty( $params[
'ignoreMissingSource'] ) ) {
151 $status->fatal(
'backend-fail-delete', $params[
'src'] );
157 unset( $this->files[$src] );
165 if ( $src ===
null ) {
166 return self::RES_ERROR;
169 if ( isset( $this->files[$src] ) ) {
171 'mtime' => $this->files[$src][
'mtime'],
172 'size' => strlen( $this->files[$src][
'data'] ),
176 return self::RES_ABSENT;
182 foreach ( $params[
'srcs'] as $srcPath ) {
184 if ( $src ===
null ) {
185 $fsFile = self::RES_ERROR;
186 } elseif ( !isset( $this->files[$src] ) ) {
187 $fsFile = self::RES_ABSENT;
191 $fsFile = $this->tmpFileFactory->newTempFSFile(
'localcopy_', $ext );
193 $bytes = file_put_contents( $fsFile->getPath(), $this->files[$src][
'data'] );
194 if ( $bytes !== strlen( $this->files[$src][
'data'] ) ) {
195 $fsFile = self::RES_ERROR;
199 $tmpFiles[$srcPath] = $fsFile;
207 $prefix = rtrim(
"$fullCont/$dirRel",
'/' ) .
'/';
208 foreach ( $this->files as
$path => $data ) {
209 if ( str_starts_with(
$path, $prefix ) ) {
220 $prefix = rtrim(
"$fullCont/$dirRel",
'/' ) .
'/';
221 $prefixLen = strlen( $prefix );
222 foreach ( $this->files as
$path => $data ) {
223 if ( str_starts_with(
$path, $prefix ) ) {
224 $relPath = substr(
$path, $prefixLen );
225 if ( !str_contains( $relPath,
'/' ) ) {
228 $parts = array_slice( explode(
'/', $relPath ), 0, -1 );
229 if ( !empty( $params[
'topOnly'] ) ) {
230 $dirs[$parts[0]] = 1;
233 foreach ( $parts as $part ) {
234 $dirRel = ( $current ===
'' ) ? $part :
"$current/$part";
242 return array_keys( $dirs );
248 $prefix = rtrim(
"$fullCont/$dirRel",
'/' ) .
'/';
249 $prefixLen = strlen( $prefix );
250 foreach ( $this->files as
$path => $data ) {
251 if ( str_starts_with(
$path, $prefix ) ) {
252 $relPath = substr(
$path, $prefixLen );
255 ( !empty( $params[
'topOnly'] ) && str_contains( $relPath,
'/' ) )
279 if ( $relPath ===
null ) {
283 return ( $relPath !==
'' ) ?
"$fullCont/$relPath" : $fullCont;
288class_alias( MemoryFileBackend::class,
'MemoryFileBackend' );