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