Completed
Pull Request — master (#46)
by ignace nyamagana
05:56 queued 03:24
created

HierarchicalPath::append()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 12
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 3

Importance

Changes 4
Bugs 0 Features 0
Metric Value
c 4
b 0
f 0
dl 0
loc 12
ccs 9
cts 9
cp 1
rs 9.4285
cc 3
eloc 7
nc 2
nop 1
crap 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.1.1
10
 * @link      https://github.com/thephpleague/uri/
11
 */
12
namespace League\Uri\Components;
13
14
use InvalidArgumentException;
15
use League\Uri\Interfaces\HierarchicalPath as HierarchicalPathInterface;
16
17
/**
18
 * Value object representing a URI path component.
19
 *
20
 * @package League.uri
21
 * @author  Ignace Nyamagana Butera <[email protected]>
22
 * @since   1.0.0
23
 */
24
class HierarchicalPath extends AbstractHierarchicalComponent implements HierarchicalPathInterface
25
{
26
    use PathTrait;
27
28
    /**
29
     * @inheritdoc
30
     */
31
    protected static $separator = '/';
32
33
    /**
34
     * @inheritdoc
35
     */
36
    protected static $invalidCharactersRegex = ',[?#],';
37
38
    /**
39
     * New Instance
40
     *
41
     * @param string $path
42
     */
43 904
    public function __construct($path = '')
44
    {
45 904
        $path = $this->validateString($path);
46 895
        $this->assertValidComponent($path);
47 889
        $this->isAbsolute = self::IS_RELATIVE;
48 889
        if (static::$separator == mb_substr($path, 0, 1, 'UTF-8')) {
49 716
            $this->isAbsolute = self::IS_ABSOLUTE;
50 716
            $path = mb_substr($path, 1, mb_strlen($path), 'UTF-8');
51 477
        }
52
53 889
        $append_delimiter = false;
54 889
        if (static::$separator === mb_substr($path, -1, 1, 'UTF-8')) {
55 105
            $path = mb_substr($path, 0, -1, 'UTF-8');
56 105
            $append_delimiter = true;
57 70
        }
58
59 889
        $this->data = $this->validate($path);
60 889
        if ($append_delimiter) {
61 105
            $this->data[] = '';
62 70
        }
63 889
    }
64
65
    /**
66
     * validate the submitted data
67
     *
68
     * @param string $data
69
     *
70
     * @return array
71
     */
72
    protected function validate($data)
73
    {
74 889
        $filterSegment = function ($segment) {
75 889
            return isset($segment);
76 889
        };
77
78 889
        $data = $this->decodePath($data);
79
80 889
        return array_filter(explode(static::$separator, $data), $filterSegment);
81
    }
82
83
    /**
84
     * Retrieves a single path segment.
85
     *
86
     * Retrieves a single path segment. If the segment offset has not been set,
87
     * returns the default value provided.
88
     *
89
     * @param string $offset  the segment offset
90
     * @param mixed  $default Default value to return if the offset does not exist.
91
     *
92
     * @return mixed
93
     */
94 9
    public function getSegment($offset, $default = null)
95
    {
96 9
        if (isset($this->data[$offset])) {
97 6
            return $this->data[$offset];
98
        }
99
100 3
        return $default;
101
    }
102
103
    /**
104
     * @inheritdoc
105
     */
106 9
    public static function __set_state(array $properties)
107
    {
108 9
        return static::createFromArray($properties['data'], $properties['isAbsolute']);
109
    }
110
111
    /**
112
     * @inheritdoc
113
     */
114 856
    public function __toString()
115
    {
116 856
        $front_delimiter = '';
117 856
        if ($this->isAbsolute == self::IS_ABSOLUTE) {
118 674
            $front_delimiter = static::$separator;
119 449
        }
120
121 856
        return $front_delimiter.$this->encodePath(implode(static::$separator, $this->data));
122
    }
123
124
    /**
125
     * Returns an instance with the specified component appended
126
     *
127
     * This method MUST retain the state of the current instance, and return
128
     * an instance that contains the modified component with the appended data
129
     *
130
     * @param HierarchicalComponent|string $component the component to append
131
     *
132
     * @return static
133
     */
134 72
    public function append($component)
135
    {
136 72
        $source = $this->toArray();
137 72
        if (!empty($source) && '' === end($source)) {
138 30
            array_pop($source);
139 20
        }
140
141 72
        return $this->newCollectionInstance(array_merge(
142 48
            $source,
143 72
            $this->validateComponent($component)->toArray()
144 48
        ));
145
    }
146
147
    /**
148
     * Returns the path basename
149
     *
150
     * @return string
151
     */
152 63
    public function getBasename()
153
    {
154 63
        $data = $this->data;
155
156 63
        return (string) array_pop($data);
157
    }
158
159
    /**
160
     * Returns parent directory's path
161
     *
162
     * @return string
163
     */
164 24
    public function getDirname()
165
    {
166 24
        return str_replace(
167 24
            ['\\', "\0"],
168 24
            [static::$separator, '\\'],
169 24
            dirname(str_replace('\\', "\0", $this->__toString()))
170 16
        );
171
    }
172
173
    /**
174
     * Returns the basename extension
175
     *
176
     * @return string
177
     */
178 57
    public function getExtension()
179
    {
180 57
        list($basename, ) = explode(';', $this->getBasename(), 2);
181
182 57
        return pathinfo($basename, PATHINFO_EXTENSION);
183
    }
184
185
    /**
186
     * Returns an instance with the specified basename extension
187
     *
188
     * This method MUST retain the state of the current instance, and return
189
     * an instance that contains the extension basename modified.
190
     *
191
     * @param string $extension the new extension
192
     *                          can preceeded with or without the dot (.) character
193
     *
194
     * @throws InvalidArgumentException If the extension is invalid
195
     *
196
     * @return static
197
     */
198 63
    public function withExtension($extension)
199
    {
200 63
        $extension = $this->formatExtension($extension);
201 57
        $segments = $this->toArray();
202 57
        $basename = array_pop($segments);
203 57
        $parts = explode(';', $basename, 2);
204 57
        $basenamePart = array_shift($parts);
205 57
        if ('' === $basenamePart || is_null($basenamePart)) {
206 6
            return $this;
207
        }
208
209 51
        $newBasename = $this->buildBasename($basenamePart, $extension, array_shift($parts));
210 51
        if ($basename === $newBasename) {
211 3
            return $this;
212
        }
213 48
        $segments[] = $newBasename;
214
215 48
        return $this->newCollectionInstance($segments);
216
    }
217
218
    /**
219
     * create a new basename with a new extension
220
     *
221
     * @param string $basenamePart  the basename file part
222
     * @param string $extension     the new extension to add
223
     * @param string $parameterPart the basename parameter part
224
     *
225
     * @return string
226
     */
227 51
    protected function buildBasename($basenamePart, $extension, $parameterPart)
228
    {
229 51
        $length = mb_strrpos($basenamePart, '.'.pathinfo($basenamePart, PATHINFO_EXTENSION), 'UTF-8');
230 51
        if (false !== $length) {
231 45
            $basenamePart = mb_substr($basenamePart, 0, $length, 'UTF-8');
232 30
        }
233
234 51
        $parameterPart = trim($parameterPart);
235 51
        if ('' !== $parameterPart) {
236 24
            $parameterPart = ";$parameterPart";
237 16
        }
238
239 51
        $extension = trim($extension);
240 51
        if ('' !== $extension) {
241 36
            $extension = ".$extension";
242 24
        }
243
244 51
        return $basenamePart.$extension.$parameterPart;
245
    }
246
247
    /**
248
     * validate and format the given extension
249
     *
250
     * @param string $extension the new extension to use
251
     *
252
     * @throws InvalidArgumentException If the extension is not valid
253
     *
254
     * @return string
255
     */
256 63
    protected function formatExtension($extension)
257
    {
258 63
        if (0 === strpos($extension, '.')) {
259 3
            throw new InvalidArgumentException('an extension sequence can not contain a leading `.` character');
260
        }
261
262 60
        if (strpos($extension, static::$separator)) {
263 3
            throw new InvalidArgumentException('an extension sequence can not contain a path delimiter');
264
        }
265
266 57
        return implode(static::$separator, $this->validate($extension));
267
    }
268
}
269