*/ private array $files = []; /** @var array */ private array $directories = []; /** @var array */ private array $timestamps = []; public readonly PermissionChecker $permissions; public readonly FiberManager $fiberManager; public function __construct() { // InMemoryStorage doesn't need real permission checks - create mock PathProvider $mockPathProvider = new \App\Framework\Core\PathProvider('/tmp'); $this->permissions = new PermissionChecker($mockPathProvider); $this->fiberManager = $this->createDefaultFiberManager(); } private function createDefaultFiberManager(): FiberManager { // Create minimal dependencies for FiberManager $clock = new \App\Framework\DateTime\SystemClock(); $timer = new \App\Framework\DateTime\SystemTimer($clock); return new FiberManager($clock, $timer); } // Methods required by InMemoryStreamableStorageTrait /** * @return array */ protected function getFilesArray(): array { return $this->files; } protected function setFileContent(string $path, string $content): void { $this->files[$path] = $content; $this->timestamps[$path] = time(); } public function get(string $path): string { if (! isset($this->files[$path])) { throw new FileNotFoundException($path); } return $this->files[$path]; } public function put(string $path, string $content): void { $dir = dirname($path); if (! isset($this->directories[$dir])) { $this->createDirectory($dir); } $this->files[$path] = $content; $this->timestamps[$path] = time(); } public function exists(string $path): bool { return isset($this->files[$path]); } public function delete(string $path): void { if (! isset($this->files[$path])) { throw new FileNotFoundException($path); } unset($this->files[$path]); unset($this->timestamps[$path]); } public function copy(string $source, string $destination): void { if (! isset($this->files[$source])) { throw new FileNotFoundException($source); } $dir = dirname($destination); if (! isset($this->directories[$dir])) { $this->createDirectory($dir); } $this->files[$destination] = $this->files[$source]; $this->timestamps[$destination] = time(); } public function size(string $path): int { if (! isset($this->files[$path])) { throw new FileNotFoundException($path); } return strlen($this->files[$path]); } public function lastModified(string $path): int { if (! isset($this->files[$path])) { throw new FileNotFoundException($path); } return $this->timestamps[$path] ?? time(); } public function listDirectory(string $directory): array { if (! isset($this->directories[$directory])) { throw new DirectoryCreateException($directory); } $result = []; $prefix = $directory . DIRECTORY_SEPARATOR; $prefixLength = strlen($prefix); // Dateien im Verzeichnis suchen foreach (array_keys($this->files) as $path) { if (str_starts_with($path, $prefix)) { // Nur direkte Kinder, keine tieferen Ebenen $relativePath = substr($path, $prefixLength); if (! str_contains($relativePath, DIRECTORY_SEPARATOR)) { $result[] = $path; } } } // Verzeichnisse im Verzeichnis suchen foreach (array_keys($this->directories) as $path) { if ($path !== $directory && strpos($path, $prefix) === 0) { // Nur direkte Kinder $relativePath = substr($path, $prefixLength); if (! str_contains($relativePath, DIRECTORY_SEPARATOR)) { $result[] = $path; } } } return $result; } public function createDirectory(string $path, int $permissions = 0755, bool $recursive = true): void { if (isset($this->directories[$path])) { return; // Verzeichnis existiert bereits } if ($recursive) { // Übergeordnete Verzeichnisse erstellen $parent = dirname($path); if ($parent !== '.' && $parent !== '/' && $parent !== $path) { $this->createDirectory($parent, $permissions, true); } } $this->directories[$path] = true; } /** * Fügt eine Datei direkt zum In-Memory-Speicher hinzu (für Tests) */ public function addFile(string $path, string $content): void { $dir = dirname($path); $this->createDirectory($dir); $this->files[$path] = $content; $this->timestamps[$path] = time(); } /** * Löscht alle Daten im Speicher (für Tests) */ public function clear(): void { $this->files = []; $this->directories = []; $this->timestamps = []; } /** * Erstellt ein File-Objekt mit Lazy-Loading für teure Eigenschaften */ public function file(string $path): File { return FilesystemFactory::createFile($path, $this); } /** * Erstellt ein Directory-Objekt mit Lazy-Loading für teure Eigenschaften */ public function directory(string $path): Directory { return FilesystemFactory::createDirectory($path, $this); } public function getMimeType(string $path): string { if (! isset($this->files[$path])) { throw new FileNotFoundException($path); } $finfo = finfo_open(FILEINFO_MIME_TYPE); if ($finfo === false) { throw new FileReadException($path); } $mimeType = finfo_buffer($finfo, $this->files[$path]); finfo_close($finfo); return $mimeType ?: 'application/octet-stream'; } public function isReadable(string $path): bool { return true; // Alle Dateien sind im In-Memory-Speicher lesbar } public function isWritable(string $path): bool { return true; } public function batch(array $operations): array { return $this->fiberManager->batch($operations); } public function getMultiple(array $paths): array { $operations = []; foreach ($paths as $path) { $operations[$path] = fn () => $this->get($path); } return $this->batch($operations); } public function putMultiple(array $files): void { $operations = []; foreach ($files as $path => $content) { $operations[$path] = fn () => $this->put($path, $content); } $this->batch($operations); } public function getMetadataMultiple(array $paths): array { $operations = []; foreach ($paths as $path) { $operations[$path] = fn () => new FileMetadata( path: $path, size: $this->size($path), lastModified: $this->lastModified($path), mimeType: $this->getMimeType($path), isReadable: $this->isReadable($path), isWritable: $this->isWritable($path) ); } return $this->batch($operations); } }