Completed
Push — master ( 1bc4b8...c77c49 )
by ignace nyamagana
02:36
created

PathTrait::getTypecode()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 8
ccs 0
cts 0
cp 0
rs 9.4285
cc 2
eloc 4
nc 2
nop 0
crap 6
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
     * Filter the encoded path string
52
     *
53
     * @param string $str the encoded path
54
     *
55
     * @throws InvalidArgumentException If the encoded path is invalid
56
     *
57
     * @return string
58
     */
59
    protected function filterEncodedPath($str)
60
    {
61
        if (!preg_match(',[?#],', $str)) {
62
            return $str;
63
        }
64
65
        throw new InvalidArgumentException(sprintf(
66
            'The encoded path `%s` contains invalid characters',
67
            $str
68
        ));
69
    }
70
71
    /**
72
     * Returns the instance string representation; If the
73
     * instance is not defined an empty string is returned
74
     *
75
     * @return string
76
     */
77
    abstract public function __toString();
78
79
    /**
80
     * Returns an instance with the specified string
81
     *
82
     * This method MUST retain the state of the current instance, and return
83
     * an instance that contains the modified data
84
     *
85
     * @param string $value
86
     *
87
     * @return static
88
     */
89
    abstract public function withContent($value = null);
90
91 6
    /**
92
     * DEPRECATION WARNING! This method will be removed in the next major point release
93 6
     *
94
     * @deprecated deprecated since version 4.2
95
     *
96
     * @see withContent
97
     *
98
     * Returns an instance with the specified string
99
     *
100
     * This method MUST retain the state of the current instance, and return
101
     * an instance that contains the modified data
102
     *
103
     * @param string $value
104
     *
105
     * @return static
106
     */
107
    abstract public function modify($value);
108
109
    /**
110
     * @inheritdoc
111
     */
112 228
    public function __debugInfo()
113
    {
114 228
        return ['path' => $this->getContent()];
115 228
    }
116 93
117
    /**
118
     * The component raw data
119 138
     *
120 138
     * @return mixed
121 138
     */
122 45
    abstract public function getContent();
123 30
124
    /**
125 138
     * Returns an instance without dot segments
126
     *
127
     * This method MUST retain the state of the current instance, and return
128
     * an instance that contains the path component normalized by removing
129
     * the dot segment.
130
     *
131
     * @return static
132
     */
133
    public function withoutDotSegments()
134
    {
135
        $current = $this->__toString();
136
        if (false === strpos($current, '.')) {
137
            return $this;
138 138
        }
139
140 138
        $input = explode('/', $current);
141 81
        $new = implode('/', array_reduce($input, [$this, 'filterDotSegments'], []));
142
        if (isset(static::$dotSegments[end($input)])) {
143 81
            $new .= '/';
144
        }
145
146 138
        return $this->withContent($new);
147 138
    }
148 92
149
    /**
150 138
     * Filter Dot segment according to RFC3986
151
     *
152
     * @see http://tools.ietf.org/html/rfc3986#section-5.2.4
153
     *
154
     * @param array  $carry   Path segments
155
     * @param string $segment a path segment
156
     *
157
     * @return array
158
     */
159
    protected function filterDotSegments(array $carry, $segment)
160
    {
161
        if ('..' === $segment) {
162 15
            array_pop($carry);
163
164 15
            return $carry;
165
        }
166
167
        if (!isset(static::$dotSegments[$segment])) {
168
            $carry[] = $segment;
169
        }
170
171
        return $carry;
172 60
    }
173
174 60
    /**
175
     * Returns an instance without duplicate delimiters
176 60
     *
177
     * This method MUST retain the state of the current instance, and return
178
     * an instance that contains the path component normalized by removing
179
     * multiple consecutive empty segment
180
     *
181
     * @return static
182
     */
183
    public function withoutEmptySegments()
184
    {
185
        return $this->withContent(preg_replace(',/+,', '/', $this->__toString()));
186
    }
187
188 21
    /**
189
     * Returns whether or not the path has a trailing delimiter
190 21
     *
191
     * @return bool
192
     */
193
    public function hasTrailingSlash()
194
    {
195
        $path = $this->__toString();
196
197
        return '' !== $path && '/' === mb_substr($path, -1, 1, 'UTF-8');
198
    }
199
200
    /**
201 21
     * Returns an instance with a trailing slash
202
     *
203 21
     * This method MUST retain the state of the current instance, and return
204
     * an instance that contains the path component with a trailing slash
205
     *
206
     *
207
     * @return static
208
     */
209
    public function withTrailingSlash()
210
    {
211 180
        return $this->hasTrailingSlash() ? $this : $this->withContent($this->__toString().'/');
212
    }
213 180
214
    /**
215 180
     * Returns an instance without a trailing slash
216
     *
217
     * This method MUST retain the state of the current instance, and return
218
     * an instance that contains the path component without a trailing slash
219
     *
220
     * @return static
221
     */
222
    public function withoutTrailingSlash()
223
    {
224
        return !$this->hasTrailingSlash() ? $this : $this->withContent(mb_substr($this, 0, -1, 'UTF-8'));
225
    }
226
227 150
    /**
228
     * Returns whether or not the path is absolute or relative
229 150
     *
230
     * @return bool
231
     */
232
    public function isAbsolute()
233
    {
234
        $path = $this->__toString();
235
236
        return '' !== $path && '/' === mb_substr($path, 0, 1, 'UTF-8');
237
    }
238
239
    /**
240 18
     * Returns an instance with a leading slash
241
     *
242 18
     * This method MUST retain the state of the current instance, and return
243
     * an instance that contains the path component with a leading slash
244
     *
245
     *
246
     * @return static
247
     */
248
    public function withLeadingSlash()
249
    {
250
        return $this->isAbsolute() ? $this : $this->withContent('/'.$this->__toString());
251
    }
252
253
    /**
254
     * Returns an instance without a leading slash
255
     *
256
     * This method MUST retain the state of the current instance, and return
257
     * an instance that contains the path component without a leading slash
258
     *
259
     * @return static
260
     */
261
    public function withoutLeadingSlash()
262
    {
263
        return !$this->isAbsolute() ? $this : $this->withContent(mb_substr($this, 1, null, 'UTF-8'));
264
    }
265
266
    /**
267
     * DEPRECATION WARNING! This method will be removed in the next major point release
268
     *
269
     * @deprecated deprecated since version 4.2
270
     *
271
     * Retrieve the optional type associated to the path.
272
     *
273
     * The value returned MUST be one of the interface constant type
274
     * If no type is associated the return constant must be self::FTP_TYPE_EMPTY
275
     *
276
     * @see http://tools.ietf.org/html/rfc1738#section-3.2.2
277
     *
278
     * @return int a typecode constant.
279
     */
280
    public function getTypecode()
281
    {
282
        if (preg_match(self::$typeRegex, $this->__toString(), $matches)) {
283
            return self::$typecodeList[$matches['typecode']];
284
        }
285
286
        return PathInterface::FTP_TYPE_EMPTY;
287
    }
288
289
    /**
290
     * DEPRECATION WARNING! This method will be removed in the next major point release
291
     *
292
     * @deprecated deprecated since version 4.2
293
     *
294
     * Return an instance with the specified typecode.
295
     *
296
     * This method MUST retain the state of the current instance, and return
297
     * an instance that contains the specified type appended to the path.
298
     * if not
299
     *
300
     * Using self::FTP_TYPE_EMPTY is equivalent to removing the typecode.
301
     *
302
     * @param int $type one typecode constant.
303
     *
304
     * @throws InvalidArgumentException for invalid typecode.
305
     *
306
     * @return static
307
     *
308
     */
309
    public function withTypecode($type)
310
    {
311
        if (!in_array($type, self::$typecodeList)) {
312
            throw new InvalidArgumentException('invalid typecode');
313
        }
314
315
        $path = $this->__toString();
316
        if (preg_match(self::$typeRegex, $path, $matches)) {
317
            $path = $matches['basename'];
318
        }
319
320
        $extension = array_search($type, self::$typecodeList);
321
        $extension = trim($extension);
322
        if ('' !== $extension) {
323
            $extension = ';type='.$extension;
324
        }
325
326
        return $this->withContent($path.$extension);
327
    }
328
}
329