Passed
Push — master ( 6d45fb...cc795e )
by mcfog
01:22
created

IpAddress::getIpAddressFromRequest()   B

Complexity

Conditions 6
Paths 4

Size

Total Lines 30
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 16
nc 4
nop 3
dl 0
loc 30
rs 8.439
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Lit\Middleware;
6
7
use Nimo\AbstractMiddleware;
8
use Nimo\Traits\MiddlewareTrait;
9
use Psr\Http\Message\ResponseInterface;
10
use Psr\Http\Message\ServerRequestInterface;
11
12
class IpAddress extends AbstractMiddleware
13
{
14
    use MiddlewareTrait;
15
16
    /**
17
     * @var array
18
     */
19
    protected $trustedProxies;
20
    /**
21
     * @var array
22
     */
23
    protected $headers;
24
    /**
25
     * @var string
26
     */
27
    protected $ipAddress;
28
29
    public function __construct(
30
        array $trustedProxies = [],
31
        array $headers = []
32
    ) {
33
        $this->trustedProxies = $trustedProxies;
34
        $this->headers = $headers;
35
    }
36
37
    /**
38
     * @return string
39
     */
40
    public function getIpAddress(): ?string
41
    {
42
        return $this->ipAddress;
43
    }
44
45
    public static function getIpAddressFromRequest(
46
        ServerRequestInterface $request,
47
        array $trustedProxies = [],
48
        array $headers = []
49
    ): ?string {
50
        $headers = $headers ?: [
51
            'Forwarded',
52
            'X-Forwarded-For',
53
            'X-Forwarded',
54
            'X-Cluster-Client-Ip',
55
            'Client-Ip',
56
        ];
57
58
        $params = $request->getServerParams();
59
60
        $remoteAddr = $params['REMOTE_ADDR'] ?? '';
61
        if (!self::isValidIpAddress($remoteAddr)) {
62
            return null;
63
        }
64
65
        if (empty($trustedProxies) || !in_array($remoteAddr, $trustedProxies)) {
66
            return $remoteAddr;
67
        }
68
69
        $ip = self::getIpAddressFromHeaders($request, $headers);
70
        if (!empty($ip)) {
71
            return $ip;
72
        }
73
74
        return $remoteAddr;
75
    }
76
77
    protected function main(): ResponseInterface
78
    {
79
        $this->attachToRequest();
80
        $this->ipAddress = static::getIpAddressFromRequest($this->request, $this->trustedProxies, $this->headers);
81
82
        return $this->delegate();
83
    }
84
85
    protected static function isValidIpAddress(string $ip): bool
86
    {
87
        return false !== filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_IPV6);
88
    }
89
90
    /**
91
     * @param ServerRequestInterface $request
92
     * @param string[] $headers
93
     * @return null|string
94
     */
95
    protected static function getIpAddressFromHeaders(ServerRequestInterface $request, array $headers): ?string
96
    {
97
        foreach ($headers as $headerName) {
98
            $headerValue = trim(explode(',', $request->getHeaderLine($headerName))[0]);
99
            if (empty($headerValue)) {
100
                continue;
101
            }
102
103
            if (strtolower($headerName) == 'forwarded') {
104
                $headerValue = static::parseForwarded($headerValue);
105
            }
106
107
            if (static::isValidIpAddress($headerValue)) {
108
                return $headerValue;
109
            }
110
        }
111
112
        return null;
113
    }
114
115
    protected static function parseForwarded($headerValue): ?string
116
    {
117
        foreach (explode(';', $headerValue) as $headerPart) {
118
            if (strtolower(substr($headerPart, 0, 4)) == 'for=') {
119
                $for = explode(']', $headerPart);
120
                return trim(substr($for[0], 4), " \t\n\r\0\x0B\"[]");
121
            }
122
        }
123
124
        return null;
125
    }
126
}
127