Completed
Push — master ( 86323e...0fee6d )
by Nazar
04:10
created

Server::init_server()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 14
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 1
Metric Value
c 2
b 0
f 1
dl 0
loc 14
rs 9.4285
cc 1
eloc 9
nc 1
nop 1
1
<?php
2
/**
3
 * @package   CleverStyle CMS
4
 * @author    Nazar Mokrynskyi <[email protected]>
5
 * @copyright Copyright (c) 2016, Nazar Mokrynskyi
6
 * @license   MIT License, see license.txt
7
 */
8
namespace cs\Request;
9
10
trait Server {
11
	/**
12
	 * Uppercase method, GET by default
13
	 *
14
	 * @var string
15
	 */
16
	public $method;
17
	/**
18
	 * The best guessed host
19
	 *
20
	 * @var string
21
	 */
22
	public $host;
23
	/**
24
	 * Schema `http` or `https`
25
	 *
26
	 * @var string
27
	 */
28
	public $scheme;
29
	/**
30
	 * Is requested with HTTPS
31
	 *
32
	 * @var bool
33
	 */
34
	public $secure;
35
	/**
36
	 * Protocol, for instance: `HTTP/1.0`, `HTTP/1.1` (default), HTTP/2.0
37
	 *
38
	 * @var string
39
	 */
40
	public $protocol;
41
	/**
42
	 * Path
43
	 *
44
	 * @var string
45
	 */
46
	public $path;
47
	/**
48
	 * URI, basically `$path?$query_string` (without `?` is query string is empty), `/` by default
49
	 *
50
	 * @var string
51
	 */
52
	public $uri;
53
	/**
54
	 * Query string
55
	 *
56
	 * @var string
57
	 */
58
	public $query_string;
59
	/**
60
	 * Headers are normalized to lowercase keys with hyphen as separator, for instance: `connection`, `referer`, `content-type`, `accept-language`
61
	 *
62
	 * @var string[]
63
	 */
64
	/**
65
	 * Where request came from, not necessary real IP of client, `127.0.0.1` by default
66
	 *
67
	 * @var string
68
	 */
69
	public $remote_addr;
70
	/**
71
	 * The best guessed IP of client (based on all known headers), `$this->remote_addr` by default
72
	 *
73
	 * @var string
74
	 */
75
	public $ip;
76
	public $headers;
77
	/**
78
	 * @param string[] $server Typically `$_SERVER`
79
	 */
80
	function init_server ($server = []) {
81
		$this->fill_headers($server);
82
		/**
83
		 * Add some defaults to avoid isset() hell afterwards
84
		 */
85
		$server += [
86
			'QUERY_STRING'    => '',
87
			'REMOTE_ADDR'     => '127.0.0.1',
88
			'REQUEST_URI'     => '/',
89
			'REQUEST_METHOD'  => 'GET',
90
			'SERVER_PROTOCOL' => 'HTTP/1.1'
91
		];
92
		$this->fill_server_properties($server);
93
	}
94
	/**
95
	 * @param string[] $server
96
	 */
97
	protected function fill_server_properties ($server) {
98
		$this->method       = strtoupper($server['REQUEST_METHOD']);
99
		$this->host         = $this->host($server);
100
		$this->scheme       = $this->secure ? 'https' : 'http';
101
		$this->secure       = $this->secure($server);
102
		$this->protocol     = $server['SERVER_PROTOCOL'];
103
		$this->query_string = $server['QUERY_STRING'];
104
		$this->uri          = null_byte_filter(urldecode($server['REQUEST_URI'])) ?: '/';
105
		$this->path         = explode('?', $this->uri, 2)[0];
106
		$this->remote_addr  = $server['REMOTE_ADDR'];
107
		$this->ip           = $this->ip($_SERVER);
108
	}
109
	/**
110
	 * @param string[] $server
111
	 */
112
	protected function fill_headers ($server) {
113
		$headers = [];
114
		foreach ($server as $header => $header_content) {
115
			if (strpos($header, 'HTTP_') === 0) {
116
				$header = substr($header, 5);
117
			} elseif (strpos($header, 'CONTENT_') !== 0) {
118
				continue;
119
			}
120
			$header           = strtolower(str_replace('_', '-', $header));
121
			$headers[$header] = $header_content;
122
		}
123
		$this->headers = $headers;
124
	}
125
	/**
126
	 * The best guessed IP of client (based on all known headers), `127.0.0.1` by default
127
	 *
128
	 * @param string[] $server
129
	 *
130
	 * @return string
131
	 */
132
	protected function ip ($server) {
133
		$all_possible_keys = [
134
			'HTTP_X_FORWARDED_FOR',
135
			'HTTP_CLIENT_IP',
136
			'HTTP_X_FORWARDED',
137
			'HTTP_X_CLUSTER_CLIENT_IP',
138
			'HTTP_FORWARDED_FOR',
139
			'HTTP_FORWARDED'
140
		];
141
		foreach ($all_possible_keys as $key) {
142
			if (isset($server[$key])) {
143
				$ip = trim(explode(',', $server[$key])[0]);
144
				if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
145
					return $ip;
146
				}
147
			}
148
		}
149
		return $this->remote_addr;
150
	}
151
	/**
152
	 * The best guessed host
153
	 *
154
	 * @param string[] $server
155
	 *
156
	 * @return string
157
	 */
158
	protected function host ($server) {
159
		$host          = @$server['SERVER_NAME'] ?: '';
160
		$port          = '';
161
		$expected_port = $this->secure ? 443 : 80;
162
		if (!$host && isset($server['HTTP_X_FORWARDED_HOST'])) {
163
			$host = $server['HTTP_X_FORWARDED_HOST'];
164
			if (
165
				isset($server['HTTP_X_FORWARDED_PORT']) &&
166
				$server['HTTP_X_FORWARDED_PORT'] != $expected_port
167
			) {
168
				$port = (int)$server['HTTP_X_FORWARDED_PORT'];
169
			}
170
		} elseif (isset($server['HTTP_HOST'])) {
171
			/** @noinspection NotOptimalIfConditionsInspection */
172
			if (!$host || filter_var($host, FILTER_VALIDATE_IP)) {
173
				$host = $server['HTTP_HOST'];
174
			} elseif (strpos($server['HTTP_HOST'], ':') !== false) {
175
				$port = (int)explode(':', $server['HTTP_HOST'])[1];
176
				if ($port == $expected_port) {
177
					$port = '';
178
				}
179
			}
180
		}
181
		if (preg_replace('/(?:^\[)?[a-zA-Z0-9-:\]_]+\.?/', '', $host) !== '') {
182
			return '';
183
		}
184
		return $host.($port ? ":$port" : '');
185
	}
186
	/**
187
	 * Secure protocol detection
188
	 *
189
	 * @param array $server
190
	 *
191
	 * @return bool
192
	 */
193
	protected function secure ($server) {
194
		return isset($server['HTTPS']) && $server['HTTPS'] ? $server['HTTPS'] !== 'off' : (
195
			isset($server['HTTP_X_FORWARDED_PROTO']) && $server['HTTP_X_FORWARDED_PROTO'] === 'https'
196
		);
197
	}
198
	/**
199
	 * Get header by name
200
	 *
201
	 * @param string $name
202
	 *
203
	 * @return string Header content if exists or `false` otherwise
204
	 */
205
	function header ($name) {
206
		return isset($this->headers[$name]) ? $this->headers[$name] : '';
207
	}
208
}
209