Completed
Push — master ( f56ee5...b16611 )
by ignace nyamagana
06:11
created

Host::setLiteral()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 2

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 4
ccs 3
cts 3
cp 1
rs 10
cc 2
eloc 2
nc 2
nop 0
crap 2
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\Host as HostInterface;
16
17
/**
18
 * Value object representing a URI host component.
19
 *
20
 * @package League.uri
21
 * @author  Ignace Nyamagana Butera <[email protected]>
22
 * @since   1.0.0
23
 */
24
class Host extends AbstractHierarchicalComponent implements HostInterface
25
{
26
    use HostIpTrait;
27
28
    use HostnameInfoTrait;
29
30
    use HostnameTrait;
31
32
    /**
33
     * HierarchicalComponent delimiter
34
     *
35
     * @var string
36
     */
37
    protected static $separator = '.';
38
39
    /**
40
     * Host literal representation
41
     *
42
     * @var string
43
     */
44
    protected $host;
45
46
    /**
47
     * DEPRECATION WARNING! This method will be removed in the next major point release
48
     *
49
     * @deprecated deprecated since version 4.2
50
     *
51
     * return a new instance from an array or a traversable object
52
     *
53
     * @param \Traversable|string[] $data The segments list
54
     * @param int                   $type one of the constant IS_ABSOLUTE or IS_RELATIVE
55
     *
56
     * @throws InvalidArgumentException If $type is not a recognized constant
57
     *
58
     * @return static
59
     */
60
    public static function createFromArray($data, $type = self::IS_RELATIVE)
61
    {
62
        return self::createFromLabels($data, $type);
63
    }
64
65
    /**
66
     * return a new instance from an array or a traversable object
67
     *
68
     * @param \Traversable|string[] $data The segments list
69
     * @param int                   $type one of the constant IS_ABSOLUTE or IS_RELATIVE
70
     *
71
     * @throws InvalidArgumentException If $type is not a recognized constant
72
     *
73
     * @return static
74
     */
75 165
    public static function createFromLabels($data, $type = self::IS_RELATIVE)
76
    {
77 165
        static $type_list = [self::IS_ABSOLUTE => 1, self::IS_RELATIVE => 1];
78
79 165
        if (!isset($type_list[$type])) {
80 3
            throw new InvalidArgumentException('Please verify the submitted constant');
81
        }
82
83 162
        if (empty($data)) {
84 12
            return new static();
85
        }
86
87 150
        if ([''] === $data) {
88 3
            return new static('');
89
        }
90
91 147
        return new static(static::format($data, $type));
92
    }
93
94
    /**
95
     * Return a formatted host string
96
     *
97
     * @param \Traversable|string[] $data The segments list
98
     * @param int                   $type
99
     *
100
     * @throws InvalidArgumentException If $data is invalid
101
     *
102
     * @return string
103
     */
104 892
    protected static function format($data, $type)
105
    {
106 892
        $hostname = implode(static::$separator, array_reverse(static::validateIterator($data)));
107 880
        if (self::IS_ABSOLUTE == $type) {
108 36
            return $hostname.static::$separator;
109
        }
110
111 856
        return $hostname;
112
    }
113
114
    /**
115
     * New instance
116
     *
117
     * @param null|string $host
118
     */
119 1144
    public function __construct($host = null)
120
    {
121 1144
        $this->data = $this->validate($host);
122 1078
        $this->host = !$this->isIp() ? $this->__toString() : $this->data[0];
123 1078
    }
124
125
    /**
126
     * validate the submitted data
127
     *
128
     * @param string $str
129
     *
130
     * @return array
131
     */
132 1144
    protected function validate($str)
133
    {
134 1144
        if (null === $str) {
135 354
            return [];
136
        }
137
138 1024
        $str = $this->validateString($str);
139 1015
        if ('' === $str) {
140 24
            return [''];
141
        }
142
143 1006
        $res = $this->validateIpHost($str);
144 1006
        if (!empty($res)) {
145 120
            return $res;
146
        }
147
148 907
        return $this->validateStringHost($str);
149
    }
150
151
    /**
152
     * Return a new instance when needed
153
     *
154
     * @param array $data
155
     *
156
     * @return static
157
     */
158 45
    protected function newCollectionInstance(array $data)
159
    {
160 45
        return $this->createFromLabels($data, $this->isAbsolute);
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->createFrom...ta, $this->isAbsolute); (League\Uri\Components\Host) is incompatible with the return type declared by the abstract method League\Uri\Components\Ab...::newCollectionInstance of type League\Uri\Types\ImmutableCollectionTrait.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
161
    }
162
163
    /**
164
     * Returns whether or not the host is an IDN
165
     *
166
     * @return bool
167
     */
168 9
    public function isIdn()
169
    {
170 9
        return $this->isIdn;
171
    }
172
173
    /**
174
     * Returns whether or not the host is an IP address
175
     *
176
     * @return bool
177
     */
178 1078
    public function isIp()
179
    {
180 1078
        return $this->hostAsIpv4 || $this->hostAsIpv6;
181
    }
182
183
    /**
184
     * Returns whether or not the host is an IPv4 address
185
     *
186
     * @return bool
187
     */
188 39
    public function isIpv4()
189
    {
190 39
        return $this->hostAsIpv4;
191
    }
192
193
    /**
194
     * Returns whether or not the host is an IPv6 address
195
     *
196
     * @return bool
197
     */
198 39
    public function isIpv6()
199
    {
200 39
        return $this->hostAsIpv6;
201
    }
202
203
    /**
204
     * Returns whether or not the host has a ZoneIdentifier
205
     *
206
     * @return bool
207
     *
208
     * @see http://tools.ietf.org/html/rfc6874#section-4
209
     */
210 12
    public function hasZoneIdentifier()
211
    {
212 12
        return $this->hasZoneIdentifier;
213
    }
214
215
    /**
216
     * DEPRECATION WARNING! This method will be removed in the next major point release
217
     *
218
     * @deprecated deprecated since version 4.2
219
     *
220
     * Returns the instance literal representation
221
     * without encoding
222
     *
223
     * @return string
224
     */
225
    public function getLiteral()
226
    {
227
        return $this->host;
228
    }
229
230
    /**
231
     * Retrieves a single host label.
232
     *
233
     * Retrieves a single host label. If the label offset has not been set,
234
     * returns the default value provided.
235
     *
236
     * @param string $offset  the label offset
237
     * @param mixed  $default Default value to return if the offset does not exist.
238
     *
239
     * @return mixed
240
     */
241 3
    public function getLabel($offset, $default = null)
242
    {
243 3
        if (isset($this->data[$offset])) {
244 3
            return $this->isIdn ? $this->data[$offset] : idn_to_ascii($this->data[$offset]);
245
        }
246
247 3
        return $default;
248
    }
249
250
    /**
251
     * Returns an array representation of the host
252
     *
253
     * @return array
254
     */
255 871
    public function toArray()
256
    {
257 871
        return $this->convertToAscii($this->data, !$this->isIdn);
258
    }
259
260
    /**
261
     * @inheritdoc
262
     */
263 1054
    public function getContent()
264
    {
265 1054
        if ([] === $this->data) {
266 369
            return null;
267
        }
268
269 928
        if ($this->isIp()) {
270 75
            return $this->formatIp($this->data[0]);
271
        }
272
273 865
        return $this->format($this->toArray(), $this->isAbsolute);
274
    }
275
276
    /**
277
     * @inheritdoc
278
     */
279 2
    public function __debugInfo()
280
    {
281 2
        return ['host' => $this->getContent()];
282
    }
283
284
    /**
285
     * @inheritdoc
286
     */
287 15
    public static function __set_state(array $properties)
288
    {
289 15
        $host = static::createFromLabels($properties['data'], $properties['isAbsolute']);
290 15
        $host->hostnameInfoLoaded = $properties['hostnameInfoLoaded'];
291 15
        $host->hostnameInfo = $properties['hostnameInfo'];
292
293 15
        return $host;
294
    }
295
296
    /**
297
     * Returns a host in his punycode encoded form
298
     *
299
     * This method MUST retain the state of the current instance, and return
300
     * an instance with the host transcoded using to ascii the RFC 3492 rules
301
     *
302
     * @see http://tools.ietf.org/html/rfc3492
303
     *
304
     * @return static
305
     */
306 108
    public function toAscii()
307
    {
308 108
        if ($this->isIp() || !$this->isIdn) {
309 36
            return $this;
310
        }
311
312 75
        return $this->modify($this->format(
313 75
            $this->convertToAscii($this->data, $this->isIdn),
314 75
            $this->isAbsolute
315 50
        ));
316
    }
317
318
    /**
319
     * Returns a host in his IDN form
320
     *
321
     * This method MUST retain the state of the current instance, and return
322
     * an instance with the host in its IDN form using RFC 3492 rules
323
     *
324
     * @see http://tools.ietf.org/html/rfc3492
325
     *
326
     * @return static
327
     */
328 81
    public function toUnicode()
329
    {
330 81
        if ($this->isIp() || $this->isIdn) {
331 69
            return $this;
332
        }
333
334 72
        return $this->modify($this->format($this->data, $this->isAbsolute));
335
    }
336
337
    /**
338
     * Return an host without its zone identifier according to RFC6874
339
     *
340
     * This method MUST retain the state of the current instance, and return
341
     * an instance without the host zone identifier according to RFC6874
342
     *
343
     * @see http://tools.ietf.org/html/rfc6874#section-4
344
     *
345
     * @return static
346
     */
347 18
    public function withoutZoneIdentifier()
348
    {
349 18
        if ($this->hasZoneIdentifier) {
350 9
            return $this->modify(substr($this->data[0], 0, strpos($this->data[0], '%')));
351
        }
352
353 9
        return $this;
354
    }
355
356
    /**
357
     * Validated the Host Label Count
358
     *
359
     * @param array $labels Host labels
360
     *
361
     * @throws InvalidArgumentException If the validation fails
362
     */
363 892
    protected function assertLabelsCount(array $labels)
364
    {
365 892
        if (127 <= count(array_merge($this->data, $labels))) {
366 3
            throw new InvalidArgumentException('Invalid Hostname, verify labels count');
367
        }
368 889
    }
369
370
    /**
371
     * set the FQDN property
372
     *
373
     * @param string $str
374
     *
375
     * @return string
376
     */
377 907
    protected function setIsAbsolute($str)
378
    {
379 907
        $this->isAbsolute = self::IS_RELATIVE;
380 907
        if ('.' == mb_substr($str, -1, 1, 'UTF-8')) {
381 42
            $this->isAbsolute = self::IS_ABSOLUTE;
382 42
            $str = mb_substr($str, 0, -1, 'UTF-8');
383 28
        }
384
385 907
        return $str;
386
    }
387
388
    /**
389
     * @inheritdoc
390
     */
391 30
    public function prepend($component)
392
    {
393 30
        return $this->createFromLabels(
394 30
                $this->validateComponent($component),
395 30
                $this->isAbsolute
396 30
            )->append($this);
397
    }
398
399
    /**
400
     * @inheritdoc
401
     */
402 60
    public function append($component)
403
    {
404 60
        return $this->createFromLabels(array_merge(
405 60
            $this->validateComponent($component)->toArray(),
406 60
            $this->toArray()
407 60
        ), $this->isAbsolute);
408
    }
409
}
410