Passed
Push — test ( 9a8848...0c4d14 )
by Tom
02:23
created

LibFs::symlink()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 2
c 1
b 0
f 0
nc 1
nop 2
dl 0
loc 4
ccs 2
cts 2
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
76 8
        $count = strspn($path, '/', 0, 3) % 2;
77
78 8
        return (bool)$count;
79
    }
80
81
    /**
82
     * check if path is basename
83
     *
84
     * @param string $path
85
     * @return bool
86
     */
87 5
    public static function isBasename($path)
88
    {
89 5
        if (in_array($path, array('', '.', '..'), true)) {
90 1
            return false;
91
        }
92
93 4
        if (false !== strpos($path, '/')) {
94 3
            return false;
95
        }
96
97 1
        return true;
98
    }
99
100
    /**
101
     * @param string $path
102
     * @return bool
103
     */
104 12
    public static function isReadableFile($path)
105
    {
106 12
        if (!is_file($path)) {
107 4
            return false;
108
        }
109
110 10
        return is_readable($path) ?: self::canFopen($path, 'rb');
111
    }
112
113
    /**
114
     * @param string $path
115
     * @return bool
116
     */
117 2
    public static function isStreamUri($path)
118
    {
119 2
        $scheme = parse_url($path, PHP_URL_SCHEME);
120 2
        if (null === $scheme) {
121 1
            return false;
122
        }
123
124 1
        return in_array($scheme, stream_get_wrappers(), true);
125
    }
126
127
    /**
128
     * create directory if not yet exists
129
     *
130
     * @param string $path
131
     * @param int $mode [optional]
132
     * @return string
133
     */
134 7
    public static function mkDir($path, $mode = 0777)
135
    {
136 7
        if (!is_dir($path)) {
137
            /** @noinspection NestedPositiveIfStatementsInspection */
138 6
            if (!mkdir($path, $mode, true) && !is_dir($path)) {
139
                // @codeCoverageIgnoreStart
140
                throw new \RuntimeException(
141
                    sprintf('Directory "%s" was not created', $path)
142
                );
143
                // @codeCoverageIgnoreEnd
144
            }
145
        }
146
147 7
        return $path;
148
    }
149
150
    /**
151
     * Resolve relative path segments in a path on it's own
152
     *
153
     * This is not realpath, not resolving any links.
154
     *
155
     * @param string $path
156
     * @return string
157
     */
158 13
    public static function normalizePathSegments($path)
159
    {
160 13
        if ('' === $path) {
161 1
            return $path;
162
        }
163
164 12
        $buffer = $path;
165
166 12
        $prefix = '';
167 12
        $len = strspn($buffer, '/');
168 12
        if (0 < $len) {
169 6
            $prefix = substr($path, 0, $len);
170 6
            $buffer = substr($path, $len);
171
        }
172
173 12
        $buffer = rtrim($buffer, '/');
174
175 12
        if (in_array($buffer, array('', '.'), true)) {
176 4
            return $prefix;
177
        }
178
179 8
        $pos = strpos($buffer, '/');
180 8
        if (false === $pos) {
181 2
            return $prefix . $buffer;
182
        }
183
184 6
        $buffer = preg_replace('~/+~', '/', $buffer);
185
186 6
        $segments = explode('/', $buffer);
187 6
        $stack = array();
188 6
        foreach ($segments as $segment) {
189 6
            $i = count($stack) - 1;
190 6
            if ('.' === $segment) {
191 2
                continue;
192
            }
193
194 6
            if ('..' !== $segment) {
195 6
                $stack[] = $segment;
196
197 6
                continue;
198
            }
199
200 4
            if (($i > -1) && '..' !== $stack[$i]) {
201 4
                array_pop($stack);
202
203 4
                continue;
204
            }
205
206 1
            $stack[] = $segment;
207
        }
208
209 6
        return $prefix . implode('/', $stack);
210
    }
211
212
    /**
213
     * rename a file
214
     *
215
     * @param string $old
216
     * @param string $new
217
     * @return string new file-name
218
     */
219 1
    public static function rename($old, $new)
220
    {
221 1
        if (!@rename($old, $new)) {
222 1
            throw new \RuntimeException(sprintf('Failed to rename "%s" to "%s"', $old, $new));
223
        }
224
225 1
        return $new;
226
    }
227
228
    /**
229
     * @param string $file
230
     * @return string
231
     */
232 6
    public static function rm($file)
233
    {
234 6
        if (self::isReadableFile($file)) {
235 6
            unlink($file);
236
        }
237
238 6
        return $file;
239
    }
240
241
    /**
242
     * @param string $dir
243
     */
244 3
    public static function rmDir($dir)
245
    {
246 3
        $result = @lstat($dir);
247 3
        if (false === $result) {
248 1
            return;
249
        }
250
251 3
        $dirs = array();
252 3
        $dirs[] = $dir;
253 3
        for ($i = 0; isset($dirs[$i]); $i++) {
254 3
            $current = $dirs[$i];
255 3
            $result = @scandir($current);
256 3
            if (false === $result) {
257 1
                throw new UnexpectedValueException(sprintf('Failed to open directory: %s', $current));
258
            }
259 2
            $files = array_diff($result, array('.', '..'));
260 2
            foreach ($files as $file) {
261 1
                $path = "${current}/${file}";
262 1
                if (is_dir($path)) {
263 1
                    $dirs[] = $path;
264 1
                } elseif (is_file($path)) {
265 1
                    self::rm($path);
266
                }
267
            }
268
        }
269
270 2
        while (null !== ($pop = array_pop($dirs))) {
271
            /* @scrutinizer ignore-unhandled */
272 2
            @rmdir($pop);
273
        }
274 2
    }
275
276
    /**
277
     * create symbolic link, recreate if it exists
278
     *
279
     * @param string $target
280
     * @param string $link
281
     */
282
    public static function symlink($target, $link)
283
    {
284 1
        self::unlink($link);
285 1
        symlink($target, $link);
286 1
    }
287
288
    /**
289
     * Create temporary directory (which does not get cleaned up)
290
     *
291
     * @param string $prefix [optional]
292
     * @return string path
293
     */
294
    public static function tmpDir($prefix = '')
295
    {
296 2
        $path = tempnam(sys_get_temp_dir(), $prefix);
297 2
        self::rm($path);
298 2
        self::mkDir($path, 0700);
299
300 2
        return $path;
301
    }
302
303
    /**
304
     * Create handle and path of a temporary file (which gets cleaned up)
305
     *
306
     * @return array(handle, string)
307
     */
308
    public static function tmpFile()
309
    {
310 2
        $handle = tmpfile();
311 2
        if (false === $handle) {
312
            // @codeCoverageIgnoreStart
313
            throw new UnexpectedValueException('Unable to create temporary file');
314
            // @codeCoverageIgnoreEnd
315
        }
316 2
        $meta = stream_get_meta_data($handle);
317
318 2
        return array($handle, $meta['uri']);
319
    }
320
321
    /**
322
     * Create temporary file w/ contents
323
     *
324
     * @param string $buffer
325
     * @return string path of temporary file
326
     */
327
    public static function tmpFilePut($buffer)
328
    {
329 1
        list(, $path) = self::tmpFile();
330 1
        file_put_contents($path, $buffer);
331
332 1
        return $path;
333
    }
334
335
    /**
336
     * @param string $link
337
     */
338
    public static function unlink($link)
339
    {
340 1
        if (is_link($link)) {
341 1
            unlink($link);
342
        }
343 1
    }
344
}
345