Passed
Push — master ( fca16e...a09e23 )
by Alexander
04:10
created

RequestClientIP   A

Complexity

Total Complexity 19

Size/Duplication

Total Lines 175
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 42
dl 0
loc 175
rs 10
c 1
b 0
f 0
wmc 19

9 Methods

Rating   Name   Duplication   Size   Complexity  
A getClientLongIp() 0 3 1
A setClientIp() 0 17 5
A getServerHeader() 0 3 1
A setServerHeader() 0 9 5
A getIpServerHeaders() 0 3 1
A getClientIp() 0 3 1
A validateIp() 0 7 2
A __construct() 0 5 1
A SetClientLongIp() 0 10 2
1
<?php
2
3
/**
4
 * Lenevor Framework
5
 *
6
 * LICENSE
7
 *
8
 * This source file is subject to the new BSD license that is bundled
9
 * with this package in the file license.md.
10
 * It is also available through the world-wide-web at this URL:
11
 * https://lenevor.com/license
12
 * If you did not receive a copy of the license and are unable to
13
 * obtain it through the world-wide-web, please send an email
14
 * to [email protected] so we can send you a copy immediately.
15
 *
16
 * @package     Lenevor
17
 * @subpackage  Base
18
 * @link        https://lenevor.com
19
 * @copyright   Copyright (c) 2019 - 2023 Alexander Campo <[email protected]>
20
 * @license     https://opensource.org/licenses/BSD-3-Clause New BSD license or see https://lenevor.com/license or see /license.md
21
 */
22
23
namespace Syscodes\Components\Http\Helpers;
24
25
/**
26
 * This is a lightweight class for detecting client IP address:
27
 * - It uses specific HTTP headers to detect the real/original.
28
 * - Also, client IP address not final proxy IP.
29
 */
30
class RequestClientIP
31
{
32
    /**
33
     * Get the client IP.
34
     * 
35
     * @var mixed $clientIp
36
     */
37
    protected $clientIp = null;
38
39
    /**
40
     * Get the client long IP.
41
     * 
42
     * @var mixed $clientLongIp
43
     */
44
    protected $clientLongIp = null;
45
46
    /**
47
     * Get the HTTP servers.
48
     * 
49
     * @var array $headers
50
     */
51
    protected $headers = [];
52
53
    /**
54
     * All possible HTTP headers for represent the 
55
     * IP address string.
56
     * 
57
     * @var array $ipServerHeaders
58
     */
59
    protected $ipServerHeaders = [
60
        'VIA',
61
        'X-REAL-IP',
62
        'REMOTE_ADDR',
63
        'HTTP_CLIENT_IP',
64
        'HTTP_FORWARDED',
65
        'X_FORWARDED_FOR',
66
        'HTTP_X_FORWARDED',
67
        'HTTP_FORWARDED_FOR',
68
        'HTTP_X_FORWARDED_FOR',        
69
        'HTTP_X_CLUSTER_CLIENT_IP',         
70
    ];
71
72
    /**
73
     * Constructor. The create a new RequestClientIP class instance.
74
     * 
75
     * @param  array  $headers  Get headers from the request
76
     * 
77
     * @return void
78
     */
79
    public function __construct(array $headers = [])
80
    {
81
        $this->setServerHeader($headers);
82
        $this->setClientIp();
83
        $this->setClientLongIp($this->getclientIp());
84
    }
85
86
    /**
87
     * Set the SERVER Headers. This method set IP headers 
88
     * data with sent manually headers array.
89
     * 
90
     * @param  array  $headers
91
     * 
92
     * @return void
93
     */
94
    public function setServerHeader(array $headers = []): void
95
    {
96
        if ( ! is_array($headers) || ! count($headers)) {
0 ignored issues
show
introduced by
The condition is_array($headers) is always true.
Loading history...
97
            $headers = $_SERVER;
98
        }
99
100
        foreach ($this->getIpServerHeaders() as $key) {
101
            if (array_key_exists($key, $headers)) {
102
                $this->headers[$key] = $headers[$key];
103
            }
104
        }
105
    }
106
107
    /**
108
     * Get all possible SERVER headers that can contain 
109
     * the IP address.
110
     * 
111
     * @return array
112
     */
113
    protected function getIpServerHeaders(): array
114
    {
115
        return $this->ipServerHeaders;
116
    }
117
118
    /**
119
     * Retrieves the IP detect headers.
120
     * 
121
     * @return array
122
     */
123
    protected function getServerHeader(): array
124
    {
125
        return $this->headers;
126
    }
127
128
    /**
129
     * Return client IPv4.
130
     * 
131
     * @return mixed
132
     */
133
    public function getClientIp()
134
    {
135
        return $this->clientIp;
136
    }
137
    
138
    /**
139
     * Set the real valid IP address from serverHeaders.
140
     * 
141
     * @return bool|string
142
     */
143
    protected function setClientIp()
144
    {
145
        foreach ($this->getIpServerHeaders() as $ipServerHeader) {
146
            if (isset($this->headers[$ipServerHeader])) {
147
                foreach (explode(',', $this->headers[$ipServerHeader]) as $ip) {
148
                    $ip = trim($ip);
149
                    
150
                    if ($this->validateIp($ip)) {
151
                        $this->clientIp = $ip;
152
                        
153
                        return $ip;
154
                    }
155
                }
156
            }
157
        }
158
        
159
        return false;
160
    }
161
162
    /**
163
     * Return client LongIpv4.
164
     * 
165
     * @return mixed
166
     */
167
    public function getClientLongIp()
168
    {
169
        return $this->clientLongIp;
170
    }
171
    
172
    /**
173
     * Set the real valid long IP address.
174
     * 
175
     * @param  string|null  $ip  IPv4
176
     * 
177
     * @return bool|null
178
     */
179
    protected function SetClientLongIp($ip = null)
180
    {
181
        if ($this->validateIp($ip)) {
182
            // Fix bug to ip2long returning negative val
183
            $this->clientLongIp = sprintf('%u', ip2long($ip));
0 ignored issues
show
Bug introduced by
It seems like $ip can also be of type null; however, parameter $ip of ip2long() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

183
            $this->clientLongIp = sprintf('%u', ip2long(/** @scrutinizer ignore-type */ $ip));
Loading history...
184
            
185
            return $this->clientLongIp;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->clientLongIp returns the type string which is incompatible with the documented return type boolean|null.
Loading history...
186
        }
187
        
188
        return false;
189
    }
190
    
191
    /**
192
     * Ensures an ip address is both a valid IP.
193
     * 
194
     * @param  string  $ip  IPv4
195
     * 
196
     * @return bool
197
     */
198
    public function validateIp(?string $ip): bool
199
    {
200
        if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_NO_PRIV_RANGE)) {
201
            return true;
202
        }
203
        
204
        return false;
205
    }
206
}