Completed
Push — master ( c26f50...f56ee5 )
by ignace nyamagana
04:19
created

Host::toArray()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
cc 1
eloc 2
nc 1
nop 0
crap 1
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
     * Return a new instance when needed
48
     *
49
     * @param array $data
50
     *
51
     * @return static
52
     */
53 45
    protected function newCollectionInstance(array $data)
54
    {
55 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...
56
    }
57
58
    /**
59
     * return a new instance from an array or a traversable object
60
     *
61
     * @param \Traversable|string[] $data The segments list
62
     * @param int                   $type one of the constant IS_ABSOLUTE or IS_RELATIVE
63
     *
64
     * @throws InvalidArgumentException If $type is not a recognized constant
65
     *
66
     * @return static
67
     */
68 165
    public static function createFromLabels($data, $type = self::IS_RELATIVE)
69
    {
70 165
        static $type_list = [self::IS_ABSOLUTE => 1, self::IS_RELATIVE => 1];
71
72 165
        if (!isset($type_list[$type])) {
73 3
            throw new InvalidArgumentException('Please verify the submitted constant');
74
        }
75
76 162
        if (empty($data)) {
77 12
            return new static();
78
        }
79
80 150
        if ([''] === $data) {
81 3
            return new static('');
82
        }
83
84 147
        return new static(static::format($data, $type));
0 ignored issues
show
Documentation introduced by
$type is of type integer, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
85
    }
86
87
    /**
88
     * DEPRECATION WARNING! This method will be removed in the next major point release
89
     *
90
     * @deprecated deprecated since version 4.2
91
     *
92
     * return a new instance from an array or a traversable object
93
     *
94
     * @param \Traversable|string[] $data The segments list
95
     * @param int                   $type one of the constant IS_ABSOLUTE or IS_RELATIVE
96
     *
97
     * @throws InvalidArgumentException If $type is not a recognized constant
98
     *
99
     * @return static
100
     */
101
    public static function createFromArray($data, $type = self::IS_RELATIVE)
102
    {
103
        return self::createFromLabels($data, $type);
104
    }
105
106
    /**
107
     * New instance
108
     *
109
     * @param null|string $host
110
     */
111 1141
    public function __construct($host = null)
112
    {
113 1141
        $this->data = $this->validate($host);
114 1075
        $this->setLiteral();
115 1075
    }
116
117
    /**
118
     * Returns whether or not the host is an IDN
119
     *
120
     * @return bool
121
     */
122 9
    public function isIdn()
123
    {
124 9
        return $this->isIdn;
125
    }
126
127
    /**
128
     * Returns whether or not the host is an IP address
129
     *
130
     * @return bool
131
     */
132 1075
    public function isIp()
133
    {
134 1075
        return $this->hostAsIpv4 || $this->hostAsIpv6;
135
    }
136
137
    /**
138
     * Returns whether or not the host is an IPv4 address
139
     *
140
     * @return bool
141
     */
142 39
    public function isIpv4()
143
    {
144 39
        return $this->hostAsIpv4;
145
    }
146
147
    /**
148
     * Returns whether or not the host is an IPv6 address
149
     *
150
     * @return bool
151
     */
152 39
    public function isIpv6()
153
    {
154 39
        return $this->hostAsIpv6;
155
    }
156
157
    /**
158
     * Returns whether or not the host has a ZoneIdentifier
159
     *
160
     * @return bool
161
     *
162
     * @see http://tools.ietf.org/html/rfc6874#section-4
163
     */
164 12
    public function hasZoneIdentifier()
165
    {
166 12
        return $this->hasZoneIdentifier;
167
    }
168
169
    /**
170
     * Host literal setter
171
     */
172 1075
    protected function setLiteral()
173
    {
174 1075
        $this->host = !$this->isIp() ? $this->__toString() : $this->data[0];
175 1075
    }
176
177
    /**
178
     * DEPRECATION WARNING! This method will be removed in the next major point release
179
     *
180
     * @deprecated deprecated since version 4.2
181
     *
182
     * Returns the instance literal representation
183
     * without encoding
184
     *
185
     * @return string
186
     */
187
    public function getLiteral()
188
    {
189
        return $this->host;
190
    }
191
192
    /**
193
     * validate the submitted data
194
     *
195
     * @param string $str
196
     *
197
     * @return array
198
     */
199 1141
    protected function validate($str)
200
    {
201 1141
        if (null === $str) {
202 354
            return [];
203
        }
204
205 1021
        $str = $this->validateString($str);
206 1012
        if ('' === $str) {
207 24
            return [''];
208
        }
209
210 1003
        $res = $this->validateIpHost($str);
211 1003
        if (!empty($res)) {
212 120
            return $res;
213
        }
214
215 904
        return $this->validateStringHost($str);
216
    }
217
218
    /**
219
     * Retrieves a single host label.
220
     *
221
     * Retrieves a single host label. If the label offset has not been set,
222
     * returns the default value provided.
223
     *
224
     * @param string $offset  the label offset
225
     * @param mixed  $default Default value to return if the offset does not exist.
226
     *
227
     * @return mixed
228
     */
229 3
    public function getLabel($offset, $default = null)
230
    {
231 3
        if (isset($this->data[$offset])) {
232 3
            return $this->isIdn ? $this->data[$offset] : idn_to_ascii($this->data[$offset]);
233
        }
234
235 3
        return $default;
236
    }
237
238
    /**
239
     * Returns an array representation of the host
240
     *
241
     * @return array
242
     */
243 868
    public function toArray()
244
    {
245 868
        return $this->convertToAscii($this->data, !$this->isIdn);
246
    }
247
248
    /**
249
     * @inheritdoc
250
     */
251 1051
    public function getContent()
252
    {
253 1051
        if ([] === $this->data) {
254 369
            return null;
255
        }
256
257 925
        if ($this->isIp()) {
258 75
            return $this->formatIp($this->data[0]);
259
        }
260
261 862
        return $this->format($this->toArray(), $this->isAbsolute);
0 ignored issues
show
Documentation introduced by
$this->isAbsolute is of type integer, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
262
    }
263
264
    /**
265
     * @inheritdoc
266
     */
267 2
    public function __debugInfo()
268
    {
269 2
        return ['host' => $this->getContent()];
270
    }
271
272
    /**
273
     * @inheritdoc
274
     */
275 15
    public static function __set_state(array $properties)
276
    {
277 15
        $host = static::createFromLabels($properties['data'], $properties['isAbsolute']);
278 15
        $host->hostnameInfoLoaded = $properties['hostnameInfoLoaded'];
279 15
        $host->hostnameInfo = $properties['hostnameInfo'];
280
281 15
        return $host;
282
    }
283
284
    /**
285
     * Returns a host in his punycode encoded form
286
     *
287
     * This method MUST retain the state of the current instance, and return
288
     * an instance with the host transcoded using to ascii the RFC 3492 rules
289
     *
290
     * @see http://tools.ietf.org/html/rfc3492
291
     *
292
     * @return static
293
     */
294 108
    public function toAscii()
295
    {
296 108
        if ($this->isIp() || !$this->isIdn) {
297 36
            return $this;
298
        }
299
300 75
        return $this->modify($this->format(
301 75
            $this->convertToAscii($this->data, $this->isIdn),
302 75
            $this->isAbsolute
0 ignored issues
show
Documentation introduced by
$this->isAbsolute is of type integer, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
303 50
        ));
304
    }
305
306
    /**
307
     * Returns a host in his IDN form
308
     *
309
     * This method MUST retain the state of the current instance, and return
310
     * an instance with the host in its IDN form using RFC 3492 rules
311
     *
312
     * @see http://tools.ietf.org/html/rfc3492
313
     *
314
     * @return static
315
     */
316 81
    public function toUnicode()
317
    {
318 81
        if ($this->isIp() || $this->isIdn) {
319 69
            return $this;
320
        }
321
322 72
        return $this->modify($this->format($this->data, $this->isAbsolute));
0 ignored issues
show
Documentation introduced by
$this->isAbsolute is of type integer, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
323
    }
324
325
    /**
326
     * Return a formatted host string
327
     *
328
     * @param \Traversable|string[] $data The segments list
329
     * @param bool                  $type
330
     *
331
     * @throws InvalidArgumentException If $data is invalid
332
     *
333
     * @return string
334
     */
335 889
    protected static function format($data, $type)
336
    {
337 889
        $hostname = implode(static::$separator, array_reverse(static::validateIterator($data)));
338 877
        if (self::IS_ABSOLUTE == $type) {
339 36
            return $hostname.static::$separator;
340
        }
341
342 853
        return $hostname;
343
    }
344
345
    /**
346
     * Return an host without its zone identifier according to RFC6874
347
     *
348
     * This method MUST retain the state of the current instance, and return
349
     * an instance without the host zone identifier according to RFC6874
350
     *
351
     * @see http://tools.ietf.org/html/rfc6874#section-4
352
     *
353
     * @return static
354
     */
355 18
    public function withoutZoneIdentifier()
356
    {
357 18
        if ($this->hasZoneIdentifier) {
358 9
            return $this->modify(substr($this->data[0], 0, strpos($this->data[0], '%')));
359
        }
360
361 9
        return $this;
362
    }
363
364
    /**
365
     * Validated the Host Label Count
366
     *
367
     * @param array $labels Host labels
368
     *
369
     * @throws InvalidArgumentException If the validation fails
370
     */
371 889
    protected function assertLabelsCount(array $labels)
372
    {
373 889
        if (127 <= count(array_merge($this->data, $labels))) {
374 3
            throw new InvalidArgumentException('Invalid Hostname, verify labels count');
375
        }
376 886
    }
377
378
    /**
379
     * set the FQDN property
380
     *
381
     * @param string $str
382
     *
383
     * @return string
384
     */
385 904
    protected function setIsAbsolute($str)
386
    {
387 904
        $this->isAbsolute = self::IS_RELATIVE;
388 904
        if ('.' == mb_substr($str, -1, 1, 'UTF-8')) {
389 42
            $this->isAbsolute = self::IS_ABSOLUTE;
390 42
            $str = mb_substr($str, 0, -1, 'UTF-8');
391 28
        }
392
393 904
        return $str;
394
    }
395
396
    /**
397
     * @inheritdoc
398
     */
399 30
    public function prepend($component)
400
    {
401 30
        return $this->createFromLabels(
402 30
                $this->validateComponent($component),
403 30
                $this->isAbsolute
404 30
            )->append($this);
405
    }
406
407
    /**
408
     * @inheritdoc
409
     */
410 60
    public function append($component)
411
    {
412 60
        return $this->createFromLabels(array_merge(
413 60
            $this->validateComponent($component)->toArray(),
414 60
            $this->toArray()
415 60
        ), $this->isAbsolute);
416
    }
417
}
418