LibFsPath::containsRelativeSegment()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 2
c 0
b 0
f 0
nc 2
nop 1
dl 0
loc 5
ccs 3
cts 3
cp 1
crap 2
rs 10
1
<?php
2
3
/* this file is part of pipelines */
4
5
namespace Ktomk\Pipelines;
6
7
/**
8
 * Class LibFsPath - path utility functions
9
 *
10
 * @package Ktomk\Pipelines
11
 */
12
class LibFsPath
13
{
14
    /**
15
     * check if path is absolute
16
     *
17
     * @param string $path
18
     *
19
     * @return bool
20
     */
21 13
    public static function isAbsolute($path)
22
    {
23
        // TODO: a variant with PHP stream wrapper prefix support
24
        /* @see LibFsStream::isUri */
25
26 13
        $count = strspn($path, '/', 0, 3) % 2;
27
28 13
        return (bool)$count;
29
    }
30
31
    /**
32
     * check if path is basename
33
     *
34
     * @param string $path
35
     *
36
     * @return bool
37
     */
38 5
    public static function isBasename($path)
39
    {
40 5
        if (in_array($path, array('', '.', '..'), true)) {
41 1
            return false;
42
        }
43
44 4
        if (false !== strpos($path, '/')) {
45 3
            return false;
46
        }
47
48 1
        return true;
49
    }
50
51
    /**
52
     * Resolve relative path segments in a path on it's own
53
     *
54
     * This is not realpath, not resolving any links.
55
     *
56
     * @param string $path
57
     *
58
     * @return string
59
     */
60 36
    public static function normalizeSegments($path)
61
    {
62 36
        if ('' === $path) {
63 1
            return $path;
64
        }
65
66 35
        $buffer = $path;
67
68 35
        $prefix = '';
69 35
        $len = strspn($buffer, '/');
70 35
        if (0 < $len) {
71 22
            $prefix = substr($buffer, 0, $len);
72 22
            $buffer = substr($buffer, $len);
73
        }
74
75 35
        $buffer = rtrim($buffer, '/');
76
77 35
        if (in_array($buffer, array('', '.'), true)) {
78 4
            return $prefix;
79
        }
80
81 31
        $pos = strpos($buffer, '/');
82 31
        if (false === $pos) {
83 4
            return $prefix . $buffer;
84
        }
85
86 27
        $buffer = preg_replace('~/+~', '/', $buffer);
87
88 27
        $segments = explode('/', $buffer);
89 27
        $stack = array();
90 27
        foreach ($segments as $segment) {
91 27
            $i = count($stack) - 1;
92 27
            if ('.' === $segment) {
93 10
                continue;
94
            }
95
96 27
            if ('..' !== $segment) {
97 23
                $stack[] = $segment;
98
99 23
                continue;
100
            }
101
102 18
            if (($i > -1) && '..' !== $stack[$i]) {
103 14
                array_pop($stack);
104
105 14
                continue;
106
            }
107
108 9
            $stack[] = $segment;
109
        }
110
111 27
        return $prefix . implode('/', $stack);
112
    }
113
114
    /**
115
     * @param string $path
116
     *
117
     * @return bool
118
     */
119 5
    public static function containsRelativeSegment($path)
120
    {
121 5
        $segments = array_flip(explode('/', $path));
122
123 5
        return isset($segments['.']) || isset($segments['..']);
124
    }
125
126
    /**
127
     * Normalize a path as/if common in PHP
128
     *
129
     * E.g. w/ phar:// in front which means w/ stream wrappers in
130
     * mind.
131
     *
132
     * @param string $path
133
     *
134
     * @return string
135
     */
136 5
    public static function normalize($path)
137
    {
138 5
        $buffer = $path;
139
140 5
        $scheme = '';
141
        // TODO support for all supported stream wrappers (w/ absolute/relative notation?)
142
        /* @see LibFsStream::isUri */
143
        /* @see LibFsPath::isAbsolute */
144 5
        if (0 === strpos($buffer, 'phar://') || 0 === strpos($buffer, 'file://')) {
145 4
            $scheme = substr($buffer, 0, 7);
146 4
            $buffer = substr($buffer, 7);
147
        }
148
149 5
        $normalized = self::normalizeSegments($buffer);
150
151 5
        return $scheme . $normalized;
152
    }
153
154
    /**
155
     * @param string $path
156
     *
157
     * @return bool
158
     */
159 16
    public static function isPortable($path)
160
    {
161 16
        return 1 === Preg::match('(^(?>/?(?!-)[A-Za-z0-9._-]+)+$)D', $path);
162
    }
163
164
    /**
165
     * @param string $path
166
     *
167
     * @return string
168
     */
169 5
    public static function gateAbsolutePortable($path)
170
    {
171 5
        if (!self::isAbsolute($path)) {
172 1
            throw new \InvalidArgumentException(sprintf('not an absolute path: "%s"', $path));
173
        }
174
175 4
        $normalized = self::normalizeSegments($path);
176 4
        if (self::containsRelativeSegment($normalized)) {
177 1
            throw new \InvalidArgumentException(sprintf('not a fully qualified path: "%s"', $path));
178
        }
179
180 3
        if (!self::isPortable($path)) {
181 1
            throw new \InvalidArgumentException(sprintf('not a portable path: "%s"', $path));
182
        }
183
184 2
        return $normalized;
185
    }
186
}
187