Completed
Push — master ( 03e1a0...07d10c )
by ignace nyamagana
04:33
created

HierarchicalPath   B

Complexity

Total Complexity 36

Size/Duplication

Total Lines 337
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 2

Test Coverage

Coverage 100%

Importance

Changes 23
Bugs 1 Features 0
Metric Value
c 23
b 1
f 0
dl 0
loc 337
ccs 110
cts 110
cp 1
rs 8.8
wmc 36
lcom 1
cbo 2

19 Methods

Rating   Name   Duplication   Size   Complexity  
A createFromArray() 0 4 1
A createFromSegments() 0 15 3
A __construct() 0 20 4
A validate() 0 10 1
A __set_state() 0 4 1
A newCollectionInstance() 0 4 1
A getSegments() 0 4 1
A getSegment() 0 8 2
A getContent() 0 4 1
A getDecoded() 0 9 2
A __toString() 0 4 1
A prepend() 0 7 1
A append() 0 12 3
A getBasename() 0 6 1
A getDirname() 0 8 1
A getExtension() 0 6 1
A withExtension() 0 19 4
A buildBasename() 0 19 4
A formatExtension() 0 12 3
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\HierarchicalComponent;
16
use League\Uri\Interfaces\HierarchicalPath as HierarchicalPathInterface;
17
18
/**
19
 * Value object representing a URI path component.
20
 *
21
 * @package League.uri
22
 * @author  Ignace Nyamagana Butera <[email protected]>
23
 * @since   1.0.0
24
 */
25
class HierarchicalPath extends AbstractHierarchicalComponent implements HierarchicalPathInterface
26
{
27
    use PathTrait;
28
29
    /**
30
     * @inheritdoc
31
     */
32
    protected static $separator = '/';
33
34
    /**
35
     * DEPRECATION WARNING! This method will be removed in the next major point release
36
     *
37
     * @deprecated deprecated since version 4.2
38
     *
39
     * return a new instance from an array or a traversable object
40
     *
41
     * @param \Traversable|string[] $data The segments list
42
     * @param int                   $type one of the constant IS_ABSOLUTE or IS_RELATIVE
43
     *
44
     * @throws InvalidArgumentException If $data is invalid
45
     * @throws InvalidArgumentException If $type is not a recognized constant
46
     *
47
     * @return static
48
     */
49
    public static function createFromArray($data, $type = self::IS_RELATIVE)
50
    {
51
        return static::createFromSegments($data, $type);
52
    }
53
54
    /**
55
     * return a new instance from an array or a traversable object
56
     *
57
     * @param \Traversable|string[] $data The segments list
58
     * @param int                   $type one of the constant IS_ABSOLUTE or IS_RELATIVE
59
     *
60
     * @throws InvalidArgumentException If $data is invalid
61
     * @throws InvalidArgumentException If $type is not a recognized constant
62
     *
63
     * @return static
64
     */
65 237
    public static function createFromSegments($data, $type = self::IS_RELATIVE)
66
    {
67 237
        static $type_list = [self::IS_ABSOLUTE => 1, self::IS_RELATIVE => 1];
68
69 237
        if (!isset($type_list[$type])) {
70 3
            throw new InvalidArgumentException('Please verify the submitted constant');
71
        }
72
73 234
        $path = implode(static::$separator, static::validateIterator($data));
74 222
        if (self::IS_ABSOLUTE === $type) {
75 171
            $path = static::$separator.$path;
76 114
        }
77
78 222
        return new static($path);
79
    }
80
81
    /**
82
     * New Instance
83
     *
84
     * @param string $path
85
     */
86 1075
    public function __construct($path = '')
87
    {
88 1075
        $path = $this->validateString($path);
89 1066
        $this->isAbsolute = self::IS_RELATIVE;
90 1066
        if (static::$separator === substr($path, 0, 1)) {
91 851
            $this->isAbsolute = self::IS_ABSOLUTE;
92 851
            $path = substr($path, 1, strlen($path));
93 567
        }
94
95 1066
        $append_delimiter = false;
96 1066
        if (static::$separator === substr($path, -1, 1)) {
97 159
            $path = substr($path, 0, -1);
98 159
            $append_delimiter = true;
99 106
        }
100
101 1066
        $this->data = $this->validate($path);
102 1066
        if ($append_delimiter) {
103 159
            $this->data[] = '';
104 106
        }
105 1066
    }
106
107
    /**
108
     * validate the submitted data
109
     *
110
     * @param string $data
111
     *
112
     * @return array
113
     */
114
    protected function validate($data)
115
    {
116 1066
        $filterSegment = function ($segment) {
117 1066
            return isset($segment);
118 1066
        };
119
120 1066
        $data = $this->decodePath($data);
121
122 1066
        return array_filter(explode(static::$separator, $data), $filterSegment);
123
    }
124
125
    /**
126
     * @inheritdoc
127
     */
128 9
    public static function __set_state(array $properties)
129
    {
130 9
        return static::createFromSegments($properties['data'], $properties['isAbsolute']);
131
    }
132
133
    /**
134
     * Return a new instance when needed
135
     *
136
     * @param array $data
137
     *
138
     * @return static
139
     */
140 63
    protected function newCollectionInstance(array $data)
141
    {
142 63
        return $this->createFromSegments($data, $this->isAbsolute);
143
    }
144
145
    /**
146
     * Returns an array representation of the HierarchicalPath
147
     *
148
     * @return array
149
     */
150 129
    public function getSegments()
151
    {
152 129
        return $this->data;
153
    }
154
155
    /**
156
     * Retrieves a single path segment.
157
     *
158
     * Retrieves a single path segment. If the segment offset has not been set,
159
     * returns the default value provided.
160
     *
161
     * @param string $offset  the segment offset
162
     * @param mixed  $default Default value to return if the offset does not exist.
163
     *
164
     * @return mixed
165
     */
166 9
    public function getSegment($offset, $default = null)
167
    {
168 9
        if (isset($this->data[$offset])) {
169 6
            return $this->data[$offset];
170
        }
171
172 3
        return $default;
173
    }
174
175
    /**
176
     * @inheritdoc
177
     */
178 1042
    public function getContent()
179
    {
180 1042
        return $this->encodePath($this->getDecoded());
181 1042
    }
182 812
183 541
    /**
184
     * Return the decoded string representation of the component
185 1042
     *
186 1042
     * @return string
187 694
     */
188
    public function getDecoded()
189
    {
190
        $front_delimiter = '';
191
        if ($this->isAbsolute === self::IS_ABSOLUTE) {
192
            $front_delimiter = static::$separator;
193 1042
        }
194
195 1042
        return $front_delimiter.implode(static::$separator, $this->data);
196
    }
197
198
    /**
199
     * @inheritdoc
200
     */
201 30
    public function __toString()
202
    {
203 30
        return (string) $this->getContent();
204 30
    }
205 30
206 30
    /**
207
     * @inheritdoc
208
     */
209
    public function prepend($component)
210
    {
211
        return $this->createFromSegments(
212
                $this->validateComponent($component),
213
                $this->isAbsolute
214
            )->append($this->__toString());
215
    }
216
217
    /**
218
     * Returns an instance with the specified component appended
219 72
     *
220
     * This method MUST retain the state of the current instance, and return
221 72
     * an instance that contains the modified component with the appended data
222 72
     *
223 30
     * @param HierarchicalComponent|string $component the component to append
224 20
     *
225
     * @return static
226 72
     */
227 48
    public function append($component)
228 72
    {
229 72
        $source = $this->getSegments();
230
        if (!empty($source) && '' === end($source)) {
231
            array_pop($source);
232
        }
233
234
        return $this->createFromSegments(array_merge(
235
            $source,
236
            iterator_to_array($this->validateComponent($component))
237 63
        ), $this->isAbsolute);
238
    }
239 63
240
    /**
241 63
     * Returns the path basename
242
     *
243
     * @return string
244
     */
245
    public function getBasename()
246
    {
247
        $data = $this->data;
248
249 24
        return (string) array_pop($data);
250
    }
251 24
252 24
    /**
253 24
     * Returns parent directory's path
254 24
     *
255 16
     * @return string
256
     */
257
    public function getDirname()
258
    {
259
        return str_replace(
260
            ['\\', "\0"],
261
            [static::$separator, '\\'],
262
            dirname(str_replace('\\', "\0", $this->__toString()))
263 57
        );
264
    }
265 57
266
    /**
267 57
     * Returns the basename extension
268
     *
269
     * @return string
270
     */
271
    public function getExtension()
272
    {
273
        list($basename, ) = explode(';', $this->getBasename(), 2);
274
275
        return pathinfo($basename, PATHINFO_EXTENSION);
276
    }
277
278
    /**
279
     * Returns an instance with the specified basename extension
280
     *
281
     * This method MUST retain the state of the current instance, and return
282
     * an instance that contains the extension basename modified.
283 63
     *
284
     * @param string $extension the new extension
285 63
     *                          can preceeded with or without the dot (.) character
286 57
     *
287 57
     * @throws InvalidArgumentException If the extension is invalid
288 57
     *
289 57
     * @return static
290 57
     */
291 6
    public function withExtension($extension)
292
    {
293
        $extension = $this->formatExtension($extension);
294 51
        $segments = $this->getSegments();
295 51
        $basename = array_pop($segments);
296 3
        $parts = explode(';', $basename, 2);
297
        $basenamePart = array_shift($parts);
298 48
        if ('' === $basenamePart || is_null($basenamePart)) {
299
            return $this;
300 48
        }
301
302
        $newBasename = $this->buildBasename($basenamePart, $extension, array_shift($parts));
303
        if ($basename === $newBasename) {
304
            return $this;
305
        }
306
        $segments[] = $newBasename;
307
308
        return $this->createFromSegments($segments, $this->isAbsolute);
309
    }
310
311
    /**
312 51
     * create a new basename with a new extension
313
     *
314 51
     * @param string $basenamePart  the basename file part
315 51
     * @param string $extension     the new extension to add
316 45
     * @param string $parameterPart the basename parameter part
317 30
     *
318
     * @return string
319 51
     */
320 51
    protected function buildBasename($basenamePart, $extension, $parameterPart)
321 24
    {
322 16
        $length = mb_strrpos($basenamePart, '.'.pathinfo($basenamePart, PATHINFO_EXTENSION), 'UTF-8');
323
        if (false !== $length) {
324 51
            $basenamePart = mb_substr($basenamePart, 0, $length, 'UTF-8');
325 51
        }
326 36
327 24
        $parameterPart = trim($parameterPart);
328
        if ('' !== $parameterPart) {
329 51
            $parameterPart = ";$parameterPart";
330
        }
331
332
        $extension = trim($extension);
333
        if ('' !== $extension) {
334
            $extension = ".$extension";
335
        }
336
337
        return $basenamePart.$extension.$parameterPart;
338
    }
339
340
    /**
341 63
     * validate and format the given extension
342
     *
343 63
     * @param string $extension the new extension to use
344 3
     *
345
     * @throws InvalidArgumentException If the extension is not valid
346
     *
347 60
     * @return string
348 3
     */
349
    protected function formatExtension($extension)
350
    {
351 57
        if (0 === strpos($extension, '.')) {
352
            throw new InvalidArgumentException('an extension sequence can not contain a leading `.` character');
353
        }
354
355
        if (strpos($extension, static::$separator)) {
356
            throw new InvalidArgumentException('an extension sequence can not contain a path delimiter');
357
        }
358
359
        return implode(static::$separator, $this->validate($extension));
360
    }
361
}
362