You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
383 lines
8.1 KiB
383 lines
8.1 KiB
<?php |
|
/** |
|
* Sleepy - a REST framework |
|
* |
|
* |
|
* A PHP Rest Framework valuing convention over configuration, |
|
* but aiming to be as flexible as possible |
|
* |
|
* @author Timothy J. Warren |
|
*/ |
|
|
|
namespace Sleepy\Core; |
|
|
|
/** |
|
* Class for accessing request data |
|
* |
|
* @method array server() |
|
* @method array env() |
|
* @method array get() |
|
* @method array post() |
|
* @method array put() |
|
* @method array delete() |
|
* @method array cookie() |
|
*/ |
|
class Input { |
|
|
|
// -------------------------------------------------------------------------- |
|
// ! Stuff PHP Forgot |
|
// -------------------------------------------------------------------------- |
|
|
|
/** |
|
* The HTTP verb for the current request |
|
* |
|
* @var string |
|
*/ |
|
protected $verb = 'get'; |
|
|
|
/** |
|
* Class member for put data |
|
* |
|
* @var array |
|
*/ |
|
protected $put = []; |
|
|
|
/** |
|
* Class member for delete data |
|
* |
|
* @var array |
|
*/ |
|
protected $delete = []; |
|
|
|
/** |
|
* Class member for options data |
|
* |
|
* @var array |
|
*/ |
|
protected $options = []; |
|
|
|
// -------------------------------------------------------------------------- |
|
// ! Working around PHP for nicer usability |
|
// -------------------------------------------------------------------------- |
|
|
|
/** |
|
* An array mapping the function to the appropriate superglobal |
|
* |
|
* @var array |
|
*/ |
|
protected $var_map = []; |
|
|
|
/** |
|
* The request's HTTP headers from the $_SERVER superglobal |
|
* |
|
* @var array |
|
*/ |
|
protected $request_headers = []; |
|
|
|
/** |
|
* The request headers parsed into a more useful array |
|
* |
|
* @var array |
|
*/ |
|
protected $parsed_headers = []; |
|
|
|
// -------------------------------------------------------------------------- |
|
// ! Methods |
|
// -------------------------------------------------------------------------- |
|
|
|
/** |
|
* Instantiates the class |
|
*/ |
|
public function __construct() |
|
{ |
|
// Type of HTTP request |
|
$this->verb = \strtolower($_SERVER['REQUEST_METHOD']); |
|
|
|
// Parse put and delete requests into input variables |
|
// @codeCoverageIgnoreStart |
|
if (isset($this->{$this->verb})) |
|
{ |
|
$raw = \file_get_contents('php://input'); |
|
\parse_str($raw, $this->{$this->verb}); |
|
} |
|
|
|
// Set mapping for superglobals, since |
|
// variable variables don't seem to work |
|
// with superglobals :/ |
|
$this->var_map = [ |
|
'get' => $_GET, |
|
'post' => $_POST, |
|
'server' => $_SERVER, |
|
'env' => $_ENV, |
|
'cookie' => $_COOKIE |
|
]; |
|
// @codeCoverageIgnoreEnd |
|
|
|
// Parse request headers from $_SERVER |
|
foreach($_SERVER as $key => $val) |
|
{ |
|
if (strpos($key, 'HTTP_') === 0) |
|
{ |
|
$new_key = \strtolower(\strtr($key, [ |
|
'HTTP_' => '', |
|
'_' => '-' |
|
])); |
|
$this->request_headers[$new_key] = $val; |
|
} |
|
} |
|
} |
|
|
|
/** |
|
* Wrapper method for input arrays |
|
* |
|
* Actually works as get,post,put,delete,cookie,server,and env functions |
|
* all wrapped up in one - because boilerplate is bad! |
|
* |
|
* @param string $name - name of input array |
|
* @param array $args - function arguments |
|
* @return mixed |
|
* @throws \DomainException |
|
*/ |
|
public function __call($name, $args=[]) |
|
{ |
|
// Predefine arguments for input array getters |
|
if ( ! isset($args[0])) $args[0] = NULL; |
|
if ( ! isset($args[1])) $args[1] = FILTER_SANITIZE_STRING; |
|
$index = $args[0]; |
|
$filter = $args[1]; |
|
|
|
if (isset($this->var_map[$name]) || isset($this->$name)) |
|
{ |
|
// Get an input variable value |
|
return $this->get_var($name, $index, $filter); |
|
} |
|
|
|
// What kind of request are you trying to make?! |
|
throw new \DomainException('Invalid input array.'); |
|
} |
|
|
|
/** |
|
* Return header(s) sent from the request |
|
* |
|
* @param string $index |
|
* @return mixed |
|
*/ |
|
public function header($index = NULL) |
|
{ |
|
if ($index !== NULL) |
|
{ |
|
$index = (\strtolower(\str_replace([' ', '_'], '-', $index))); |
|
|
|
if (isset($this->request_headers[$index])) |
|
{ |
|
return $this->request_headers[$index]; |
|
} |
|
|
|
return NULL; |
|
} |
|
|
|
return $this->request_headers; |
|
} |
|
|
|
/** |
|
* Return parsed header(s) sent from the request |
|
* |
|
* @param string $index |
|
* @return mixed |
|
*/ |
|
public function header_array($index = NULL) |
|
{ |
|
if (empty($this->parsed_headers)) |
|
{ |
|
$this->parsed_headers = $this->parse_headers(); |
|
} |
|
|
|
if ($index !== NULL) |
|
{ |
|
$index = (str_replace([' ', '-'], '_', $index)); |
|
|
|
if (isset($this->parsed_headers[$index])) |
|
{ |
|
return $this->parsed_headers[$index]; |
|
} |
|
|
|
return NULL; |
|
} |
|
|
|
return $this->parsed_headers; |
|
} |
|
|
|
/** |
|
* Convert headers to a parsed array of values |
|
* |
|
* @return array |
|
*/ |
|
protected function parse_headers() |
|
{ |
|
foreach($this->request_headers as $header => $value) |
|
{ |
|
$has_semi = strpos($value, ';') !== FALSE; |
|
$has_comma = strpos($value, ',') !== FALSE; |
|
$has_eq = strpos($value, '=') !== FALSE; |
|
|
|
// Parse the user agent separately |
|
if ($header === 'user-agent') |
|
{ |
|
$this->parsed_headers[$header] = $this->parse_user_agent($value); |
|
continue; |
|
} |
|
// Parse accept-type headers separately as well |
|
else if (strpos($header, 'accept') === 0) |
|
{ |
|
$this->parsed_headers[$header] = $this->parse_accept_header($value); |
|
} |
|
// If the header has a comma, and not a semicolon, split on the comma |
|
else if ( ! $has_semi && $has_comma) |
|
{ |
|
$this->parsed_headers[$header] = explode(",", $value); |
|
continue; |
|
} |
|
// Parse cookies and other headers with values like query strings |
|
else if ($has_eq && ! $has_semi) |
|
{ |
|
parse_str($value, $this->parsed_headers[$header]); |
|
continue; |
|
} |
|
// Anything else, just leave it as a string |
|
else |
|
{ |
|
$this->parsed_headers[$header] = $value; |
|
continue; |
|
} |
|
} |
|
|
|
return $this->parsed_headers; |
|
} |
|
|
|
/** |
|
* Parse a browser useragent into a set of useful key-value pairs |
|
* |
|
* @param string $ua_string |
|
* @return array |
|
*/ |
|
protected function parse_user_agent($ua_string) |
|
{ |
|
$user_agent = []; |
|
|
|
$slash_matches = []; |
|
$slash_pattern = "`[A-Z]+/[0-9]+((\.[0-9]+)+)?`i"; |
|
|
|
$paren_matches = []; |
|
$paren_pattern = "`\(.*?\)`i"; |
|
|
|
// Get all the foo/12.3 paterns from the user agent string |
|
preg_match_all($slash_pattern, $ua_string, $slash_matches); |
|
foreach($slash_matches[0] as $arr) |
|
{ |
|
list($key, $version) = explode("/", $arr); |
|
$user_agent['versions'][$key] = $version; |
|
} |
|
|
|
// Get all the info from parenthasized items |
|
preg_match_all($paren_pattern, $ua_string, $paren_matches); |
|
foreach($paren_matches[0] as $arr) |
|
{ |
|
$arr = str_replace(['(',')'], '', $arr); |
|
if (strpos($arr, ';') !== FALSE) |
|
{ |
|
$user_agent['os'] = explode('; ', $arr); |
|
} |
|
else |
|
{ |
|
$user_agent['misc'] = $arr; |
|
} |
|
} |
|
|
|
return $user_agent; |
|
} |
|
|
|
/** |
|
* Parse an accept-type header into an ordered list |
|
* of values |
|
* |
|
* @param string $value |
|
* @return array |
|
*/ |
|
protected function parse_accept_header($value) |
|
{ |
|
$q_types = []; |
|
$high_types = []; |
|
$count = 1; |
|
|
|
// Split into segments of different values |
|
$groups = \explode(',', $value); |
|
|
|
foreach($groups as $group) |
|
{ |
|
$group = \trim($group); |
|
$pair = \explode(';q=', $group); |
|
|
|
if (\count($pair) === 2) |
|
{ |
|
list($val, $q) = $pair; |
|
$q_types[$q] = $val; |
|
} |
|
else |
|
{ |
|
$high_types[$count] = \current($pair); |
|
$count++; |
|
} |
|
} |
|
|
|
// Add an additional fake value so we can |
|
// have a 1-indexed array |
|
$high_types[$count] = 'foo'; |
|
$high_types = \array_reverse($high_types); |
|
unset($high_types[0]); |
|
|
|
$output = $q_types; |
|
|
|
// Merge the arrays manually to maintain |
|
// keys, and thus ordering |
|
foreach($high_types as $k => $v) |
|
{ |
|
$output[$k] = $v; |
|
} |
|
|
|
\krsort($output, SORT_NUMERIC); |
|
|
|
return $output; |
|
} |
|
|
|
/** |
|
* Get superglobal or generated input array value |
|
* @param string $type - HTTP verb |
|
* @param string $index - variable in the input array |
|
* @param int $filter - PHP filter_var flag |
|
* @return mixed |
|
*/ |
|
protected function get_var($type, $index=NULL, $filter=\FILTER_SANITIZE_STRING) |
|
{ |
|
// Determine if superglobal or generated |
|
$var = array_key_exists($type, $this->var_map) |
|
? $this->var_map[$type] |
|
: $this->$type; |
|
|
|
// Return the whole array if the index is null |
|
if ($index === NULL) |
|
{ |
|
return ($filter !== NULL) |
|
? \filter_var_array($var, $filter) |
|
: $var; |
|
} |
|
|
|
// Prevent errors for non-existant variables |
|
if ( ! isset($var[$index])) return NULL; |
|
|
|
return ($filter !== NULL) |
|
? \filter_var($var[$index], $filter) |
|
: $var[$index]; |
|
} |
|
|
|
} |
|
// End of core/Input.php
|