1<?php 2 3 4namespace ComboStrap; 5 6 7use DateTime; 8 9class LocalFileSystem implements FileSystem 10{ 11 12 // same as the uri: ie local file os system 13 public const SCHEME = "file"; 14 15 /** 16 * @var LocalFileSystem 17 */ 18 private static $localFs; 19 20 public static function getOrCreate(): LocalFileSystem 21 { 22 if (self::$localFs === null) { 23 self::$localFs = new LocalFileSystem(); 24 } 25 return self::$localFs; 26 } 27 28 function exists(Path $path): bool 29 { 30 return file_exists($path->toAbsolutePath()->toAbsoluteId()); 31 } 32 33 /** 34 * @param $path 35 * @return string - textual content 36 * @throws ExceptionNotFound - if the file does not exist 37 */ 38 public function getContent($path): string 39 { 40 /** 41 * Mime check 42 */ 43 try { 44 $mime = FileSystems::getMime($path); 45 if (!$mime->isTextBased()) { 46 LogUtility::error("This mime content ($mime) is not text based (for the path $path). We can't return a text."); 47 return ""; 48 } 49 } catch (ExceptionNotFound $e) { 50 LogUtility::error("The mime is unknown for the path ($path). Trying to returning the content as text."); 51 } 52 $content = @file_get_contents($path->toAbsolutePath()->toAbsoluteId()); 53 if ($content === false) { 54 // file does not exists 55 throw new ExceptionNotFound("The file ($path) does not exists"); 56 } 57 return $content; 58 } 59 60 /** 61 * @param LocalPath $path 62 * @return DateTime 63 * @throws ExceptionNotFound - if the file does not exist 64 */ 65 public function getModifiedTime($path): DateTime 66 { 67 if (!self::exists($path)) { 68 throw new ExceptionNotFound("Local File System Modified Time: The file ($path) does not exist"); 69 } 70 $timestamp = filemtime($path->toCanonicalAbsolutePath()->toAbsoluteId()); 71 return Iso8601Date::createFromTimestamp($timestamp)->getDateTime(); 72 } 73 74 /** 75 * @throws ExceptionNotFound 76 */ 77 public function getCreationTime(Path $path) 78 { 79 if (!$this->exists($path)) { 80 throw new ExceptionNotFound("The path ($path) does not exists, no creation time"); 81 } 82 $filePath = $path->toAbsolutePath()->toAbsoluteId(); 83 $timestamp = filectime($filePath); 84 return Iso8601Date::createFromTimestamp($timestamp)->getDateTime(); 85 } 86 87 /** 88 * @throws ExceptionFileSystem - if the action cannot be performed 89 */ 90 public function delete(Path $path) 91 { 92 $absolutePath = $path->toAbsolutePath()->toAbsoluteId(); 93 $success = unlink($absolutePath); 94 if(!$success){ 95 throw new ExceptionFileSystem("Unable to delete the file ($absolutePath)"); 96 } 97 } 98 99 /** 100 * @return false|int 101 * @var LocalPath $path 102 */ 103 public function getSize(Path $path) 104 { 105 return filesize($path->toAbsolutePath()->toAbsoluteId()); 106 } 107 108 /** 109 * @throws ExceptionCompile 110 */ 111 public function createDirectory(Path $dirPath): Path 112 { 113 $result = mkdir($dirPath->toAbsolutePath()->toAbsoluteId(), $mode = 0770, $recursive = true); 114 if ($result === false) { 115 throw new ExceptionCompile("Unable to create the directory path ($dirPath)"); 116 } 117 return $dirPath; 118 } 119 120 public function isDirectory(Path $path): bool 121 { 122 return is_dir($path->toAbsolutePath()); 123 } 124 125 /** 126 * @param LocalPath $path 127 * @param string|null $type container / leaf (ie directory / file or namespace/page) 128 * @return LocalPath[] 129 */ 130 public function getChildren(Path $path, string $type = null): array 131 { 132 133 /** 134 * Same as {@link scandir()}, they output 135 * the current and parent relative directory (ie `.` and `..`) 136 */ 137 $directoryHandle = @opendir($path->toAbsolutePath()); 138 if (!$directoryHandle) return []; 139 try { 140 $localChildren = []; 141 while (($fileName = readdir($directoryHandle)) !== false) { 142 if (in_array($fileName, [LocalPath::RELATIVE_CURRENT, LocalPath::RELATIVE_PARENT])) { 143 continue; 144 } 145 $childPath = $path->resolve($fileName); 146 if ($type === null) { 147 $localChildren[] = $childPath; 148 continue; 149 } 150 /** 151 * Filter is not null, filter 152 */ 153 switch ($type) { 154 case FileSystems::CONTAINER: 155 if (FileSystems::isDirectory($childPath)) { 156 $localChildren[] = $childPath; 157 } 158 break; 159 case FileSystems::LEAF: 160 if (!FileSystems::isDirectory($childPath)) { 161 $localChildren[] = $childPath; 162 } 163 break; 164 default: 165 LogUtility::internalError("The type of file ($type) is unknown. It should be `" . FileSystems::CONTAINER . "` or `" . FileSystems::LEAF . "`"); 166 $localChildren[] = $childPath; 167 } 168 } 169 /** 170 * With the default, the file '10_....' is before the file '01....' 171 */ 172 sort($localChildren, SORT_NATURAL); 173 return $localChildren; 174 } finally { 175 closedir($directoryHandle); 176 } 177 178 } 179 180 /** 181 * @param LocalPath $path 182 * @param string $lastFullName 183 * @return Path 184 * @throws ExceptionNotFound 185 */ 186 public function closest(Path $path, string $lastFullName): Path 187 { 188 if (FileSystems::isDirectory($path)) { 189 $closest = $path->resolve($lastFullName); 190 if (FileSystems::exists($closest)) { 191 return $closest; 192 } 193 } 194 $parent = $path; 195 while (true) { 196 try { 197 $parent = $parent->getParent(); 198 } catch (ExceptionNotFound $e) { 199 break; 200 } 201 $closest = $parent->resolve($lastFullName); 202 if (FileSystems::exists($closest)) { 203 return $closest; 204 } 205 } 206 throw new ExceptionNotFound("No closest was found for the file name ($lastFullName) from the path ($path)"); 207 } 208 209 /** 210 * @param LocalPath $path 211 * @return void 212 */ 213 public function createRegularFile(Path $path) 214 { 215 touch($path->toAbsoluteId()); 216 } 217 218 public function setContent(Path $path, string $content) 219 { 220 221 $file = $path->toAbsoluteId(); 222 /** 223 * the {@link io_saveFile()} dokuwiki function 224 * expects the path to be with unix separator 225 * It fails to calculate the parent because it just don't use 226 * {@link dirname()} but search for the last / 227 * in {@link io_mkdir_p()} 228 */ 229 $file = str_replace('\\','/',$file); 230 io_saveFile($file, $content, false); 231 } 232 233} 234