1c3437056SNickeau<?php 2c3437056SNickeau 3c3437056SNickeau 4c3437056SNickeaunamespace ComboStrap; 5c3437056SNickeau 6*04fd306cSNickeauuse ComboStrap\Web\Url; 7*04fd306cSNickeau 8c3437056SNickeau/** 9c3437056SNickeau * Class LocalPath 10c3437056SNickeau * @package ComboStrap 11c3437056SNickeau * A local file system path 12*04fd306cSNickeau * 13*04fd306cSNickeau * File protocol Uri: 14*04fd306cSNickeau * 15*04fd306cSNickeau * file://[HOST]/[PATH] 16c3437056SNickeau */ 17c3437056SNickeauclass LocalPath extends PathAbs 18c3437056SNickeau{ 19c3437056SNickeau 20c3437056SNickeau 21e4026cd1Sgerardnico /** 22e4026cd1Sgerardnico * The characters that cannot be in the path for windows 23e4026cd1Sgerardnico * @var string[] 24e4026cd1Sgerardnico */ 25e4026cd1Sgerardnico public const RESERVED_WINDOWS_CHARACTERS = ["\\", "/", ":", "*", "?", "\"", "<", ">", "|"]; 26e4026cd1Sgerardnico 27*04fd306cSNickeau const RELATIVE_CURRENT = "."; 28*04fd306cSNickeau const RELATIVE_PARENT = ".."; 29*04fd306cSNickeau const LINUX_SEPARATOR = "/"; 30*04fd306cSNickeau const WINDOWS_SEPARATOR = '\\'; 31*04fd306cSNickeau const CANONICAL = "support"; 32*04fd306cSNickeau 33*04fd306cSNickeau /** 34*04fd306cSNickeau * @throws ExceptionBadArgument 35*04fd306cSNickeau */ 36*04fd306cSNickeau public static function createFromUri($uri): LocalPath 37*04fd306cSNickeau { 38*04fd306cSNickeau if (strpos($uri, LocalFileSystem::SCHEME) !== 0) { 39*04fd306cSNickeau throw new ExceptionBadArgument("$uri is not a local path uri"); 40*04fd306cSNickeau } 41*04fd306cSNickeau return new LocalPath($uri); 42*04fd306cSNickeau } 43*04fd306cSNickeau 44*04fd306cSNickeau 45*04fd306cSNickeau /** 46*04fd306cSNickeau * @throws ExceptionBadArgument 47*04fd306cSNickeau * @throws ExceptionCast 48*04fd306cSNickeau */ 49*04fd306cSNickeau public static function createFromPathObject(Path $path): LocalPath 50*04fd306cSNickeau { 51*04fd306cSNickeau if ($path instanceof LocalPath) { 52*04fd306cSNickeau return $path; 53*04fd306cSNickeau } 54*04fd306cSNickeau if ($path instanceof WikiPath) { 55*04fd306cSNickeau return $path->toLocalPath(); 56*04fd306cSNickeau } 57*04fd306cSNickeau throw new ExceptionBadArgument("The path is not a local path nor a wiki path, we can't transform it"); 58*04fd306cSNickeau } 59*04fd306cSNickeau 60*04fd306cSNickeau /** 61*04fd306cSNickeau * 62*04fd306cSNickeau * @throws ExceptionNotFound - if the env directory is not found 63*04fd306cSNickeau */ 64*04fd306cSNickeau public static function createDesktopDirectory(): LocalPath 65*04fd306cSNickeau { 66*04fd306cSNickeau return LocalPath::createHomeDirectory()->resolve("Desktop"); 67*04fd306cSNickeau } 68*04fd306cSNickeau 69*04fd306cSNickeau 70*04fd306cSNickeau public function toUriString(): string 71*04fd306cSNickeau { 72*04fd306cSNickeau return $this->getUrl()->toString(); 73*04fd306cSNickeau } 74*04fd306cSNickeau 75c3437056SNickeau private $path; 76*04fd306cSNickeau /** 77*04fd306cSNickeau * @var mixed 78*04fd306cSNickeau */ 79*04fd306cSNickeau private $sep = DIRECTORY_SEPARATOR; 80*04fd306cSNickeau 81*04fd306cSNickeau private ?string $host = null; 82c3437056SNickeau 83c3437056SNickeau /** 84c3437056SNickeau * LocalPath constructor. 85*04fd306cSNickeau * @param string $path - relative or absolute, or a locale file uri 86*04fd306cSNickeau * @param string|null $sep - the directory separator - it permits to test linux path on windows, and vice-versa 87c3437056SNickeau */ 88*04fd306cSNickeau public function __construct(string $path, string $sep = null) 89c3437056SNickeau { 90*04fd306cSNickeau /** 91*04fd306cSNickeau * php mon amour, 92*04fd306cSNickeau * if we pass a {@link LocalPath}, no error, 93*04fd306cSNickeau * it just pass the {@link PathAbs::__toString()} 94*04fd306cSNickeau */ 95*04fd306cSNickeau if (strpos($path, LocalFileSystem::SCHEME) === 0) { 96*04fd306cSNickeau try { 97*04fd306cSNickeau $path = Url::createFromString($path)->getPath(); 98*04fd306cSNickeau LogUtility::errorIfDevOrTest("The path given as constructor should not be an uri or a path object"); 99*04fd306cSNickeau } catch (ExceptionBadArgument|ExceptionBadSyntax|ExceptionNotFound $e) { 100*04fd306cSNickeau LogUtility::internalError("The uri path could not be created", self::CANONICAL, $e); 101*04fd306cSNickeau } 102*04fd306cSNickeau } 103*04fd306cSNickeau if ($sep != null) { 104*04fd306cSNickeau $this->sep = $sep; 105*04fd306cSNickeau } 106*04fd306cSNickeau // The network share windows/wiki styles with with two \\ and not // 107*04fd306cSNickeau $networkShare = "\\\\"; 108*04fd306cSNickeau if (substr($path, 0, 2) === $networkShare) { 109*04fd306cSNickeau // window share 110*04fd306cSNickeau $pathWithoutNetworkShare = substr($path, 2); 111*04fd306cSNickeau $pathWithoutNetworkShare = str_replace("\\", "/", $pathWithoutNetworkShare); 112*04fd306cSNickeau [$this->host, $relativePath] = explode("/", $pathWithoutNetworkShare, 2); 113*04fd306cSNickeau $this->path = "/$relativePath"; 114*04fd306cSNickeau return; 115*04fd306cSNickeau } 116*04fd306cSNickeau $this->path = self::normalizeToOsSeparator($path); 117c3437056SNickeau } 118c3437056SNickeau 119c3437056SNickeau 1204cadd4f8SNickeau /** 1214cadd4f8SNickeau * @param string $filePath 1224cadd4f8SNickeau * @return LocalPath 123*04fd306cSNickeau * @deprecated for {@link LocalPath::createFromPathString()} 1244cadd4f8SNickeau */ 125c3437056SNickeau public static function create(string $filePath): LocalPath 126c3437056SNickeau { 127c3437056SNickeau return new LocalPath($filePath); 128c3437056SNickeau } 129c3437056SNickeau 130*04fd306cSNickeau /** 131*04fd306cSNickeau * @param $path 132*04fd306cSNickeau * @return array|string|string[] 133*04fd306cSNickeau * 134*04fd306cSNickeau * For whatever reason, it seems that php/dokuwiki uses always the / separator on windows also 135*04fd306cSNickeau * but not always (ie https://www.php.net/manual/en/function.realpath.php output \ on windows) 136*04fd306cSNickeau * 137*04fd306cSNickeau * Because we want to be able to copy the path value and to be able to use 138*04fd306cSNickeau * it directly, we normalize it to the OS separator at build time 139*04fd306cSNickeau */ 140*04fd306cSNickeau private function normalizeToOsSeparator($path) 141c3437056SNickeau { 142*04fd306cSNickeau if ($path === self::RELATIVE_CURRENT || $path === self::RELATIVE_PARENT) { 143*04fd306cSNickeau return realpath($path); 144*04fd306cSNickeau } 145*04fd306cSNickeau $directorySeparator = $this->getDirectorySeparator(); 146*04fd306cSNickeau if ($directorySeparator === self::WINDOWS_SEPARATOR) { 147*04fd306cSNickeau return str_replace(self::LINUX_SEPARATOR, self::WINDOWS_SEPARATOR, $path); 148*04fd306cSNickeau } else { 149*04fd306cSNickeau return str_replace(self::WINDOWS_SEPARATOR, self::LINUX_SEPARATOR, $path); 150*04fd306cSNickeau } 151*04fd306cSNickeau } 152*04fd306cSNickeau 153*04fd306cSNickeau /** 154*04fd306cSNickeau * @throws ExceptionNotFound 155*04fd306cSNickeau */ 156*04fd306cSNickeau public static function createHomeDirectory(): LocalPath 157*04fd306cSNickeau { 158*04fd306cSNickeau $home = getenv("HOME"); 159*04fd306cSNickeau if ($home === false) { 160*04fd306cSNickeau $home = getenv("USERPROFILE"); 161*04fd306cSNickeau } 162*04fd306cSNickeau if ($home === false) { 163*04fd306cSNickeau throw new ExceptionNotFound(" The home directory variable could not be found"); 164*04fd306cSNickeau } 165*04fd306cSNickeau return LocalPath::createFromPathString($home); 166*04fd306cSNickeau } 167*04fd306cSNickeau 168*04fd306cSNickeau 169*04fd306cSNickeau public static function createFromPathString(string $string, string $sep = null): LocalPath 170*04fd306cSNickeau { 171*04fd306cSNickeau return new LocalPath($string, $sep); 172c3437056SNickeau } 173c3437056SNickeau 174c3437056SNickeau function getScheme(): string 175c3437056SNickeau { 176*04fd306cSNickeau return LocalFileSystem::SCHEME; 177c3437056SNickeau } 178c3437056SNickeau 179*04fd306cSNickeau function getLastName(): string 180c3437056SNickeau { 181c3437056SNickeau $names = $this->getNames(); 182c3437056SNickeau $sizeof = sizeof($names); 183c3437056SNickeau if ($sizeof === 0) { 184*04fd306cSNickeau throw new ExceptionNotFound("No last name for the path ($this)"); 185c3437056SNickeau } 186c3437056SNickeau return $names[$sizeof - 1]; 187c3437056SNickeau 188c3437056SNickeau } 189c3437056SNickeau 190*04fd306cSNickeau 191*04fd306cSNickeau public function getExtension(): string 192c3437056SNickeau { 193*04fd306cSNickeau $extension = pathinfo($this->path, PATHINFO_EXTENSION); 194*04fd306cSNickeau if ($extension === "") { 195*04fd306cSNickeau throw new ExceptionNotFound("No extension found for the path ($this)"); 196*04fd306cSNickeau } 197*04fd306cSNickeau return $extension; 198c3437056SNickeau } 199c3437056SNickeau 200c3437056SNickeau function getNames() 201c3437056SNickeau { 202c3437056SNickeau $directorySeparator = $this->getDirectorySeparator(); 203c3437056SNickeau return explode($directorySeparator, $this->path); 204c3437056SNickeau } 205c3437056SNickeau 206c3437056SNickeau 207*04fd306cSNickeau function toAbsoluteId(): string 208c3437056SNickeau { 209c3437056SNickeau return $this->path; 210c3437056SNickeau } 211c3437056SNickeau 212*04fd306cSNickeau public function getParent(): Path 213c3437056SNickeau { 214c3437056SNickeau $absolutePath = pathinfo($this->path, PATHINFO_DIRNAME); 215*04fd306cSNickeau if ($absolutePath === $this->path || empty($absolutePath)) { 216*04fd306cSNickeau // the directory on windows of the root (ie C:\) is (C:\), yolo ! 217*04fd306cSNickeau throw new ExceptionNotFound("No parent"); 218c3437056SNickeau } 219c3437056SNickeau return new LocalPath($absolutePath); 220c3437056SNickeau } 221c3437056SNickeau 222c3437056SNickeau function toAbsolutePath(): Path 223c3437056SNickeau { 2244cadd4f8SNickeau 2254cadd4f8SNickeau if ($this->isAbsolute()) { 226c3437056SNickeau return $this; 2274cadd4f8SNickeau } 2284cadd4f8SNickeau 229*04fd306cSNickeau return $this->toCanonicalAbsolutePath(); 230c3437056SNickeau 231c3437056SNickeau } 232c3437056SNickeau 233c3437056SNickeau 2344cadd4f8SNickeau /** 235*04fd306cSNickeau * @throws ExceptionBadArgument - if the path is not inside a drive 2364cadd4f8SNickeau */ 237*04fd306cSNickeau public function toWikiPath(): WikiPath 2384cadd4f8SNickeau { 239*04fd306cSNickeau return WikiPath::createFromPathObject($this); 2404cadd4f8SNickeau } 2414cadd4f8SNickeau 2424cadd4f8SNickeau public function resolve(string $name): LocalPath 2434cadd4f8SNickeau { 2444cadd4f8SNickeau 245*04fd306cSNickeau $newPath = $this->toCanonicalAbsolutePath()->toAbsoluteId() . $this->getDirectorySeparator() . utf8_encodeFN($name); 246*04fd306cSNickeau return self::createFromPathString($newPath); 2474cadd4f8SNickeau 2484cadd4f8SNickeau } 2494cadd4f8SNickeau 2504cadd4f8SNickeau /** 251*04fd306cSNickeau * @throws ExceptionBadArgument - if the path cannot be relativized 2524cadd4f8SNickeau */ 2534cadd4f8SNickeau public function relativize(LocalPath $localPath): LocalPath 2544cadd4f8SNickeau { 2554cadd4f8SNickeau 256*04fd306cSNickeau /** 257*04fd306cSNickeau * One of the problem of relativization is 258*04fd306cSNickeau * that it may be: 259*04fd306cSNickeau * * logical (when using a symling) 260*04fd306cSNickeau * * physical 261*04fd306cSNickeau */ 262*04fd306cSNickeau if (!$this->isAbsolute() || $this->isShortName()) { 263*04fd306cSNickeau /** 264*04fd306cSNickeau * This is not a logical resolution 265*04fd306cSNickeau * (if the path is logically not absolute and is a symlink, 266*04fd306cSNickeau * we have a problem) 267*04fd306cSNickeau */ 268*04fd306cSNickeau $actualPath = $this->toCanonicalAbsolutePath(); 269*04fd306cSNickeau } else { 270*04fd306cSNickeau $actualPath = $this; 271*04fd306cSNickeau } 272*04fd306cSNickeau if (!$localPath->isAbsolute() || $localPath->isShortName()) { 273*04fd306cSNickeau $localPath = $localPath->toCanonicalAbsolutePath(); 274*04fd306cSNickeau } 275*04fd306cSNickeau 276*04fd306cSNickeau if (strpos($actualPath->toAbsoluteId(), $localPath->toAbsoluteId()) === 0) { 277*04fd306cSNickeau if ($actualPath->toAbsoluteId() === $localPath->toAbsoluteId()) { 278*04fd306cSNickeau return LocalPath::createFromPathString(""); 2794cadd4f8SNickeau } 2804cadd4f8SNickeau $sepCharacter = 1; // delete the sep characters 281*04fd306cSNickeau $relativePath = substr($actualPath->toAbsoluteId(), strlen($localPath->toAbsoluteId()) + $sepCharacter); 282*04fd306cSNickeau $relativePath = str_replace($this->getDirectorySeparator(), WikiPath::NAMESPACE_SEPARATOR_DOUBLE_POINT, $relativePath); 283*04fd306cSNickeau return LocalPath::createFromPathString($relativePath); 284*04fd306cSNickeau } 285*04fd306cSNickeau /** 286*04fd306cSNickeau * May be a symlink link 287*04fd306cSNickeau */ 288*04fd306cSNickeau if ($this->isSymlink()) { 289*04fd306cSNickeau $realPath = $this->toCanonicalAbsolutePath(); 290*04fd306cSNickeau return $realPath->relativize($localPath); 291*04fd306cSNickeau } 292*04fd306cSNickeau if ($localPath->isSymlink()) { 293*04fd306cSNickeau $localPath = $localPath->toCanonicalAbsolutePath(); 294*04fd306cSNickeau $this->relativize($localPath); 295*04fd306cSNickeau } 296*04fd306cSNickeau throw new ExceptionBadArgument("The path ($localPath) is not a parent path of the actual path ($actualPath)"); 2974cadd4f8SNickeau 2984cadd4f8SNickeau } 2994cadd4f8SNickeau 3004cadd4f8SNickeau public function isAbsolute(): bool 3014cadd4f8SNickeau { 3024cadd4f8SNickeau /** 3034cadd4f8SNickeau * / 304*04fd306cSNickeau * or a-z:\ 3054cadd4f8SNickeau */ 306*04fd306cSNickeau if (preg_match("/^(\/|[a-z]:\\\\?).*/i", $this->path)) { 3074cadd4f8SNickeau return true; 3084cadd4f8SNickeau } 3094cadd4f8SNickeau return false; 3104cadd4f8SNickeau 3114cadd4f8SNickeau } 3124cadd4f8SNickeau 3134cadd4f8SNickeau /** 3144cadd4f8SNickeau * An absolute path may not be canonical 3154cadd4f8SNickeau * (ie windows short name or the path separator is not consistent (ie / in place of \ on windows) 3164cadd4f8SNickeau * 3174cadd4f8SNickeau * This function makes the path canonical meaning that two canonical path can be compared. 318*04fd306cSNickeau * This is also needed when you path a path string to a php function such as `clearstatcache` 319*04fd306cSNickeau * 320*04fd306cSNickeau * If this is a symlink, it will resolve it to the real path 3214cadd4f8SNickeau */ 322*04fd306cSNickeau public function toCanonicalAbsolutePath(): LocalPath 3234cadd4f8SNickeau { 3244cadd4f8SNickeau 3254cadd4f8SNickeau /** 3264cadd4f8SNickeau * realpath() is just a system/library call to actual realpath() function supported by OS. 3274cadd4f8SNickeau * real path handle also the windows name ie USERNAME~ 3284cadd4f8SNickeau */ 329*04fd306cSNickeau $isSymlink = $this->isSymlink(); 3304cadd4f8SNickeau $realPath = realpath($this->path); 331*04fd306cSNickeau if($isSymlink){ 332*04fd306cSNickeau /** 333*04fd306cSNickeau * 334*04fd306cSNickeau * What fucked is fucked up 335*04fd306cSNickeau * 336*04fd306cSNickeau * With the symlink 337*04fd306cSNickeau * D:/dokuwiki-animals/combo.nico.lan/data/pages 338*04fd306cSNickeau * if you pass it to realpath: 339*04fd306cSNickeau * ``` 340*04fd306cSNickeau * realpath("D:/dokuwiki-animals/combo.nico.lan/data/pages") 341*04fd306cSNickeau * ``` 342*04fd306cSNickeau * you get: `d:\dokuwiki\website\pages` 343*04fd306cSNickeau * if you pass the result again in realpath 344*04fd306cSNickeau * ``` 345*04fd306cSNickeau * realpath(d:\dokuwiki\website\pages) 346*04fd306cSNickeau * ``` 347*04fd306cSNickeau * we get another result `D:\dokuwiki\website\pages` 348*04fd306cSNickeau * 349*04fd306cSNickeau */ 350*04fd306cSNickeau $realPath = realpath($realPath); 351*04fd306cSNickeau } 3524cadd4f8SNickeau if ($realPath !== false) { 353*04fd306cSNickeau return LocalPath::createFromPathString($realPath); 3544cadd4f8SNickeau } 3554cadd4f8SNickeau 3564cadd4f8SNickeau /** 3574cadd4f8SNickeau * It returns false on on file that does not exists. 3584cadd4f8SNickeau * The suggestion on the realpath man page 3594cadd4f8SNickeau * is to look for an existing parent directory. 3604cadd4f8SNickeau * https://man7.org/linux/man-pages/man3/realpath.3.html 3614cadd4f8SNickeau */ 3624cadd4f8SNickeau $parts = null; 3634cadd4f8SNickeau $isRoot = false; 3644cadd4f8SNickeau $counter = 0; // breaker 3654cadd4f8SNickeau $workingPath = $this->path; 3664cadd4f8SNickeau while ($realPath === false) { 3674cadd4f8SNickeau $counter++; 3684cadd4f8SNickeau $parent = dirname($workingPath); 3694cadd4f8SNickeau /** 3704cadd4f8SNickeau * From the doc: https://www.php.net/manual/en/function.dirname.php 3714cadd4f8SNickeau * dirname('.'); // Will return '.'. 3724cadd4f8SNickeau * dirname('/'); // Will return `\` on Windows and '/' on *nix systems. 3734cadd4f8SNickeau * dirname('\\'); // Will return `\` on Windows and '.' on *nix systems. 3744cadd4f8SNickeau * dirname('C:\\'); // Will return 'C:\' on Windows and '.' on *nix systems. 3754cadd4f8SNickeau * dirname('\'); // Will return `C:\` on Windows and ??? on *nix systems. 3764cadd4f8SNickeau */ 3774cadd4f8SNickeau if (preg_match("/^(\.|\/|\\\\|[a-z]:\\\\)$/i", $parent) 3784cadd4f8SNickeau || $parent === $workingPath 3794cadd4f8SNickeau || $parent === "\\" // bug on regexp 3804cadd4f8SNickeau ) { 3814cadd4f8SNickeau $isRoot = true; 3824cadd4f8SNickeau } 3834cadd4f8SNickeau // root, no need to delete the last sep 3844cadd4f8SNickeau $lastSep = 1; 3854cadd4f8SNickeau if ($isRoot) { 3864cadd4f8SNickeau $lastSep = 0; 3874cadd4f8SNickeau } 3884cadd4f8SNickeau $parts[] = substr($workingPath, strlen($parent) + $lastSep); 3894cadd4f8SNickeau 3904cadd4f8SNickeau $realPath = realpath($parent); 3914cadd4f8SNickeau if ($isRoot) { 3924cadd4f8SNickeau break; 3934cadd4f8SNickeau } 3944cadd4f8SNickeau if ($counter > 200) { 3954cadd4f8SNickeau $message = "Bad absolute local path file ($this->path)"; 3964cadd4f8SNickeau if (PluginUtility::isDevOrTest()) { 397*04fd306cSNickeau throw new ExceptionRuntime($message); 3984cadd4f8SNickeau } else { 3994cadd4f8SNickeau LogUtility::msg($message); 4004cadd4f8SNickeau } 4014cadd4f8SNickeau return $this; 4024cadd4f8SNickeau } 4034cadd4f8SNickeau if ($realPath === false) { 4044cadd4f8SNickeau // loop 4054cadd4f8SNickeau $workingPath = $parent; 4064cadd4f8SNickeau } 4074cadd4f8SNickeau } 4084cadd4f8SNickeau if ($parts !== null) { 4094cadd4f8SNickeau if (!$isRoot) { 410*04fd306cSNickeau $realPath .= $this->getDirectorySeparator(); 4114cadd4f8SNickeau } 4124cadd4f8SNickeau $parts = array_reverse($parts); 413*04fd306cSNickeau $realPath .= implode($this->getDirectorySeparator(), $parts); 4144cadd4f8SNickeau } 415*04fd306cSNickeau return LocalPath::createFromPathString($realPath); 416*04fd306cSNickeau } 417*04fd306cSNickeau 418*04fd306cSNickeau public function getDirectorySeparator() 419*04fd306cSNickeau { 420*04fd306cSNickeau return $this->sep; 4214cadd4f8SNickeau } 4224cadd4f8SNickeau 4234cadd4f8SNickeau 424*04fd306cSNickeau function getUrl(): Url 425*04fd306cSNickeau { 426*04fd306cSNickeau 427*04fd306cSNickeau /** 428*04fd306cSNickeau * file://host/path 429*04fd306cSNickeau */ 430*04fd306cSNickeau $uri = LocalFileSystem::SCHEME . '://'; 431*04fd306cSNickeau try { 432*04fd306cSNickeau // Windows share host 433*04fd306cSNickeau $uri = "$uri{$this->getHost()}"; 434*04fd306cSNickeau } catch (ExceptionNotFound $e) { 435*04fd306cSNickeau // ok 436*04fd306cSNickeau } 437*04fd306cSNickeau $pathNormalized = str_replace(self::WINDOWS_SEPARATOR, self::LINUX_SEPARATOR, $this->path); 438*04fd306cSNickeau if ($pathNormalized[0] !== "/") { 439*04fd306cSNickeau $uri = $uri . "/" . $pathNormalized; 440*04fd306cSNickeau } else { 441*04fd306cSNickeau $uri = $uri . $pathNormalized; 442*04fd306cSNickeau } 443*04fd306cSNickeau try { 444*04fd306cSNickeau return Url::createFromString($uri); 445*04fd306cSNickeau } catch (ExceptionBadSyntax|ExceptionBadArgument $e) { 446*04fd306cSNickeau $message = "Local Uri Path has a bad syntax ($uri)"; 447*04fd306cSNickeau // should not happen 448*04fd306cSNickeau LogUtility::internalError($message); 449*04fd306cSNickeau throw new ExceptionRuntime($message); 450*04fd306cSNickeau } 451*04fd306cSNickeau 452*04fd306cSNickeau } 453*04fd306cSNickeau 454*04fd306cSNickeau /** 455*04fd306cSNickeau * @throws ExceptionNotFound 456*04fd306cSNickeau */ 457*04fd306cSNickeau function getHost(): string 458*04fd306cSNickeau { 459*04fd306cSNickeau if ($this->host === null) { 460*04fd306cSNickeau throw new ExceptionNotFound("No host. Localhost should be the default"); 461*04fd306cSNickeau } 462*04fd306cSNickeau return $this->host; 463*04fd306cSNickeau } 464*04fd306cSNickeau 465*04fd306cSNickeau public function isSymlink(): bool 466*04fd306cSNickeau { 467*04fd306cSNickeau return is_link($this->path); 468*04fd306cSNickeau } 469*04fd306cSNickeau 470*04fd306cSNickeau private function isShortName(): bool 471*04fd306cSNickeau { 472*04fd306cSNickeau /** 473*04fd306cSNickeau * See short name in windows 474*04fd306cSNickeau * https://datacadamia.com/os/windows/path#pathname 475*04fd306cSNickeau */ 476*04fd306cSNickeau return strpos($this->path, "~1") !== false; 477*04fd306cSNickeau } 478c3437056SNickeau} 479