Completed
Push — master ( 1f3173...3bc19d )
by ignace nyamagana
04:55
created

HierarchicalPath::prepend()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 5
nc 1
nop 1
dl 0
loc 7
ccs 5
cts 5
cp 1
crap 1
rs 9.4285
c 0
b 0
f 0
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
        $data = $this->filterEncodedPath($data);
117 1066
118 1066
        $filterSegment = function ($segment) {
119
            return isset($segment);
120 1066
        };
121
122 1066
        $data = $this->decodePath($data);
123
124
        return array_filter(explode(static::$separator, $data), $filterSegment);
125
    }
126
127
    /**
128 9
     * @inheritdoc
129
     */
130 9
    public static function __set_state(array $properties)
131
    {
132
        return static::createFromSegments($properties['data'], $properties['isAbsolute']);
133
    }
134
135
    /**
136
     * Return a new instance when needed
137
     *
138
     * @param array $data
139
     *
140 63
     * @return static
141
     */
142 63
    protected function newCollectionInstance(array $data)
143
    {
144
        return $this->createFromSegments($data, $this->isAbsolute);
145
    }
146
147
    /**
148
     * Returns an array representation of the HierarchicalPath
149
     *
150 129
     * @return array
151
     */
152 129
    public function getSegments()
153
    {
154
        return $this->data;
155
    }
156
157
    /**
158
     * Retrieves a single path segment.
159
     *
160
     * Retrieves a single path segment. If the segment offset has not been set,
161
     * returns the default value provided.
162
     *
163
     * @param string $offset  the segment offset
164
     * @param mixed  $default Default value to return if the offset does not exist.
165
     *
166 9
     * @return mixed
167
     */
168 9
    public function getSegment($offset, $default = null)
169 6
    {
170
        if (isset($this->data[$offset])) {
171
            return $this->data[$offset];
172 3
        }
173
174
        return $default;
175
    }
176
177
    /**
178 1042
     * @inheritdoc
179
     */
180 1042
    public function getContent()
181 1042
    {
182 812
        $front_delimiter = '';
183 541
        if ($this->isAbsolute === self::IS_ABSOLUTE) {
184
            $front_delimiter = static::$separator;
185 1042
        }
186 1042
187 694
        return $this->encodePath($front_delimiter.implode(static::$separator, $this->data));
188
    }
189
190
    /**
191
     * @inheritdoc
192
     */
193 1042
    public function __toString()
194
    {
195 1042
        return (string) $this->getContent();
196
    }
197
198
    /**
199
     * @inheritdoc
200
     */
201 30
    public function prepend($component)
202
    {
203 30
        return $this->createFromSegments(
204 30
            $this->validateComponent($component),
205 30
            $this->isAbsolute
206 30
        )->append($this->__toString());
207
    }
208
209
    /**
210
     * Returns an instance with the specified component appended
211
     *
212
     * This method MUST retain the state of the current instance, and return
213
     * an instance that contains the modified component with the appended data
214
     *
215
     * @param HierarchicalComponent|string $component the component to append
216
     *
217
     * @return static
218
     */
219 72
    public function append($component)
220
    {
221 72
        $source = $this->getSegments();
222 72
        if (!empty($source) && '' === end($source)) {
223 30
            array_pop($source);
224 20
        }
225
226 72
        return $this->createFromSegments(array_merge(
227 48
            $source,
228 72
            iterator_to_array($this->validateComponent($component))
229 72
        ), $this->isAbsolute);
230
    }
231
232
    /**
233
     * Returns the path basename
234
     *
235
     * @return string
236
     */
237 63
    public function getBasename()
238
    {
239 63
        $data = $this->data;
240
241 63
        return (string) array_pop($data);
242
    }
243
244
    /**
245
     * Returns parent directory's path
246
     *
247
     * @return string
248
     */
249 24
    public function getDirname()
250
    {
251 24
        return str_replace(
252 24
            ['\\', "\0"],
253 24
            [static::$separator, '\\'],
254 24
            dirname(str_replace('\\', "\0", $this->__toString()))
255 16
        );
256
    }
257
258
    /**
259
     * Returns the basename extension
260
     *
261
     * @return string
262
     */
263 57
    public function getExtension()
264
    {
265 57
        list($basename, ) = explode(';', $this->getBasename(), 2);
266
267 57
        return pathinfo($basename, PATHINFO_EXTENSION);
268
    }
269
270
    /**
271
     * Returns an instance with the specified basename extension
272
     *
273
     * This method MUST retain the state of the current instance, and return
274
     * an instance that contains the extension basename modified.
275
     *
276
     * @param string $extension the new extension
277
     *                          can preceeded with or without the dot (.) character
278
     *
279
     * @throws InvalidArgumentException If the extension is invalid
280
     *
281
     * @return static
282
     */
283 63
    public function withExtension($extension)
284
    {
285 63
        $extension = $this->formatExtension($extension);
286 57
        $segments = $this->getSegments();
287 57
        $basename = array_pop($segments);
288 57
        $parts = explode(';', $basename, 2);
289 57
        $basenamePart = array_shift($parts);
290 57
        if ('' === $basenamePart || is_null($basenamePart)) {
291 6
            return $this;
292
        }
293
294 51
        $newBasename = $this->buildBasename($basenamePart, $extension, array_shift($parts));
295 51
        if ($basename === $newBasename) {
296 3
            return $this;
297
        }
298 48
        $segments[] = $newBasename;
299
300 48
        return $this->createFromSegments($segments, $this->isAbsolute);
301
    }
302
303
    /**
304
     * create a new basename with a new extension
305
     *
306
     * @param string $basenamePart  the basename file part
307
     * @param string $extension     the new extension to add
308
     * @param string $parameterPart the basename parameter part
309
     *
310
     * @return string
311
     */
312 51
    protected function buildBasename($basenamePart, $extension, $parameterPart)
313
    {
314 51
        $length = mb_strrpos($basenamePart, '.'.pathinfo($basenamePart, PATHINFO_EXTENSION), 'UTF-8');
315 51
        if (false !== $length) {
316 45
            $basenamePart = mb_substr($basenamePart, 0, $length, 'UTF-8');
317 30
        }
318
319 51
        $parameterPart = trim($parameterPart);
320 51
        if ('' !== $parameterPart) {
321 24
            $parameterPart = ";$parameterPart";
322 16
        }
323
324 51
        $extension = trim($extension);
325 51
        if ('' !== $extension) {
326 36
            $extension = ".$extension";
327 24
        }
328
329 51
        return $basenamePart.$extension.$parameterPart;
330
    }
331
332
    /**
333
     * validate and format the given extension
334
     *
335
     * @param string $extension the new extension to use
336
     *
337
     * @throws InvalidArgumentException If the extension is not valid
338
     *
339
     * @return string
340
     */
341 63
    protected function formatExtension($extension)
342
    {
343 63
        if (0 === strpos($extension, '.')) {
344 3
            throw new InvalidArgumentException('an extension sequence can not contain a leading `.` character');
345
        }
346
347 60
        if (strpos($extension, static::$separator)) {
348 3
            throw new InvalidArgumentException('an extension sequence can not contain a path delimiter');
349
        }
350
351 57
        return implode(static::$separator, $this->validate($extension));
352
    }
353
}
354