Completed
Pull Request — master (#66)
by Anton
02:21
created

ClientIp::serverOptions()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 6
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 3
nc 1
nop 1
1
<?php
2
3
namespace Psr7Middlewares\Middleware;
4
5
use Psr\Http\Message\ResponseInterface;
6
use Psr\Http\Message\ServerRequestInterface;
7
use Psr7Middlewares\Utils;
8
9
/**
10
 * Middleware returns the client ip.
11
 *
12
 * Note server values check:
13
 *
14
 * @see https://en.wikipedia.org/wiki/X-Forwarded-For
15
 * @see http://stackoverflow.com/questions/4262081/serverremote-addr-gives-server-ip-rather-than-visitor-ip
16
 * @see http://php.net/manual/en/reserved.variables.server.php
17
 * @see https://github.com/oscarotero/psr7-middlewares/pull/65
18
 */
19
class ClientIp
20
{
21
    use Utils\AttributeTrait;
22
23
    const KEY = 'CLIENT_IPS';
24
25
    /**
26
     * @var bool
27
     */
28
    private $remote = false;
29
30
    /**
31
     * @var array The trusted headers
32
     */
33
    private $headers = [
34
        'Forwarded',
35
        'Forwarded-For',
36
        'Client-Ip',
37
        'X-Forwarded',
38
        'X-Forwarded-For',
39
        'X-Cluster-Client-Ip',
40
    ];
41
42
    /**
43
     * @var array Server options to be checked for IP location, checked in a given order.
44
     */
45
    private $serverOptions = [
46
        'REMOTE_ADDR',
47
        'HTTP_X_FORWARDED_FOR'
48
    ];
49
50
    /**
51
     * Returns all ips found.
52
     *
53
     * @param ServerRequestInterface $request
54
     *
55
     * @return array|null
56
     */
57
    public static function getIps(ServerRequestInterface $request)
58
    {
59
        return self::getAttribute($request, self::KEY);
60
    }
61
62
    /**
63
     * Return the client ip.
64
     *
65
     * @param ServerRequestInterface $request
66
     *
67
     * @return string|null
68
     */
69
    public static function getIp(ServerRequestInterface $request)
70
    {
71
        $ips = self::getIps($request);
72
73
        return isset($ips[0]) ? $ips[0] : null;
74
    }
75
76
    /**
77
     * Constructor. Defines de trusted headers.
78
     *
79
     * @param null|array $headers
80
     */
81
    public function __construct(array $headers = null)
82
    {
83
        if ($headers !== null) {
84
            $this->headers($headers);
85
        }
86
    }
87
88
    /**
89
     * Configure the trusted headers.
90
     *
91
     * @param array $headers
92
     *
93
     * @return self
94
     */
95
    public function headers(array $headers)
96
    {
97
        $this->headers = $headers;
98
99
        return $this;
100
    }
101
102
    /**
103
     * Configure the serverOptions to be checked for id address.
104
     *
105
     * @param array $serverOptions
106
     *
107
     * @return self
108
     */
109
    public function serverOptions(array $serverOptions)
110
    {
111
        $this->serverOptions = $serverOptions;
112
113
        return $this;
114
    }
115
116
    /**
117
     * To get the ip from a remote service.
118
     * Useful for testing purposes on localhost.
119
     *
120
     * @param bool $remote
121
     *
122
     * @return self
123
     */
124
    public function remote($remote = true)
125
    {
126
        $this->remote = $remote;
127
128
        return $this;
129
    }
130
131
    /**
132
     * Execute the middleware.
133
     *
134
     * @param ServerRequestInterface $request
135
     * @param ResponseInterface      $response
136
     * @param callable               $next
137
     *
138
     * @return ResponseInterface
139
     */
140
    public function __invoke(
141
        ServerRequestInterface $request,
142
        ResponseInterface $response,
143
        callable $next
144
    ) {
145
        $request = self::setAttribute($request, self::KEY, $this->scanIps($request));
146
147
        return $next($request, $response);
148
    }
149
150
    /**
151
     * Detect and return all ips found.
152
     *
153
     * @param ServerRequestInterface $request
154
     *
155
     * @return array
156
     */
157
    private function scanIps(ServerRequestInterface $request)
158
    {
159
        $server = $request->getServerParams();
160
        $ips = [];
161
162
        if ($this->remote) {
163
            $ips[] = file_get_contents('http://ipecho.net/plain');
164
        }
165
166
        foreach ($this->serverOptions as $option) {
167
            //Default order: REMOTE_ADDR, HTTP_X_FORWARDED_FOR
168
            if (!empty($server[$option]) && filter_var($server[$option], FILTER_VALIDATE_IP)) {
169
                //Found ip candidate from server params
170
                $ips[] = $server[$option];
171
            }
172
        }
173
174
        foreach ($this->headers as $name) {
175
            $header = $request->getHeaderLine($name);
176
177
            if (!empty($header)) {
178
                foreach (array_map('trim', explode(',', $header)) as $ip) {
179
                    if ((array_search($ip, $ips) === false) && filter_var($ip,
180
                            FILTER_VALIDATE_IP)
181
                    ) {
182
                        $ips[] = $ip;
183
                    }
184
                }
185
            }
186
        }
187
188
        return $ips;
189
    }
190
}
191