xref: /plugin/combo/ComboStrap/LocalFileSystem.php (revision 8a02de253a917c46f37c36fa9a08a02cf573cba4)
104fd306cSNickeau<?php
204fd306cSNickeau
304fd306cSNickeau
404fd306cSNickeaunamespace ComboStrap;
504fd306cSNickeau
604fd306cSNickeau
704fd306cSNickeauuse DateTime;
804fd306cSNickeau
904fd306cSNickeauclass LocalFileSystem implements FileSystem
1004fd306cSNickeau{
1104fd306cSNickeau
1204fd306cSNickeau    // same as the uri: ie local file os system
1304fd306cSNickeau    public const SCHEME = "file";
1404fd306cSNickeau
1504fd306cSNickeau    /**
1604fd306cSNickeau     * @var LocalFileSystem
1704fd306cSNickeau     */
1804fd306cSNickeau    private static $localFs;
1904fd306cSNickeau
2004fd306cSNickeau    public static function getOrCreate(): LocalFileSystem
2104fd306cSNickeau    {
2204fd306cSNickeau        if (self::$localFs === null) {
2304fd306cSNickeau            self::$localFs = new LocalFileSystem();
2404fd306cSNickeau        }
2504fd306cSNickeau        return self::$localFs;
2604fd306cSNickeau    }
2704fd306cSNickeau
2804fd306cSNickeau    function exists(Path $path): bool
2904fd306cSNickeau    {
3004fd306cSNickeau        return file_exists($path->toAbsolutePath()->toAbsoluteId());
3104fd306cSNickeau    }
3204fd306cSNickeau
3304fd306cSNickeau    /**
3404fd306cSNickeau     * @param $path
3504fd306cSNickeau     * @return string - textual content
3604fd306cSNickeau     * @throws ExceptionNotFound - if the file does not exist
3704fd306cSNickeau     */
3804fd306cSNickeau    public function getContent($path): string
3904fd306cSNickeau    {
4004fd306cSNickeau        /**
4104fd306cSNickeau         * Mime check
4204fd306cSNickeau         */
4304fd306cSNickeau        try {
4404fd306cSNickeau            $mime = FileSystems::getMime($path);
4504fd306cSNickeau            if (!$mime->isTextBased()) {
4604fd306cSNickeau                LogUtility::error("This mime content ($mime) is not text based (for the path $path). We can't return a text.");
4704fd306cSNickeau                return "";
4804fd306cSNickeau            }
4904fd306cSNickeau        } catch (ExceptionNotFound $e) {
5004fd306cSNickeau            LogUtility::error("The mime is unknown for the path ($path). Trying to returning the content as text.");
5104fd306cSNickeau        }
5204fd306cSNickeau        $content = @file_get_contents($path->toAbsolutePath()->toAbsoluteId());
5304fd306cSNickeau        if ($content === false) {
5404fd306cSNickeau            // file does not exists
5504fd306cSNickeau            throw new ExceptionNotFound("The file ($path) does not exists");
5604fd306cSNickeau        }
5704fd306cSNickeau        return $content;
5804fd306cSNickeau    }
5904fd306cSNickeau
6004fd306cSNickeau    /**
6104fd306cSNickeau     * @param LocalPath $path
6204fd306cSNickeau     * @return DateTime
6304fd306cSNickeau     * @throws ExceptionNotFound - if the file does not exist
6404fd306cSNickeau     */
6504fd306cSNickeau    public function getModifiedTime($path): DateTime
6604fd306cSNickeau    {
6704fd306cSNickeau        if (!self::exists($path)) {
6804fd306cSNickeau            throw new ExceptionNotFound("Local File System Modified Time: The file ($path) does not exist");
6904fd306cSNickeau        }
7004fd306cSNickeau        $timestamp = filemtime($path->toCanonicalAbsolutePath()->toAbsoluteId());
7104fd306cSNickeau        return Iso8601Date::createFromTimestamp($timestamp)->getDateTime();
7204fd306cSNickeau    }
7304fd306cSNickeau
7404fd306cSNickeau    /**
7504fd306cSNickeau     * @throws ExceptionNotFound
7604fd306cSNickeau     */
7704fd306cSNickeau    public function getCreationTime(Path $path)
7804fd306cSNickeau    {
7904fd306cSNickeau        if (!$this->exists($path)) {
8004fd306cSNickeau            throw new ExceptionNotFound("The path ($path) does not exists, no creation time");
8104fd306cSNickeau        }
8204fd306cSNickeau        $filePath = $path->toAbsolutePath()->toAbsoluteId();
8304fd306cSNickeau        $timestamp = filectime($filePath);
8404fd306cSNickeau        return Iso8601Date::createFromTimestamp($timestamp)->getDateTime();
8504fd306cSNickeau    }
8604fd306cSNickeau
8704fd306cSNickeau    /**
8804fd306cSNickeau     * @throws ExceptionFileSystem - if the action cannot be performed
8904fd306cSNickeau     */
9004fd306cSNickeau    public function delete(Path $path)
9104fd306cSNickeau    {
9204fd306cSNickeau        $absolutePath = $path->toAbsolutePath()->toAbsoluteId();
9304fd306cSNickeau        $success = unlink($absolutePath);
9404fd306cSNickeau        if(!$success){
9504fd306cSNickeau            throw new ExceptionFileSystem("Unable to delete the file ($absolutePath)");
9604fd306cSNickeau        }
9704fd306cSNickeau    }
9804fd306cSNickeau
9904fd306cSNickeau    /**
10004fd306cSNickeau     * @return false|int
10104fd306cSNickeau     * @var LocalPath $path
10204fd306cSNickeau     */
10304fd306cSNickeau    public function getSize(Path $path)
10404fd306cSNickeau    {
10504fd306cSNickeau        return filesize($path->toAbsolutePath()->toAbsoluteId());
10604fd306cSNickeau    }
10704fd306cSNickeau
10804fd306cSNickeau    /**
10904fd306cSNickeau     * @throws ExceptionCompile
11004fd306cSNickeau     */
11104fd306cSNickeau    public function createDirectory(Path $dirPath): Path
11204fd306cSNickeau    {
11304fd306cSNickeau        $result = mkdir($dirPath->toAbsolutePath()->toAbsoluteId(), $mode = 0770, $recursive = true);
11404fd306cSNickeau        if ($result === false) {
11504fd306cSNickeau            throw new ExceptionCompile("Unable to create the directory path ($dirPath)");
11604fd306cSNickeau        }
11704fd306cSNickeau        return $dirPath;
11804fd306cSNickeau    }
11904fd306cSNickeau
12004fd306cSNickeau    public function isDirectory(Path $path): bool
12104fd306cSNickeau    {
12204fd306cSNickeau        return is_dir($path->toAbsolutePath());
12304fd306cSNickeau    }
12404fd306cSNickeau
12504fd306cSNickeau    /**
12604fd306cSNickeau     * @param LocalPath $path
12704fd306cSNickeau     * @param string|null $type container / leaf (ie directory / file or namespace/page)
12804fd306cSNickeau     * @return LocalPath[]
12904fd306cSNickeau     */
13004fd306cSNickeau    public function getChildren(Path $path, string $type = null): array
13104fd306cSNickeau    {
13204fd306cSNickeau
13304fd306cSNickeau        /**
13404fd306cSNickeau         * Same as {@link scandir()}, they output
13504fd306cSNickeau         * the current and parent relative directory (ie `.` and `..`)
13604fd306cSNickeau         */
13704fd306cSNickeau        $directoryHandle = @opendir($path->toAbsolutePath());
13804fd306cSNickeau        if (!$directoryHandle) return [];
13904fd306cSNickeau        try {
14004fd306cSNickeau            $localChildren = [];
14104fd306cSNickeau            while (($fileName = readdir($directoryHandle)) !== false) {
14204fd306cSNickeau                if (in_array($fileName, [LocalPath::RELATIVE_CURRENT, LocalPath::RELATIVE_PARENT])) {
14304fd306cSNickeau                    continue;
14404fd306cSNickeau                }
14504fd306cSNickeau                $childPath = $path->resolve($fileName);
14604fd306cSNickeau                if ($type === null) {
14704fd306cSNickeau                    $localChildren[] = $childPath;
14804fd306cSNickeau                    continue;
14904fd306cSNickeau                }
15004fd306cSNickeau                /**
15104fd306cSNickeau                 * Filter is not null, filter
15204fd306cSNickeau                 */
15304fd306cSNickeau                switch ($type) {
15404fd306cSNickeau                    case FileSystems::CONTAINER:
15504fd306cSNickeau                        if (FileSystems::isDirectory($childPath)) {
15604fd306cSNickeau                            $localChildren[] = $childPath;
15704fd306cSNickeau                        }
15804fd306cSNickeau                        break;
15904fd306cSNickeau                    case FileSystems::LEAF:
16004fd306cSNickeau                        if (!FileSystems::isDirectory($childPath)) {
16104fd306cSNickeau                            $localChildren[] = $childPath;
16204fd306cSNickeau                        }
16304fd306cSNickeau                        break;
16404fd306cSNickeau                    default:
16504fd306cSNickeau                        LogUtility::internalError("The type of file ($type) is unknown. It should be `" . FileSystems::CONTAINER . "` or `" . FileSystems::LEAF . "`");
16604fd306cSNickeau                        $localChildren[] = $childPath;
16704fd306cSNickeau                }
16804fd306cSNickeau            }
169*8a02de25Sgerardnico            /**
170*8a02de25Sgerardnico             * With the default, the file '10_....' is before the file '01....'
171*8a02de25Sgerardnico             */
172*8a02de25Sgerardnico            sort($localChildren, SORT_NATURAL);
17304fd306cSNickeau            return $localChildren;
17404fd306cSNickeau        } finally {
17504fd306cSNickeau            closedir($directoryHandle);
17604fd306cSNickeau        }
17704fd306cSNickeau
17804fd306cSNickeau    }
17904fd306cSNickeau
18004fd306cSNickeau    /**
18104fd306cSNickeau     * @param LocalPath $path
18204fd306cSNickeau     * @param string $lastFullName
18304fd306cSNickeau     * @return Path
18404fd306cSNickeau     * @throws ExceptionNotFound
18504fd306cSNickeau     */
18604fd306cSNickeau    public function closest(Path $path, string $lastFullName): Path
18704fd306cSNickeau    {
18804fd306cSNickeau        if (FileSystems::isDirectory($path)) {
18904fd306cSNickeau            $closest = $path->resolve($lastFullName);
19004fd306cSNickeau            if (FileSystems::exists($closest)) {
19104fd306cSNickeau                return $closest;
19204fd306cSNickeau            }
19304fd306cSNickeau        }
19404fd306cSNickeau        $parent = $path;
19504fd306cSNickeau        while (true) {
19604fd306cSNickeau            try {
19704fd306cSNickeau                $parent = $parent->getParent();
19804fd306cSNickeau            } catch (ExceptionNotFound $e) {
19904fd306cSNickeau                break;
20004fd306cSNickeau            }
20104fd306cSNickeau            $closest = $parent->resolve($lastFullName);
20204fd306cSNickeau            if (FileSystems::exists($closest)) {
20304fd306cSNickeau                return $closest;
20404fd306cSNickeau            }
20504fd306cSNickeau        }
20604fd306cSNickeau        throw new ExceptionNotFound("No closest was found for the file name ($lastFullName) from the path ($path)");
20704fd306cSNickeau    }
20804fd306cSNickeau
20904fd306cSNickeau    /**
21004fd306cSNickeau     * @param LocalPath $path
21104fd306cSNickeau     * @return void
21204fd306cSNickeau     */
21304fd306cSNickeau    public function createRegularFile(Path $path)
21404fd306cSNickeau    {
21504fd306cSNickeau        touch($path->toAbsoluteId());
21604fd306cSNickeau    }
21704fd306cSNickeau
21804fd306cSNickeau    public function setContent(Path $path, string $content)
21904fd306cSNickeau    {
22004fd306cSNickeau
22104fd306cSNickeau        $file = $path->toAbsoluteId();
22204fd306cSNickeau        /**
22304fd306cSNickeau         * the {@link io_saveFile()} dokuwiki function
22404fd306cSNickeau         * expects the path to be with unix separator
22504fd306cSNickeau         * It fails to calculate the parent because it just don't use
22604fd306cSNickeau         * {@link dirname()} but search for the last /
22704fd306cSNickeau         * in {@link io_mkdir_p()}
22804fd306cSNickeau         */
22904fd306cSNickeau        $file = str_replace('\\','/',$file);
23004fd306cSNickeau        io_saveFile($file, $content, false);
23104fd306cSNickeau    }
23204fd306cSNickeau
23304fd306cSNickeau}
234