This repository has been archived on 2018-10-11. You can view files and clone it, but cannot push or open issues or pull requests.
sleepy/Sleepy/Core/Input.php

380 lines
8.4 KiB
PHP
Raw Permalink Normal View History

2014-05-07 14:05:13 -04:00
<?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
2014-05-14 10:32:31 -04:00
*
2014-05-14 20:32:30 -04:00
* @method array server(string $index=null, int $filter=\FILTER_SANITIZE_STRING)
* @method array env(string $index=null, int $filter=\FILTER_SANITIZE_STRING)
* @method array get(string $index=null, int $filter=\FILTER_SANITIZE_STRING)
* @method array post(string $index=null, int $filter=\FILTER_SANITIZE_STRING)
* @method array put(string $index=null, int $filter=\FILTER_SANITIZE_STRING)
* @method array delete(string $index=null, int $filter=\FILTER_SANITIZE_STRING)
* @method array cookie(string $index=null, int $filter=\FILTER_SANITIZE_STRING)
2014-05-07 14:05:13 -04:00
*/
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 = [];
2014-05-14 10:32:31 -04:00
/**
* Class member for options data
*
* @var array
*/
protected $options = [];
2014-05-07 14:05:13 -04:00
// --------------------------------------------------------------------------
// ! 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
*/
2014-05-14 11:58:49 -04:00
public function __construct()
2014-05-07 14:05:13 -04:00
{
// Type of HTTP request
$this->verb = \strtolower($_SERVER['REQUEST_METHOD']);
// Parse put and delete requests into input variables
2014-05-14 10:32:31 -04:00
// @codeCoverageIgnoreStart
2014-05-07 14:05:13 -04:00
if (isset($this->{$this->verb}))
{
$raw = \file_get_contents('php://input');
\parse_str($raw, $this->{$this->verb});
}
2014-05-14 10:32:31 -04:00
2014-05-07 14:05:13 -04:00
// 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
];
2014-05-14 10:32:31 -04:00
// @codeCoverageIgnoreEnd
2014-05-07 14:05:13 -04:00
// Parse request headers from $_SERVER
foreach($_SERVER as $key => $val)
{
if (strpos($key, 'HTTP_') === 0)
{
2014-05-14 10:32:31 -04:00
$new_key = \strtolower(\strtr($key, [
'HTTP_' => '',
'_' => '-'
]));
2014-05-07 14:05:13 -04:00
$this->request_headers[$new_key] = $val;
}
}
2014-05-14 20:32:30 -04:00
$this->parsed_headers = $this->parse_headers();
2014-05-07 14:05:13 -04:00
}
/**
* 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
2014-05-14 10:32:31 -04:00
* @throws \DomainException
2014-05-07 14:05:13 -04:00
*/
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];
2014-05-14 11:58:49 -04:00
if (isset($this->var_map[$name]) || isset($this->$name))
2014-05-07 14:05:13 -04:00
{
2014-05-14 11:58:49 -04:00
// Get an input variable value
return $this->get_var($name, $index, $filter);
2014-05-07 14:05:13 -04:00
}
// 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)
{
2014-05-14 20:32:30 -04:00
return $this->_header($this->request_headers, $index);
2014-05-07 14:05:13 -04:00
}
/**
* Return parsed header(s) sent from the request
*
* @param string $index
* @return mixed
*/
public function header_array($index = NULL)
{
2014-05-14 20:32:30 -04:00
return $this->_header($this->parsed_headers, $index);
}
2014-05-07 14:05:13 -04:00
2014-05-14 20:32:30 -04:00
/**
* Retrieve headers
*
* @param array $var
* @param string|null $index
* @return mixed
*/
protected function _header($var, $index = NULL)
{
2014-05-07 14:05:13 -04:00
if ($index !== NULL)
{
$index = (str_replace([' ', '-'], '_', $index));
2014-05-14 20:32:30 -04:00
if (array_key_exists($index, $var))
2014-05-07 14:05:13 -04:00
{
2014-05-14 20:32:30 -04:00
return $var[$index];
2014-05-07 14:05:13 -04:00
}
return NULL;
}
2014-05-14 20:32:30 -04:00
return $var;
2014-05-07 14:05:13 -04:00
}
/**
* 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
2014-05-14 10:32:31 -04:00
if ($header === 'user-agent')
2014-05-07 14:05:13 -04:00
{
$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";
2014-05-14 20:32:30 -04:00
// Get all the foo/12.3 patterns from the user agent string
2014-05-07 14:05:13 -04:00
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)
{
2014-05-14 10:32:31 -04:00
$q_types = [];
$high_types = [];
$count = 1;
2014-05-07 14:05:13 -04:00
// Split into segments of different values
$groups = \explode(',', $value);
2014-05-07 14:05:13 -04:00
foreach($groups as $group)
{
2014-05-14 10:32:31 -04:00
$group = \trim($group);
$pair = \explode(';q=', $group);
2014-05-07 14:05:13 -04:00
if (\count($pair) === 2)
2014-05-07 14:05:13 -04:00
{
list($val, $q) = $pair;
2014-05-14 10:32:31 -04:00
$q_types[$q] = $val;
2014-05-07 14:05:13 -04:00
}
else
{
$high_types[$count] = \current($pair);
2014-05-14 10:32:31 -04:00
$count++;
2014-05-07 14:05:13 -04:00
}
}
2014-05-14 10:32:31 -04:00
// Add an additional fake value so we can
// have a 1-indexed array
$high_types[$count] = 'foo';
$high_types = \array_reverse($high_types);
2014-05-14 10:32:31 -04:00
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);
2014-05-07 14:05:13 -04:00
return $output;
}
2014-05-14 11:58:49 -04:00
2014-05-07 14:05:13 -04:00
/**
2014-05-14 11:58:49 -04:00
* Get superglobal or generated input array value
* @param string $type - HTTP verb
2014-05-07 14:05:13 -04:00
* @param string $index - variable in the input array
* @param int $filter - PHP filter_var flag
* @return mixed
*/
2014-05-14 11:58:49 -04:00
protected function get_var($type, $index=NULL, $filter=\FILTER_SANITIZE_STRING)
2014-05-07 14:05:13 -04:00
{
2014-05-14 11:58:49 -04:00
// Determine if superglobal or generated
$var = array_key_exists($type, $this->var_map)
? $this->var_map[$type]
: $this->$type;
2014-05-07 14:05:13 -04:00
// Return the whole array if the index is null
if ($index === NULL)
{
return ($filter !== NULL)
? \filter_var_array($var, $filter)
: $var;
2014-05-07 14:05:13 -04:00
}
2014-05-14 11:58:49 -04:00
2014-05-07 14:05:13 -04:00
// Prevent errors for non-existant variables
if ( ! isset($var[$index])) return NULL;
2014-05-14 11:58:49 -04:00
return ($filter !== NULL)
? \filter_var($var[$index], $filter)
: $var[$index];
2014-05-07 14:05:13 -04:00
}
}
// End of core/Input.php