From 54471c361bccbc518f171a63a4bc32497922c6a3 Mon Sep 17 00:00:00 2001 From: Joshua Sherman Date: Mon, 30 Dec 2013 00:38:13 -0500 Subject: [PATCH] So much refactoring... --- classes/Config.php | 18 +- classes/Controller.php | 589 ++++++++++++++++--------------- classes/Dynamic.php | 4 +- classes/Error.php | 104 ------ classes/Profiler.php | 6 +- pickles.php | 351 ++++-------------- tests/bootstrap.php | 17 +- tests/classes/ControllerTest.php | 176 +++++++++ tests/classes/ConvertTest.php | 4 +- 9 files changed, 562 insertions(+), 707 deletions(-) delete mode 100644 classes/Error.php create mode 100644 tests/classes/ControllerTest.php diff --git a/classes/Config.php b/classes/Config.php index 88e51f6..694e7a2 100644 --- a/classes/Config.php +++ b/classes/Config.php @@ -32,10 +32,9 @@ class Config extends Object /** * Config data * - * @access private - * @var array + * @var array */ - private $data = array(); + public $data = array(); /** * Constructor @@ -236,19 +235,6 @@ class Config extends Object return parent::getInstance($class); } - /** - * Magic Setter Method - * - * Prohibits the direct modification of module variables. - * - * @param string $name name of the variable to be set - * @param mixed $value value of the variable to be set - */ - public function __set($name, $value) - { - throw new Exception('Cannot set config variables directly', E_USER_ERROR); - } - /** * Magic Getter Method * diff --git a/classes/Controller.php b/classes/Controller.php index e47ef40..df3cccb 100644 --- a/classes/Controller.php +++ b/classes/Controller.php @@ -39,341 +39,346 @@ class Controller extends Object parent::__construct(); // Generate a generic "site down" message if the site is set to be disabled - // @todo Clean this up to be just a single sanity check - if (isset($this->config->pickles['disabled']) && $this->config->pickles['disabled'] == true) + try { - Error::fatal($_SERVER['SERVER_NAME'] . ' is currently
down for maintenance'); - } - - // Checks for attributes passed in the URI - if (strstr($_REQUEST['request'], ':')) - { - $parts = explode('/', $_REQUEST['request']); - $_REQUEST['request'] = ''; - - foreach ($parts as $part) + // @todo Clean this up to be just a single sanity check + if (isset($this->config->pickles['disabled']) && $this->config->pickles['disabled'] == true) { - if (strstr($part, ':')) + // @todo Add support for custom templates + throw new Exception(' +

Down for Maintenance

+

' . $_SERVER['SERVER_NAME'] . ' is currently down for maintenance. Please check back in a few minutes.

+

Additionally, a custom maintenance template was not found.

+
+ Powered by PICKLES + '); + } + + // Checks for attributes passed in the URI + if (strstr($_REQUEST['request'], ':')) + { + $parts = explode('/', $_REQUEST['request']); + $_REQUEST['request'] = ''; + + foreach ($parts as $part) { - list($variable, $value) = explode(':', $part); - Browser::set($variable, $value); + if (strstr($part, ':')) + { + list($variable, $value) = explode(':', $part); + Browser::set($variable, $value); + } + else + { + $_REQUEST['request'] .= ($_REQUEST['request'] ? '/' : '') . $part; + } + } + } + + // 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(); + } + + // Validates security level + if ($module->security) + { + $is_authenticated = false; + + if (is_array($module->security)) + { + $module_security = $module->security; + $security_check_class = 'isLevel'; + + // Checks the type and validates it + if (isset($module_security['type'])) + { + $security_check_type = strtoupper($module_security['type']); + + if (in_array($security_check_type, ['IS', 'HAS', 'BETWEEN'])) + { + $security_check_class = $security_check_type; + } + + unset($module_security['type']); + } + + $module_security_levels = []; + + // If there's a level(s) key use it + foreach (['level', 'levels'] as $security_level_key) + { + if (isset($module_security[$security_level_key])) + { + if (is_array($module_security[$security_level_key])) + { + array_merge($module_security_levels, $module_security[$security_level_key]); + } + else + { + $module_security_levels[] = $module_security[$security_level_key]; + } + + unset($module_security[$security_level_key]); + } + } + + // Assume everything left in the array is a level and add it to the array + array_merge($module_security_levels, $module_security); + $security_level_count = count($module_security_levels); + + switch ($security_check_class) + { + // @todo Thinking of removing this? + case 'BETWEEN': + if ($security_level_count >= 2) + { + $is_authenticated = Security::betweenLevel($module_security_levels[0], array_pop($module_security_levels)); + } + break; + + case 'HAS': + if ($security_level_count) + { + $is_authenticated = Security::hasLevel($module_security_levels); + } + break; + + case 'IS': + if ($security_level_count) + { + $is_authenticated = Security::isLevel($module_security_levels); + } + break; + } } else { - $_REQUEST['request'] .= ($_REQUEST['request'] ? '/' : '') . $part; + $is_authenticated = Security::isLevel($module->security); } - } - } - // 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); - exit; - } - - // 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) - { - // @todo Is this redundant because of our autoloader? - require_once $module_filename; - - 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']) - { - Browser::redirect('http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']); - } - elseif ($module->secure == true && (!isset($_SERVER['HTTPS']) || $_SERVER['HTTPS'] == false)) - { - Browser::redirect('https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']); - } - - // Validates security level - if ($module->security) - { - $is_authenticated = false; - - if (is_array($module->security)) - { - $module_security = $module->security; - $security_check_class = 'isLevel'; - - // Checks the type and validates it - if (isset($module_security['type'])) + if (!$is_authenticated) { - $security_check_type = strtoupper($module_security['type']); - - if (in_array($security_check_type, ['IS', 'HAS', 'BETWEEN'])) + if ($_SERVER['REQUEST_METHOD'] == 'POST') { - $security_check_class = $security_check_type; + // @todo Perhaps I could force a logout / redirect to the login page + exit('{"status": "error", "message": "You are not properly authenticated, try logging out and back in."}'); } - - unset($module_security['type']); - } - - $module_security_levels = []; - - // If there's a level(s) key use it - foreach (['level', 'levels'] as $security_level_key) - { - if (isset($module_security[$security_level_key])) + else { - if (is_array($module_security[$security_level_key])) - { - array_merge($module_security_levels, $module_security[$security_level_key]); - } - else - { - $module_security_levels[] = $module_security[$security_level_key]; - } + // Sets variable for the destination + $_SESSION['__pickles']['login']['destination'] = $_REQUEST['request'] ? $_REQUEST['request'] : '/'; - unset($module_security[$security_level_key]); + // Redirect to login page + Browser::redirect('/login'); } } + } - // Assume everything left in the array is a level and add it to the array - array_merge($module_security_levels, $module_security); - $security_level_count = count($module_security_levels); + // Gets the profiler status + $profiler = $this->config->pickles['profiler']; + $profiler = $profiler === true || stripos($profiler, 'timers') !== false; - switch ($security_check_class) + $default_method = '__default'; + $role_method = null; + + if (isset($_SESSION['__pickles']['security']['role']) && !String::isEmpty($_SESSION['__pickles']['security']['role'])) + { + $role_method = '__default_' . $_SESSION['__pickles']['security']['role']; + + if (method_exists($module, $role_method)) { - // @todo Thinking of removing this? - case 'BETWEEN': - if ($security_level_count >= 2) - { - $is_authenticated = Security::betweenLevel($module_security_levels[0], array_pop($module_security_levels)); - } - break; - - case 'HAS': - if ($security_level_count) - { - $is_authenticated = Security::hasLevel($module_security_levels); - } - break; - - case 'IS': - if ($security_level_count) - { - $is_authenticated = Security::isLevel($module_security_levels); - } - break; + $default_method = $role_method; } } - else - { - $is_authenticated = Security::isLevel($module->security); - } - if (!$is_authenticated) + // Attempts to execute the default method + // @todo Seems a bit redundant, refactor + if ($default_method == $role_method || method_exists($module, $default_method)) { - if ($_SERVER['REQUEST_METHOD'] == 'POST') + // Starts a timer before the module is executed + if ($profiler) { - // @todo Perhaps I could force a logout / redirect to the login page - exit('{"status": "error", "message": "You are not properly authenticated, try logging out and back in."}'); + 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) + { + $error_message = 'There was a problem with your request method.'; + } } else { - // Sets variable for the destination - $_SESSION['__pickles']['login']['destination'] = $_REQUEST['request'] ? $_REQUEST['request'] : '/'; - - // Redirect to login page - Browser::redirect('/login'); + $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->return_data; + } + else + { + $module_return = array_merge($module_return, $module->return_data); + } + } + + // Stops the module timer + if ($profiler) + { + Profiler::timer('module ' . $default_method); + } + + // @todo Set this in the module and use $module->return and rename module->return to module->data? + $module->return = ['template', 'json']; + + // Checks if we have any templates + $parent_template = $module->template; + $template_exists = $this->validateTemplates($module, $parent_template); + + // No templates? 404 that shit + if (!$module_exists && !$template_exists) + { + Browser::status(404); + $_REQUEST['request'] = '__shared/404'; + + if (!$this->validateTemplates($module, $parent_template)) + { + throw new Exception(' +

Not Found

+

The requested URL /' . $request . ' was not found on this server.

+

Additionally, a custom error template was not found.

+
+ Powered by PICKLES + '); + } + } + + $display = new Display(); + $display->return = $module->return; + $display->templates = $module->template; + $display->module = isset($module_return) ? $module_return : ['status' => 'error', 'message' => $error_message]; + + // @todo Check for $module->meta variable first, then remove entirely when sites are updated + $display->meta = [ + 'title' => $module->title, + 'description' => $module->description, + 'keywords' => $module->keywords + ]; } - } - // Gets the profiler status - $profiler = $this->config->pickles['profiler']; - $profiler = $profiler === true || stripos($profiler, 'timers') !== false; - - $default_method = '__default'; - $role_method = null; - - if (isset($_SESSION['__pickles']['security']['role']) && !String::isEmpty($_SESSION['__pickles']['security']['role'])) - { - $role_method = '__default_' . $_SESSION['__pickles']['security']['role']; - - if (method_exists($module, $role_method)) - { - $default_method = $role_method; - } - } - - // Attempts to execute the default method - if ($default_method == $role_method || method_exists($module, $default_method)) - { - // Starts a timer before the module is executed + // Starts a timer for the display rendering if ($profiler) { - Profiler::timer('module ' . $default_method); + Profiler::timer('display render'); } - $valid_request = false; - $error_message = 'An unexpected error has occurred.'; + // Renders the content + $output = $display->render(); - // Determines if the request method is valid for this request - if ($module->method) - { - if (!is_array($module->method)) - { - $module->method = [$module->method]; - } - - $request_method = $_SERVER['REQUEST_METHOD']; - - foreach ($module->method as $method) - { - if ($request_method == strtoupper($method)) - { - $valid_request = true; - break; - } - } - - if (!$valid_request) - { - $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->return_data; - } - else - { - $module_return = array_merge($module_return, $module->return_data); - } - } - - // Stops the module timer + // Stops the display timer if ($profiler) { - Profiler::timer('module ' . $default_method); + Profiler::timer('display render'); } + } + catch (Exception $e) + { + $output = $e->getMessage(); + } + finally + { + echo $output; - // @todo Set this in the module and use $module->return and rename module->return to module->data? - $module->return = ['template', 'json']; - - // Checks if we have any templates - $parent_template = $module->template; - $template_exists = $this->validateTemplates($module, $parent_template); - - // No templates? 404 that shit - if (!$module_exists && !$template_exists) + // Display the Profiler's report if the stars are aligned + if ($this->config->pickles['profiler']) { - Browser::status(404); - $_REQUEST['request'] = '__shared/404'; - - if (!$this->validateTemplates($module, $parent_template)) - { - exit(' -

Not Found

-

The requested URL /' . $request . ' was not found on this server.

-

Additionally, a custom error template was not found.

-
- Powered by PICKLES - '); - } + Profiler::report(); } - - $display = new Display(); - $display->return = $module->return; - $display->templates = $module->template; - $display->module = isset($module_return) ? $module_return : ['status' => 'error', 'message' => $error_message]; - - // @todo Check for $module->meta variable first, then remove entirely when sites are updated - $display->meta = [ - 'title' => $module->title, - 'description' => $module->description, - 'keywords' => $module->keywords - ]; - } - - // Starts a timer for the display rendering - if ($profiler) - { - Profiler::timer('display render'); - } - - // Renders the content - $output = $display->render(); - - echo $output; - - // Stops the display timer - if ($profiler) - { - Profiler::timer('display render'); - } - } - - /** - * Destructor - * - * Dumps out the Profiler's report if applicable. - */ - public function __destruct() - { - parent::__destruct(); - - // Display the Profiler's report if the stars are aligned - if ($this->config->pickles['profiler']) - { - Profiler::report(); } } + // @todo Document me private function validateTemplates(&$module, $parent_template) { $templates = [ diff --git a/classes/Dynamic.php b/classes/Dynamic.php index 15416a7..5804e15 100644 --- a/classes/Dynamic.php +++ b/classes/Dynamic.php @@ -265,7 +265,9 @@ class Dynamic extends Object && extension_loaded('curl') && $this->config->pickles['minify'] === true) { - exec('java -jar ' . PICKLES_PATH . 'vendors/google/closure-compiler/compiler.jar --js=' . $original_filename . ' --compilation_level=' . ($level . '_' . ($level == 'WHITESPACE' ? 'ONLY' : 'OPTIMIZATIONS')) . ' --js_output_file=' . $minified_filename); + $config = Config::getInstance(); + + exec('java -jar ' . $config->pickles['path'] . 'vendors/google/closure-compiler/compiler.jar --js=' . $original_filename . ' --compilation_level=' . ($level . '_' . ($level == 'WHITESPACE' ? 'ONLY' : 'OPTIMIZATIONS')) . ' --js_output_file=' . $minified_filename); $reference = $minified_reference; } diff --git a/classes/Error.php b/classes/Error.php deleted file mode 100644 index 2ca12c4..0000000 --- a/classes/Error.php +++ /dev/null @@ -1,104 +0,0 @@ - - * @copyright Copyright 2007-2013, Joshua Sherman - * @license http://www.opensource.org/licenses/mit-license.html - * @package PICKLES - * @link https://github.com/joshtronic/pickles - */ - -/** - * Error Class - * - * Standardized error reporting, mostly used to display fatal errors. - */ -class Error -{ - /** - * Fatal Error - * - * Displays a friendly error to the user via HTML, logs it then exits. - * - * @static - * @param string $message the message to be displayed to the user - */ - public static function fatal($message) - { - $config = Config::getInstance(); - - if ($config->pickles['logging'] === true) - { - if (Log::error($message) == false) - { - $message .= '

This error message could not be logged as the log path or log file is not writable'; - } - } - ?> - - - - <?php echo $_SERVER['SERVER_NAME']; ?> - error - - - -

-
- Powered by PICKLES - - - diff --git a/classes/Profiler.php b/classes/Profiler.php index 7c52b89..159684a 100644 --- a/classes/Profiler.php +++ b/classes/Profiler.php @@ -182,7 +182,7 @@ class Profiler 'log' => $log, 'type' => $data_type, 'time' => $time, - 'elapsed' => $time - PICKLES_START_TIME, + 'elapsed' => $time - $_SERVER['REQUEST_TIME_FLOAT'], 'memory' => memory_get_usage(), ); } @@ -325,9 +325,9 @@ class Profiler } else { - $start_time = PICKLES_START_TIME; + $start_time = $_SERVER['REQUEST_TIME_FLOAT']; $peak_usage = self::formatSize(memory_get_peak_usage()); - $end_time = self::$profile[count(self::$profile) - 1]['time']; // TODO + $end_time = self::$profile[count(self::$profile) - 1]['time']; // @todo No idea what though? $duration = ($end_time - $start_time); $logs = count(self::$profile); diff --git a/pickles.php b/pickles.php index 851c1d3..6e96c9e 100644 --- a/pickles.php +++ b/pickles.php @@ -23,30 +23,27 @@ // {{{ PICKLES Constants -// Grabs the start time in case we're profiling -define('PICKLES_START_TIME', microtime(true)); - -// Establishes our PICKLES paths -define('PICKLES_PATH', dirname(__FILE__) . '/'); -define('PICKLES_CLASS_PATH', PICKLES_PATH . 'classes/'); -define('PICKLES_VENDOR_PATH', PICKLES_PATH . 'vendors/'); - +// @todo Finish reworking constants to be part of the Config object // Establishes our site paths, sanity check is to allow vfsStream in our tests if (!defined('SITE_PATH')) { define('SITE_PATH', getcwd() . '/../'); } -define('SITE_CLASS_PATH', SITE_PATH . 'classes/'); -define('SITE_MODEL_PATH', SITE_PATH . 'models/'); -define('SITE_MODULE_PATH', SITE_PATH . 'modules/'); -define('SITE_TEMPLATE_PATH', SITE_PATH . 'templates/'); +if (!defined('SITE_CLASS_PATH')) +{ + define('SITE_CLASS_PATH', SITE_PATH . 'classes/'); + define('SITE_MODEL_PATH', SITE_PATH . 'models/'); + // @todo The following 2 constants are being used in sites will need to update them before removing + define('SITE_MODULE_PATH', SITE_PATH . 'modules/'); + define('SITE_TEMPLATE_PATH', SITE_PATH . 'templates/'); -define('PRIVATE_PATH', SITE_PATH . 'private/'); -define('LOG_PATH', PRIVATE_PATH . 'logs/'); + define('PRIVATE_PATH', SITE_PATH . 'private/'); + define('LOG_PATH', PRIVATE_PATH . 'logs/'); -// Creates a variable to flag if we're on the command line -define('IS_CLI', !isset($_SERVER['REQUEST_METHOD'])); + // Creates a variable to flag if we're on the command line + define('IS_CLI', !isset($_SERVER['REQUEST_METHOD'])); +} // }}} // {{{ Defaults some important configuration options @@ -55,10 +52,6 @@ define('IS_CLI', !isset($_SERVER['REQUEST_METHOD'])); ini_set('display_errors', true); error_reporting(-1); -// Sets the error and exception handlers -// set_error_handler('__handleError'); -// set_exception_handler('__handleException'); - // Defaults timezone to UTC if not set if (ini_get('date.timezone') == '') { @@ -74,14 +67,75 @@ ini_set('session.gc_probability', 1); ini_set('session.gc_divisor', 1000); ini_set('session.hash_function', 1); +// }}} +// {{{ Auto[magical] Loader + +/** + * Magic function to automatically load classes + * + * Attempts to load a core PICKLES class or a site level data model or + * module class. + * + * @param string $class Name of the class to be loaded + * @return boolean Return value of require_once() or false (default) + */ +function __autoload($class) +{ + $loaded = false; + $filename = preg_replace('/_/', '/', $class) . '.php'; + + $pickles_path = dirname(__FILE__) . '/'; + $pickles_paths = [ + 'class' => $pickles_path . 'classes/', + 'vendor' => $pickles_path . 'vendors/', + ]; + + if ($class == 'AYAH') + { + $loaded = require_once $pickles_paths['vendor'] . 'ayah/' . strtolower($filename); + } + else + { + // Path as the key, boolean value is whether ot not to convert back to hyphenated + $paths = [ + $pickles_paths['class'] => false, + SITE_CLASS_PATH => false, + SITE_MODEL_PATH => false, + SITE_MODULE_PATH => true, + ]; + + foreach ($paths as $path => $hyphenated) + { + // Converts the filename back to hypenated + if ($hyphenated == true) + { + $filename = strtolower(preg_replace('/([A-Z]{1})/', '-$1', $filename));; + } + + if (file_exists($path . $filename)) + { + $loaded = require_once $path . $filename; + break; + } + } + } + + return $loaded; +} + +spl_autoload_register('__autoload'); + // }}} // {{{ Loads the configuration file and sets any configuration options // Loads the base config $config = Config::getInstance(); +// Injects PICKLES variables into the config +$config->data['pickles']['path'] = dirname(__FILE__) . '/'; + // Configures any available PHP configuration options -if (is_array($config->php) && count($config->php) > 0) +if (is_array($config->php) && count($config->php)) { foreach ($config->php as $variable => $value) { @@ -115,261 +169,6 @@ if (!isset($_REQUEST['request'])) $_REQUEST['request'] = 'home'; } -// }}} -// {{{ Auto[magical] Loader - -/** - * Magic function to automatically load classes - * - * Attempts to load a core PICKLES class or a site level data model or - * module class. - * - * @param string $class Name of the class to be loaded - * @return boolean Return value of require_once() or false (default) - */ -function __autoload($class) -{ - $loaded = false; - $filename = preg_replace('/_/', '/', $class) . '.php'; - - if ($class == 'AYAH') - { - $loaded = require_once PICKLES_VENDOR_PATH . 'ayah/' . strtolower($filename); - } - else - { - // Path as the key, boolean value is whether ot not to convert back to hyphenated - $paths = array( - PICKLES_CLASS_PATH => false, - SITE_CLASS_PATH => false, - SITE_MODEL_PATH => false, - SITE_MODULE_PATH => true, - ); - - foreach ($paths as $path => $hyphenated) - { - // Converts the filename back to hypenated - if ($hyphenated == true) - { - $filename = strtolower(preg_replace('/([A-Z]{1})/', '-$1', $filename));; - } - - if (file_exists($path . $filename)) - { - $loaded = require_once $path . $filename; - break; - } - } - } - - return $loaded; -} - -spl_autoload_register('__autoload'); - -// }}} -// {{{ Error Handler - -/** - * Error handling function that thinks it's magical - * - * Catches errors (warnings and the like) and throws it back out as an - * ErrorException. This really helps trapping complex errors that need a ton of - * sanity checks, just try / catch and you're good. Also, this isn't a magic - * function, but I opted to use the __ prefix to help avoid a naming collision - * since namespace support is 5.3+ and PICKLES strives to be 5.0+ compatible. - * - * Keep in mind that fatal errors cannot and will not be handled. - * - * @param integer $errno the level of the error raised - * @param string $errstr the error message - * @param string $errfile filename that the error was raised in - * @param integer $errline line number the error was raised at - * @param array $errcontext array of every variable that existed in scope - * @return ErrorException not really returned, but worth documenting - */ -function __handleError($errno, $errstr, $errfile, $errline, array $errcontext) -{ - // Handle hacktastic @ error suppression. Seriously, don't ever use @ - if (error_reporting() === 0) - { - return false; - } - - throw new ErrorException($errstr, 0, $errno, $errfile, $errline); -} - -// }}} -// {{{ Exception Handler - -/** - * Top level exception handling function - * - * Catches uncaught exceptions and displays them. - * - * @param object $exception the exception - */ -function __handleException($exception) -{ - if (IS_CLI == true) - { - $lines = array(); - $maxes = array('key' => 0, 'method' => 0, 'file' => 0, 'line' => 4); - - $trace = $exception->getTrace(); - rsort($trace); - - foreach ($trace as $key => $data) - { - $method = ''; - - if (isset($data['class'])) - { - $method .= $data['class'] . $data['type']; - } - - $method .= $data['function'] . '()'; - - $line = array( - 'key' => $key + 1 . '.', - 'method' => $method, - 'file' => (isset($data['file']) ? $data['file'] : __FILE__), - 'line' => (isset($data['line']) ? $data['line'] : '0') - ); - - foreach (array_keys($maxes) as $variable) - { - $length = strlen($line[$variable]); - - if ($length > $maxes[$variable]) - { - $maxes[$variable] = $length; - } - } - - $lines[] = $line; - } - - $max_length = array_sum($maxes) + 11; - $horizontal_border = '+' . str_repeat('-', $max_length) . '+' . "\n"; - - echo $horizontal_border; - echo '|' . str_pad('Uncaught Exception', $max_length, ' ', STR_PAD_BOTH) . '|' . "\n"; - echo $horizontal_border; - echo '|' . str_pad(' ' . $exception->getMessage(), $max_length) . '|' . "\n"; - echo '|' . str_pad(' in ' . $exception->getFile() . ' on line ' . $exception->getLine(), $max_length) . '|' . "\n"; - - echo $horizontal_border; - echo '| ' . str_pad('Trace', $maxes['key'] + $maxes['method'] + 3) . ' | ' . str_pad('File', $maxes['file']) . ' | ' . str_pad('Line', $maxes['line']) . ' |' . "\n"; - echo $horizontal_border; - - foreach ($lines as $line) - { - echo '| '; - - echo implode( - array( - str_pad($line['key'], $maxes['key'], ' ', STR_PAD_LEFT), - str_pad($line['method'], $maxes['method']), - str_pad($line['file'], $maxes['file']), - str_pad($line['line'], $maxes['line'], ' ', STR_PAD_LEFT) - ), - ' | ' - ); - - echo ' |' . "\n"; - } - - echo $horizontal_border; - } - else - { - ?> - -
- Uncaught Exception

- - - -
-
getMessage(); ?>
-
-
getFile(); ?> on line getLine(); ?>
-
- - - - - - - - getTrace(); - rsort($trace); - - foreach ($trace as $key => $data) - { - $method = ''; - - if (isset($data['class'])) - { - $method .= $data['class'] . $data['type']; - } - - $method .= $data['function'] . '()'; - ?> - - - - - - - -
TraceFileLine
.
-
-

- diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 9946a5b..8a9f8ca 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -5,20 +5,11 @@ ob_start(); require_once '.composer/autoload.php'; $root = org\bovigo\vfs\vfsStream::setup('site'); -define('SITE_PATH', org\bovigo\vfs\vfsStream::url('site/')); -require_once 'classes/Convert.php'; -require_once 'classes/Date.php'; -require_once 'classes/Time.php'; -require_once 'classes/String.php'; - -require_once 'classes/Object.php'; -require_once 'classes/Config.php'; -require_once 'classes/Display.php'; -require_once 'classes/File.php'; - -require_once 'classes/API/Common.php'; -require_once 'classes/API/Gravatar.php'; +if (!defined('SITE_PATH')) +{ + define('SITE_PATH', org\bovigo\vfs\vfsStream::url('site/')); +} require_once 'pickles.php'; diff --git a/tests/classes/ControllerTest.php b/tests/classes/ControllerTest.php new file mode 100644 index 0000000..25717a9 --- /dev/null +++ b/tests/classes/ControllerTest.php @@ -0,0 +1,176 @@ +config = Config::getInstance(); + $this->config->data['pickles']['disabled'] = false; + $this->config->data['pickles']['profiler'] = false; + $_SERVER['REQUEST_URI'] = ''; + + if (!file_exists(SITE_MODULE_PATH)) + { + mkdir(SITE_MODULE_PATH, 0644); + } + + unlink(SITE_MODULE_PATH . 'testing.php'); + + $_SERVER['HTTP_HOST'] = 'testsite.com'; + $_SERVER['REQUEST_URI'] = '/home'; + $_REQUEST['request'] = 'home'; + + $module = ''; + + file_put_contents(SITE_MODULE_PATH . 'home.php', $module); + } + + public function testSiteDown() + { + $_SERVER['SERVER_NAME'] = 'Test Server'; + + $this->config->data['pickles']['disabled'] = true; + + $this->expectOutputRegex('/Test Server is currently down for maintenance/'); + + new Controller(); + } + + /* + public function testCustomSiteDown() + { + $this->fail(); + } + + public function testAttributesInURI() + { + /testing/id:123/foo:bar + $this->fail(); + } + */ + + public function testUpperCaseURI() + { + $_SERVER['REQUEST_URI'] = '/TESTING'; + $_REQUEST['request'] = 'TESTING'; + + new Controller(); + + $this->assertTrue(in_array('Location: /testing', xdebug_get_headers())); + } + + public function testForceSecure() + { + $_SERVER['REQUEST_URI'] = '/secure'; + $_REQUEST['request'] = 'secure'; + + $module = ' + + '; + + file_put_contents(SITE_MODULE_PATH . 'secure.php', $module); + + new Controller(); + + $this->assertTrue(in_array('Location: https://testsite.com/secure', xdebug_get_headers())); + } + + public function testForceInsecure() + { + $_SERVER['HTTPS'] = 'on'; + $_SERVER['REQUEST_URI'] = '/insecure'; + $_REQUEST['request'] = 'insecure'; + + $module = ' + + '; + + file_put_contents(SITE_MODULE_PATH . 'insecure.php', $module); + + new Controller(); + + $this->assertTrue(in_array('Location: http://testsite.com/insecure', xdebug_get_headers())); + } + + /* + public function testNotAuthenticated() + { + $this->fail(); + } + + public function testNotAuthenticatedPOST() + { + $this->fail(); + } + + public function testAuthenticated() + { + $this->fail(); + } + + public function testHasLevelAccess() + { + $this->fail(); + } + + public function testIsLevelAccess() + { + $this->fail(); + } + + public function testRoleDefaultMethod() + { + $this->fail(); + } + + public function testBadRequestMethod() + { + $this->fail(); + } + + // @todo Reuse one of the Module tests? + public function testValidationErrors() + { + $this->fail(); + } + + public function testError404() + { + $this->fail(); + } + + public function testCustomError404() + { + $this->fail(); + } + + // @todo Reuse one of the Display tests? + public function testOutput() + { + $this->fail(); + } + */ + + public function testProfilerOutput() + { + $this->config->data['pickles']['profiler'] = true; + + $this->expectOutputRegex('/id="pickles-profiler"/'); + + new Controller(); + } +} + +?> diff --git a/tests/classes/ConvertTest.php b/tests/classes/ConvertTest.php index 56d65b1..ea24d74 100644 --- a/tests/classes/ConvertTest.php +++ b/tests/classes/ConvertTest.php @@ -3,8 +3,8 @@ class ConvertTest extends PHPUnit_Framework_TestCase { /** - * @dataProvider providerArrayToXML - */ + * @dataProvider providerArrayToXML + */ public function testArrayToXML($a, $b, $c) { $this->assertEquals(Convert::arrayToXML($a, $b), $c);