Test Failed
Push — master ( aebabc...cbd8ea )
by Sebastian
03:14
created

AbstractPathInfo::pathHasEndingSlash()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 12
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

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