Passed
Push — master ( 0b916c...6ef6f5 )
by Sebastian
03:00
created

AbstractPathInfo::getPath()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 1
c 1
b 0
f 0
dl 0
loc 3
rs 10
cc 1
nc 1
nop 0
1
<?php
2
/**
3
 * File containing the class {@see \AppUtils\FileHelper\AbstractPathInfo}.
4
 *
5
 * @package Application Utils
6
 * @subpackage FileHelper
7
 * @see \AppUtils\FileHelper\AbstractPathInfo
8
 */
9
10
declare(strict_types=1);
11
12
namespace AppUtils\FileHelper;
13
14
use AppUtils\FileHelper;
15
use AppUtils\FileHelper_Exception;
16
use AppUtils\Interface_Stringable;
17
use AppUtils\Interfaces\RenderableInterface;
18
use DateTime;
19
use DirectoryIterator;
20
use SplFileInfo;
21
22
/**
23
 * Abstract implementation of the path info interface.
24
 *
25
 * @package Application Utils
26
 * @subpackage FileHelper
27
 * @author Sebastian Mordziol <[email protected]>
28
 */
29
abstract class AbstractPathInfo implements PathInfoInterface
30
{
31
    protected string $path;
32
33
    /**
34
     * @var array<string,mixed>
35
     */
36
    private array $runtimeProperties = array();
37
38
    protected function __construct(string $path)
39
    {
40
        $this->path = FileHelper::normalizePath($path);
41
    }
42
43
    /**
44
     * The full path to the file/folder.
45
     * @return string
46
     */
47
    public function getPath() : string
48
    {
49
        return $this->path;
50
    }
51
52
    /**
53
     * Gets the file name without path, e.g. "filename.txt",
54
     * or the folder name if it's a folder.
55
     *
56
     * NOTE: This includes the file extension. To get a
57
     * file's base name, get a file info instance, and use
58
     * {@see FileInfo::getBaseName()}.
59
     *
60
     * @return string
61
     */
62
    public function getName() : string
63
    {
64
        return basename($this->path);
65
    }
66
67
    final public function isFolder() : bool
68
    {
69
        return $this instanceof FolderInfo;
70
    }
71
72
    final public function isIndeterminate() : bool
73
    {
74
        return $this instanceof IndeterminatePath;
75
    }
76
77
    final public function isFile() : bool
78
    {
79
        return $this instanceof FileInfo;
80
    }
81
82
    public function exists() : bool
83
    {
84
        return file_exists($this->path);
85
    }
86
87
    public function isWritable() : bool
88
    {
89
        return is_writable($this->path);
90
    }
91
92
    public function isReadable() : bool
93
    {
94
        return is_readable($this->path);
95
    }
96
97
    /**
98
     * The real path without symlinks to the file/folder.
99
     * @return string
100
     *
101
     * @throws FileHelper_Exception
102
     * @see FileHelper::ERROR_REAL_PATH_NOT_FOUND
103
     */
104
    public function getRealPath() : string
105
    {
106
        $this->requireExists();
107
108
        $path = realpath($this->path);
109
110
        if($path !== false)
111
        {
112
            return FileHelper::normalizePath($path);
113
        }
114
115
        throw new FileHelper_Exception(
116
            sprintf('Real path for [%s] not found.', $this->getName()),
117
            sprintf('Tried accessing real path for [%s].', $this->getPath()),
118
            FileHelper::ERROR_REAL_PATH_NOT_FOUND
119
        );
120
    }
121
122
    /**
123
     * @param bool $condition
124
     * @param string $conditionLabel
125
     * @param int|null $errorCode
126
     * @return $this
127
     * @throws FileHelper_Exception
128
     */
129
    private function requireTrue(bool $condition, string $conditionLabel, ?int $errorCode=null) : self
130
    {
131
        if($condition === true)
132
        {
133
            return $this;
134
        }
135
136
        if($errorCode === null)
137
        {
138
            $errorCode = FileHelper::ERROR_FILE_DOES_NOT_EXIST;
139
        }
140
141
        throw new FileHelper_Exception(
142
            sprintf('Path [%s] %s.', $this->getName(), $conditionLabel),
143
            sprintf('Tried accessing the path [%s].', $this->getPath()),
144
            $errorCode
145
        );
146
    }
147
148
    /**
149
     * @param int|null $errorCode
150
     * @return $this
151
     * @throws FileHelper_Exception
152
     */
153
    public function requireExists(?int $errorCode=null) : self
154
    {
155
        return $this->requireTrue(
156
            !empty($this->path) && realpath($this->path) !== false,
157
            'does not exist',
158
            FileHelper::ERROR_FILE_DOES_NOT_EXIST
159
        );
160
    }
161
162
    /**
163
     * @param int|NULL $errorCode
164
     * @return $this
165
     * @throws FileHelper_Exception
166
     */
167
    public function requireReadable(?int $errorCode=null) : self
168
    {
169
        $this->requireExists($errorCode);
170
171
        return $this->requireTrue(
172
            $this->isReadable(),
173
            'is not readable',
174
            FileHelper::ERROR_FILE_NOT_READABLE
175
        );
176
    }
177
178
    /**
179
     * @param int|null $errorCode
180
     * @return $this
181
     * @throws FileHelper_Exception
182
     */
183
    public function requireWritable(?int $errorCode=null) : self
184
    {
185
        return $this->requireTrue(
186
            $this->isWritable(),
187
            'is not writable',
188
            FileHelper::ERROR_PATH_NOT_WRITABLE
189
        );
190
    }
191
192
    /**
193
     * @return FileInfo
194
     *
195
     * @throws FileHelper_Exception
196
     * @see FileHelper::ERROR_PATH_IS_NOT_A_FILE
197
     */
198
    public function requireIsFile() : FileInfo
199
    {
200
        if($this instanceof FileInfo)
201
        {
202
            return $this;
203
        }
204
205
        throw new FileHelper_Exception(
206
            'Target path is not a file',
207
            sprintf(
208
                'Path: [%s].',
209
                $this->path
210
            ),
211
            FileHelper::ERROR_PATH_IS_NOT_A_FILE
212
        );
213
    }
214
215
    /**
216
     * @return FolderInfo
217
     *
218
     * @throws FileHelper_Exception
219
     * @see FileHelper::ERROR_PATH_IS_NOT_A_FOLDER
220
     */
221
    public function requireIsFolder() : FolderInfo
222
    {
223
        if($this instanceof FolderInfo)
224
        {
225
            return $this;
226
        }
227
228
        throw new FileHelper_Exception(
229
            'Target path is not a folder',
230
            sprintf(
231
                'Path: [%s].',
232
                $this->path
233
            ),
234
            FileHelper::ERROR_PATH_IS_NOT_A_FOLDER
235
        );
236
    }
237
238
    /**
239
     * @param string|PathInfoInterface|SplFileInfo $path
240
     * @return string
241
     */
242
    public static function type2string($path) : string
243
    {
244
        if($path instanceof PathInfoInterface)
245
        {
246
            return $path->getPath();
247
        }
248
249
        if($path instanceof SplFileInfo)
250
        {
251
            return $path->getPathname();
252
        }
253
254
        return trim($path);
255
    }
256
257
    /**
258
     * Resolves the type of the target path: file or folder.
259
     *
260
     * NOTE: Requires the file or folder to exist in the
261
     * file system, and will throw an exception otherwise.
262
     *
263
     * @param string|PathInfoInterface|SplFileInfo $path
264
     * @return PathInfoInterface
265
     *
266
     * @throws FileHelper_Exception
267
     * @see FileHelper::ERROR_PATH_INVALID
268
     */
269
    public static function resolveType($path) : PathInfoInterface
270
    {
271
        if($path instanceof PathInfoInterface)
272
        {
273
            return $path;
274
        }
275
276
        $path = self::type2string($path);
277
278
        if(FolderInfo::is_dir($path))
279
        {
280
            return FolderInfo::factory($path);
281
        }
282
283
        if(FileInfo::is_file($path))
284
        {
285
            return FileInfo::factory($path);
286
        }
287
288
        if(!empty($path) && $path !== '.' && $path !== '..')
289
        {
290
            return new IndeterminatePath($path);
291
        }
292
293
        throw new FileHelper_Exception(
294
            'Empty or invalid path given.',
295
            '',
296
            FileHelper::ERROR_PATH_INVALID
297
        );
298
    }
299
300
    /**
301
     * Stores an arbitrary value in this object, which can
302
     * be retrieved again with {@see AbstractPathInfo::getRuntimeProperty()}.
303
     *
304
     * These properties have no functionality beyond offering
305
     * a way to store custom data.
306
     *
307
     * @param string $name
308
     * @param mixed $value
309
     * @return $this
310
     */
311
    public function setRuntimeProperty(string $name, $value) : self
312
    {
313
        $this->runtimeProperties[$name] = $value;
314
        return $this;
315
    }
316
317
    /**
318
     * Retrieves a previously set property, if any.
319
     *
320
     * @param string $name
321
     * @return mixed|null The stored value, or null if it does not exist (or has a null value).
322
     */
323
    public function getRuntimeProperty(string $name)
324
    {
325
        return $this->runtimeProperties[$name] ?? null;
326
    }
327
328
    public function __toString() : string
329
    {
330
        return $this->getPath();
331
    }
332
333
    /**
334
     * Retrieves the last modified date for the specified file or folder.
335
     *
336
     * Note: If the target does not exist, returns null.
337
     *
338
     * @return DateTime|NULL
339
     */
340
    public function getModifiedDate() : ?DateTime
341
    {
342
        $time = filemtime($this->getPath());
343
        if($time === false) {
344
            return null;
345
        }
346
347
        $date = new DateTime();
348
        $date->setTimestamp($time);
349
        return $date;
350
    }
351
}
352