From 94f46fc583dce4625ae6f4a5e62969daf494f044 Mon Sep 17 00:00:00 2001 From: Josh Sherman Date: Thu, 25 Sep 2014 20:55:11 -0400 Subject: [PATCH 1/4] Controller -> Router, Display -> Response Just gutting the brains of this thing. --- src/classes/Controller.php | 206 ------------------------------------- src/classes/Display.php | 96 ----------------- src/classes/Module.php | 166 ++---------------------------- src/classes/Response.php | 46 +++++++++ src/classes/Router.php | 190 ++++++++++++++++++++++++++++++++++ 5 files changed, 246 insertions(+), 458 deletions(-) delete mode 100644 src/classes/Controller.php delete mode 100644 src/classes/Display.php create mode 100644 src/classes/Response.php create mode 100644 src/classes/Router.php 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(); + } + } +} + From 69b14085b443da8256f0124304321485e1be59ab Mon Sep 17 00:00:00 2001 From: Josh Sherman Date: Thu, 25 Sep 2014 20:57:05 -0400 Subject: [PATCH 2/4] Module -> Resource Dropping those dated naming conventions --- src/classes/{Module.php => Resource.php} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename src/classes/{Module.php => Resource.php} (97%) diff --git a/src/classes/Module.php b/src/classes/Resource.php similarity index 97% rename from src/classes/Module.php rename to src/classes/Resource.php index b3c1917..4723468 100644 --- a/src/classes/Module.php +++ b/src/classes/Resource.php @@ -1,7 +1,7 @@ Date: Thu, 25 Sep 2014 21:33:43 -0400 Subject: [PATCH 3/4] Reworked error handling. --- src/classes/Router.php | 176 ++++++++++++++++------------------------- 1 file changed, 67 insertions(+), 109 deletions(-) diff --git a/src/classes/Router.php b/src/classes/Router.php index e2519a8..52c97db 100644 --- a/src/classes/Router.php +++ b/src/classes/Router.php @@ -38,18 +38,10 @@ class Router extends Object { parent::__construct(); + $response = new Response(); + 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); @@ -70,121 +62,87 @@ class Router extends Object } } + // Creates our class name array_unshift($nouns, $version); - $class = implode('_', $nouns); + // Creates our filename array_unshift($nouns, SITE_MODULE_PATH); - $filename = implode('/', $nouns) . '.php'; - if (file_exists($filename)) + if (!file_exists($filename)) { - if (class_exists($class)) + throw new Exception('Cannot find the file ' . $filename); + } + + if (!class_exists($class)) + { + throw new Exception('Cannot find the class ' . $class); + } + + $resource = new $class($uids); + + // Determines if we need to serve over HTTP or HTTPS + if ($resource->secure == false && isset($_SERVER['HTTPS']) && $_SERVER['HTTPS']) + { + throw new Exception('This resource expects HTTPS communication.'); + } + elseif ($resource->secure == true && (!isset($_SERVER['HTTPS']) || $_SERVER['HTTPS'] == false)) + { + throw new Exception('This resource expects HTTP communication.'); + } + + // 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)) + { + throw new Exception('Cannot find the method ' . $class . '::' . $method); + } + + // Starts a timer before the resource is executed + if ($profiler) + { + Profiler::timer('resource ' . $method); + } + + if ($resource->validate) + { + $validation_errors = $resource->__validate(); + + if ($validation_errors) { - $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'); + $response->status = 400; + $response->message = implode(' ', $validation_errors); } } - else + + if ($response->status == 200) { - throw new Exception('Missing file'); + $resource_return = $resource->$method(); + + if ($resource_return) + { + $response->response = $resource_return; + } + } + + // Stops the resource timer + if ($profiler) + { + Profiler::timer('resource ' . $method); } } catch (Exception $e) { - // @todo - exit('fuuuu'); - $output = $e->getMessage(); + $response->status = 500; + $response->message = $e->getMessage(); } + + $response->respond(); } } From 26e913a3d9e889df072ab35f07c8ffc5b10b8780 Mon Sep 17 00:00:00 2001 From: Josh Sherman Date: Thu, 25 Sep 2014 21:48:13 -0400 Subject: [PATCH 4/4] Dropped Dynamic Class Pretty sure we don't need it anymore. Could come back up in the future when an API is returning URIs for static assets and they are being cached and it's not being flushed correctly. --- src/classes/Dynamic.php | 99 ----------------------------------------- 1 file changed, 99 deletions(-) delete mode 100644 src/classes/Dynamic.php diff --git a/src/classes/Dynamic.php b/src/classes/Dynamic.php deleted file mode 100644 index 2880abb..0000000 --- a/src/classes/Dynamic.php +++ /dev/null @@ -1,99 +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 - */ - -/** - * Dynamic Class - * - * Handles generating links to static content that are a timestamp injected as - * to avoid hard caching. Also minifies content where applicable. - * - * Note: you will want to add a mod_rewrite line to your .htaccess to support - * the routing to the filenames with the timestamp injected: - * - * RewriteRule ^(.+)\.([\d]+)\.(css|js|gif|png|jpg|jpeg)$ /$1.$3 [NC,QSA] - */ -class Dynamic extends Object -{ - /** - * Generate Reference - * - * Appends a dynamic piece of information to the passed reference in the - * form of a UNIX timestamp added to the query string. - * - * @param string $reference URI reference of the file - * @param string $failover URI reference to use if the reference can't be found - * @return string URI reference reference with dynamic content - */ - public function reference($reference, $failover = false) - { - // Checks if the URI reference is absolute, and not relative - if (substr($reference, 0, 1) == '/') - { - $query_string = ''; - - // Checks for ? and extracts query string - if (strstr($reference, '?')) - { - list($reference, $query_string) = explode('?', $reference); - } - - // Adds the dot so the file functions can find the file - $file = '.' . $reference; - - if (file_exists($file)) - { - // Replaces the extension with time().extension - $parts = explode('.', $reference); - - if (count($parts) == 1) - { - throw new Exception('Filename must have an extension (e.g. /path/to/file.png)'); - } - else - { - end($parts); - $parts[key($parts)] = filemtime($file) . '.' . current($parts); - $reference = implode('.', $parts); - } - - // Adds the query string back - if ($query_string != '') - { - $reference .= '?' . $query_string; - } - } - else - { - if ($failover != false) - { - $reference = $failover; - } - else - { - throw new Exception('Supplied reference does not exist (' . $reference . ')'); - } - } - } - else - { - throw new Exception('Reference value must be absolute (e.g. /path/to/file.png)'); - } - - return $reference; - } -} -