commit
4280489558
21 changed files with 1416 additions and 0 deletions
@ -0,0 +1,391 @@
|
||||
<?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 |
@ -0,0 +1,181 @@
|
||||
<?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; |
||||
|
||||
/** |
||||
* Default output class |
||||
*/ |
||||
class Output { |
||||
|
||||
/** |
||||
* The data to serialize and output |
||||
* |
||||
* @var mixed |
||||
*/ |
||||
protected $data; |
||||
|
||||
/** |
||||
* A list of HTTP headers to send with a response |
||||
* |
||||
* @var array |
||||
*/ |
||||
protected $headers = []; |
||||
|
||||
/** |
||||
* The serialization object for the current data type |
||||
* |
||||
* @var Sleepy\core\aType |
||||
*/ |
||||
protected $type_wrapper; |
||||
|
||||
/** |
||||
* The input object |
||||
* |
||||
* @var Sleepy\core\Input |
||||
*/ |
||||
protected $input; |
||||
|
||||
// -------------------------------------------------------------------------- |
||||
// ! Methods |
||||
// -------------------------------------------------------------------------- |
||||
|
||||
/** |
||||
* Create the output object |
||||
* |
||||
* @param array $config |
||||
*/ |
||||
public function __construct(Input $input) |
||||
{ |
||||
$this->input = $input; |
||||
} |
||||
|
||||
/** |
||||
* Output the data to the client |
||||
*/ |
||||
public function __destruct() |
||||
{ |
||||
// Output the headers |
||||
$this->_output_headers(); |
||||
|
||||
// Echo the response |
||||
echo $this->type_wrapper; |
||||
} |
||||
|
||||
/** |
||||
* Add a header to be output |
||||
* |
||||
* @param mixed $header |
||||
* @return void |
||||
*/ |
||||
public function set_header($header) |
||||
{ |
||||
if (is_array($header)) |
||||
{ |
||||
array_merge($this->headers, $header); |
||||
} |
||||
else |
||||
{ |
||||
$this->headers[] = $header; |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Set the data to be output to the endpoint |
||||
* |
||||
* @param string $type - The datatype to send |
||||
* @param mixed $data |
||||
* @return void |
||||
*/ |
||||
public function set_data($type = 'json', $data) |
||||
{ |
||||
// Get the appropriate output format for the client |
||||
// And set the data |
||||
$this->get_accepted_type($type, $data); |
||||
} |
||||
|
||||
// -------------------------------------------------------------------------- |
||||
// ! Private helper methods |
||||
// -------------------------------------------------------------------------- |
||||
|
||||
/** |
||||
* Get the type more accepted for output |
||||
* |
||||
* @param mixed $types |
||||
* @param mixed $data |
||||
* @return void |
||||
*/ |
||||
protected function get_accepted_type($types, $data) |
||||
{ |
||||
$types = (array) $types; |
||||
$types = array_map('strtoupper', $types); |
||||
|
||||
$headers = $this->input->header_array(); |
||||
$accept = array_flip($headers['accept']); |
||||
|
||||
$type_map = []; |
||||
$accepted = []; |
||||
$classes = []; |
||||
|
||||
foreach($types as $t) |
||||
{ |
||||
$type_class = "Sleepy\\Type\\{$t}"; |
||||
$classes[$type_class] = new $type_class($data); |
||||
$mime = $classes[$type_class]->get_mime(); |
||||
|
||||
$type_map[$mime] = $type_class; |
||||
} |
||||
|
||||
foreach($accept as $type => $q) |
||||
{ |
||||
if (array_key_exists($type, $type_map)) |
||||
{ |
||||
$accepted[$q] = $type; |
||||
} |
||||
} |
||||
|
||||
krsort($accepted); |
||||
|
||||
$class = $type_map[current($accepted)]; |
||||
$this->type_wrapper = $classes[$class]; |
||||
|
||||
// Make sure to set the content-type header |
||||
if (empty($this->headers)) |
||||
{ |
||||
$mime = $this->type_wrapper->get_mime(); |
||||
$this->set_header("Content-type: {$mime}"); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Set the applicable response headers |
||||
* |
||||
* @return void |
||||
*/ |
||||
protected function _output_headers() |
||||
{ |
||||
foreach($this->headers as $name => $val) |
||||
{ |
||||
if (is_numeric($name)) |
||||
{ |
||||
$output_header = $val; |
||||
} |
||||
else |
||||
{ |
||||
$output_header = implode(": ", [$name, $val]); |
||||
} |
||||
|
||||
@header($output_header); |
||||
} |
||||
} |
||||
} |
||||
// End of core/Output.php |
@ -0,0 +1,26 @@
|
||||
<?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 Router { |
||||
|
||||
protected $class; |
||||
protected $method; |
||||
|
||||
public function __construct($config = []) |
||||
{ |
||||
|
||||
} |
||||
|
||||
} |
||||
// End of core/Router.php |
@ -0,0 +1,78 @@
|
||||
<?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; |
||||
|
||||
use Sleepy\Exception\NotImplementedException; |
||||
|
||||
/** |
||||
* Abstract class with helpful functionality implementing type functionality |
||||
*/ |
||||
abstract class aType implements iType { |
||||
|
||||
/** |
||||
* The data in the current type wrapper |
||||
* |
||||
* @var mixed |
||||
*/ |
||||
protected $data; |
||||
|
||||
/** |
||||
* The mime type to output for the current type |
||||
* |
||||
* @var string |
||||
*/ |
||||
protected $mime; |
||||
|
||||
/** |
||||
* Create the type object with the specified data |
||||
* |
||||
* @param mixed $data |
||||
* @throws NotImplementedException |
||||
*/ |
||||
public function __construct($data = NULL) |
||||
{ |
||||
|
||||
if (empty($this->mime)) |
||||
{ |
||||
throw new NotImplementedException("Output types must have a mime type defined."); |
||||
} |
||||
|
||||
if ( ! is_null($data)) |
||||
{ |
||||
$this->data = $data; |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Returns the mime type needed |
||||
* for the current type |
||||
* |
||||
* @return string |
||||
*/ |
||||
public function get_mime() |
||||
{ |
||||
return $this->mime; |
||||
} |
||||
|
||||
/** |
||||
* Output the data as a string |
||||
* |
||||
* @return string |
||||
*/ |
||||
public function __toString() |
||||
{ |
||||
return $this->serialize($this->data); |
||||
} |
||||
|
||||
} |
||||
// End of core/aType.php |
@ -0,0 +1,37 @@
|
||||
<?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; |
||||
|
||||
/** |
||||
* Interface for output formats |
||||
*/ |
||||
interface iType { |
||||
|
||||
/** |
||||
* Convert the data to the output format |
||||
* |
||||
* @param mixed $data |
||||
* @return string |
||||
*/ |
||||
public function serialize($data); |
||||
|
||||
/** |
||||
* Convert the format to php data |
||||
* |
||||
* @param string $data_string |
||||
* @return mixed |
||||
*/ |
||||
public function unserialize($data_string); |
||||
|
||||
} |
||||
// End of core/iType.php |
@ -0,0 +1,33 @@
|
||||
<?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/exceptions |
||||
*/ |
||||
|
||||
namespace Sleepy\Exception; |
||||
|
||||
/** |
||||
* Exception for enforcing properties that need to be defined, |
||||
* or covering methods that are not yet implemented. |
||||
*/ |
||||
class NotImplementedException extends \LogicException { |
||||
|
||||
/** |
||||
* Have a default message for the exception |
||||
* |
||||
* @param string $message - The exception message |
||||
*/ |
||||
public function __construct($message = "Hmm...you stumbled onto something that is not implemented.") |
||||
{ |
||||
parent::__construct($message); |
||||
} |
||||
|
||||
}; |
||||
|
||||
// End of exceptions/NotImplementedException.php |
@ -0,0 +1,80 @@
|
||||
<?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/types |
||||
*/ |
||||
|
||||
namespace Sleepy\Type; |
||||
|
||||
use Sleepy\Core\aType; |
||||
|
||||
/** |
||||
* Class for HTML output |
||||
*/ |
||||
class HTML extends aType { |
||||
|
||||
/** |
||||
* The mime type for output |
||||
* |
||||
* @var string |
||||
*/ |
||||
protected $mime = 'text/html'; |
||||
|
||||
/** |
||||
* Convert the data into the output format |
||||
* |
||||
* @param mixed $data |
||||
* @return string |
||||
*/ |
||||
public function serialize($data = NULL) |
||||
{ |
||||
if ( ! is_null($data)) |
||||
{ |
||||
$this->data = $data; |
||||
} |
||||
else |
||||
{ |
||||
$data = $this->data; |
||||
} |
||||
|
||||
if (is_string($data)) return $data; |
||||
|
||||
// Lets use JSON as an output format if the value isn't scalar |
||||
return '<pre>' . json_encode($data, JSON_PRETTY_PRINT) . '</pre>'; |
||||
} |
||||
|
||||
/** |
||||
* Convert the encoded data to a native format |
||||
* |
||||
* @param string $data_string |
||||
* @return object |
||||
*/ |
||||
public function unserialize($data_string) |
||||
{ |
||||
return NULL; |
||||
} |
||||
|
||||
} |
||||
|
||||
// -------------------------------------------------------------------------- |
||||
// ! Helper function |
||||
// -------------------------------------------------------------------------- |
||||
|
||||
/** |
||||
* Function to simplify type instantiation |
||||
* |
||||
* @param mixed $data |
||||
* @return JSON |
||||
*/ |
||||
function HTML($data = NULL) |
||||
{ |
||||
return new JSON($data); |
||||
} |
||||
|
||||
// End of types/JSON.php |
@ -0,0 +1,77 @@
|
||||
<?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/types |
||||
*/ |
||||
|
||||
namespace Sleepy\Type; |
||||
|
||||
use Sleepy\Core\aType; |
||||
|
||||
/** |
||||
* Class for JSON output |
||||
*/ |
||||
class JSON extends aType { |
||||
|
||||
/** |
||||
* The mime type for output |
||||
* |
||||
* @var string |
||||
*/ |
||||
protected $mime = 'application/json'; |
||||
|
||||
/** |
||||
* Convert the data into the output format |
||||
* |
||||
* @param mixed $data |
||||
* @return string |
||||
*/ |
||||
public function serialize($data = NULL) |
||||
{ |
||||
if ( ! is_null($data)) |
||||
{ |
||||
$this->data = $data; |
||||
} |
||||
else |
||||
{ |
||||
$data = $this->data; |
||||
} |
||||
|
||||
return json_encode($data, JSON_PRETTY_PRINT); |
||||
} |
||||
|
||||
/** |
||||
* Convert the encoded data to a native format |
||||
* |
||||
* @param string $data_string |
||||
* @return object |
||||
*/ |
||||
public function unserialize($data_string) |
||||
{ |
||||
return json_decode($data_string); |
||||
} |
||||
|
||||
} |
||||
|
||||
// -------------------------------------------------------------------------- |
||||
// ! Helper function |
||||
// -------------------------------------------------------------------------- |
||||
|
||||
/** |
||||
* Function to simplify type instantiation |
||||
* |
||||
* @param mixed $data |
||||
* @return JSON |
||||
*/ |
||||
function JSON($data = NULL) |
||||
{ |
||||
return new JSON($data); |
||||
} |
||||
|
||||
// End of types/JSON.php |
@ -0,0 +1,64 @@
|
||||
<?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/types |
||||
*/ |
||||
|
||||
namespace Sleepy\types; |
||||
|
||||
use Sleepy\core\aType; |
||||
use Symfony\Component\Yaml as YML; |
||||
|
||||
/** |
||||
* Class for YAML output |
||||
*/ |
||||
class YAML extends aType { |
||||
|
||||
/** |
||||
* The mime type for output |
||||
* |
||||
* @var string |
||||
*/ |
||||
protected $mime = 'text/yaml'; |
||||
|
||||
/** |
||||
* Convert the data into the output format |
||||
* |
||||
* @param mixed $data |
||||
* @return string |
||||
*/ |
||||
public function serialize($data = null) |
||||
{ |
||||
if ( ! is_null($data)) |
||||
{ |
||||
$this->data = $data; |
||||
} |
||||
else |
||||
{ |
||||
$data = $this->data; |
||||
} |
||||
|
||||
// Yaml class doesn't support objects, cast them to arrays |
||||
$data = (array) $data; |
||||
|
||||
return YML::dump($data); |
||||
} |
||||
|
||||
/** |
||||
* Convert the encoded data to a native format |
||||
* |
||||
* @param string $data_string |
||||
* @return object |
||||
*/ |
||||
public function unserialize($data_string) |
||||
{ |
||||
return YML::parse($data_string, FALSE, TRUE); |
||||
} |
||||
} |
||||
// End of types/YAML.php |
@ -0,0 +1,28 @@
|
||||
<?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 |
||||
*/ |
||||
|
||||
$config = [ |
||||
|
||||
// -------------------------------------------------------------------------- |
||||
// The default format of data to be sent. Can be overwritten in controllers |
||||
// -------------------------------------------------------------------------- |
||||
'default_output_format' => 'json', |
||||
|
||||
// -------------------------------------------------------------------------- |
||||
// The default format of data recieved. Can be form data or one of the |
||||
// types defined in either the application/types or Sleepy/types folders |
||||
// -------------------------------------------------------------------------- |
||||
'default_input_format' => 'json', |
||||
|
||||
]; |
||||
|
||||
// End of config/general.php |
@ -0,0 +1,23 @@
|
||||
<?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 |
||||
*/ |
||||
|
||||
$routes = [ |
||||
|
||||
// -------------------------------------------------------------------------- |
||||
// The index controller, called if not specified in the routes or url |
||||
// -------------------------------------------------------------------------- |
||||
'default_controller' => 'index', |
||||
|
||||
|
||||
]; |
||||
|
||||
// End of config/routes.php |
@ -0,0 +1,14 @@
|
||||
{ |
||||
"authors": [{ |
||||
"name": "Timothy J. Warren", |
||||
"email": "tim@timshomepage.net", |
||||
"homepage": "https://timshomepage.net", |
||||
"role": "Developer" |
||||
}], |
||||
"require-dev": { |
||||
"phpunit/phpunit":"3.7.*" |
||||
}, |
||||
"require": { |
||||
"php": ">=5.4.0" |
||||
} |
||||
} |
@ -0,0 +1,55 @@
|
||||
<?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 |
||||
*/ |
||||
|
||||
namespace Sleepy; |
||||
|
||||
define('BASEPATH', __DIR__ . '/'); |
||||
|
||||
define('APPPATH', __DIR__ . '/application/'); |
||||
|
||||
\spl_autoload_register(function($item) { |
||||
$path_items = \explode('\\', \ltrim($item, '\\')); |
||||
|
||||
// If the namespace is a straight mapping to the class, just load it |
||||
$simple_path = \implode('/', $path_items); |
||||
$file = BASEPATH . "{$simple_path}.php"; |
||||
if (file_exists($file)) |
||||
{ |
||||
require_once($file); |
||||
return; |
||||
} |
||||
|
||||
// Check the application folder |
||||
$file = str_replace("Sleepy", 'application', $file); |
||||
if (file_exists($file)) |
||||
{ |
||||
require_once($file); |
||||
return; |
||||
} |
||||
}); |
||||
|
||||
$i = new \Sleepy\Core\Input(); |
||||
$o = new \Sleepy\Core\Output($i); |
||||
$browser = \get_browser(NULL); |
||||
$browser->browser_name_regex = utf8_encode($browser->browser_name_regex); |
||||
$o->set_data(['json','html'], [ |
||||
'$_SERVER' => $i->server(), |
||||
'$_GET' => $i->get(), |
||||
'$_POST' => $i->post(), |
||||
'$_ENV' => $i->env(), |
||||
'$_COOKIE' => $i->cookie(), |
||||
'browser' => $browser, |
||||
'raw headers' => $i->header(), |
||||
'parsed headers' => $i->header_array() |
||||
]); |
||||
|
||||
// End of index.php |
@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?> |
||||
<phpdoc> |
||||
<title>Sleepy</title> |
||||
<parser> |
||||
<target>docs</target> |
||||
</parser> |
||||
<transformer> |
||||
<target>docs</target> |
||||
</transformer> |
||||
<transformations> |
||||
<template name="responsive" /> |
||||
</transformations> |
||||
<files> |
||||
<directory>.</directory> |
||||
<directory>tests</directory> |
||||
<directory>application</directory> |
||||
<directory>vendor</directory> |
||||
<ignore>tests/*</ignore> |
||||
<ignore>application/*</ignore> |
||||
<ignore>vendor/*</ignore> |
||||
</files> |
||||
</phpdoc> |
@ -0,0 +1,32 @@
|
||||
<?php |
||||
|
||||
define('BASEPATH', realpath('../Sleepy/').'/'); |
||||
define('SPATH', realpath('../').'/'); |
||||
|
||||
/** |
||||
* Autoloader for Sleepy |
||||
*/ |
||||
\spl_autoload_register(function($item) { |
||||
$path_items = \explode('\\', \ltrim($item, '\\')); |
||||
|
||||
// If the namespace is a straight mapping to the class, just load it |
||||
$simple_path = \implode('/', $path_items); |
||||
$file = SPATH . "{$simple_path}.php"; |
||||
if (file_exists($file)) |
||||
{ |
||||
require_once($file); |
||||
return; |
||||
} |
||||
}); |
||||
|
||||
class Sleepy_TestCase extends PHPUnit_Framework_TestCase { |
||||
|
||||
|
||||
public function setUp() |
||||
{ |
||||
if ( ! isset($_SERVER['REQUEST_METHOD'])) |
||||
{ |
||||
$_SERVER['REQUEST_METHOD'] = 'GET'; |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,189 @@
|
||||
<?php |
||||
|
||||
require_once(BASEPATH . 'core/Input.php'); |
||||
|
||||
class InputTest extends Sleepy_TestCase { |
||||
|
||||
public function setUp() |
||||
{ |
||||
parent::setUp(); |
||||
|
||||
// Set some http headers as test items |
||||
$_SERVER['HTTP_USER_AGENT'] = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.57 Safari/537.36 OPR/18.0.1284.49'; |
||||
$_SERVER['HTTP_ACCEPT'] = 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8'; |
||||
$_SERVER['HTTP_COOKIE'] = 'thp_tcms_session_id=9h4nvk15tjjegbeg8uvejncc9blkd81m'; |
||||
$_SERVER['HTTP_HOST'] = 'www.example.com'; |
||||
$_SERVER['HTTP_FOO'] = 'foo,bar'; |
||||
|
||||
// Set test $_COOKIE array |
||||
$_COOKIE = [ |
||||
'thp_tcms_session_id' => '9h4nvk15tjjegbeg8uvejncc9blkd81m' |
||||
]; |
||||
|
||||
// Set test $_GET array |
||||
$_GET = [ |
||||
'foo' => 'bar', |
||||
'baz' => 'foobar' |
||||
]; |
||||
|
||||
// Set test $_POST array |
||||
$_POST = [ |
||||
'this' => 'that', |
||||
'tisket' => 'tasket' |
||||
]; |
||||
|
||||
$this->input = new Sleepy\core\Input(); |
||||
} |
||||
|
||||
public function dataTestHeader() |
||||
{ |
||||
return [ |
||||
'all' => [ |
||||
'index' => NULL, |
||||
'expected' => [ |
||||
'user_agent' => 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.57 Safari/537.36 OPR/18.0.1284.49', |
||||
'accept' => 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', |
||||
'cookie' => 'thp_tcms_session_id=9h4nvk15tjjegbeg8uvejncc9blkd81m', |
||||
'host' => 'www.example.com', |
||||
'foo' => 'foo,bar' |
||||
] |
||||
], |
||||
'index' => [ |
||||
'index' => 'cookie', |
||||
'expected' => 'thp_tcms_session_id=9h4nvk15tjjegbeg8uvejncc9blkd81m' |
||||
], |
||||
'invalid' => [ |
||||
'index' => 'boo', |
||||
'expected' => NULL |
||||
] |
||||
]; |
||||
} |
||||
|
||||
public function dataHeaderArray() |
||||
{ |
||||
return [ |
||||
'all' => [ |
||||
'index' => NULL, |
||||
'expected' => [ |
||||
'user_agent' => [ |
||||
'versions' => [ |
||||
'Mozilla' => '5.0', |
||||
'AppleWebKit' => '537.36', |
||||
'Chrome' => '31.0.1650.57', |
||||
'Safari' => '537.36', |
||||
'OPR' => '18.0.1284.49', |
||||
], |
||||
'os' => [ |
||||
'Macintosh', |
||||
'Intel Mac OS X 10_9_0', |
||||
], |
||||
'misc' => 'KHTML, like Gecko' |
||||
], |
||||
'accept' => [ |
||||
'text/html', |
||||
'application/xhtml+xml', |
||||
['application/xml', 'q=0.9'], |
||||
['*/*', 'q=0.8'] |
||||
], |
||||
'cookie' => [ |
||||
'thp_tcms_session_id' => '9h4nvk15tjjegbeg8uvejncc9blkd81m' |
||||
], |
||||
'host' => 'www.example.com', |
||||
'foo' => [ |
||||
'foo', |
||||
'bar' |
||||
] |
||||
] |
||||
], |
||||
'index' => [ |
||||
'index' => 'cookie', |
||||
'expected' => [ |
||||
'thp_tcms_session_id' => '9h4nvk15tjjegbeg8uvejncc9blkd81m' |
||||
] |
||||
], |
||||
'invalid' => [ |
||||
'index' => 'red', |
||||
'expected' => NULL |
||||
] |
||||
]; |
||||
} |
||||
|
||||
public function dataTestPost() |
||||
{ |
||||
return [ |
||||
'all' => [ |
||||
'index' => NULL, |
||||
'expected' => [ |
||||
'this' => 'that', |
||||
'tisket' => 'tasket' |
||||
] |
||||
], |
||||
'index' => [ |
||||
'index' => 'this', |
||||
'expected' => 'that' |
||||
], |
||||
'invalid' => [ |
||||
'index' => 'puppies', |
||||
'expected' => NULL |
||||
] |
||||
]; |
||||
} |
||||
|
||||
public function dataTestGet() |
||||
{ |
||||
return [ |
||||
'all' => [ |
||||
'index' => NULL, |
||||
'expected' => [ |
||||
'foo' => 'bar', |
||||
'baz' => 'foobar' |
||||
] |
||||
], |
||||
'index' => [ |
||||
'index' => 'foo', |
||||
'expected' => 'bar' |
||||
], |
||||
'invalid' => [ |
||||
'index' => 'applesauce', |
||||
'expected' => NULL |
||||
] |
||||
]; |
||||
} |
||||
|
||||
/** |
||||
* @dataProvider dataTestHeader |
||||
*/ |
||||
public function testHeader($index, $expected) |
||||
{ |
||||
$res = $this->input->header($index); |
||||
$this->assertEquals($expected, $res); |
||||
} |
||||
|
||||
/** |
||||
* @dataProvider dataHeaderArray |
||||
*/ |
||||
public function testHeaderArray($index, $expected) |
||||
{ |
||||
$result = $this->input->header_array($index); |
||||
$this->assertEquals($expected, $result); |
||||
} |
||||
|
||||
/** |
||||
* @dataProvider dataTestPost |
||||
*/ |
||||
public function testPost($index, $expected) |
||||
{ |
||||
$result = $this->input->post($index); |
||||
$this->assertEquals($expected, $result); |
||||
} |
||||
|
||||
/** |
||||
* @dataProvider dataTestGet |
||||
*/ |
||||
public function testGet($index, $expected) |
||||
{ |
||||
$result = $this->input->get($index); |
||||
$this->assertEquals($expected, $result); |
||||
} |
||||
} |
||||
// End of InputTest.php |
@ -0,0 +1,17 @@
|
||||
<?php |
||||
|
||||
require_once(BASEPATH . 'core/iType.php'); |
||||
require_once(BASEPATH . 'core/aType.php'); |
||||
|
||||
use Sleepy\core\iType; |
||||
use Sleepy\core\aType; |
||||
|
||||
class aTypeTest extends Sleepy_TestCase { |
||||
|
||||
public function testSanity() |
||||
{ |
||||
$this->assertEquals(['Sleepy\\core\\iType' => 'Sleepy\\core\\iType'], class_implements('Sleepy\\core\\aType')); |
||||
} |
||||
|
||||
} |
||||
// End of aTypeTest.php |
@ -0,0 +1,41 @@
|
||||
<?php |
||||
|
||||
require_once(BASEPATH . 'core/iType.php'); |
||||
require_once(BASEPATH . 'core/aType.php'); |
||||
require_once(BASEPATH . 'types/JSON.php'); |
||||
|
||||
use Sleepy\core; |
||||
use Sleepy\types\JSON; |
||||
use Sleepy\execeptions; |
||||
|
||||
class MockJSON extends JSON { |
||||
|
||||
protected $mime = ''; |
||||
} |
||||
|
||||
class JSONTest extends Sleepy_Testcase { |
||||
|
||||
public function setUp() { |
||||
$this->JSON = new JSON([]); |
||||
} |
||||
|
||||
public function testSanity() { |
||||
$this->assertTrue(is_a($this->JSON, 'Sleepy\\types\\JSON')); |
||||
$this->assertTrue(is_a($this->JSON, 'Sleepy\\core\\aType')); |
||||
$this->assertEquals(['Sleepy\\core\\iType' => 'Sleepy\\core\\iType'], class_implements('Sleepy\\types\\JSON')); |
||||
} |
||||
|
||||
public function testFunction() |
||||
|
||||
|
||||
public function testNIE() { |
||||
try { |
||||
$json = new MockJSON([]); |
||||
} |
||||
catch (Sleepy\exceptions\NotImplementedException $e) { |
||||
$this->assertTrue(TRUE); |
||||
} |
||||
} |
||||
|
||||
} |
||||
// End of JSONTest |
@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
|
||||
<phpunit |
||||
bootstrap="./Bootstrap.php" |
||||
colors="true" |
||||
convertNoticesToExceptions="true" |
||||
convertWarningsToExceptions="true" |
||||
stopOnError="false" |
||||
stopOnFailure="false" |
||||
stopOnIncomplete="false" |
||||
stopOnSkipped="false"> |
||||
<testsuites> |
||||
<testsuite name="Sleepy Core Test Suite"> |
||||
<directory suffix="Test.php">./core</directory> |
||||
<directory suffix="Test.php">./exceptions</directory> |
||||
<directory suffix="Test.php">./types</directory> |
||||
</testsuite> |
||||
</testsuites> |
||||
<filter> |
||||
<blacklist> |
||||
<directory suffix=".php">PEAR_INSTALL_DIR</directory> |
||||
<directory suffix=".php">PHP_LIBDIR</directory> |
||||
<directory suffix=".php">../vendor</directory> |
||||
</blacklist> |
||||
</filter> |
||||
</phpunit> |
Reference in new issue