Completed
Push — master ( b4abb9...a8f089 )
by ignace nyamagana
14:58
created

HostIpTrait::hasZoneIdentifier()   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.1.0
10
 * @link      https://github.com/thephpleague/uri/
11
 */
12
namespace League\Uri\Components;
13
14
/**
15
 * A Trait to validate a IP type Host
16
 *
17
 * @package League.uri
18
 * @author  Ignace Nyamagana Butera <[email protected]>
19
 * @since   4.0.0
20
 */
21
trait HostIpTrait
22
{
23
    /**
24
     * Is the Host an IPv4
25
     *
26
     * @var bool
27
     */
28
    protected $hostAsIpv4 = false;
29
30
    /**
31
     * Is the Host an IPv6
32
     *
33
     * @var bool
34
     */
35
    protected $hostAsIpv6 = false;
36
37
    /**
38
     * Tell whether the IP has a zone Identifier
39
     *
40
     * @var bool
41
     */
42
    protected $hasZoneIdentifier = false;
43
44
    /**
45
     * IPv6 Local Link binary-like prefix
46
     *
47
     * @var string
48
     */
49
    protected static $local_link_prefix = '1111111010';
50
51
    /**
52
     * Validate a Host as an IP
53
     *
54 538
     * @param string $str
55
     *
56 538
     * @return array
57
     */
58
    protected function validateIpHost($str)
59
    {
60
        $res = $this->filterIpv6Host($str);
61
        if (is_string($res)) {
62 24
            $this->hostAsIpv4 = false;
63
            $this->hostAsIpv6 = true;
64 24
65
            return [$res];
66
        }
67
68
        if (filter_var($str, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
69
            $this->hostAsIpv4 = true;
70 24
            $this->hostAsIpv6 = false;
71
72 24
            return [$str];
73
        }
74
75
        return [];
76
    }
77
78 8
    /**
79
     * validate and filter a Ipv6 Hostname
80 8
     *
81
     * @param string $str
82
     *
83
     * @return string|false
84
     */
85
    protected function filterIpv6Host($str)
86
    {
87
        preg_match(',^(?P<ldelim>[\[]?)(?P<ipv6>.*?)(?P<rdelim>[\]]?)$,', $str, $matches);
88
        if (!in_array(strlen($matches['ldelim'].$matches['rdelim']), [0, 2])) {
89
            return false;
90 604
        }
91
92 604
        if (false === strpos($str, '%')) {
93 604
            return filter_var($matches['ipv6'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV6);
94 42
        }
95 42
96
        return $this->validateScopedIpv6($matches['ipv6']);
97 42
    }
98
99
    /**
100 574
     * Scope Ip validation according to RFC6874 rules
101 40
     *
102 40
     * @see http://tools.ietf.org/html/rfc6874#section-2
103
     * @see http://tools.ietf.org/html/rfc6874#section-4
104 40
     *
105
     * @param string $ip The ip to validate
106
     *
107 544
     * @return string
108
     */
109
    protected function validateScopedIpv6($ip)
110
    {
111
        $pos = strpos($ip, '%');
112
        if (preg_match(',[^\x20-\x7f]|[?#@\[\]],', rawurldecode(substr($ip, $pos)))) {
113
            return false;
114
        }
115
116
        $ipv6 = substr($ip, 0, $pos);
117 604
        if (!$this->isLocalLink($ipv6)) {
118
            return false;
119 604
        }
120 604
121 4
        $this->hasZoneIdentifier = true;
122
123
        return strtolower(rawurldecode($ip));
124 600
    }
125 586
126
    /**
127
     * Tell whether the submitted string is a local link IPv6
128 20
     *
129
     * @param string $ipv6
130
     *
131
     * @return bool
132
     */
133
    protected function isLocalLink($ipv6)
134
    {
135
        if (!filter_var($ipv6, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
136
            return false;
137
        }
138
139
        $convert = function ($carry, $char) {
140
            return $carry.str_pad(decbin(ord($char)), 8, '0', STR_PAD_LEFT);
141 20
        };
142
        $res = array_reduce(str_split(unpack('A16', inet_pton($ipv6))[1]), $convert, '');
143 20
144 20
        return substr($res, 0, 10) === self::$local_link_prefix;
145 4
    }
146
147
    /**
148 16
     * Format an IP for string representation of the Host
149 16
     *
150 4
     * @param string $ip_address IP address
151
     *
152
     * @return string
153 12
     */
154
    protected function formatIp($ip_address)
155 12
    {
156
        $tmp = explode('%', $ip_address);
157
        if (isset($tmp[1])) {
158
            $ip_address = $tmp[0].'%25'.rawurlencode($tmp[1]);
159
        }
160
161
        if ($this->hostAsIpv6) {
162
            return "[$ip_address]";
163
        }
164
165 16
        return $ip_address;
166
    }
167
}
168