Origin::removeIp()   A
last analyzed

Complexity

Conditions 3
Paths 3

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 3
nc 3
nop 1
dl 0
loc 5
rs 10
c 1
b 0
f 0
1
<?php
2
declare(strict_types=1);
3
4
namespace Utilities\Router;
5
6
/**
7
 * Origin class
8
 *
9
 * @link    https://github.com/utilities-php/router
10
 * @author  Shahrad Elahi (https://github.com/shahradelahi)
11
 * @license https://github.com/utilities-php/router/blob/master/LICENSE (MIT License)
12
 */
13
class Origin
14
{
15
16
    /**
17
     * The allowed domains.
18
     *
19
     * @var array
20
     */
21
    protected static array $allowedDomains = [];
22
23
    /**
24
     * The allowed ip addresses.
25
     *
26
     * @var array
27
     */
28
    protected static array $allowedIps = [];
29
30
    /**
31
     * Add allowed domain.
32
     *
33
     * @param string $domain The domain. (e.g. example.com or *.example.com)
34
     * @param bool $allowCredentials If true, the credentials will be allowed.
35
     * @param int $maxAge The max age. (e.g. 86400)
36
     * @return void
37
     */
38
    public static function addDomain(string $domain, bool $allowCredentials = false, int $maxAge = 86400): void
39
    {
40
        static::$allowedDomains[] = [
41
            'domain' => self::domainToRegex($domain),
42
            'allowCredentials' => $allowCredentials,
43
            'maxAge' => $maxAge,
44
        ];
45
    }
46
47
    /**
48
     * Convert domain to Regex.
49
     *
50
     * @param string $domain The domain. (e.g. example.com or *.example.com)
51
     * @return string The Regex. (e.g. ^example\.com$ or ^.*\.example\.com$)
52
     */
53
    private static function domainToRegex(string $domain): string
54
    {
55
        $domain = str_replace('.', '\.', $domain);
56
        if (str_starts_with($domain, '*')) {
57
            return '^.*' . substr($domain, 1) . '$';
58
        }
59
60
        return '^' . $domain . '$';
61
    }
62
63
    /**
64
     * Remove allowed domain.
65
     *
66
     * @param string $domain The domain. (e.g. example.com or *.example.com)
67
     * @return void
68
     */
69
    public static function removeDomain(string $domain): void
70
    {
71
        foreach (static::$allowedDomains as $key => $value) {
72
            if ($value['domain'] == self::domainToRegex($domain)) {
73
                unset(static::$allowedDomains[$key]);
74
            }
75
        }
76
    }
77
78
    /**
79
     * Add allowed ip address.
80
     *
81
     * @param string $ip The ip address. (e.g. 31.187.72.206 or 2a02:4780:f:ab01::1)
82
     * @return void
83
     */
84
    public static function addIp(string $ip): void
85
    {
86
        static::$allowedIps[] = $ip;
87
    }
88
89
    /**
90
     * Remove allowed ip address.
91
     *
92
     * @param string $ip The ip address. (e.g. 31.187.72.206 or 2a02:4780:f:ab01::1)
93
     * @return void
94
     */
95
    public static function removeIp(string $ip): void
96
    {
97
        foreach (static::$allowedIps as $key => $value) {
98
            if ($value == $ip) {
99
                unset(static::$allowedIps[$key]);
100
            }
101
        }
102
    }
103
104
    /**
105
     * Validate. Get request and start validation.
106
     *
107
     * @param array $options [allowCredentials, maxAge, allowedHeaders]
108
     * @return bool
109
     */
110
    public static function validate(array $options = ['allowCredentials' => false, 'maxAge' => 86400, 'allowedHeaders' => []]): bool
111
    {
112
        $flag = false;
113
114
        if (isset($_SERVER['HTTP_ORIGIN'])) {
115
            foreach (static::$allowedDomains as $domain) {
116
                if (preg_match_all('/' . $domain['domain'] . '/i', parse_url($_SERVER['HTTP_ORIGIN'])['path']) > 0) {
117
                    self::sendHeader('Access-Control-Allow-Origin', $_SERVER['HTTP_ORIGIN']);
118
                    self::sendHeader('Access-Control-Allow-Credentials', $domain['allowCredentials'] ? 'true' : 'false');
119
                    self::sendHeader('Access-Control-Max-Age', $domain['maxAge'] ?? $options['maxAge']);
120
                    $flag = true;
121
                    break;
122
                }
123
            }
124
        }
125
126
        if (isset($_SERVER['REMOTE_ADDR'])) {
127
            foreach (static::$allowedIps as $ip) {
128
                if ($_SERVER['REMOTE_ADDR'] == $ip) {
129
                    $flag = true;
130
                    break;
131
                }
132
            }
133
        }
134
135
        if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') {
136
            if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_METHOD'])) {
137
                self::sendHeader('Access-Control-Allow-Methods', $_SERVER['HTTP_ACCESS_CONTROL_REQUEST_METHOD']);
138
            }
139
140
            if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS'])) {
141
                $allowedHeaders = [];
142
143
                foreach (explode(',', $_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']) as $header) {
144
                    if (in_array($header, $options['allowedHeaders'])) {
145
                        $allowedHeaders[] = $header;
146
                    }
147
                }
148
149
                self::sendHeader('Access-Control-Allow-Headers', implode(',', $allowedHeaders));
150
            }
151
152
            return true;
153
        }
154
155
        return $flag;
156
    }
157
158
    /**
159
     * Send the headers but in safe mode.
160
     *
161
     * @param string $header The header. (e.g. Access-Control-Allow-Origin)
162
     * @param string $value The value. (e.g. * or example.com)
163
     */
164
    private static function sendHeader(string $header, mixed $value): void
165
    {
166
        if (headers_sent()) {
167
            return;
168
        }
169
170
        header("{$header}: {$value}");
171
    }
172
173
}