RSI helpers  0.1
RSI helpers
Http.php
Go to the documentation of this file.
1 <?php
2 
3 namespace Rsi;
4 
5 /**
6  * HTTP request helpers.
7  */
8 class Http{
9 
10  /**
11  * Document root.
12  * @return string Document root without trailing delimiter.
13  */
14  public static function docRoot(){
15  static $root = null;
16  if($root === null){
17  $root = $_SERVER['DOCUMENT_ROOT'] ?? null;
18  if(!$root && ($filename = $_SERVER['SCRIPT_FILENAME'] ?? null))
19  $root = str_replace('\\','/',\substr($filename,0,-strlen($_SERVER['PHP_SELF'] ?? null)));
20  if(!$root && ($path = $_SERVER['PATH_TRANSLATED'] ?? null))
21  $root = str_replace(['\\','\\\\'],'/',substr($path,0,-strlen($_SERVER['PHP_SELF'] ?? null)));
22  $root = rtrim($root,'/');
23  }
24  return $root;
25  }
26  /**
27  * Protocol version.
28  * @return float Protocol version (false if unknown).
29  */
30  public static function version(){
31  static $version = null;
32  if($version === null) $version = ($protocol = $_SERVER['SERVER_PROTOCOL']) && ($i = strpos($protocol,'/'))
33  ? (float)substr($protocol,$i + 1)
34  : false;
35  return $version;
36  }
37  /**
38  * Request method.
39  * @return string Requested method, in uppercase.
40  */
41  public static function method(){
42  return strtoupper(preg_replace('/\\W+/','',$_SERVER['REQUEST_METHOD'] ?? null));
43  }
44  /**
45  * Check if the request was made over a secure connection.
46  * @return bool True if the request was made over a secure connection.
47  */
48  public static function secure(){
49  return !strcasecmp($_SERVER['HTTPS'] ?? null,'on');
50  }
51  /**
52  * Host name.
53  * @param bool $complete Include protocol and port number.
54  * @return string
55  */
56  public static function host($complete = false){
57  $host = ($_SERVER['SERVER_NAME'] ?? null) ?: gethostname();
58  if($complete){
59  $host = (($secure = self::secure()) ? 'https' : 'http') . '://' . $host;
60  if(($port = $_SERVER['SERVER_PORT'] ?? null) && ($port != ($secure ? 443 : 80))) $host .= ':' . $port;
61  }
62  return $host;
63  }
64  /**
65  * Translate a HTTP response code to a description.
66  * @param int $code HTTP response code.
67  * @return string Description (null if not found).
68  */
69  public static function codeDescr($code){
70  return array_key_exists($code,$codes = require(__DIR__ . '/../../data/httpcode.php')) ? $codes[$code] : null;
71  }
72  /**
73  * Make sure a URL starts with a protocol. Add one if not present.
74  * @param string $url Base URL.
75  * @param string $protocol Protocol to add if none present.
76  * @return string URL with protocol.
77  */
78  public static function ensureProtocol($url,$protocol = 'http'){
79  return strpos($url,'://') ? $url : $protocol . ':' . (substr($url,0,2) == '//' ? '' : '//') . $url;
80  }
81  /**
82  * Remove double dots from a URL.
83  * @param string $url Base URL.
84  * @return string URL with double dots removed (including the directory before it).
85  */
86  public static function realUrl($url){
87  do $url = preg_replace('/\/[^\/]*\/\.\.\//','/',$url,-1,$count);
88  while($count);
89  return $url;
90  }
91  /**
92  * Add parameters to a URL.
93  * @param string $url Base URL.
94  * @param mixed $params Array will beconverted to string.
95  * @return string URL with parameters added.
96  */
97  public static function addParams($url,$params){
98  $hash = null;
99  if(($i = strpos($url,'#')) !== false){
100  $hash = substr($url,$i);
101  $url = substr($url,0,$i);
102  }
103  return $url . (parse_url($url,PHP_URL_QUERY) ? '&' : '?') . (is_array($params) ? http_build_query($params) : $params) . $hash;
104  }
105  /**
106  * Check if the maximum POST size was exceeded.
107  * @param int $size Size of POST.
108  * @param int $max Maximum size.
109  * @return bool
110  */
111  public static function postExceeded(&$size = null,&$max = null){
112  $size = $max = null;
113  return
114  !$_POST &&
115  !$_FILES &&
116  ($size = $_SERVER['CONTENT_LENGTH'] ?? null) &&
117  ($max = \Rsi\Number::shorthandBytes(ini_get('post_max_size'),false)) &&
118  ($size > $max);
119  }
120  /**
121  * Set a cookie.
122  * The cookie is also directly added to the global $_COOKIE array.
123  * @param string $key Cookie key.
124  * @param mixed $value Cookie value.
125  * @param int $days Cookie lifetime (in days).
126  * @param array $options Cookie options (see https://www.php.net/setcookie; when 'secure' is undefined, the value is
127  * automaticly set to true when the request was made using a secure connection).
128  * @return bool True when successful.
129  */
130  public static function setCookie($key,$value,$days = 365,$options = null){
131  $_COOKIE[$key] = $value;
132  $options = ($options ?: []) +
133  ['expires' => $days ? time() + $days * 86400 : 0,'path' => '/','domain' => '','httponly' => true];
134  if(!array_key_exists('secure',$options)) $options['secure'] = self::secure();
135  return \Rsi::version('7.3.0')
136  ? setcookie($key,$value,$options)
137  : setcookie($key,$value,$options['expires'],$options['path'],$options['domain'],$options['secure'],$options['httponly']);
138  }
139  /**
140  * Retrieve a cookie.
141  * @param string $key Cookie key.
142  * @param mixed $default Default value if the key does not exist.
143  * @return mixed Found value, or default value if the key does not exist.
144  */
145  public static function getCookie($key,$default = null){
146  return Record::get($_COOKIE,$key,$default);
147  }
148  /**
149  * Get the prefered language from the client.
150  * @param array $accepted Accepted languages (with or without locale; empty = all).
151  * @param float $quality Minimal quality a language must have.
152  * @return string Language preference (empty = none found).
153  */
154  public static function lang($accepted = null,$quality = 0){
155  $lang = null;
156  if(preg_match_all(
157  '/([a-z]{1,8})(-[a-z]{1,8})?\s*(;\s*q\s*=\s*(1|0\.[0-9]+))?/i',
158  $_SERVER['HTTP_ACCEPT_LANGUAGE'] ?? null,
159  $client_accepted,
160  PREG_SET_ORDER
161  )) foreach($client_accepted as $client_accept){
162  $client_lang = $client_accept[1] . ($client_accept[2] ?? null);
163  if(
164  (!$accepted || in_array($client_lang,$accepted) || in_array($client_lang = $client_accept[1],$accepted)) &&
165  (($client_quality = Record::get($client_accept,4,1)) > $quality)
166  ){
167  $lang = $client_lang;
168  $quality = $client_quality;
169  }
170  }
171  return $lang;
172  }
173  /**
174  * Expand an abbreviated IPv6 address to its full representation.
175  * @param string $addr Basic address.
176  * @return string Expanded address (IPv4 addresses are untouched).
177  */
178  public static function expandAddr($addr){
179  if(count($groups = explode(':',$addr)) > 1){
180  $addr = [];
181  foreach($groups as $index => $group) if(!$index || ($group !== '')) $addr[] = Str::pad($group,4);
182  else $addr = array_merge($addr,array_fill(0,9 - count($groups),'0000'));
183  $addr = implode(':',$addr);
184  }
185  return $addr;
186  }
187  /**
188  * Returns the expanded, remote address (if available).
189  * @return string
190  */
191  public static function remoteAddr(){
192  static $remote_addr = null;
193  if($remote_addr === null) $remote_addr = self::expandAddr($_SERVER['REMOTE_ADDR'] ?? null);
194  return $remote_addr;
195  }
196  /**
197  * Check if an IP-adres lies within a certain subnet.
198  * @param string|array $subnets One or more subnet(s) in CIDR notation (e.g. 127.0.0.0/24).
199  * @param string $remote_addr The IP-address to check (defaults to remoteAddr()).
200  * @return bool
201  */
202  public static function inSubnet($subnets,$remote_addr = null){
203  foreach((array)$subnets as $subnet) switch(count($cidr = explode('/',$subnet))){
204  case 1:
205  $cidr = [$subnet,strlen(inet_pton($subnet)) << 3];
206  case 2:
207  if(strlen($ip = bin2hex(inet_pton($remote_addr ?: self::remoteAddr()))) == strlen($subnet = bin2hex(inet_pton($cidr[0])))){
208  $nibbles = $cidr[1] >> 2;
209  if((substr($ip,0,$nibbles) == substr($subnet,0,$nibbles)) && (
210  ($nibbles == strlen($ip)) ||
211  !($mask = (0xf0 >> ($cidr[1] - ($nibbles << 2)) & 0xf)) ||
212  ((hexdec($ip[$nibbles]) & $mask) == (hexdec($subnet[$nibbles]) & $mask))
213  )) return true;
214  }
215  }
216  return false;
217  }
218  /**
219  * Add a redirection header.
220  * @param string $url URL to redirect to.
221  * @param bool $permanent True to make the redirection permanent (HTTP code 301; defaul is 302).
222  */
223  public static function redirHeader($url,$permanent = false){
224  header('Location: ' . $url,true,$permanent ? 301 : 302);
225  }
226  /**
227  * Add a push header.
228  * @param string $src Location of the resource.
229  * @param string $type Type of the resource.
230  * @return bool True if push is supported and the header is set, false otherwise.
231  */
232  public static function pushHeader($src,$type){
233  if($push = self::version() >= 2) header("Link: <$src>; rel=preload; as=$type",false);
234  return $push;
235  }
236  /**
237  * Send download headers.
238  * @param string $filename Filename of the attached file.
239  * @param string $content_type Content type (defaults to 'application/' + extension of filename).
240  * @param int $size Size of the download (gives user progress indication).
241  */
242  public static function downloadHeaders($filename,$content_type = null,$size = null){
243  header('Content-Type: ' . ($content_type ?: File::mime($filename)));
244  header('Content-Transfer-Encoding: binary');
245  header('Content-Disposition: attachment; filename="' . basename($filename) . '"');
246  if(($size !== null) || is_file($filename)) header('Content-Length: ' . ($size === null ? filesize($filename) : $size));
247  header('Expires: 0');
248  header('Cache-Control: must-revalidate, post-check=0, pre-check=0'); //HTTP/1.1
249  header('Pragma: public'); //HTTP/1.0
250  }
251  /**
252  * Get content from URL.
253  * @param string $url URL to make request to.
254  * @param array $post Data to post with request (key =&gt; value pairs).
255  * @param array $headers Extra HTTP headers to add to request (key = header name, value = header value).
256  * @param array $options Extra cURL options (key = option name, value = option value).
257  * @param int $timeout Request timeout in seconds.
258  * @param int $max_redirs Maximum number of redirections to follow.
259  * @return mixed Response text if susccessful (200) or false if not found (404). Exceptions thrown on cURL error or other
260  * response codes.
261  */
262  public static function urlGetContents($url,$post = null,$headers = null,$options = null,$timeout = null,$max_redirs = null){
263  $ch = curl_init($url);
264  curl_setopt($ch,CURLOPT_RETURNTRANSFER,true);
265  if($post){
266  curl_setopt($ch,CURLOPT_POST,true);
267  curl_setopt($ch,CURLOPT_POSTFIELDS,$post);
268  }
269  if($headers){
270  foreach($headers as $key => &$value) $value = $key . ': ' . $value;
271  unset($value);
272  curl_setopt($ch,CURLOPT_HTTPHEADER,$headers);
273  }
274  if($options) curl_setopt_array($ch,$options);
275  curl_setopt($ch,CURLOPT_TIMEOUT,$timeout ?: ini_get('default_socket_timeout'));
276  if($max_redirs){
277  curl_setopt($ch,CURLOPT_FOLLOWLOCATION,true);
278  curl_setopt($ch,CURLOPT_MAXREDIRS,$max_redirs);
279  }
280  $result = curl_exec($ch);
281  $message = ($error = curl_errno($ch)) ? curl_error($ch) : null;
282  $code = curl_getinfo($ch,CURLINFO_HTTP_CODE);
283  curl_close($ch);
284  if($error) throw new \Exception("cURL error: $message ($error)");
285  switch($code){
286  case 200: return $result;
287  case 404: return false;
288  default: throw new \Exception("unexpected response ($code): $result");
289  }
290  }
291 
292 }
static inSubnet($subnets, $remote_addr=null)
Check if an IP-adres lies within a certain subnet.
Definition: Http.php:202
static redirHeader($url, $permanent=false)
Add a redirection header.
Definition: Http.php:223
static version()
Protocol version.
Definition: Http.php:30
static host($complete=false)
Host name.
Definition: Http.php:56
static postExceeded(&$size=null, &$max=null)
Check if the maximum POST size was exceeded.
Definition: Http.php:111
static addParams($url, $params)
Add parameters to a URL.
Definition: Http.php:97
static remoteAddr()
Returns the expanded, remote address (if available).
Definition: Http.php:191
static codeDescr($code)
Translate a HTTP response code to a description.
Definition: Http.php:69
static lang($accepted=null, $quality=0)
Get the prefered language from the client.
Definition: Http.php:154
static docRoot()
Document root.
Definition: Http.php:14
HTTP request helpers.
Definition: Http.php:8
static urlGetContents($url, $post=null, $headers=null, $options=null, $timeout=null, $max_redirs=null)
Get content from URL.
Definition: Http.php:262
static pushHeader($src, $type)
Add a push header.
Definition: Http.php:232
static realUrl($url)
Remove double dots from a URL.
Definition: Http.php:86
Definition: Color.php:3
static ensureProtocol($url, $protocol='http')
Make sure a URL starts with a protocol.
Definition: Http.php:78
static secure()
Check if the request was made over a secure connection.
Definition: Http.php:48
static getCookie($key, $default=null)
Retrieve a cookie.
Definition: Http.php:145
static setCookie($key, $value, $days=365, $options=null)
Set a cookie.
Definition: Http.php:130
static method()
Request method.
Definition: Http.php:41
static expandAddr($addr)
Expand an abbreviated IPv6 address to its full representation.
Definition: Http.php:178
static downloadHeaders($filename, $content_type=null, $size=null)
Send download headers.
Definition: Http.php:242