Passed
Push — test ( 32f496...c27118 )
by Tom
02:18
created

LibFs::tmpDir()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 4
c 1
b 0
f 0
nc 1
nop 1
dl 0
loc 7
ccs 4
cts 4
cp 1
crap 1
rs 10
1
<?php
2
3
/* this file is part of pipelines */
4
5
namespace Ktomk\Pipelines;
6
7
use UnexpectedValueException;
8
9
/**
10
 * Class LibFs - Low level file-system utility functions
11
 *
12
 * @covers \Ktomk\Pipelines\LibFs
13
 */
14
class LibFs
15
{
16
    /**
17
     * @param string $path
18
     * @param string $mode [optional]
19
     * @return bool
20
     */
21 2
    public static function canFopen($path, $mode = null)
22
    {
23 2
        if (null === $mode) {
24 1
            $mode = 'rb';
25
        }
26
27 2
        $handle = @fopen($path, $mode);
28 2
        if (false === $handle) {
29 1
            return false;
30
        }
31
32
        /** @scrutinizer ignore-unhandled */
33 1
        @fclose($handle);
34
35 1
        return true;
36
    }
37
38
    /**
39
     * locate (readable) file by basename upward all parent directories
40
     *
41
     * @param string $basename
42
     * @param string $directory [optional] directory to operate from, defaults
43
     *               to "." (relative path of present working directory)
44
     * @return null|string
45
     */
46 4
    public static function fileLookUp($basename, $directory = null)
47
    {
48 4
        if ('' === $directory || null === $directory) {
49 1
            $directory = '.';
50
        }
51
52
        for (
53 4
            $dirName = $directory, $old = null;
54 4
            $old !== $dirName;
55 2
            $old = $dirName, $dirName = dirname($dirName)
56
        ) {
57 4
            $test = $dirName . '/' . $basename;
58 4
            if (self::isReadableFile($test)) {
59 3
                return $test;
60
            }
61
        }
62
63 1
        return null;
64
    }
65
66
    /**
67
     * check if path is absolute
68
     *
69
     * @param string $path
70
     * @return bool
71
     */
72 8
    public static function isAbsolutePath($path)
73
    {
74
        // TODO: a variant with PHP stream wrapper prefix support
75
        /* @see isStreamUri */
76
77 8
        $count = strspn($path, '/', 0, 3) % 2;
78
79 8
        return (bool)$count;
80
    }
81
82
    /**
83
     * check if path is basename
84
     *
85
     * @param string $path
86
     * @return bool
87
     */
88 5
    public static function isBasename($path)
89
    {
90 5
        if (in_array($path, array('', '.', '..'), true)) {
91 1
            return false;
92
        }
93
94 4
        if (false !== strpos($path, '/')) {
95 3
            return false;
96
        }
97
98 1
        return true;
99
    }
100
101
    /**
102
     * see 2.2 Standards permit the exclusion of bad filenames / POSIX.1-2008
103
     * @link https://dwheeler.com/essays/fixing-unix-linux-filenames.html
104
     *
105
     * @param string $filename
106
     * @return bool
107
     */
108 10
    public static function isPortableFilename($filename)
109
    {
110
        # A-Z, a-z, 0-9, <period>, <underscore>, and <hyphen>)
111 10
        $result = preg_match('(^(?!-)[A-Za-z0-9._-]+$)', $filename);
112 10
        if (false === $result) {
113
            // @codeCoverageIgnoreStart
114
            throw new UnexpectedValueException('preg_match pattern failed');
115
            // @codeCoverageIgnoreEnd
116
        }
117
118 10
        return 1 === $result;
119
    }
120
121
    /**
122
     * @param string $path
123
     * @return bool
124
     */
125 11
    public static function isReadableFile($path)
126
    {
127 11
        if (!is_file($path)) {
128 4
            return false;
129
        }
130
131 9
        return is_readable($path) ?: self::canFopen($path, 'rb');
132
    }
133
134
    /**
135
     * @param string $path
136
     * @return bool
137
     */
138 2
    public static function isStreamUri($path)
139
    {
140 2
        $scheme = parse_url($path, PHP_URL_SCHEME);
141 2
        if (null === $scheme) {
142 1
            return false;
143
        }
144
145 1
        return in_array($scheme, stream_get_wrappers(), true);
146
    }
147
148
    /**
149
     * create directory if not yet exists
150
     *
151
     * @param string $path
152
     * @param int $mode [optional]
153
     * @return string
154
     */
155 7
    public static function mkDir($path, $mode = 0777)
156
    {
157 7
        if (!is_dir($path)) {
158
            /** @noinspection NestedPositiveIfStatementsInspection */
159 6
            if (!mkdir($path, $mode, true) && !is_dir($path)) {
160
                // @codeCoverageIgnoreStart
161
                throw new \RuntimeException(
162
                    sprintf('Directory "%s" was not created', $path)
163
                );
164
                // @codeCoverageIgnoreEnd
165
            }
166
        }
167
168 7
        return $path;
169
    }
170
171
    /**
172
     * Normalize a path as/if common in PHP
173
     *
174
     * E.g. w/ phar:// in front which means w/ stream wrappers in
175
     * mind.
176
     *
177
     * @param string $path
178
     * @return string
179
     */
180 5
    public static function normalizePath($path)
181
    {
182 5
        $buffer = $path;
183
184 5
        $scheme = '';
185
        // TODO support for all supported stream wrappers (w/ absolute/relative notation?)
186
        /* @see isStreamUri */
187
        /* @see isAbsolutePath */
188 5
        if (0 === strpos($buffer, 'phar://') || 0 === strpos($buffer, 'file://')) {
189 4
            $scheme = substr($buffer, 0, 7);
190 4
            $buffer = substr($buffer, 7);
191
        }
192
193 5
        $normalized = self::normalizePathSegments($buffer);
194
195 5
        return $scheme . $normalized;
196
    }
197
198
    /**
199
     * Resolve relative path segments in a path on it's own
200
     *
201
     * This is not realpath, not resolving any links.
202
     *
203
     * @param string $path
204
     * @return string
205
     */
206 18
    public static function normalizePathSegments($path)
207
    {
208 18
        if ('' === $path) {
209 1
            return $path;
210
        }
211
212 17
        $buffer = $path;
213
214 17
        $prefix = '';
215 17
        $len = strspn($buffer, '/');
216 17
        if (0 < $len) {
217 9
            $prefix = substr($buffer, 0, $len);
218 9
            $buffer = substr($buffer, $len);
219
        }
220
221 17
        $buffer = rtrim($buffer, '/');
222
223 17
        if (in_array($buffer, array('', '.'), true)) {
224 4
            return $prefix;
225
        }
226
227 13
        $pos = strpos($buffer, '/');
228 13
        if (false === $pos) {
229 2
            return $prefix . $buffer;
230
        }
231
232 11
        $buffer = preg_replace('~/+~', '/', $buffer);
233
234 11
        $segments = explode('/', $buffer);
235 11
        $stack = array();
236 11
        foreach ($segments as $segment) {
237 11
            $i = count($stack) - 1;
238 11
            if ('.' === $segment) {
239 2
                continue;
240
            }
241
242 11
            if ('..' !== $segment) {
243 11
                $stack[] = $segment;
244
245 11
                continue;
246
            }
247
248 9
            if (($i > -1) && '..' !== $stack[$i]) {
249 9
                array_pop($stack);
250
251 9
                continue;
252
            }
253
254 1
            $stack[] = $segment;
255
        }
256
257 11
        return $prefix . implode('/', $stack);
258
    }
259
260
    /**
261
     * rename a file
262
     *
263
     * @param string $old
264
     * @param string $new
265
     * @return string new file-name
266
     */
267 1
    public static function rename($old, $new)
268
    {
269 1
        if (!@rename($old, $new)) {
270 1
            throw new \RuntimeException(sprintf('Failed to rename "%s" to "%s"', $old, $new));
271
        }
272
273 1
        return $new;
274
    }
275
276
    /**
277
     * @param string $file
278
     * @return string
279
     */
280 5
    public static function rm($file)
281
    {
282 5
        if (self::isReadableFile($file)) {
283 5
            unlink($file);
284
        }
285
286 5
        return $file;
287
    }
288
289
    /**
290
     * @param string $dir
291
     */
292 3
    public static function rmDir($dir)
293
    {
294 3
        $result = @lstat($dir);
295 3
        if (false === $result) {
296 1
            return;
297
        }
298
299 3
        $dirs = array();
300 3
        $dirs[] = $dir;
301 3
        for ($i = 0; isset($dirs[$i]); $i++) {
302 3
            $current = $dirs[$i];
303 3
            $result = @scandir($current);
304 3
            if (false === $result) {
305 1
                throw new UnexpectedValueException(sprintf('Failed to open directory: %s', $current));
306
            }
307 2
            $files = array_diff($result, array('.', '..'));
308 2
            foreach ($files as $file) {
309 1
                $path = "${current}/${file}";
310 1
                if (is_dir($path)) {
311 1
                    $dirs[] = $path;
312 1
                } elseif (is_file($path)) {
313 1
                    self::rm($path);
314
                }
315
            }
316
        }
317
318 2
        while (null !== ($pop = array_pop($dirs))) {
319
            /* @scrutinizer ignore-unhandled */
320 2
            @rmdir($pop);
321
        }
322 2
    }
323
324
    /**
325
     * create symbolic link, recreate if it exists
326
     *
327
     * @param string $target
328
     * @param string $link
329
     */
330
    public static function symlink($target, $link)
331
    {
332 1
        self::unlink($link);
333 1
        symlink($target, $link);
334 1
    }
335
336
    /**
337
     * @param string $link
338
     */
339
    public static function unlink($link)
340
    {
341 1
        if (is_link($link)) {
342 1
            unlink($link);
343
        }
344 1
    }
345
}
346