Passed
Push — master ( bba5a4...181b61 )
by Tom
03:44
created

LibFs::rmDir()   B

Complexity

Conditions 8
Paths 12

Size

Total Lines 29
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 20
CRAP Score 8

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 8
eloc 19
c 1
b 0
f 0
nc 12
nop 1
dl 0
loc 29
ccs 20
cts 20
cp 1
crap 8
rs 8.4444
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
 * @package Ktomk\Pipelines
13
 * @covers \Ktomk\Pipelines\LibFs
14
 */
15
class LibFs
16
{
17
    /**
18
     * @param string $path
19
     * @param string $mode [optional]
20
     * @return bool
21
     */
22 2
    public static function canFopen($path, $mode = null)
23
    {
24 2
        if (null === $mode) {
25 1
            $mode = 'rb';
26
        }
27
28 2
        $handle = @fopen($path, $mode);
29 2
        if (false === $handle) {
30 1
            return false;
31
        }
32
33
        /** @scrutinizer ignore-unhandled */
34 1
        @fclose($handle);
35
36 1
        return true;
37
    }
38
39
    /**
40
     * locate (readable) file by basename upward all parent directories
41
     *
42
     * @param string $basename
43
     * @param string $directory [optional] directory to operate from, defaults
44
     *               to "." (relative path of present working directory)
45
     * @return null|string
46
     */
47 4
    public static function fileLookUp($basename, $directory = null)
48
    {
49 4
        if ('' === $directory || null === $directory) {
50 1
            $directory = '.';
51
        }
52
53
        for (
54 4
            $dirName = $directory, $old = null;
55 4
            $old !== $dirName;
56 2
            $old = $dirName, $dirName = dirname($dirName)
57
        ) {
58 4
            $test = $dirName . '/' . $basename;
59 4
            if (self::isReadableFile($test)) {
60 3
                return $test;
61
            }
62
        }
63
64 1
        return null;
65
    }
66
67
    /**
68
     * check if path is absolute
69
     *
70
     * @param string $path
71
     * @return bool
72
     */
73 8
    public static function isAbsolutePath($path)
74
    {
75
        // TODO: a variant with PHP stream wrapper prefix support
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
     * @param string $path
103
     * @return bool
104
     */
105 12
    public static function isReadableFile($path)
106
    {
107 12
        if (!is_file($path)) {
108 4
            return false;
109
        }
110
111 10
        return is_readable($path) ?: self::canFopen($path, 'rb');
112
    }
113
114
    /**
115
     * @param string $path
116
     * @return bool
117
     */
118 2
    public static function isStreamUri($path)
119
    {
120 2
        $scheme = parse_url($path, PHP_URL_SCHEME);
121 2
        if (null === $scheme) {
122 1
            return false;
123
        }
124
125 1
        return in_array($scheme, stream_get_wrappers(), true);
126
    }
127
128
    /**
129
     * create directory if not yet exists
130
     *
131
     * @param string $path
132
     * @param int $mode [optional]
133
     * @return string
134
     */
135 7
    public static function mkDir($path, $mode = 0777)
136
    {
137 7
        if (!is_dir($path)) {
138
            /** @noinspection NestedPositiveIfStatementsInspection */
139 6
            if (!mkdir($path, $mode, true) && !is_dir($path)) {
140
                // @codeCoverageIgnoreStart
141
                throw new \RuntimeException(
142
                    sprintf('Directory "%s" was not created', $path)
143
                );
144
                // @codeCoverageIgnoreEnd
145
            }
146
        }
147
148 7
        return $path;
149
    }
150
151
    /**
152
     * Resolve relative path segments in a path on it's own
153
     *
154
     * This is not realpath, not resolving any links.
155
     *
156
     * @param string $path
157
     * @return string
158
     */
159 13
    public static function normalizePathSegments($path)
160
    {
161 13
        if ('' === $path) {
162 1
            return $path;
163
        }
164
165 12
        $buffer = $path;
166
167 12
        $prefix = '';
168 12
        $len = strspn($buffer, '/');
169 12
        if (0 < $len) {
170 6
            $prefix = substr($path, 0, $len);
171 6
            $buffer = substr($path, $len);
172
        }
173
174 12
        $buffer = rtrim($buffer, '/');
175
176 12
        if (in_array($buffer, array('', '.'), true)) {
177 4
            return $prefix;
178
        }
179
180 8
        $pos = strpos($buffer, '/');
181 8
        if (false === $pos) {
182 2
            return $prefix . $buffer;
183
        }
184
185 6
        $buffer = preg_replace('~/+~', '/', $buffer);
186
187 6
        $segments = explode('/', $buffer);
188 6
        $stack = array();
189 6
        foreach ($segments as $segment) {
190 6
            $i = count($stack) - 1;
191 6
            if ('.' === $segment) {
192 2
                continue;
193
            }
194
195 6
            if ('..' !== $segment) {
196 6
                $stack[] = $segment;
197
198 6
                continue;
199
            }
200
201 4
            if (($i > -1) && '..' !== $stack[$i]) {
202 4
                array_pop($stack);
203
204 4
                continue;
205
            }
206
207 1
            $stack[] = $segment;
208
        }
209
210 6
        return $prefix . implode('/', $stack);
211
    }
212
213
    /**
214
     * rename a file
215
     *
216
     * @param string $old
217
     * @param string $new
218
     * @return string new file-name
219
     */
220 1
    public static function rename($old, $new)
221
    {
222 1
        if (!@rename($old, $new)) {
223 1
            throw new \RuntimeException(sprintf('Failed to rename "%s" to "%s"', $old, $new));
224
        }
225
226 1
        return $new;
227
    }
228
229
    /**
230
     * @param string $file
231
     * @return string
232
     */
233 6
    public static function rm($file)
234
    {
235 6
        if (self::isReadableFile($file)) {
236 6
            unlink($file);
237
        }
238
239 6
        return $file;
240
    }
241
242
    /**
243
     * @param string $dir
244
     */
245 3
    public static function rmDir($dir)
246
    {
247 3
        $result = @lstat($dir);
248 3
        if (false === $result) {
249 1
            return;
250
        }
251
252 3
        $dirs = array();
253 3
        $dirs[] = $dir;
254 3
        for ($i = 0; isset($dirs[$i]); $i++) {
255 3
            $current = $dirs[$i];
256 3
            $result = @scandir($current);
257 3
            if (false === $result) {
258 1
                throw new UnexpectedValueException(sprintf('Failed to open directory: %s', $current));
259
            }
260 2
            $files = array_diff($result, array('.', '..'));
261 2
            foreach ($files as $file) {
262 1
                $path = "${current}/${file}";
263 1
                if (is_dir($path)) {
264 1
                    $dirs[] = $path;
265 1
                } elseif (is_file($path)) {
266 1
                    self::rm($path);
267
                }
268
            }
269
        }
270
271 2
        while (null !== ($pop = array_pop($dirs))) {
272
            /* @scrutinizer ignore-unhandled */
273 2
            @rmdir($pop);
274
        }
275 2
    }
276
277
    /**
278
     * create symbolic link, recreate if it exists
279
     *
280
     * @param string $target
281
     * @param string $link
282
     */
283
    public static function symlink($target, $link)
284
    {
285 1
        self::unlink($link);
286 1
        symlink($target, $link);
287 1
    }
288
289
    /**
290
     * Create temporary directory (which does not get cleaned up)
291
     *
292
     * @param string $prefix [optional]
293
     * @return string path
294
     */
295
    public static function tmpDir($prefix = '')
296
    {
297 2
        $path = tempnam(sys_get_temp_dir(), $prefix);
298 2
        self::rm($path);
299 2
        self::mkDir($path, 0700);
300
301 2
        return $path;
302
    }
303
304
    /**
305
     * Create handle and path of a temporary file (which get's cleaned up)
306
     *
307
     * @return array(handle, string)
308
     */
309
    public static function tmpFile()
310
    {
311 2
        $handle = tmpfile();
312 2
        if (false === $handle) {
313
            // @codeCoverageIgnoreStart
314
            throw new UnexpectedValueException('Unable to create temporary file');
315
            // @codeCoverageIgnoreEnd
316
        }
317 2
        $meta = stream_get_meta_data($handle);
318
319 2
        return array($handle, $meta['uri']);
320
    }
321
322
    /**
323
     * Create temporary file w/ contents
324
     *
325
     * @param string $buffer
326
     * @return string path of temporary file
327
     */
328
    public static function tmpFilePut($buffer)
329
    {
330 1
        list(, $path) = self::tmpFile();
331 1
        file_put_contents($path, $buffer);
332
333 1
        return $path;
334
    }
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