Completed
Push — master ( b2e200...1332b8 )
by Oscar
04:48
created

ClientIp::scanIps()   D

Complexity

Conditions 9
Paths 20

Size

Total Lines 27
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 1
Metric Value
c 2
b 0
f 1
dl 0
loc 27
rs 4.9091
cc 9
eloc 14
nc 20
nop 1
1
<?php
2
3
namespace Psr7Middlewares\Middleware;
4
5
use Psr7Middlewares\Middleware;
6
use Psr\Http\Message\ServerRequestInterface;
7
use Psr\Http\Message\ResponseInterface;
8
9
/**
10
 * Middleware returns the client ip.
11
 */
12
class ClientIp
13
{
14
    const KEY = 'CLIENT_IPS';
15
16
    /**
17
     * @var bool
18
     */
19
    private $remote = false;
20
21
    /**
22
     * @var array The trusted headers
23
     */
24
    private $headers = [
25
        'Forwarded',
26
        'Forwarded-For',
27
        'Client-Ip',
28
        'X-Forwarded',
29
        'X-Forwarded-For',
30
        'X-Cluster-Client-Ip',
31
    ];
32
33
    /**
34
     * Returns all ips found.
35
     *
36
     * @param ServerRequestInterface $request
37
     *
38
     * @return array|null
39
     */
40
    public static function getIps(ServerRequestInterface $request)
41
    {
42
        return Middleware::getAttribute($request, self::KEY);
43
    }
44
45
    /**
46
     * Return the client ip.
47
     *
48
     * @param ServerRequestInterface $request
49
     *
50
     * @return string|null
51
     */
52
    public static function getIp(ServerRequestInterface $request)
53
    {
54
        $ips = self::getIps($request);
55
56
        return isset($ips[0]) ? $ips[0] : null;
57
    }
58
59
    /**
60
     * Constructor. Defines de trusted headers.
61
     *
62
     * @param null|array $headers
63
     */
64
    public function __construct(array $headers = null)
65
    {
66
        if ($headers !== null) {
67
            $this->headers($headers);
68
        }
69
    }
70
71
    /**
72
     * Configure the trusted headers.
73
     *
74
     * @param array $headers
75
     *
76
     * @return self
77
     */
78
    public function headers(array $headers)
79
    {
80
        $this->headers = $headers;
81
82
        return $this;
83
    }
84
85
    /**
86
     * To get the ip from a remote service.
87
     * Useful for testing purposes on localhost.
88
     *
89
     * @param bool $remote
90
     *
91
     * @return self
92
     */
93
    public function remote($remote = true)
94
    {
95
        $this->remote = $remote;
96
97
        return $this;
98
    }
99
100
    /**
101
     * Execute the middleware.
102
     *
103
     * @param ServerRequestInterface $request
104
     * @param ResponseInterface      $response
105
     * @param callable               $next
106
     *
107
     * @return ResponseInterface
108
     */
109
    public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next)
110
    {
111
        $request = Middleware::setAttribute($request, self::KEY, $this->scanIps($request));
112
113
        return $next($request, $response);
114
    }
115
116
    /**
117
     * Detect and return all ips found.
118
     *
119
     * @param ServerRequestInterface $request
120
     *
121
     * @return array
122
     */
123
    private function scanIps(ServerRequestInterface $request)
124
    {
125
        $server = $request->getServerParams();
126
        $ips = [];
127
128
        if ($this->remote) {
129
            $ips[] = file_get_contents('http://ipecho.net/plain');
130
        }
131
132
        if (!empty($server['REMOTE_ADDR']) && filter_var($server['REMOTE_ADDR'], FILTER_VALIDATE_IP)) {
133
            $ips[] = $server['REMOTE_ADDR'];
134
        }
135
136
        foreach ($this->headers as $name) {
137
            $header = $request->getHeaderLine($name);
138
139
            if (!empty($header)) {
140
                foreach (array_map('trim', explode(',', $header)) as $ip) {
141
                    if ((array_search($ip, $ips) === false) && filter_var($ip, FILTER_VALIDATE_IP)) {
142
                        $ips[] = $ip;
143
                    }
144
                }
145
            }
146
        }
147
148
        return $ips;
149
    }
150
}
151