diff --git a/src/classes/Controller.php b/src/classes/Controller.php deleted file mode 100644 index 015df27..0000000 --- a/src/classes/Controller.php +++ /dev/null @@ -1,206 +0,0 @@ - - * @copyright Copyright 2007-2014, Josh Sherman - * @license http://www.opensource.org/licenses/mit-license.html - * @package PICKLES - * @link https://github.com/joshtronic/pickles - */ - -/** - * Controller Class - * - * The heavy lifter of PICKLES, makes the calls to get the session and - * configuration loaded. Loads modules, serves up user authentication when the - * module asks for it, and loads the viewer that the module requested. Default - * values are present to make things easier on the user. - * - * @usage new Controller(); - */ -class Controller extends Object -{ - /** - * Constructor - * - * To save a few keystrokes, the Controller is executed as part of the - * constructor instead of via a method. You either want the Controller or - * you don't. - */ - public function __construct() - { - parent::__construct(); - - try - { - // Catches requests that aren't lowercase - $lowercase_request = strtolower($_REQUEST['request']); - - if ($_REQUEST['request'] != $lowercase_request) - { - // @todo Rework the Browser class to handle the 301 (perhaps redirect301()) to not break other code - header('Location: ' . substr_replace($_SERVER['REQUEST_URI'], $lowercase_request, 1, strlen($lowercase_request)), true, 301); - throw new Exception(); - } - - // Grabs the requested page - $request = $_REQUEST['request']; - - // Loads the module's information - $module_class = strtr($request, '/', '_'); - $module_filename = SITE_MODULE_PATH . $request . '.php'; - $module_exists = file_exists($module_filename); - - // Attempts to instantiate the requested module - if ($module_exists) - { - if (class_exists($module_class)) - { - $module = new $module_class; - } - } - - // No module instantiated, load up a generic Module - if (!isset($module)) - { - $module = new Module(); - } - - // Determines if we need to serve over HTTP or HTTPS - if ($module->secure == false && isset($_SERVER['HTTPS']) && $_SERVER['HTTPS']) - { - header('Location: http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'], true, 301); - throw new Exception(); - } - elseif ($module->secure == true && (!isset($_SERVER['HTTPS']) || $_SERVER['HTTPS'] == false)) - { - header('Location: https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'], true, 301); - throw new Exception(); - } - - // Gets the profiler status - $profiler = $this->config->pickles['profiler']; - $profiler = $profiler === true || stripos($profiler, 'timers') !== false; - - $default_method = '__default'; - $role_method = null; - - // Attempts to execute the default method - // @todo Seems a bit redundant, refactor - if ($default_method == $role_method || method_exists($module, $default_method)) - { - // Starts a timer before the module is executed - if ($profiler) - { - Profiler::timer('module ' . $default_method); - } - - $valid_request = false; - $error_message = 'An unexpected error has occurred.'; - - // Determines if the request method is valid for this request - if ($module->method) - { - if (!is_array($module->method)) - { - $module->method = [$module->method]; - } - - foreach ($module->method as $method) - { - if ($_SERVER['REQUEST_METHOD'] == $method) - { - $valid_request = true; - break; - } - } - - if (!$valid_request) - { - // @todo Should probably utilize that AJAX flag to determine the type of return - $error_message = 'There was a problem with your request method.'; - } - } - else - { - $valid_request = true; - } - - $valid_form_input = true; - - if ($valid_request && $module->validate) - { - $validation_errors = $module->__validate(); - - if ($validation_errors) - { - $error_message = implode(' ', $validation_errors); - $valid_form_input = false; - } - } - - /** - * Note to Self: When building in caching will need to let the - * module know to use the cache, either passing in a variable - * or setting it on the object - */ - if ($valid_request && $valid_form_input) - { - $module_return = $module->$default_method(); - - if (!is_array($module_return)) - { - $module_return = $module->response; - } - else - { - $module_return = array_merge($module_return, $module->response); - } - } - - // Stops the module timer - if ($profiler) - { - Profiler::timer('module ' . $default_method); - } - - $display = new Display($module); - } - - // Starts a timer for the display rendering - if ($profiler) - { - Profiler::timer('display render'); - } - - // Renders the content - $output = $display->render(); - - // Stops the display timer - if ($profiler) - { - Profiler::timer('display render'); - } - } - catch (Exception $e) - { - $output = $e->getMessage(); - } - - echo $output; - - // Display the Profiler's report if the stars are aligned - if ($this->config->pickles['profiler']) - { - Profiler::report(); - } - } -} - diff --git a/src/classes/Display.php b/src/classes/Display.php deleted file mode 100644 index be82a21..0000000 --- a/src/classes/Display.php +++ /dev/null @@ -1,96 +0,0 @@ - - * @copyright Copyright 2007-2014, Josh Sherman - * @license http://www.opensource.org/licenses/mit-license.html - * @package PICKLES - * @link https://github.com/joshtronic/pickles - */ - -/** - * Display Class - * - * If you can see it then it probably happened in here. - */ -class Display extends Object -{ - /** - * Module - * - * This is the module we are attempting to display output for. - */ - public $module = null; - - public function __construct($module = null) - { - if ($module && $module instanceof Module) - { - $this->module = $module; - } - } - - public function render() - { - try - { - // Starts up the buffer so we can capture it - ob_start(); - - if (!is_array($this->module->response)) - { - $this->module->response = [$this->module->response]; - } - - // Checks for the PHPSESSID in the query string - if (stripos($_SERVER['REQUEST_URI'], '?PHPSESSID=') === false) - { - // XHTML compliancy stuff - // @todo Wonder if this could be yanked now that we're in HTML5 land - ini_set('arg_separator.output', '&'); - ini_set('url_rewriter.tags', 'a=href,area=href,frame=src,input=src,fieldset='); - - header('Content-type: text/html; charset=UTF-8'); - } - else - { - // Redirect so Google knows to index the page without the session ID - list($request_uri, $phpsessid) = explode('?PHPSESSID=', $_SERVER['REQUEST_URI'], 2); - header('HTTP/1.1 301 Moved Permanently'); - header('Location: ' . $request_uri); - - throw new Exception('Requested URI contains PHPSESSID, redirecting.'); - } - - $response = [ - 'meta' => [ - 'status' => $this->module->status, - 'message' => $this->module->message, - ], - ]; - - if ($this->module->response) - { - $response['response'] = $this->module->response; - } - - header('Content-type: application/json'); - $pretty = isset($_REQUEST['pretty']) ? JSON_PRETTY_PRINT : false; - echo json_encode($response, $pretty); - - return ob_get_clean(); - } - catch (Exception $e) - { - return $e->getMessage(); - } - } -} - diff --git a/src/classes/Module.php b/src/classes/Module.php index 78ec6af..b3c1917 100644 --- a/src/classes/Module.php +++ b/src/classes/Module.php @@ -27,41 +27,6 @@ */ abstract class Module extends Object { - /** - * Page Title - * - * @var string, null by default - * @todo Abandon for $this->meta - */ - public $title = null; - - /** - * Meta Description - * - * @var string, null by default - * @todo Abandon for $this->meta - */ - public $description = null; - - /** - * Meta Keywords (comma separated) - * - * @var string, null by default - * @todo Abandon for $this->meta - */ - public $keywords = null; - - /** - * Meta Data - * - * @var array - */ - public $meta = [ - 'title' => '', - 'description' => '', - 'keywords' => '', - ]; - /** * Secure * @@ -72,20 +37,13 @@ abstract class Module extends Object public $secure = false; /** - * Security Settings + * Filter * - * @var boolean, null by default + * Variables to filter. + * + * @var array */ - public $security = null; - - /** - * Method - * - * Request methods that are allowed to access the module. - * - * @var string or array, null by default - */ - public $method = null; + public $filter = []; /** * Validate @@ -96,46 +54,13 @@ abstract class Module extends Object */ public $validate = []; - /** - * Template - * - * This is the parent template that will be loaded if you are using the - * 'template' return type in the Display class. Parent templates are found - * in ./templates/__shared and use the phtml extension. - * - * @var string, 'index' by default - */ - public $template = 'index'; - - /** - * Response - * - * Array of data that will be rendered as part of the display. This is - * somewhat of a one way trip as you cannot get the variable unless you - * reference the response array explicitly, $this->response['variable'] - * - * @var array - */ - public $response = []; - - /** - * Output - * - * What should the class render as output? This can be a string or an array - * containing either 'json', 'rss', 'template' or 'xml'. Default is to use - * templates and if the template is not present, fall back to JSON. - * - * @var mixed string or array - */ - public $output = ['template', 'json']; - // @todo public $status = 200; public $message = 'OK'; - public $echo = false; - public $limit = false; - public $offset = false; - public $errors = []; + public $echo = false; + public $limit = false; + public $offset = false; + public $errors = []; // @todo if $status != 200 && $message == 'OK' ... @@ -146,81 +71,10 @@ abstract class Module extends Object * variable to tell it to automatically run the __default() method. This is * typically used when a module is called outside of the scope of the * controller (the registration page calls the login page in this manner. - * - * @param boolean $autorun optional flag to autorun __default() - @ @param boolean $filter optional flag to disable autorun filtering - * @param boolean $valiate optional flag to disable autorun validation */ - public function __construct($autorun = false, $filter = true, $validate = true) + public function __construct() { parent::__construct(['cache', 'db']); - - if ($autorun) - { - if ($filter) - { - // @todo - //$this->__filter(); - } - - if ($validate) - { - $errors = $this->__validate(); - - if (!$errors) - { - // @todo Fatal error perhaps? - exit('Errors encountered, this is a @todo for form validation when calling modules from inside of modules'); - } - } - - $this->__default(); - } - } - - /** - * Default "Magic" Method - * - * The __default() method is where you want to place any code that needs to - * be executed at runtime. - * - * @abstract - */ - abstract public function __default(); - - /** - * Magic Setter Method - * - * Places undefined properties into the response array as part of the - * module's payload. - * - * @param string $variable name of the variable to be set - * @param mixed $value value of the variable to be set - */ - public function __set($variable, $value) - { - $this->response[$variable] = $value; - } - - /** - * Magic Getter Method - * - * Any variables not defined in this class are set in the response array - * and default to false if not defined there. - * - * @param string $name name of the variable requested - * @return mixed value of the variable or boolean false - */ - public function __get($name) - { - if (!isset($this->response[$name])) - { - return false; - } - else - { - return $this->response[$name]; - } } /** diff --git a/src/classes/Response.php b/src/classes/Response.php new file mode 100644 index 0000000..cea8f56 --- /dev/null +++ b/src/classes/Response.php @@ -0,0 +1,46 @@ + $this->status, + 'message' => $this->message, + ]; + + foreach (['echo', 'limit', 'offset', 'errors'] as $variable) + { + if ($this->$variable) + { + $meta[$variable] = $this->$variable; + } + } + + $response = ['meta' => $meta]; + + foreach (['response', 'profiler'] as $variable) + { + if ($this->$variable) + { + $response[$variable] = $this->$variable; + } + } + + $pretty = isset($_REQUEST['pretty']) ? JSON_PRETTY_PRINT : false; + + exit(json_encode($response, $pretty)); + } +} + diff --git a/src/classes/Router.php b/src/classes/Router.php new file mode 100644 index 0000000..e2519a8 --- /dev/null +++ b/src/classes/Router.php @@ -0,0 +1,190 @@ + + * @copyright Copyright 2007-2014, Josh Sherman + * @license http://www.opensource.org/licenses/mit-license.html + * @package PICKLES + * @link https://github.com/joshtronic/pickles + */ + +/** + * Router Class + * + * The heavy lifter of PICKLES, makes the calls to get the session and + * configuration loaded. Loads modules, serves up user authentication when the + * module asks for it, and loads the viewer that the module requested. Default + * values are present to make things easier on the user. + * + * @usage new Router(); + */ +class Router extends Object +{ + /** + * Constructor + * + * To save a few keystrokes, the Controller is executed as part of the + * constructor instead of via a method. You either want the Controller or + * you don't. + */ + public function __construct() + { + parent::__construct(); + + try + { + // Catches requests that aren't lowercase + $lowercase_request = strtolower($_REQUEST['request']); + + if ($_REQUEST['request'] != $lowercase_request) + { + // @todo Rework the Browser class to handle the 301 (perhaps redirect301()) to not break other code + header('Location: ' . substr_replace($_SERVER['REQUEST_URI'], $lowercase_request, 1, strlen($lowercase_request)), true, 301); + throw new Exception(); + } + + // Grabs the requested page + $request = $_REQUEST['request']; + $components = explode('/', $request); + $version = array_shift($components); + $nouns = []; + $uids = []; + + // Loops through the components to determine nouns and IDs + foreach ($components as $index => $component) + { + if ($index % 2) + { + $uids[end($nouns)] = $component; + } + else + { + $nouns[] = $component; + } + } + + array_unshift($nouns, $version); + + $class = implode('_', $nouns); + + array_unshift($nouns, SITE_MODULE_PATH); + + $filename = implode('/', $nouns) . '.php'; + + if (file_exists($filename)) + { + if (class_exists($class)) + { + $resource = new $class($uids); + + // Determines if we need to serve over HTTP or HTTPS + if ($resource->secure == false && isset($_SERVER['HTTPS']) && $_SERVER['HTTPS']) + { + header('Location: http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'], true, 301); + throw new Exception(); + } + elseif ($resource->secure == true && (!isset($_SERVER['HTTPS']) || $_SERVER['HTTPS'] == false)) + { + header('Location: https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'], true, 301); + throw new Exception(); + } + + // Checks for the PHPSESSID in the query string + if (stripos($_SERVER['REQUEST_URI'], '?PHPSESSID=') === false) + { + // XHTML compliancy stuff + // @todo Wonder if this could be yanked now that we're in HTML5 land + ini_set('arg_separator.output', '&'); + ini_set('url_rewriter.tags', 'a=href,area=href,frame=src,input=src,fieldset='); + + // @todo Will want to generate the header based on if we're pushing documentation or API + header('Content-type: text/html; charset=UTF-8'); + // header('Content-type: application/json'); + //header('Content-type: application/json; charset=UTF-8'); + } + else + { + // Redirect so Google knows to index the page without the session ID + list($request_uri, $phpsessid) = explode('?PHPSESSID=', $_SERVER['REQUEST_URI'], 2); + header('HTTP/1.1 301 Moved Permanently'); + header('Location: ' . $request_uri); + + throw new Exception('Requested URI contains PHPSESSID, redirecting.'); + } + + // Gets the profiler status + $profiler = $this->config->pickles['profiler']; + $profiler = $profiler === true || stripos($profiler, 'timers') !== false; + + $method = strtolower($_SERVER['REQUEST_METHOD']); + + if (method_exists($resource, $method)) + { + // Starts a timer before the resource is executed + if ($profiler) + { + Profiler::timer('resource ' . $method); + } + + $response = new Response(); + + if ($resource->validate) + { + $validation_errors = $resource->__validate(); + + if ($validation_errors) + { + $response->status = 400; + $response->message = implode(' ', $validation_errors); + } + } + + if ($response->status == 200) + { + $resource_return = $resource->$method(); + + if ($resource_return) + { + $response->response = $resource_return; + } + } + + // Stops the resource timer + if ($profiler) + { + Profiler::timer('resource ' . $method); + } + + $response->respond(); + } + else + { + throw new Exception('Missing method'); + } + } + else + { + throw new Exception('Missing class'); + } + } + else + { + throw new Exception('Missing file'); + } + } + catch (Exception $e) + { + // @todo + exit('fuuuu'); + $output = $e->getMessage(); + } + } +} +