416 lines
9.0 KiB
PHP
Executable File
416 lines
9.0 KiB
PHP
Executable File
<?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
|
|
*
|
|
* @param array $config
|
|
*/
|
|
public function __construct($config = [])
|
|
{
|
|
// 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]))
|
|
{
|
|
// Get a superglobal ($_VAR) value
|
|
return $this->get_superglobal_var($name, $index, $filter);
|
|
}
|
|
else if(isset($this->$name)) // @codeCoverageIgnoreStart
|
|
{
|
|
// Get a input variable not in a superglobal (eg. PUT/DELETE)
|
|
return $this->get_request_var($name, $index, $filter);
|
|
}
|
|
// @codeCoverageIgnoreEnd
|
|
|
|
// 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 = [];
|
|
// A fake value so I can shift it off to have a 1-indexed array
|
|
$high_types = [];
|
|
$output = [];
|
|
$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 input var(s) from non-defined superglobal
|
|
*
|
|
* @codeCoverageIgnore
|
|
* @param string $type - input array
|
|
* @param string $index - variable in the input array
|
|
* @param int $filter - PHP filter_var flag
|
|
* @return mixed
|
|
*/
|
|
protected function get_request_var($type, $index=NULL, $filter=FILTER_SANITIZE_STRING)
|
|
{
|
|
// If index is null, return the whole array
|
|
if ($index === NULL)
|
|
{
|
|
return ($filter !== NULL) ? \filter_var_array($this->$type, $filter) : $this->$type;
|
|
}
|
|
|
|
// Prevent errors for non-existant variables
|
|
if ( ! isset($this->$type[$index]))
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
return ($filter !== NULL) ? \filter_var($this->$type[$index], $filter) : $this->$type[$index];
|
|
}
|
|
|
|
/**
|
|
* Get index from superglobal
|
|
*
|
|
* @param string $type - superglobal
|
|
* @param string $index - variable in the superglobal
|
|
* @param int $filter - PHP filter_var flag
|
|
* @return mixed
|
|
*/
|
|
protected function get_superglobal_var($type, $index=NULL, $filter=FILTER_SANITIZE_STRING)
|
|
{
|
|
$var =& $this->var_map[$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
|