Completed
Push — master ( ee4164...27a827 )
by ignace nyamagana
03:48
created

PathTrait::decodePath()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 8
ccs 4
cts 4
cp 1
rs 9.4285
cc 1
eloc 4
nc 1
nop 1
crap 1
1
<?php
2
/**
3
 * League.Uri (http://uri.thephpleague.com)
4
 *
5
 * @package   League.uri
6
 * @author    Ignace Nyamagana Butera <[email protected]>
7
 * @copyright 2013-2015 Ignace Nyamagana Butera
8
 * @license   https://github.com/thephpleague/uri/blob/master/LICENSE (MIT License)
9
 * @version   4.2.0
10
 * @link      https://github.com/thephpleague/uri/
11
 */
12
namespace League\Uri\Components;
13
14
use InvalidArgumentException;
15
use League\Uri\Interfaces\Path as PathInterface;
16
17
/**
18
 * Value object representing a URI path component.
19
 *
20
 * @package League.uri
21
 * @author  Ignace Nyamagana Butera <[email protected]>
22
 * @since   4.0.0
23
 */
24
trait PathTrait
25
{
26
    /**
27
     * Typecode Regular expression
28
     */
29
    protected static $typeRegex = ',^(?P<basename>.*);type=(?P<typecode>a|i|d)$,';
30
31
    /**
32
     * Typecode value
33
     *
34
     * @var array
35
     */
36
    protected static $typecodeList = [
37
        'a' => PathInterface::FTP_TYPE_ASCII,
38
        'i' => PathInterface::FTP_TYPE_BINARY,
39
        'd' => PathInterface::FTP_TYPE_DIRECTORY,
40
        ''  => PathInterface::FTP_TYPE_EMPTY,
41
    ];
42
43
    /**
44
     * Dot Segment pattern
45
     *
46
     * @var array
47
     */
48
    protected static $dotSegments = ['.' => 1, '..' => 1];
49
50
    /**
51
     * Returns the instance string representation; If the
52
     * instance is not defined an empty string is returned
53
     *
54
     * @return string
55
     */
56
    abstract public function __toString();
57
58
    /**
59
     * Returns an instance with the specified string
60
     *
61
     * This method MUST retain the state of the current instance, and return
62
     * an instance that contains the modified data
63
     *
64
     * @param string $value
65
     *
66
     * @return static
67
     */
68
    abstract public function modify($value);
69
70
    /**
71
     * @inheritdoc
72
     */
73 6
    public function __debugInfo()
74
    {
75 6
        return ['path' => $this->__toString()];
76
    }
77
78
    /**
79
     * Returns an instance without dot segments
80
     *
81
     * This method MUST retain the state of the current instance, and return
82
     * an instance that contains the path component normalized by removing
83
     * the dot segment.
84
     *
85
     * @return static
86
     */
87 165
    public function withoutDotSegments()
88
    {
89 165
        $current = $this->__toString();
90 165
        if (false === strpos($current, '.')) {
91 84
            return $this;
92
        }
93
94 84
        $input = explode('/', $current);
95 84
        $new   = implode('/', array_reduce($input, [$this, 'filterDotSegments'], []));
96 84
        if (isset(static::$dotSegments[end($input)])) {
97 27
            $new .= '/';
98 18
        }
99
100 84
        return $this->modify($new);
101
    }
102
103
    /**
104
     * Filter Dot segment according to RFC3986
105
     *
106
     * @see http://tools.ietf.org/html/rfc3986#section-5.2.4
107
     *
108
     * @param array  $carry   Path segments
109
     * @param string $segment a path segment
110
     *
111
     * @return array
112
     */
113 84
    protected function filterDotSegments(array $carry, $segment)
114
    {
115 84
        if ('..' == $segment) {
116 57
            array_pop($carry);
117
118 57
            return $carry;
119
        }
120
121 84
        if (!isset(static::$dotSegments[$segment])) {
122 84
            $carry[] = $segment;
123 56
        }
124
125 84
        return $carry;
126
    }
127
128
    /**
129
     * Returns an instance without duplicate delimiters
130
     *
131
     * This method MUST retain the state of the current instance, and return
132
     * an instance that contains the path component normalized by removing
133
     * multiple consecutive empty segment
134
     *
135
     * @return static
136
     */
137 15
    public function withoutEmptySegments()
138
    {
139 15
        return $this->modify(preg_replace(',/+,', '/', $this->__toString()));
140
    }
141
142
    /**
143
     * Returns whether or not the path has a trailing delimiter
144
     *
145
     * @return bool
146
     */
147 60
    public function hasTrailingSlash()
148
    {
149 60
        $path = $this->__toString();
150
151 60
        return '' !== $path && '/' === mb_substr($path, -1, 1, 'UTF-8');
152
    }
153
154
    /**
155
     * Returns an instance with a trailing slash
156
     *
157
     * This method MUST retain the state of the current instance, and return
158
     * an instance that contains the path component with a trailing slash
159
     *
160
     *
161
     * @return static
162
     */
163 21
    public function withTrailingSlash()
164
    {
165 21
        return $this->hasTrailingSlash() ? $this : $this->modify($this->__toString().'/');
166
    }
167
168
    /**
169
     * Returns an instance without a trailing slash
170
     *
171
     * This method MUST retain the state of the current instance, and return
172
     * an instance that contains the path component without a trailing slash
173
     *
174
     * @return static
175
     */
176 21
    public function withoutTrailingSlash()
177
    {
178 21
        return !$this->hasTrailingSlash() ? $this : $this->modify(mb_substr($this->__toString(), 0, -1, 'UTF-8'));
179
    }
180
181
    /**
182
     * Returns whether or not the path is absolute or relative
183
     *
184
     * @return bool
185
     */
186 153
    public function isAbsolute()
187
    {
188 153
        $path = $this->__toString();
189
190 153
        return '' !== $path && '/' === mb_substr($path, 0, 1, 'UTF-8');
191
    }
192
193
    /**
194
     * Returns an instance with a leading slash
195
     *
196
     * This method MUST retain the state of the current instance, and return
197
     * an instance that contains the path component with a leading slash
198
     *
199
     *
200
     * @return static
201
     */
202 24
    public function withLeadingSlash()
203
    {
204 24
        return $this->isAbsolute() ? $this : $this->modify('/'.$this->__toString());
205
    }
206
207
    /**
208
     * Returns an instance without a leading slash
209
     *
210
     * This method MUST retain the state of the current instance, and return
211
     * an instance that contains the path component without a leading slash
212
     *
213
     * @return static
214
     */
215 21
    public function withoutLeadingSlash()
216
    {
217 21
        return !$this->isAbsolute() ? $this : $this->modify(mb_substr($this->__toString(), 1, null, 'UTF-8'));
218
    }
219
220
    /**
221
     * Retrieve the optional type associated to the path.
222
     *
223
     * The value returned MUST be one of the interface constant type
224
     * If no type is associated the return constant must be self::FTP_TYPE_EMPTY
225
     *
226
     * @see http://tools.ietf.org/html/rfc1738#section-3.2.2
227
     *
228
     * @return int a typecode constant.
229
     */
230 18
    public function getTypecode()
231
    {
232 18
        if (preg_match(self::$typeRegex, $this->__toString(), $matches)) {
233 9
            return self::$typecodeList[$matches['typecode']];
234
        }
235
236 9
        return PathInterface::FTP_TYPE_EMPTY;
237
    }
238
239
    /**
240
     * Return an instance with the specified typecode.
241
     *
242
     * This method MUST retain the state of the current instance, and return
243
     * an instance that contains the specified type appended to the path.
244
     * if not
245
     *
246
     * Using self::FTP_TYPE_EMPTY is equivalent to removing the typecode.
247
     *
248
     * @param int $type one typecode constant.
249
     *
250
     * @throws InvalidArgumentException for invalid typecode.
251
     *
252
     * @return static
253
     *
254
     */
255 30
    public function withTypecode($type)
256
    {
257 30
        if (!in_array($type, self::$typecodeList)) {
258 3
            throw new InvalidArgumentException('invalid typecode');
259
        }
260
261 27
        $path = $this->__toString();
262 27
        if (preg_match(self::$typeRegex, $path, $matches)) {
263 6
            $path = $matches['basename'];
264 4
        }
265
266 27
        $extension = array_search($type, self::$typecodeList);
267 27
        $extension = trim($extension);
268 27
        if ('' !== $extension) {
269 15
            $extension = ';type='.$extension;
270 10
        }
271
272 27
        return $this->modify($path.$extension);
273
    }
274
}
275