391 lines
8.5 KiB
PHP
391 lines
8.5 KiB
PHP
|
<?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
|
||
|
* @package Sleepy/core
|
||
|
*/
|
||
|
|
||
|
namespace Sleepy\Core;
|
||
|
|
||
|
/**
|
||
|
* Class for accessing request data
|
||
|
*/
|
||
|
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 = [];
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
// ! 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
|
||
|
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
|
||
|
];
|
||
|
|
||
|
// Parse request headers from $_SERVER
|
||
|
foreach($_SERVER as $key => $val)
|
||
|
{
|
||
|
if (strpos($key, 'HTTP_') === 0)
|
||
|
{
|
||
|
$new_key = strtolower(str_replace('HTTP_', '', $key));
|
||
|
$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))
|
||
|
{
|
||
|
// Get a input variable not in a superglobal (eg. PUT/DELETE)
|
||
|
return $this->get_request_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 = (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;
|
||
|
}
|
||
|
// For headers with commas and semicolons, break first on commas,
|
||
|
// then on semicolons
|
||
|
else if ($has_semi && $has_comma)
|
||
|
{
|
||
|
$values = explode(",", $value);
|
||
|
foreach($values as &$v)
|
||
|
{
|
||
|
if (strpos($v, ";") !== FALSE)
|
||
|
{
|
||
|
$v = explode(";", $v);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
$this->parsed_headers[$header] = $values;
|
||
|
}
|
||
|
// 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)
|
||
|
{
|
||
|
$output = [];
|
||
|
$index = 0;
|
||
|
|
||
|
// Split into segments of different values
|
||
|
$groups = explode(',', $value);
|
||
|
|
||
|
foreach($groups as $group)
|
||
|
{
|
||
|
$pair = explode(';q=', $group);
|
||
|
|
||
|
if (count($pair) === 2)
|
||
|
{
|
||
|
list($val, $q) = $pair;
|
||
|
$output[$q] = $val;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
$index++;
|
||
|
$output[$index] = current($pair);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ksort($output, SORT_NATURAL);
|
||
|
|
||
|
return $output;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get input var(s) from non-defined superglobal
|
||
|
*
|
||
|
* @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
|