From aaa4a0fa163ac8d649c3fda7dfefd6df01d533a5 Mon Sep 17 00:00:00 2001 From: "Making GitHub Delicious." Date: Wed, 24 Sep 2014 07:57:17 -0600 Subject: [PATCH 001/129] add waffle.io badge --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index f61f69c..32310c5 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,4 @@ +[![Stories in Ready](https://badge.waffle.io/joshtronic/pickles.png?label=ready&title=Ready)](https://waffle.io/joshtronic/pickles) # PICKLES [![Build Status](https://travis-ci.org/joshtronic/pickles.png?branch=master)](https://travis-ci.org/joshtronic/pickles) [![Coverage Status](https://coveralls.io/repos/joshtronic/pickles/badge.png)](https://coveralls.io/r/joshtronic/pickles) [![Dependency Status](https://www.versioneye.com/user/projects/52d1bc1eec13751bde00002a/badge.png)](https://www.versioneye.com/user/projects/52d1bc1eec13751bde00002a) PICKLES (PHP Interface Collection of Killer Libraries to Enhance Stuff) is an From eba206556c91b5408da7943aa2dd32b498089c78 Mon Sep 17 00:00:00 2001 From: Josh Sherman Date: Wed, 24 Sep 2014 22:28:34 -0400 Subject: [PATCH 002/129] Set up return structure. --- src/classes/Controller.php | 26 +-------- src/classes/Display.php | 110 +++++++++++++------------------------ src/classes/Module.php | 70 +++++++++++------------ 3 files changed, 77 insertions(+), 129 deletions(-) diff --git a/src/classes/Controller.php b/src/classes/Controller.php index 63e5228..54d0d3f 100644 --- a/src/classes/Controller.php +++ b/src/classes/Controller.php @@ -317,11 +317,11 @@ class Controller extends Object if (!is_array($module_return)) { - $module_return = $module->return; + $module_return = $module->response; } else { - $module_return = array_merge($module_return, $module->return); + $module_return = array_merge($module_return, $module->response); } } @@ -353,27 +353,7 @@ class Controller extends Object } } - if (!isset($module_return)) - { - $module_return = [ - 'status' => 'error', - 'message' => $error_message, - ]; - } - - // @todo Should simplify this, give Display direct acess to - // $module instead of all these variable assignment - $display = new Display(); - $display->output = $module->output; - $display->templates = $module->template; - $display->module = $module_return; - - // @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 - ]; + $display = new Display($module); } // Starts a timer for the display rendering diff --git a/src/classes/Display.php b/src/classes/Display.php index eca2628..40760ef 100644 --- a/src/classes/Display.php +++ b/src/classes/Display.php @@ -23,55 +23,20 @@ class Display extends Object { /** - * Return Type + * Module * - * This class supports loading a PHP template, displaying JSON, XML and an - * RSS flavored XML. Inside your modules you can specify either a string or - * array. Possible values include "template", "json", "xml" and "rss". - * Default behavior is to try to load a template and fallback to displaying - * JSON. The "template" option always takes precedence when used with the - * other types. - * - * @var mixed string or array to determine how to return - */ - public $return = ['template', 'json']; - - /** - * Templates - * - * Templates are found in the ./templates directory of your site. The - * template workflow is to load ./templates/__shared/index.phtml and you - * would set that template up to require $this->template, the path and - * filename for the module template (named based on the structure of the - * requested URI. Inside your module you can specify the basename of the - * parent template you would like to use or false to not use a parent - * template. - * - * @var string or boolean false the basename of the parent template - */ - public $templates = false; - - /** - * Meta Data - * - * An array of meta data that you want exposed to the template. Currently - * you set the meta data from inside your module using the class variables - * title, description and keywords. The newer [preferred] method is to - * set an array in your module using the meta variable using title, - * description and keywords as the keys. You can also specify any other - * meta keys in the array that you would like to be exposed to your - * templates. The meta data is only used by TEMPLATE and RSS return types. - */ - public $meta = []; - - /** - * Module Data - * - * Any data the module returns or is assigned inside of the module will - * be available here and exposed to the template. + * 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 @@ -79,21 +44,23 @@ class Display extends Object // Starts up the buffer so we can capture it ob_start(); - if (!is_array($this->return)) + if (!is_array($this->module->response)) { - $this->return = [$this->return]; + $this->module->response = [$this->module->response]; } - $return_json = $return_rss = $return_template = $return_xml = false; + $return_json = false; + $return_template = false; + $return_xml = false; - foreach ($this->return as $return) + foreach ($this->module->output as $return) { $variable = 'return_' . $return; $$variable = true; } // Makes sure the return type is valid - if (!$return_json && !$return_rss && !$return_template && !$return_xml) + if (!$return_json && !$return_template && !$return_xml) { throw new Exception('Invalid return type.'); } @@ -118,8 +85,6 @@ class Display extends Object throw new Exception('Requested URI contains PHPSESSID, redirecting.'); } - // @todo Derrive CSS and JS from _REQUEST['request'] no need to pass around - $loaded = false; if ($return_template) @@ -131,22 +96,14 @@ class Display extends Object // Exposes some objects and variables to the local scope of the template $this->request = $this->js_file = $_REQUEST['request']; - // @todo replace _ with - as it's more appropriate for CSS naming - $this->css_class = strtr($this->request, '/', '_'); + $this->css_class = strtr($this->request, '/', '-'); - // @todo Remove the magic $__variable when all sites are ported - $__config = $this->config; - $__css_class = $this->css_class; - $__js_file = $this->js_file; - $__meta = $this->meta; - $__module = $this->module; - - $__dynamic = $this->dynamic = new $dynamic_class(); - $__form = $this->form = new $form_class(); - $__html = $this->html = new $html_class(); + $this->dynamic = new $dynamic_class(); + $this->form = new $form_class(); + $this->html = new $html_class(); // Checks for the parent template and tries to load it - if ($this->templates) + if ($this->module->template) { $profiler = $this->config->pickles['profiler']; $profiler = $profiler === true || stripos($profiler, 'timers') !== false; @@ -158,9 +115,9 @@ class Display extends Object } // Assigns old variable - $required_template = $this->templates[0]; - $__template = $this->template = end($this->templates); - $loaded = require_once $required_template; + $required_template = $this->module->templates[0]; + $this->module->template = end($this->module->templates); + $loaded = require_once $required_template; // Stops the template loading timer if ($profiler) @@ -170,16 +127,26 @@ class Display extends Object } } + $meta = [ + 'status' => $this->module->status, + 'message' => $this->module->message, + ]; + + $response = [ + 'meta' => $meta, + 'response' => $this->module->response, + ]; + if (!$loaded) { if ($return_json) { $pretty = isset($_REQUEST['pretty']) ? JSON_PRETTY_PRINT : false; - echo json_encode($this->module, $pretty); + echo json_encode($response, $pretty); } elseif ($return_xml) { - echo Convert::arrayToXML($this->module, isset($_REQUEST['pretty'])); + echo Convert::arrayToXML($response, isset($_REQUEST['pretty'])); } } @@ -189,7 +156,8 @@ class Display extends Object // Kills any whitespace and HTML comments in templates if ($loaded) { - // The BSA exception is because their system sucks and demands there be comments present + // The BSA exception is because their system sucks and demands + // there be comments present $buffer = preg_replace(['/^[\s]+/m', '//U'], '', $buffer); } diff --git a/src/classes/Module.php b/src/classes/Module.php index 44c9a26..65a376c 100644 --- a/src/classes/Module.php +++ b/src/classes/Module.php @@ -78,20 +78,6 @@ class Module extends Object */ public $security = null; - /** - * AJAX - * - * Whether or not the module is being called via AJAX. This determines if - * errors should be returned as JSON or if it should use the Error class - * which can be interrogated from within a template. - * - * @var boolean, false (not AJAX) by default - * @todo Doesn't seem to be in use, but I have it defined on Clipinary - * don't want to remove until I drop it else it would end up in the - * module return array. - */ - public $ajax = false; - /** * Method * @@ -122,18 +108,15 @@ class Module extends Object public $template = 'index'; /** - * Return + * Response * - * Array that is returned to the template in the case of the module not - * returning anything itself. This is somewhat of a one way trip as you - * cannot get the variable unless you reference the return array explicitly - * $this->return['variable'] + * 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 - * @todo Rename __return so it's kinda obscured - * @todo Will need to update leaderbin and sndcrd to use new variable + * @var array */ - public $return = []; + public $response = []; /** * Output @@ -146,6 +129,16 @@ class Module extends Object */ public $output = ['template', 'json']; + // @todo + public $status = 200; + public $message = 'OK'; + public $echo = false; + public $limit = false; + public $offset = false; + public $errors = []; + + // @todo if $status != 200 && $message == 'OK' ... + /** * Constructor * @@ -155,19 +148,26 @@ class Module extends Object * 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, $validate = true) + public function __construct($autorun = false, $filter = true, $validate = true) { parent::__construct(['cache', 'db']); - if ($autorun === true) + if ($autorun) { - if ($validate === true) + if ($filter) + { + // @todo + //$this->__filter(); + } + + if ($validate) { $errors = $this->__validate(); - if ($errors !== false) + if (!$errors) { // @todo Fatal error perhaps? exit('Errors encountered, this is a @todo for form validation when calling modules from inside of modules'); @@ -192,35 +192,35 @@ class Module extends Object /** * Magic Setter Method * - * Places undefined properties into the return array as part of the + * Places undefined properties into the response array as part of the * module's payload. * - * @param string $name name of the variable to be set + * @param string $variable name of the variable to be set * @param mixed $value value of the variable to be set */ - public function __set($name, $value) + public function __set($variable, $value) { - $this->return[$name] = $value; + $this->response[$variable] = $value; } /** * Magic Getter Method * - * Any variables not defined in this class are set in the return array and - * default to false if not defined there. + * 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->return[$name])) + if (!isset($this->response[$name])) { return false; } else { - return $this->return[$name]; + return $this->response[$name]; } } From 0fdf2149be335eaf4e75d17ba11fb021f6b47f93 Mon Sep 17 00:00:00 2001 From: Josh Sherman Date: Wed, 24 Sep 2014 23:24:36 -0400 Subject: [PATCH 003/129] Dropped markup logic Dropped the display / template rendering layer. API-first, converting to be a lean mean data driven machine. --- src/classes/Form.php | 672 ------------------------------------- src/classes/HTML.php | 148 -------- tests/classes/FormTest.php | 295 ---------------- tests/classes/HTMLTest.php | 108 ------ 4 files changed, 1223 deletions(-) delete mode 100644 src/classes/Form.php delete mode 100644 src/classes/HTML.php delete mode 100644 tests/classes/FormTest.php delete mode 100644 tests/classes/HTMLTest.php diff --git a/src/classes/Form.php b/src/classes/Form.php deleted file mode 100644 index 100a162..0000000 --- a/src/classes/Form.php +++ /dev/null @@ -1,672 +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 - */ - -/** - * Form Class - * - * This class contains methods for easily generating form elements. There is a - * heavy focus on select boxes as they have the most overhead for a developer. - * - * @deprecated - */ -class Form extends Object -{ - // {{{ Get Instance - - /** - * Get Instance - * - * Gets an instance of the Form class - * - * @static - * @param string $class name of the class to get an instance of - * @return object instance of the class - */ - public static function getInstance($class = 'Form') - { - return parent::getInstance($class); - } - - // }}} - // {{{ Input - - /** - * Input - * - * Generates an input with the passed data. - * - * @param string $name name (and ID) for the element - * @param string $value optional preset value - * @param string $classes optional class names - * @param string $additional optional additional parameters - * @param string $type optional type of input - * @param boolean $checked optional whether the input is checked - * @return string HTML for the input - */ - public function input($name, $value = null, $classes = null, $additional = null, $type = 'text', $checked = false) - { - if ($additional) - { - $additional = ' ' . $additional; - } - - if (in_array($type, ['checkbox', 'radio']) && $checked == true) - { - $additional .= ' checked="checked"'; - } - - if ($value) - { - $additional .= ' value="' . $value . '"'; - } - - if ($classes) - { - $additional .= ' class="' . $classes . '"'; - } - - return ''; - } - - // }}} - // {{{ Hidden - - /** - * Hidden - * - * Shorthand method to generate a hidden input. - * - * @param string $name name (and ID) for the element - * @param string $value optional preset value - * @param string $classes optional class names - * @param string $additional optional additional parameters - * @return string HTML for the input - */ - public function hidden($name, $value = null, $classes = null, $additional = null) - { - return $this->input($name, $value, $classes, $additional, 'hidden'); - } - - /** - * Hidden Input - * - * Shorthand method to generate a hidden input. - * - * @deprecated Use hidden() instead - * - * @param string $name name (and ID) for the element - * @param string $value optional preset value - * @param string $classes optional class names - * @param string $additional optional additional parameters - * @return string HTML for the input - */ - public function hiddenInput($name, $value = null, $classes = null, $additional = null) - { - return $this->input($name, $value, $classes, $additional, 'hidden'); - } - - // }}} - // {{{ Password - - /** - * Password - * - * Shorthand method to generate a password input. - * - * @param string $name name (and ID) for the element - * @param string $value optional preset value - * @param string $classes optional class names - * @param string $additional optional additional parameters - * @return string HTML for the input - */ - public function password($name, $value = null, $classes = null, $additional = null) - { - return $this->input($name, $value, $classes, $additional, 'password'); - } - - /** - * Password Input - * - * Shorthand method to generate a password input. - * - * @deprecated Use password() instead - * - * @param string $name name (and ID) for the element - * @param string $value optional preset value - * @param string $classes optional class names - * @param string $additional optional additional parameters - * @return string HTML for the input - */ - public function passwordInput($name, $value = null, $classes = null, $additional = null) - { - return $this->input($name, $value, $classes, $additional, 'password'); - } - - // }}} - // {{{ Submit - - /** - * Submit - * - * Shorthand method to generate a submit input (button). - * - * @param string $name name (and ID) for the input element - * @param string $value optional preset value - * @param string $classes optional class names - * @param string $additional optional additional parameters - * @return string HTML for the input - */ - public function submit($name, $value = null, $classes = null, $additional = null) - { - return $this->input($name, $value, $classes, $additional, 'submit'); - } - - /** - * Submit Input - * - * Shorthand method to generate a submit input (button). - * - * @deprecated Use submit() instead - * - * @param string $name name (and ID) for the input element - * @param string $value optional preset value - * @param string $classes optional class names - * @param string $additional optional additional parameters - * @return string HTML for the input - */ - public function submitInput($name, $value = null, $classes = null, $additional = null) - { - return $this->input($name, $value, $classes, $additional, 'submit'); - } - - // }}} - // {{{ Security - - /** - * Security - * - * Generates a hidden input with an SHA1 hash as the value. The name of the - * field is cannot be changed as this method was only intended for use with - * forms that are submitted via AJAX to provide better security. - * - * @param string $value value to hash - * @return string HTML for the input - */ - public function security($value) - { - // Returns the hidden input - return $this->hiddenInput('security_hash', Security::generateHash($value)); - } - - /** - * Security Input - * - * Generates a hidden input with an SHA1 hash as the value. The name of the - * field is cannot be changed as this method was only intended for use with - * forms that are submitted via AJAX to provide better security. - * - * @deprecated Use security() instead - * - * @param string $value value to hash - * @return string HTML for the input - */ - public function securityInput($value) - { - // Returns the hidden input - return $this->hiddenInput('security_hash', Security::generateHash($value)); - } - - // }}} - // {{{ Checkbox - - /** - * Checkbox - * - * Generates a checkbox input with the passed data. - * - * @param string $name name (and ID) for the select element - * @param string $value optional preset value - * @param boolean $checked optional whether the checkbox is checked - * @param string $classes optional class names - * @param string $additional optional additional parameters - * @return string HTML for the input - */ - public function checkbox($name, $value = null, $checked = false, $classes = null, $additional = null) - { - return $this->input($name, $value, $classes, $additional, 'checkbox', $checked); - } - - // }}} - // {{{ Radio Button - - /** - * Radio Button - * - * Generates a radio input with the passed data. - * - * @param string $name name (and ID) for the select element - * @param string $value optional preset value - * @param boolean $checked optional whether the checkbox is checked - * @param string $classes optional class names - * @param string $additional optional additional parameters - * @return string HTML for the input - */ - public function radio($name, $value = null, $checked = false, $classes = null, $additional = null) - { - return $this->input($name, $value, $classes, $additional, 'radio', $checked); - } - - // }}} - // {{{ Textarea - - /** - * Textarea - * - * Generates a textarea with the passed data. - * - * @param string $name name (and ID) for the select element - * @param string $value optional preset value - * @param string $classes optional class names - * @param string $additional optional additional parameters - * @param string $type optional type of input - * @return string HTML for the input - */ - public function textarea($name, $value = null, $classes = null, $additional = null) - { - if ($additional) - { - $additional = ' ' . $additional; - } - - if ($classes) - { - $additional .= ' class="' . $classes . '"'; - } - - return ''; - } - - // }}} - // {{{ Select - - /** - * Select - * - * Generates a select box with the passed data. - * - * @param string $name name (and ID) for the select element - * @param array $options key/values for the option elements - * @param string $selected optional selected option - * @param string $classes optional class names - * @param string $additional optional additional parameters - * @return string HTML for the select box - */ - public function select($name, $options, $selected = null, $classes = null, $additional = null) - { - if ($additional) - { - $additional = ' ' . $additional; - } - - if ($classes) - { - $additional .= ' class="' . $classes . '"'; - } - - return ''; - } - - // }}} - // {{{ Options - - /** - * Options - * - * Generates the option elements from the passed array - * - * @param array $options key/values for the options - * @param string $selected optional default option - * @return string HTML for the options - */ - public function options($options, $selected = null) - { - $found_selected = false; - $options_html = ''; - - if (is_array($options)) - { - foreach ($options as $main_key => $main_label) - { - if (is_array($main_label)) - { - $options_html .= ''; - - foreach ($main_label as $sub_key => $sub_label) - { - $selected_attribute = false; - if ($selected !== null && $found_selected === false) - { - if ($selected == $sub_key) - { - $selected_attribute = ' selected="selected"'; - $found_selected = true; - } - } - - $options_html .= ''; - } - - $options_html .= ''; - } - else - { - $selected_attribute = false; - if ($selected !== null && $found_selected === false) - { - if ($selected == $main_key) - { - $selected_attribute = ' selected="selected"'; - $found_selected = true; - } - } - - $options_html .= ''; - } - } - } - - if ($selected !== null && $found_selected === false) - { - $options_html .= ''; - } - - return $options_html; - } - - // }}} - // {{{ State Select - - /** - * State Select - * - * Generates a select box with the United States, Puerto Rico and miliary - * options - * - * @param string $name optional name (and ID) for the select element - * @param string $selected optional selected option - * @param string $classes optional class names - * @param string $additional optional additional parameters - * @return string HTML for the select box - */ - public function stateSelect($name = 'state', $selected = null, $classes = null, $additional = null) - { - $options = [ - null => '-- Select State --', - 'AK' => 'Alaska', - 'AL' => 'Alabama', - 'AS' => 'American Samoa', - 'AZ' => 'Arizona', - 'AR' => 'Arkansas', - 'CA' => 'California', - 'CO' => 'Colorado', - 'CT' => 'Connecticut', - 'DE' => 'Delaware', - 'DC' => 'District of Columbia', - 'FL' => 'Florida', - 'GA' => 'Georgia', - 'GU' => 'Guam', - 'HI' => 'Hawaii', - 'ID' => 'Idaho', - 'IL' => 'Illinois', - 'IN' => 'Indiana', - 'IA' => 'Iowa', - 'KS' => 'Kansas', - 'KY' => 'Kentucky', - 'LA' => 'Louisiana', - 'ME' => 'Maine', - 'MH' => 'Marshall Islands', - 'MD' => 'Maryland', - 'MA' => 'Massachusetts', - 'MI' => 'Michigan', - 'MN' => 'Minnesota', - 'MS' => 'Mississippi', - 'MO' => 'Missouri', - 'MT' => 'Montana', - 'NE' => 'Nebraska', - 'NV' => 'Nevada', - 'NH' => 'New Hampshire', - 'NJ' => 'New Jersey', - 'NM' => 'New Mexico', - 'NY' => 'New York', - 'NC' => 'North Carolina', - 'ND' => 'North Dakota', - 'MP' => 'Northern Mariana Islands', - 'OH' => 'Ohio', - 'OK' => 'Oklahoma', - 'OR' => 'Oregon', - 'PW' => 'Palau', - 'PA' => 'Pennsylvania', - 'PR' => 'Puerto Rico', - 'RI' => 'Rhode Island', - 'SC' => 'South Carolina', - 'SD' => 'South Dakota', - 'TN' => 'Tennessee', - 'TX' => 'Texas', - 'UT' => 'Utah', - 'VT' => 'Vermont', - 'VI' => 'Virgin Islands', - 'VA' => 'Virginia', - 'WA' => 'Washington', - 'WV' => 'West Virginia', - 'WI' => 'Wisconsin', - 'WY' => 'Wyoming', - 'AE' => 'Armed Forces Africa', - 'AA' => 'Armed Forces Americas (except Canada)', - 'AE' => 'Armed Forces Canada', - 'AE' => 'Armed Forces Europe', - 'AE' => 'Armed Forces Middle East', - 'AP' => 'Armed Forces Pacific' - ]; - - return $this->select($name, $options, $selected, $classes, $additional); - } - - // }}} - // {{{ Date Select - - /** - * Date Select - * - * Generates 3 select boxes (month, day, year) - * - * @param string $name optional name (and ID) for the select element - * @param string $selected optional selected option - * @param string $classes optional class names - * @param string $additional optional additional parameters - * @param integer $start_year optional first year to display - * @param integer $end_year optional last year to display - * @return string HTML for the select boxes - */ - public function dateSelect($name = 'date', $selected = null, $classes = null, $additional = null, $start_year = null, $end_year = null) - { - $html = ''; - - // Breaks apart the selected value if present - if ($selected == null || $selected == '0000-00-00') - { - $selected_month = null; - $selected_day = null; - $selected_year = null; - } - else - { - list($selected_year, $selected_month, $selected_day) = explode('-', $selected); - } - - $month_options = [ - null => 'Month', - '01' => 'January', - '02' => 'February', - '03' => 'March', - '04' => 'April', - '05' => 'May', - '06' => 'June', - '07' => 'July', - '08' => 'August', - '09' => 'September', - '10' => 'October', - '11' => 'November', - '12' => 'December', - ]; - - $day_options = [null => 'Day']; - $year_options = [null => 'Year']; - - // Generates the list of days - for ($i = 1; $i <= 31; ++$i) - { - $day_options[str_pad($i, 2, '0', STR_PAD_LEFT)] = $i; - } - - // Generates the list of years - $current_year = date('Y'); - $start_year = $start_year == null ? $current_year - 10 : $start_year; - $end_year = $end_year == null ? $current_year + 10 : $end_year; - - for ($i = $start_year; $i >= $end_year; --$i) - { - $year_options[$i] = $i; - } - - // Loops through and generates the selects - foreach (['month', 'day', 'year'] as $part) - { - $options = $part . '_options'; - $selected = 'selected_' . $part; - $html .= ($html == '' ? '' : ' ') . $this->select($name . '[' . $part . ']', $$options, $$selected, $classes, $additional); - } - - return $html; - } - - // }}} - // {{{ Date of Birth Select - - /** - * Date of Birth Select - * - * Generates 3 select boxes (month, day, year) - * - * @param string $name optional name (and ID) for the select element - * @param string $selected optional selected option - * @param string $classes optional class names - * @param string $additional optional additional parameters - * @return string HTML for the select boxes - */ - public function dobSelect($name = 'dob', $selected = null, $classes = null, $additional = null) - { - // Note: Start year based on oldest living person: http://en.wikipedia.org/wiki/Oldest_people as of November 2010 - // Note: Start and end year may seem backwards, but we want them in descending order when rendered - return $this->dateSelect($name, $selected, $classes, $additional, date('Y'), 1896); - } - - // }}} - // {{{ Polar Select - - /** - * Polar Select - * - * Generates a polar (yes / no) select box. - * - * @param string $name optional name (and ID) for the select element - * @param string $selected optional selected option - * @param string $classes optional class names - * @param string $additional optional additional parameters - */ - public function polarSelect($name = 'decision', $selected = 0, $classes = null, $additional = null) - { - $options = [1 => 'Yes', 0 => 'No']; - - return $this->select($name, $options, $selected, $classes, $additional); - } - - // }}} - // {{{ Phone Input - - /** - * Phone Input - * - * Generates 3 inputs for a phone number from the passed values. - * - * @param string $name optional name (and ID) for the input elements - * @param string $value optional existing value - * @param string $classes optional class names - * @param string $additional optional additional parameters - */ - public function phoneInput($name = 'phone', $value = null, $classes = null, $additional = null) - { - if ($value == null) - { - $value = [ - 'area_code' => '', - 'prefix' => '', - 'line_number' => '' - ]; - } - else - { - $value = str_replace('-', '', $value); - $value = [ - 'area_code' => substr($value, 0, 3), - 'prefix' => substr($value, 3, 3), - 'line_number' => substr($value, 6) - ]; - } - - $parts = [ - 'area_code' => 3, - 'prefix' => 3, - 'line_number' => 4 - ]; - - if ($additional) - { - $additional = ' ' . $additional; - } - - $additional .= ' class="digits'; - - if ($classes) - { - $additional .= ' ' . $classes; - } - - $additional .= '"'; - - $html = ''; - foreach ($parts as $part => $size) - { - $html .= ($html != '' ? ' ' : ''); - $html .= ''; - } - - return $html; - } - - // }}} -} - diff --git a/src/classes/HTML.php b/src/classes/HTML.php deleted file mode 100644 index af39b32..0000000 --- a/src/classes/HTML.php +++ /dev/null @@ -1,148 +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 - */ - -/** - * HTML Class - * - * This class contains methods for easily generating HTML elements. - */ -class HTML extends Object -{ - private $self_closing = ['br', 'hr', 'img', 'input', 'link', 'meta']; - - public function __call($method, $arguments) - { - $attributes = null; - $contents = null; - - if (isset($arguments[0])) - { - $attributes = $arguments[0]; - } - - if (isset($arguments[1])) - { - $contents = $arguments[1]; - } - - // ->inputType('name', $attributes); - if (preg_match('/^input/', $method) && !isset($attributes['label'])) - { - $type = strtolower(str_replace('input', '', $method)); - - switch ($type) - { - case 'datetimelocal': $type = 'datetime-local'; break; - case '': $type = 'text'; break; - } - - $method = 'input'; - - if (is_array($attributes)) - { - $attributes['type'] = $type; - } - else - { - $attributes = ['type' => $type]; - } - } - - if (is_array($attributes) && isset($attributes['label'])) - { - if (isset($attributes['name'])) - { - $label = $this->label(['for' => $attributes['name']], $attributes['label']); - } - else - { - $label = $this->label($attributes['label']); - } - - unset($attributes['label']); - - return $label . $this->$method($attributes, $contents); - } - else - { - return $this->element($method, $attributes, $contents); - } - } - - // {{{ Get Instance - - /** - * Get Instance - * - * Gets an instance of the Form class - * - * @static - * @param string $class name of the class to get an instance of - * @return object instance of the class - */ - public static function getInstance($class = 'HTML') - { - return parent::getInstance($class); - } - - // }}} - - public function element($element) - { - $attributes = null; - $contents = null; - - foreach (func_get_args() as $key => $value) - { - if ($key && $key < 3) - { - if (is_array($value)) - { - $attributes = $value; - } - elseif ($value) - { - $contents = $value; - } - } - } - - $element = strtolower($element); - $html = '<' . $element; - - if ($attributes) - { - if (is_array($attributes)) - { - foreach ($attributes as $attribute => $value) - { - $html .= ' ' . $attribute . '="' . str_replace('"', '\"', $value) . '"'; - } - } - } - - $html .= '>'; - - if (!in_array($element, $this->self_closing)) - { - $html .= $contents . ''; - } - - return $html; - } -} - diff --git a/tests/classes/FormTest.php b/tests/classes/FormTest.php deleted file mode 100644 index a264218..0000000 --- a/tests/classes/FormTest.php +++ /dev/null @@ -1,295 +0,0 @@ -form = Form::getInstance(); - $this->options = [ - '1' => 'one', - '2' => 'two', - '3' => 'three', - ]; - } - - public function testGetInstance() - { - $this->assertInstanceOf('Form', $this->form); - } - - public function testInput() - { - $this->assertEquals( - '', - $this->form->input('name') - ); - } - - public function testInputWithClasses() - { - $this->assertEquals( - '', - $this->form->input('name', null, 'foo bar') - ); - } - - public function testInputWithAdditional() - { - $this->assertEquals( - '', - $this->form->input('name', null, null, 'test="ing"') - ); - } - - public function testHidden() - { - $this->assertEquals( - '', - $this->form->hidden('name') - ); - } - - public function testHiddenInput() - { - $this->assertEquals( - '', - $this->form->hiddenInput('name') - ); - } - - public function testPassword() - { - $this->assertEquals( - '', - $this->form->password('name') - ); - } - - public function testPasswordInput() - { - $this->assertEquals( - '', - $this->form->passwordInput('name') - ); - } - - public function testSubmit() - { - $this->assertEquals( - '', - $this->form->submit('name') - ); - } - - public function testSubmitInput() - { - $this->assertEquals( - '', - $this->form->submitInput('name') - ); - } - - public function testSecurity() - { - $this->assertEquals( - '', - $this->form->security('secret') - ); - } - - public function testSecurityInput() - { - $this->assertEquals( - '', - $this->form->securityInput('secret') - ); - } - - public function testCheckbox() - { - $this->assertEquals( - '', - $this->form->checkbox('name') - ); - } - - public function testCheckboxChecked() - { - $this->assertEquals( - '', - $this->form->checkbox('name', null, true) - ); - } - - public function testRadio() - { - $this->assertEquals( - '', - $this->form->radio('name') - ); - } - - public function testTextarea() - { - $this->assertEquals( - '', - $this->form->textarea('name') - ); - } - - public function testTextareaWithClasses() - { - $this->assertEquals( - '', - $this->form->textarea('name', null, 'foo bar') - ); - } - - public function testTextareaWithAdditional() - { - $this->assertEquals( - '', - $this->form->textarea('name', null, null, 'test="ing"') - ); - } - - public function testSelect() - { - $this->assertEquals( - '', - $this->form->select('name', $this->options) - ); - } - - public function testSelectWithClasses() - { - $this->assertEquals( - '', - $this->form->select('name', $this->options, null, 'foo bar') - ); - } - - public function testSelectWithAdditional() - { - $this->assertEquals( - '', - $this->form->select('name', $this->options, null, null, 'test="ing"') - ); - } - - public function testOptions() - { - $this->assertEquals( - '', - $this->form->options($this->options) - ); - } - - public function testOptionsWithMissingSelected() - { - $this->assertEquals( - '', - $this->form->options($this->options, 4) - ); - } - - public function testOptionsOptGroup() - { - $this->assertEquals( - '', - $this->form->options(['group' => $this->options]) - ); - } - - public function testOptionsOptGroupSelected() - { - $this->assertEquals( - '', - $this->form->options(['group' => $this->options], 2) - ); - } - - public function testStateSelect() - { - $this->assertRegExp( - '/^ ', - $this->form->dateSelect() - ); - } - - public function testDateSelectWithDate() - { - $this->assertEquals( - ' ', - $this->form->dateSelect('date', '1981-02-23', null, null, 1990, 1980) - ); - } - - public function testDOBSelect() - { - $this->assertEquals( - ' ', - $this->form->dobSelect() - ); - } - - public function testPolarSelect() - { - $this->assertEquals( - '', - $this->form->polarSelect() - ); - } - - public function testPhoneInput() - { - $this->assertEquals( - ' ', - $this->form->phoneInput() - ); - } - - public function testPhoneInputWithValue() - { - $this->assertEquals( - ' ', - $this->form->phoneInput('phone', '3025550134') - ); - } - - public function testPhoneInputWithValueWithDashes() - { - $this->assertEquals( - ' ', - $this->form->phoneInput('phone', '302-555-0134') - ); - } - - public function testPhoneInputWithClasses() - { - $this->assertEquals( - ' ', - $this->form->phoneInput('phone', null, 'foo bar') - ); - } - - public function testPhoneInputWithAdditional() - { - $this->assertEquals( - ' ', - $this->form->phoneInput('phone', null, null, 'test="ing"') - ); - } -} - diff --git a/tests/classes/HTMLTest.php b/tests/classes/HTMLTest.php deleted file mode 100644 index d3b35b4..0000000 --- a/tests/classes/HTMLTest.php +++ /dev/null @@ -1,108 +0,0 @@ -html = HTML::getInstance(); - } - - public function testGetInstance() - { - $this->assertInstanceOf('HTML', $this->html); - } - - public function testInput() - { - $this->assertEquals('', $this->html->input()); - } - - public function testInputDateTimeLocal() - { - $this->assertEquals('', $this->html->inputDateTimeLocal()); - } - - public function testInputEmail() - { - $this->assertEquals('', $this->html->inputEmail()); - } - - public function testInputWithAttributes() - { - $this->assertEquals( - '', - $this->html->input([ - 'id' => 'id', - 'class' => 'class', - 'value' => 'value', - ]) - ); - } - - public function testInputPasswordWithLabel() - { - $this->assertEquals( - '', - $this->html->inputPassword([ - 'name' => 'password', - 'label' => 'Enter Password', - ]) - ); - - } - - public function testNestedElements() - { - $this->assertEquals( - '

Nested!

', - $this->html->div( - $this->html->p('Nested!') - ) - ); - } - - public function testNestedElementsWithAttributes() - { - $this->assertEquals( - '

Nested!

', - $this->html->div( - ['class' => 'outer'], - $this->html->p( - ['class' => 'inner'], - 'Nested!' - ) - ) - ); - } - - public function testClosingTag() - { - $this->assertEquals('', $this->html->textarea()); - } - - public function testElement() - { - $this->assertEquals('
', $this->html->element('div')); - } - - public function testReversedParameters() - { - $this->assertEquals( - '
string
', - $this->html->div('string', ['class' => 'fancy']) - ); - } - - public function testLabelWithInputWithoutName() - { - $this->assertEquals( - '', - $this->html->input([ - 'label' => 'Label', - ]) - ); - } -} - From e299d268c8da3ccc296d620851fb06bbaf126989 Mon Sep 17 00:00:00 2001 From: Josh Sherman Date: Wed, 24 Sep 2014 23:26:00 -0400 Subject: [PATCH 004/129] Setting headers and shit --- src/classes/Display.php | 27 ++++++++++++++++----------- src/classes/Module.php | 13 ++++++------- 2 files changed, 22 insertions(+), 18 deletions(-) diff --git a/src/classes/Display.php b/src/classes/Display.php index 40760ef..59bd53c 100644 --- a/src/classes/Display.php +++ b/src/classes/Display.php @@ -87,7 +87,7 @@ class Display extends Object $loaded = false; - if ($return_template) + if ($return_template && $this->module->templates) { // Determines if we're using a custom class or not $dynamic_class = (class_exists('CustomDynamic') ? 'CustomDynamic' : 'Dynamic'); @@ -127,25 +127,30 @@ class Display extends Object } } - $meta = [ - 'status' => $this->module->status, - 'message' => $this->module->message, - ]; - - $response = [ - 'meta' => $meta, - 'response' => $this->module->response, - ]; - if (!$loaded) { + if (!$return_template || !$this->module->templates) + { + $meta = [ + 'status' => $this->module->status, + 'message' => $this->module->message, + ]; + + $response = [ + 'meta' => $meta, + 'response' => $this->module->response, + ]; + } + if ($return_json) { + header('Content-type: application/json'); $pretty = isset($_REQUEST['pretty']) ? JSON_PRETTY_PRINT : false; echo json_encode($response, $pretty); } elseif ($return_xml) { + header('Content-type: text/xml'); echo Convert::arrayToXML($response, isset($_REQUEST['pretty'])); } } diff --git a/src/classes/Module.php b/src/classes/Module.php index 65a376c..78ec6af 100644 --- a/src/classes/Module.php +++ b/src/classes/Module.php @@ -25,7 +25,7 @@ * be JSON encoded. In the future this may need to be changed out for logic * that allows the requested module to specify what display type(s) it can use. */ -class Module extends Object +abstract class Module extends Object { /** * Page Title @@ -181,13 +181,12 @@ class Module extends Object /** * Default "Magic" Method * - * This function is overloaded by the module. The __default() method is - * where you want to place any code that needs to be executed at runtime. + * The __default() method is where you want to place any code that needs to + * be executed at runtime. + * + * @abstract */ - public function __default() - { - - } + abstract public function __default(); /** * Magic Setter Method From 1381fd82a0d64b087adaa44cded451008487d6dc Mon Sep 17 00:00:00 2001 From: Josh Sherman Date: Wed, 24 Sep 2014 23:33:03 -0400 Subject: [PATCH 005/129] Dropped other classes we don't need any longer --- src/classes/Display.php | 5 - src/classes/Security.php | 342 --------------------------------- src/classes/Session.php | 125 ------------ tests/classes/SecurityTest.php | 153 --------------- tests/classes/SessionTest.php | 98 ---------- 5 files changed, 723 deletions(-) delete mode 100644 src/classes/Security.php delete mode 100644 src/classes/Session.php delete mode 100644 tests/classes/SecurityTest.php delete mode 100644 tests/classes/SessionTest.php diff --git a/src/classes/Display.php b/src/classes/Display.php index 59bd53c..2efc838 100644 --- a/src/classes/Display.php +++ b/src/classes/Display.php @@ -89,11 +89,6 @@ class Display extends Object if ($return_template && $this->module->templates) { - // Determines if we're using a custom class or not - $dynamic_class = (class_exists('CustomDynamic') ? 'CustomDynamic' : 'Dynamic'); - $form_class = (class_exists('CustomForm') ? 'CustomForm' : 'Form'); - $html_class = (class_exists('CustomHTML') ? 'CustomHTML' : 'HTML'); - // Exposes some objects and variables to the local scope of the template $this->request = $this->js_file = $_REQUEST['request']; $this->css_class = strtr($this->request, '/', '-'); diff --git a/src/classes/Security.php b/src/classes/Security.php deleted file mode 100644 index 3e2e4c5..0000000 --- a/src/classes/Security.php +++ /dev/null @@ -1,342 +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 - */ - -/** - * Security Class - * - * Collection of static methods for handling security within a website running - * on PICKLES. Requires sessions to be enabled. - * - * @usage Security::login(10); - * @usage Security::isLevel(SECURITY_LEVEL_ADMIN); - */ -class Security -{ - /** - * Lookup Cache - * - * Used to minimize database lookups - * - * @static - * @access private - * @var array - */ - private static $cache = []; - - /** - * Generate Hash - * - * Generates an SHA1 hash from the provided string. Salt optional. - * - * @param string $source value to hash - * @param mixed $salts optional salt or salts - * @return string SHA1 hash - * @todo Transition away from this - */ - public static function generateHash($source, $salts = null) - { - // Determines which salt(s) to use - if ($salts == null) - { - $config = Config::getInstance(); - - if (isset($config->security['salt']) && $config->security['salt'] != null) - { - $salts = $config->security['salt']; - } - else - { - $salts = ['P1ck73', 'Ju1C3']; - } - } - - // Forces the variable to be an array - if (!is_array($salts)) - { - $salts = [$salts]; - } - - // Loops through the salts, applies them and calculates the hash - $hash = $source; - - foreach ($salts as $salt) - { - $hash = sha1($salt . $hash); - } - - return $hash; - } - - /** - * Generate SHA-256 Hash - * - * Generates an SHA-256 hash from the provided string and salt. Borrowed the - * large iteration logic from fCryptography::hashWithSalt() as, and I quote, - * "makes rainbow table attacks infesible". - * - * @param string $source value to hash - * @param mixed $salt value to use as salt - * @return string SHA-256 hash - * @link https://github.com/flourishlib/flourish-classes/blob/master/fCryptography.php - */ - public static function generateSHA256Hash($source, $salt) - { - $sha256 = sha1($salt . $source); - - for ($i = 0; $i < 1000; $i++) - { - $sha256 = hash('sha256', $sha256 . (($i % 2 == 0) ? $source : $salt)); - } - - return $sha256; - } - - /** - * Check Session - * - * Checks if sessions are enabled. - * - * @static - * @access private - * @return boolean whether or not sessions are enabled - */ - private static function checkSession() - { - if (session_id() == '') - { - return false; - } - else - { - return true; - } - } - - /** - * Check Level - * - * Checks if a passed level is an integer and/or properly defined in the - * site's configuration file. - * - * @static - * @access private - * @param mixed access level to validate - * @return whether ot not the access level is valid - */ - private static function checkLevel(&$access_level) - { - return is_int($access_level); - } - - /** - * Login - * - * Creates a session variable containing the user ID and generated token. - * The token is also assigned to a cookie to be used when validating the - * security level. When the level value is present, the class will by pass - * the database look up and simply use that value when validating (the less - * paranoid scenario). - * - * @static - * @param integer $user_id ID of the user that's been logged in - * @param integer $level optional level for the user being logged in - * @param string $role textual representation of the user's level - * @return boolean whether or not the login could be completed - */ - public static function login($user_id, $level = null, $role = null) - { - if (self::checkSession()) - { - $token = sha1(microtime()); - - $_SESSION['__pickles']['security'] = [ - 'token' => $token, - 'user_id' => (int)$user_id, - 'level' => $level, - 'role' => $role, - ]; - - setcookie('pickles_security_token', $token); - - return true; - } - else - { - return false; - } - } - - /** - * Logout - * - * Clears out the security information in the session and the cookie. - * - * @static - * @return boolean true - */ - public static function logout() - { - if (isset($_SESSION['__pickles']['security'])) - { - $_SESSION['__pickles']['security'] = null; - unset($_SESSION['__pickles']['security']); - - setcookie('pickles_security_token', '', time() - 3600); - } - - return true; - } - - /** - * Get User Level - * - * Looks up the user level in the database and caches it. Cache is used - * for any subsequent look ups for the user. Also validates the session - * variable against the cookie to ensure everything is legit. If the user - * level is set in the session, that value will take precedence. - * - * return integer user level or false - */ - private static function getUserLevel() - { - if (self::checkSession() == true && isset($_SESSION['__pickles']['security']['user_id'])) - { - // Checks the session against the cookie - if (isset($_SESSION['__pickles']['security']['token'], $_COOKIE['pickles_security_token']) - && $_SESSION['__pickles']['security']['token'] != $_COOKIE['pickles_security_token']) - { - Security::logout(); - } - elseif (isset($_SESSION['__pickles']['security']['level']) && $_SESSION['__pickles']['security']['level'] != null) - { - return $_SESSION['__pickles']['security']['level']; - } - // Used to hit the database to determine the user's level, found it - // to be overkill and just opted for a simple logout. - else - { - Security::logout(); - } - } - - return false; - } - - /** - * Is Level - * - * Checks the user's access level is exactly the passed level - * - * @static - * @param integer $access_level access level to be checked against - * @return boolean whether or not the user is that level - */ - public static function isLevel() - { - $is_level = false; - - if (self::checkSession()) - { - $arguments = func_get_args(); - if (is_array($arguments[0])) - { - $arguments = $arguments[0]; - } - - foreach ($arguments as $access_level) - { - if (self::checkLevel($access_level)) - { - if (self::getUserLevel() == $access_level) - { - $is_level = true; - } - } - } - } - - return $is_level; - } - - /** - * Has Level - * - * Checks the user's access level against the passed level. - * - * @static - * @param integer $access_level access level to be checked against - * @return boolean whether or not the user has access - */ - public static function hasLevel() - { - $has_level = false; - - if (self::checkSession()) - { - $arguments = func_get_args(); - - if (is_array($arguments[0])) - { - $arguments = $arguments[0]; - } - - foreach ($arguments as $access_level) - { - if (self::checkLevel($access_level)) - { - if (self::getUserLevel() >= $access_level) - { - $has_level = true; - } - } - } - } - - return $has_level; - } - - /** - * Between Level - * - * Checks the user's access level against the passed range. - * - * @static - * @param integer $low access level to be checked against - * @param integer $high access level to be checked against - * @return boolean whether or not the user has access - */ - public static function betweenLevel($low, $high) - { - $between_level = false; - - if (self::checkSession()) - { - if (self::checkLevel($low) && self::checkLevel($high)) - { - $user_level = self::getUserLevel(); - - if ($user_level >= $low && $user_level <= $high) - { - $between_level = true; - } - } - } - - return $between_level; - } -} - diff --git a/src/classes/Session.php b/src/classes/Session.php deleted file mode 100644 index 62b6869..0000000 --- a/src/classes/Session.php +++ /dev/null @@ -1,125 +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 - */ - -/** - * Session Class - * - * Provides session handling via database instead of the file based session - * handling built into PHP. Using this class requires an array to be defined - * in place of the boolean true/false (on/off). If simply an empty array, the - * datasource will default to the value in $config['pickles']['datasource'] and - * if the table will default to "sessions". The format is as follows: - */ -class Session extends Object -{ - /** - * Constructor - * - * All of our set up logic for the session in contained here. This class is - * initially instantiated from pickles.php. Non-file handlers need to be - * configured in the site's config. MySQL support was dropped in favor of - * in memory stores or simply relying on file based sessions. Why? Because - * using MySQL for sessions is very write intensive and having done it in - * the past I don't recommend it. If you run a single server, files are - * good enough if your volume is lower. Memcache[d] is fine if you don't - * mind logging all of your users off your site when you restart the - * service and/or you run out of memory for the process. Redis is the best - * choice as it can be configured to be persistent and lives in memory. - * This is assuming you don't just want to roll your own sessions, which is - * pretty damn easy as well. - */ - public function __construct() - { - if (isset($_SERVER['REQUEST_METHOD'])) - { - parent::__construct(); - - // Sets up our configuration variables - if (isset($this->config->pickles['sessions'])) - { - $session = $this->config->pickles['sessions']; - } - - $datasources = $this->config->datasources; - $handler = 'files'; - $datasource = false; - - if (isset($session, $datasources[$session])) - { - $datasource = $datasources[$session]; - $handler = $datasource['type']; - - if ($handler != 'files') - { - if (isset($datasource['hostname'], $datasource['port'])) - { - $host = ($handler != 'memcached' ? 'tcp://' : '') - . $datasource['hostname'] . ':' . $datasource['port']; - } - else - { - throw new Exception('You must provide both the hostname and port for the datasource.'); - } - } - } - - switch ($handler) - { - case 'memcache': - ini_set('session.save_handler', 'memcache'); - ini_set('session.save_path', $host . '?persistent=1&weight=1&timeout=1&retry_interval=15'); - break; - - case 'memcached': - ini_set('session.save_handler', 'memcached'); - ini_set('session.save_path', $host); - break; - - case 'redis': - $save_path = $host . '?weight=1'; - - // Database ignored by phpredis when this was coded - if (isset($datasource['database'])) - { - $save_path .= '&database=' . $datasource['database']; - } - - if (isset($datasource['prefix'])) - { - $save_path .= '&prefix=' . $datasource['prefix']; - } - - ini_set('session.save_handler', 'redis'); - ini_set('session.save_path', $save_path); - break; - - case 'files': - ini_set('session.save_handler', 'files'); - break; - } - - // Don't start sessions for people without a user agent and bots. - if (isset($_SERVER['HTTP_USER_AGENT']) - && !String::isEmpty($_SERVER['HTTP_USER_AGENT']) - && !preg_match('/(Baidu|Gigabot|Googlebot|libwww-perl|lwp-trivial|msnbot|SiteUptime|Slurp|WordPress|ZIBB|ZyBorg)/i', $_SERVER['HTTP_USER_AGENT'])) - { - session_start(); - } - } - } -} - diff --git a/tests/classes/SecurityTest.php b/tests/classes/SecurityTest.php deleted file mode 100644 index 7f3d041..0000000 --- a/tests/classes/SecurityTest.php +++ /dev/null @@ -1,153 +0,0 @@ -assertEquals( - '4940e793006aa897db22751bba80dff4cb6a3e08', - Security::generateHash('source') - ); - } - - public function testGenerateHashWithCustomSalts() - { - $config = Config::getInstance(); - $config->data['security']['salt'] = 'salt'; - - $this->assertEquals( - '4eac88c934c33cfa9a80c0b2eb322f23ac3b13c5', - Security::generateHash('source') - ); - } - - public function testGenerateSHA256Hash() - { - $this->assertEquals( - '3d04f805aff4838ecaf98c7260a813fffd2b7a8a7f957add8018908a1bbdad04', - Security::generateSHA256Hash('source', 'salt') - ); - } - - public function testLogin() - { - $this->assertTrue(Security::login(1, 10, 'USER')); - $this->assertTrue(isset($_SESSION['__pickles']['security'])); - } - - public function testLoginNoSession() - { - session_destroy(); - $this->assertFalse(Security::login(1, 10, 'USER')); - } - - public function testLogout() - { - session_start(); - Security::login(1, 10, 'USER'); - - $this->assertTrue(Security::logout()); - $this->assertFalse(isset($_SESSION['__pickles']['security'])); - } - - public function testIsLevel() - { - Security::login(1, 10, 'USER'); - - $this->assertTrue(Security::isLevel(SECURITY_LEVEL_USER)); - } - - public function testIsLevelArray() - { - Security::login(1, 10, 'USER'); - - $this->assertTrue(Security::isLevel([SECURITY_LEVEL_USER, SECURITY_LEVEL_ADMIN])); - } - - public function testHasLevel() - { - Security::login(1, 10, 'USER'); - - $this->assertTrue(Security::hasLevel(SECURITY_LEVEL_USER)); - } - - public function testHasLevelArray() - { - Security::login(1, 10, 'USER'); - - $this->assertTrue(Security::hasLevel([SECURITY_LEVEL_USER, SECURITY_LEVEL_ADMIN])); - } - - public function testBetweenLevel() - { - Security::login(1, 10, 'USER'); - - $this->assertTrue(Security::betweenLevel(SECURITY_LEVEL_USER, SECURITY_LEVEL_ADMIN)); - } - - public function testTokenMismatch() - { - Security::login(1, 10, 'USER'); - - $_SESSION['__pickles']['security']['token'] = 'foo'; - $_COOKIE['pickles_security_token'] = 'bar'; - - $this->assertFalse(Security::isLevel(SECURITY_LEVEL_USER)); - } - - public function testIsLevelDB() - { - $config = Config::getInstance(); - - $config->data = [ - 'pickles' => [ - 'datasource' => 'mysql', - 'cache' => 'memcache', - ], - 'datasources' => [ - 'mysql' => [ - 'type' => 'mysql', - 'driver' => 'pdo_mysql', - 'hostname' => 'localhost', - 'username' => '', - 'password' => '', - 'database' => 'test', - 'cache' => true, - ], - 'memcache' => [ - 'type' => 'memcache', - 'hostname' => 'localhost', - 'port' => 11211, - 'namespace' => '', - ], - ], - 'security' => ['model' => 'MockUserModel'], - ]; - - $model = new MockUserModel(); - $model->record['username'] = 'pickles'; - $model->commit(); - - setUpConfig([ - - ]); - - new Config(); - - Security::login(1, 10, 'USER'); - - unset( - $_SESSION['__pickles']['security']['token'], - $_COOKIE['pickles_security_token'], - $_SESSION['__pickles']['security']['level'] - ); - - $this->assertFalse(Security::isLevel([SECURITY_LEVEL_USER, SECURITY_LEVEL_ADMIN])); - } -} - diff --git a/tests/classes/SessionTest.php b/tests/classes/SessionTest.php deleted file mode 100644 index 85759a4..0000000 --- a/tests/classes/SessionTest.php +++ /dev/null @@ -1,98 +0,0 @@ -data['pickles']['sessions'] = 'files'; - - new Session(); - - $_SESSION['test'] = 'files'; - $this->assertEquals('files', $_SESSION['test']); - } - - public function testMemcache() - { - $config = Config::getInstance(); - $config->data['pickles']['sessions'] = 'memcache'; - $config->data['datasources']['memcache'] = [ - 'type' => 'memcache', - 'hostname' => 'localhost', - 'port' => '11211', - ]; - - new Session(); - - $_SESSION['test'] = 'memcache'; - $this->assertEquals('memcache', $_SESSION['test']); - } - - public function testMemcached() - { - $config = Config::getInstance(); - $config->data['pickles']['sessions'] = 'memcached'; - $config->data['datasources']['memcached'] = [ - 'type' => 'memcached', - 'hostname' => 'localhost', - 'port' => '11211', - ]; - - new Session(); - - $_SESSION['test'] = 'memcached'; - $this->assertEquals('memcached', $_SESSION['test']); - } - - public function testRedis() - { - $config = Config::getInstance(); - $config->data['pickles']['sessions'] = 'redis'; - $config->data['datasources']['redis'] = [ - 'type' => 'redis', - 'hostname' => 'localhost', - 'port' => '6379', - 'database' => '1', - 'prefix' => 'p:', - ]; - - new Session(); - - $_SESSION['test'] = 'redis'; - $this->assertEquals('redis', $_SESSION['test']); - } - - /** - * @expectedException Exception - * @expectedExceptionMessage You must provide both the hostname and port for the datasource. - */ - public function testMissingHostname() - { - $_SERVER['REQUEST_METHOD'] = 'GET'; - - $config = Config::getInstance(); - $config->data['pickles']['sessions'] = 'redis'; - $config->data['datasources']['redis'] = [ - 'type' => 'redis', - 'port' => '6379', - ]; - - new Session(); - - $_SESSION['test'] = 'redis'; - $this->assertEquals('redis', $_SESSION['test']); - } -} - From 2fa2b6ad03be6428dec44c89144096c052d08470 Mon Sep 17 00:00:00 2001 From: Josh Sherman Date: Wed, 24 Sep 2014 23:57:39 -0400 Subject: [PATCH 006/129] Started gutting logic from the Controller and Display classes. --- src/classes/Controller.php | 207 ------------------------------------- src/classes/Display.php | 95 ++++------------- src/pickles.php | 18 ---- 3 files changed, 21 insertions(+), 299 deletions(-) diff --git a/src/classes/Controller.php b/src/classes/Controller.php index 54d0d3f..015df27 100644 --- a/src/classes/Controller.php +++ b/src/classes/Controller.php @@ -38,55 +38,8 @@ class Controller extends Object { parent::__construct(); - // Generate a generic "site down" message if the site is set to be disabled try { - // @todo Clean this up to be just a single sanity check - if (isset($this->config->pickles['disabled']) && $this->config->pickles['disabled']) - { - $custom_template = SITE_TEMPLATE_PATH . '__shared/maintenance.phtml'; - - if (file_exists($custom_template)) - { - require_once $custom_template; - } - else - { - echo ' -

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 - '; - } - - throw new Exception(); - } - - // Checks for attributes passed in the URI - if (strstr($_REQUEST['request'], ':')) - { - $parts = explode('/', $_REQUEST['request']); - $_REQUEST['request'] = ''; - - foreach ($parts as $part) - { - 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']); @@ -132,109 +85,6 @@ class Controller extends Object 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])) - { - $module_security_levels = 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 - { - $is_authenticated = Security::isLevel($module->security); - } - - if (!$is_authenticated) - { - if ($_SERVER['REQUEST_METHOD'] == 'POST') - { - // @todo Perhaps I could force a logout / redirect to the login page - Browser::status(401); - - throw new Exception(json_encode([ - 'status' => 401, - 'message' => 'You are not properly authenticated, try logging out and back in.', - ])); - } - else - { - // Sets variable for the destination - $_SESSION['__pickles']['login']['destination'] = $_REQUEST['request'] ? $_REQUEST['request'] : '/'; - - // Redirect to login page - Browser::redirect('/login'); - - // Resolves testing error due to undefined $output - $output = ''; - } - } - } - // Gets the profiler status $profiler = $this->config->pickles['profiler']; $profiler = $profiler === true || stripos($profiler, 'timers') !== false; @@ -242,16 +92,6 @@ class Controller extends Object $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 // @todo Seems a bit redundant, refactor if ($default_method == $role_method || method_exists($module, $default_method)) @@ -331,28 +171,6 @@ class Controller extends Object Profiler::timer('module ' . $default_method); } - // 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($module); } @@ -384,30 +202,5 @@ class Controller extends Object Profiler::report(); } } - - // @todo Document me - private function validateTemplates(&$module, $parent_template) - { - $templates = [ - SITE_TEMPLATE_PATH . '__shared/' . $parent_template . '.phtml', - SITE_TEMPLATE_PATH . $_REQUEST['request'] . '.phtml', - ]; - - $module->template = []; - $child_exists = file_exists($templates[1]); - - if (file_exists($templates[0]) && $child_exists) - { - $module->template = $templates; - return true; - } - elseif ($child_exists) - { - $module->template = [$templates[1]]; - return true; - } - - return false; - } } diff --git a/src/classes/Display.php b/src/classes/Display.php index 2efc838..66e72bc 100644 --- a/src/classes/Display.php +++ b/src/classes/Display.php @@ -49,9 +49,8 @@ class Display extends Object $this->module->response = [$this->module->response]; } - $return_json = false; - $return_template = false; - $return_xml = false; + $return_json = false; + $return_xml = false; foreach ($this->module->output as $return) { @@ -60,7 +59,7 @@ class Display extends Object } // Makes sure the return type is valid - if (!$return_json && !$return_template && !$return_xml) + if (!$return_json && !$return_xml) { throw new Exception('Invalid return type.'); } @@ -85,83 +84,31 @@ class Display extends Object throw new Exception('Requested URI contains PHPSESSID, redirecting.'); } - $loaded = false; + $response = [ + 'meta' => [ + 'status' => $this->module->status, + 'message' => $this->module->message, + ], + ]; - if ($return_template && $this->module->templates) + if ($this->module->response) { - // Exposes some objects and variables to the local scope of the template - $this->request = $this->js_file = $_REQUEST['request']; - $this->css_class = strtr($this->request, '/', '-'); - - $this->dynamic = new $dynamic_class(); - $this->form = new $form_class(); - $this->html = new $html_class(); - - // Checks for the parent template and tries to load it - if ($this->module->template) - { - $profiler = $this->config->pickles['profiler']; - $profiler = $profiler === true || stripos($profiler, 'timers') !== false; - - // Starts a timer for the loading of the template - if ($profiler) - { - Profiler::timer('loading template'); - } - - // Assigns old variable - $required_template = $this->module->templates[0]; - $this->module->template = end($this->module->templates); - $loaded = require_once $required_template; - - // Stops the template loading timer - if ($profiler) - { - Profiler::timer('loading template'); - } - } + $response['response'] = $this->module->response; } - if (!$loaded) + if ($return_json) { - if (!$return_template || !$this->module->templates) - { - $meta = [ - 'status' => $this->module->status, - 'message' => $this->module->message, - ]; - - $response = [ - 'meta' => $meta, - 'response' => $this->module->response, - ]; - } - - if ($return_json) - { - header('Content-type: application/json'); - $pretty = isset($_REQUEST['pretty']) ? JSON_PRETTY_PRINT : false; - echo json_encode($response, $pretty); - } - elseif ($return_xml) - { - header('Content-type: text/xml'); - echo Convert::arrayToXML($response, isset($_REQUEST['pretty'])); - } + header('Content-type: application/json'); + $pretty = isset($_REQUEST['pretty']) ? JSON_PRETTY_PRINT : false; + echo json_encode($response, $pretty); + } + elseif ($return_xml) + { + header('Content-type: text/xml'); + echo Convert::arrayToXML($response, isset($_REQUEST['pretty'])); } - // Grabs the buffer so we can massage it a bit - $buffer = ob_get_clean(); - - // Kills any whitespace and HTML comments in templates - if ($loaded) - { - // The BSA exception is because their system sucks and demands - // there be comments present - $buffer = preg_replace(['/^[\s]+/m', '//U'], '', $buffer); - } - - return $buffer; + return ob_get_clean(); } catch (Exception $e) { diff --git a/src/pickles.php b/src/pickles.php index accdec6..ae2e4ad 100644 --- a/src/pickles.php +++ b/src/pickles.php @@ -132,24 +132,6 @@ if (is_array($config->php) && count($config->php)) } } -// Starts session handling (old) -if (isset($config->pickles['session'])) -{ - if (session_id() == '' && $config->pickles['session'] !== false) - { - new Session(); - } -} - -// Starts session handling (new) -if (isset($config->pickles['sessions'])) -{ - if (session_id() == '' && $config->pickles['sessions'] !== false) - { - new Session(); - } -} - // }}} // {{{ Defaults some internals for ease of use From b67269a2023080445d11b6b135fff3d2b91d1634 Mon Sep 17 00:00:00 2001 From: Josh Sherman Date: Thu, 25 Sep 2014 09:27:59 -0400 Subject: [PATCH 007/129] Dropped that dirty dirty XML support. --- src/classes/Display.php | 29 +++-------------------------- 1 file changed, 3 insertions(+), 26 deletions(-) diff --git a/src/classes/Display.php b/src/classes/Display.php index 66e72bc..be82a21 100644 --- a/src/classes/Display.php +++ b/src/classes/Display.php @@ -49,21 +49,6 @@ class Display extends Object $this->module->response = [$this->module->response]; } - $return_json = false; - $return_xml = false; - - foreach ($this->module->output as $return) - { - $variable = 'return_' . $return; - $$variable = true; - } - - // Makes sure the return type is valid - if (!$return_json && !$return_xml) - { - throw new Exception('Invalid return type.'); - } - // Checks for the PHPSESSID in the query string if (stripos($_SERVER['REQUEST_URI'], '?PHPSESSID=') === false) { @@ -96,17 +81,9 @@ class Display extends Object $response['response'] = $this->module->response; } - if ($return_json) - { - header('Content-type: application/json'); - $pretty = isset($_REQUEST['pretty']) ? JSON_PRETTY_PRINT : false; - echo json_encode($response, $pretty); - } - elseif ($return_xml) - { - header('Content-type: text/xml'); - echo Convert::arrayToXML($response, isset($_REQUEST['pretty'])); - } + header('Content-type: application/json'); + $pretty = isset($_REQUEST['pretty']) ? JSON_PRETTY_PRINT : false; + echo json_encode($response, $pretty); return ob_get_clean(); } From 17d19728e36f4d8a2ab3c1030e0752e7159a91d3 Mon Sep 17 00:00:00 2001 From: Josh Sherman Date: Thu, 25 Sep 2014 10:06:11 -0400 Subject: [PATCH 008/129] Update README.md Tweaked buttons --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 32310c5..cfbfcb4 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ +# PICKLES + +[![Build Status](https://travis-ci.org/joshtronic/pickles.png?branch=master)](https://travis-ci.org/joshtronic/pickles) [![Coverage Status](https://coveralls.io/repos/joshtronic/pickles/badge.png)](https://coveralls.io/r/joshtronic/pickles) [![Dependency Status](https://www.versioneye.com/user/projects/52d1bc1eec13751bde00002a/badge.png)](https://www.versioneye.com/user/projects/52d1bc1eec13751bde00002a) [![Stories in Ready](https://badge.waffle.io/joshtronic/pickles.png?label=ready&title=Ready)](https://waffle.io/joshtronic/pickles) -# PICKLES [![Build Status](https://travis-ci.org/joshtronic/pickles.png?branch=master)](https://travis-ci.org/joshtronic/pickles) [![Coverage Status](https://coveralls.io/repos/joshtronic/pickles/badge.png)](https://coveralls.io/r/joshtronic/pickles) [![Dependency Status](https://www.versioneye.com/user/projects/52d1bc1eec13751bde00002a/badge.png)](https://www.versioneye.com/user/projects/52d1bc1eec13751bde00002a) PICKLES (PHP Interface Collection of Killer Libraries to Enhance Stuff) is an open source framework for rapid PHP development. PICKLES aims to be an “API From 94f46fc583dce4625ae6f4a5e62969daf494f044 Mon Sep 17 00:00:00 2001 From: Josh Sherman Date: Thu, 25 Sep 2014 20:55:11 -0400 Subject: [PATCH 009/129] 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 010/129] 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 011/129] 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 012/129] 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; - } -} - From 39d5b8d40ba16c580d7436ad034d46e27e2468c0 Mon Sep 17 00:00:00 2001 From: Josh Sherman Date: Thu, 25 Sep 2014 23:11:29 -0400 Subject: [PATCH 013/129] Cleaned up unit tests Wanted to get back to the point that the tests were running and not erroring. --- src/classes/Resource.php | 2 +- tests/classes/ControllerTest.php | 358 ------------------ tests/classes/DisplayTest.php | 132 ------- tests/classes/DynamicTest.php | 120 ------ .../{ModuleTest.php => ResourceTest.php} | 20 +- tests/classes/RouterTest.php | 230 +++++++++++ 6 files changed, 241 insertions(+), 621 deletions(-) delete mode 100644 tests/classes/ControllerTest.php delete mode 100644 tests/classes/DisplayTest.php delete mode 100644 tests/classes/DynamicTest.php rename tests/classes/{ModuleTest.php => ResourceTest.php} (72%) create mode 100644 tests/classes/RouterTest.php diff --git a/src/classes/Resource.php b/src/classes/Resource.php index 4723468..2dd893c 100644 --- a/src/classes/Resource.php +++ b/src/classes/Resource.php @@ -25,7 +25,7 @@ * be JSON encoded. In the future this may need to be changed out for logic * that allows the requested module to specify what display type(s) it can use. */ -abstract class Resource extends Object +class Resource extends Object { /** * Secure diff --git a/tests/classes/ControllerTest.php b/tests/classes/ControllerTest.php deleted file mode 100644 index cdb99e2..0000000 --- a/tests/classes/ControllerTest.php +++ /dev/null @@ -1,358 +0,0 @@ -config = Config::getInstance(); - $this->config->data['pickles']['disabled'] = false; - $this->config->data['pickles']['profiler'] = false; - $this->config->data['security']['levels'][10] = 'USER'; - $this->config->data['security']['levels'][20] = 'ADMIN'; - - setUpRequest('home'); - - $module = 'config->data['pickles']['disabled'] = true; - - $this->expectOutputRegex('/Test Server is currently down for maintenance/'); - - new Controller(); - } - - public function testCustomSiteDown() - { - $this->config->data['pickles']['disabled'] = true; - - file_put_contents(SITE_TEMPLATE_PATH . '__shared/maintenance.phtml', '

Custom Down for Maintenance

'); - - new Controller(); - - $this->expectOutputRegex('/

Custom Down for Maintenance<\/h1>/'); - } - - public function testAttributesInURI() - { - setUpRequest('home/id:123'); - - new Controller(); - - $this->assertEquals(123, Browser::get('id')); - - setUpRequest('home/id:456/foo:bar'); - - new Controller(); - - // Compensates for 2 empty template executions of the Controller - $this->expectOutputString('[][]'); - $this->assertEquals(456, Browser::get('id')); - $this->assertEquals('bar', Browser::get('foo')); - } - - public function testUpperCaseURI() - { - setUpRequest('TESTING'); - - new Controller(); - - $this->assertTrue(in_array('Location: /testing', xdebug_get_headers())); - } - - public function testForceSecure() - { - setUpRequest('secure'); - - $module = 'assertTrue(in_array('Location: https://testsite.com/secure', xdebug_get_headers())); - } - - public function testForceInsecure() - { - setUpRequest('insecure'); - $_SERVER['HTTPS'] = 'on'; - - $module = 'assertTrue(in_array('Location: http://testsite.com/insecure', xdebug_get_headers())); - } - - public function testNotAuthenticated() - { - setUpRequest('notauth'); - - $module = 'expectOutputString('[]'); - - $this->assertTrue(in_array('Location: http://testsite.com/login', xdebug_get_headers())); - } - - public function testSecurityArray() - { - setUpRequest('securityarray'); - - $module = 'expectOutputString('[]'); - - $this->assertTrue(in_array('Location: http://testsite.com/login', xdebug_get_headers())); - } - - public function testSecurityArrayTypeString() - { - setUpRequest('securityarraytypestring'); - - $module = ' "IS", "level" => SECURITY_LEVEL_USER]; }'; - - file_put_contents(SITE_MODULE_PATH . 'securityarraytypestring.php', $module); - - new Controller(); - - // Compensates for an empty template due to exit() being skipped - $this->expectOutputString('[]'); - - $this->assertTrue(in_array('Location: http://testsite.com/login', xdebug_get_headers())); - } - - public function testSecurityArrayTypeArray() - { - setUpRequest('securityarraytypearray'); - - $module = ' "IS", "level" => [SECURITY_LEVEL_USER, SECURITY_LEVEL_ADMIN]]; }'; - - file_put_contents(SITE_MODULE_PATH . 'securityarraytypearray.php', $module); - - new Controller(); - - // Compensates for an empty template due to exit() being skipped - $this->expectOutputString('[]'); - - $this->assertTrue(in_array('Location: http://testsite.com/login', xdebug_get_headers())); - } - - public function testSecurityArrayTypeBetween() - { - setUpRequest('securityarraytypebetween'); - - $module = ' "BETWEEN", "levels" => [SECURITY_LEVEL_USER, SECURITY_LEVEL_ADMIN]]; }'; - - file_put_contents(SITE_MODULE_PATH . 'securityarraytypebetween.php', $module); - - new Controller(); - - // Compensates for an empty template due to exit() being skipped - $this->expectOutputString('[]'); - - $this->assertTrue(in_array('Location: http://testsite.com/login', xdebug_get_headers())); - } - - public function testSecurityArrayTypeHas() - { - setUpRequest('securityarraytypehas'); - - $module = ' "HAS", "level" => SECURITY_LEVEL_USER]; }'; - - file_put_contents(SITE_MODULE_PATH . 'securityarraytypehas.php', $module); - - new Controller(); - - // Compensates for an empty template due to exit() being skipped - $this->expectOutputString('[]'); - - $this->assertTrue(in_array('Location: http://testsite.com/login', xdebug_get_headers())); - } - - public function testNotAuthenticatedPOST() - { - setUpRequest('notauthpost', 'POST'); - - $module = 'expectOutputRegex('/You are not properly authenticated/'); - } - - public function testAuthenticated() - { - setUpRequest('auth'); - - $module = ' "bar"]; }' - . '}'; - - file_put_contents(SITE_MODULE_PATH . 'auth.php', $module); - - Security::login(1, 10, 'USER'); - new Controller(); - - $this->expectOutputString('{"foo":"bar"}'); - } - - public function testRoleDefaultMethod() - { - setUpRequest('rolemethod'); - - $module = ' "bar"]; }' - . 'public function __default_USER() { return ["user" => "me"]; }' - . '}'; - - file_put_contents(SITE_MODULE_PATH . 'rolemethod.php', $module); - - Security::login(1, 10, 'USER'); - new Controller(); - - $this->expectOutputString('{"user":"me"}'); - } - - public function testValidRequestMethod() - { - setUpRequest('validrequestmethod'); - - $module = ' "bar"]; }' - . '}'; - - file_put_contents(SITE_MODULE_PATH . 'validrequestmethod.php', $module); - - new Controller(); - - $this->expectOutputString('{"foo":"bar"}'); - } - - public function testInvalidRequestMethod() - { - setUpRequest('invalidrequestmethod'); - - $module = ' "bar"]; }' - . '}'; - - file_put_contents(SITE_MODULE_PATH . 'invalidrequestmethod.php', $module); - - new Controller(); - - $this->expectOutputString('{"status":"error","message":"There was a problem with your request method."}'); - } - - public function testValidationErrors() - { - setUpRequest('validationerrors'); - - $module = ' "bar"]; }' - . '}'; - - file_put_contents(SITE_MODULE_PATH . 'validationerrors.php', $module); - - new Controller(); - - $this->expectOutputString('{"status":"error","message":"The test field is required."}'); - } - - public function testError404() - { - setUpRequest('fourohfour'); - - new Controller(); - - $this->assertTrue(in_array('Status: 404 Not Found', xdebug_get_headers())); - $this->expectOutputRegex('/

Not Found<\/h1>/'); - } - - public function testCustomError404() - { - setUpRequest('customfourohfour'); - - file_put_contents(SITE_TEMPLATE_PATH . '__shared/404.phtml', '

Custom Not Found

'); - - new Controller(); - - $this->assertTrue(in_array('Status: 404 Not Found', xdebug_get_headers())); - $this->expectOutputRegex('/

Custom Not Found<\/h1>/'); - } - - public function testProfilerOutput() - { - $this->config->data['pickles']['profiler'] = true; - - $this->expectOutputRegex('/id="pickles-profiler"/'); - - new Controller(); - } - - public function testTwoValidTemplates() - { - $this->config->data['pickles']['profiler'] = true; - - setUpRequest('validtemplates'); - - $module = 'child template'); - - // Vim syntax highlighting borks unless ----v - $child = 'template; ?' . '>' . "\n"; - - $html = << - - -

parent template

- {$child} - - -HTML; - - file_put_contents(SITE_TEMPLATE_PATH . '__shared/index.phtml', $html); - - new Controller(); - - $this->expectOutputRegex('/^ - - -

parent template<\/h1> -
child template<\/div> -<\/body> -<\/html>.+ -
- PICKLES Profiler

- There is nothing to profile. This often happens when the profiler configuration is set to either "queries" or "explains" and there are no database queries on the page (common on pages that only have a template). You may want to set the profiler to boolean true to ensure you get a profile of the page.'; - } - else - { - $start_time = $_SERVER['REQUEST_TIME_FLOAT']; - $peak_usage = self::formatSize(memory_get_peak_usage()); - $end_time = self::$profile[count(self::$profile) - 1]['time']; // @todo No idea what though? - $duration = ($end_time - $start_time); + $report = [ + 'request_time' => $_SERVER['REQUEST_TIME_FLOAT'], + 'execution_time' => self::$logs[count(self::$logs) - 1]['timestamp'] + - $_SERVER['REQUEST_TIME_FLOAT'], + 'peak_memory_usage' => memory_get_peak_usage(), + 'max_execution_time' => ini_get('max_execution_time'), + 'memory_limit' => ini_get('memory_limit'), + 'included_files' => count(get_included_files()), + 'logs' => self::$logs, + ]; - $logs = count(self::$profile); - $logs .= ' Log' . ($logs == 1 ? '' : 's'); + self::$logs = []; + self::$timers = []; - $files = count(get_included_files()); - $files .= ' File' . ($files == 1 ? '' : 's'); - - $queries = self::$queries . ' Quer'. (self::$queries == 1 ? 'y' : 'ies'); - ?> - - - - - - - - -
- Console -
-
- Load Time -
-
- Memory Usage -
-
- Database -
-
- Includes -
-
- - - - - - - $entry) - { - ?> - - - - - - - -
ConsoleMemoryTime
ms
- -
-

- config['pickles']['profiler']) { - Profiler::timer('resource ' . $method); + $timer = get_class($this) . '->' . $method . '()'; + Profiler::timer($timer); } - */ $this->response = $this->$method(); - /* // Stops the resource timer if ($this->config['pickles']['profiler']) { - Profiler::timer('resource ' . $method); + Profiler::timer($timer); } - */ } } } @@ -402,6 +399,11 @@ class Resource extends Object } } + if ($this->config['pickles']['profiler']) + { + $response['profiler'] = Profiler::report(); + } + $pretty = isset($_REQUEST['pretty']) ? JSON_PRETTY_PRINT : false; echo json_encode($response, $pretty); From 9e65b2cfc20f5a34b689a0e8969865cd281d55d2 Mon Sep 17 00:00:00 2001 From: Josh Sherman Date: Wed, 1 Oct 2014 22:08:11 -0400 Subject: [PATCH 055/129] Spruced up profiler, need to finish up tests --- src/Database.php | 28 +++++++++++++++++----------- src/Profiler.php | 20 +++++++++++--------- tests/ProfilerTest.php | 26 +++----------------------- 3 files changed, 31 insertions(+), 43 deletions(-) diff --git a/src/Database.php b/src/Database.php index c5a71e8..9985df4 100644 --- a/src/Database.php +++ b/src/Database.php @@ -302,7 +302,7 @@ class Database extends Object * @param array $input_parameters optional key/values to be bound * @return integer ID of the last inserted row or sequence number */ - public function execute($sql, $input_parameters = null) + public function execute($sql, $input_parameters = null, $explain = false) { $this->open(); @@ -314,20 +314,20 @@ class Database extends Object // Establishes if we're working on an EXPLAIN if ($this->config['pickles']['profiler']) { - $explain = preg_match('/^SELECT /i', $sql); + $explain_results = preg_match('/^SELECT /i', $sql); } else { - $explain = false; + $explain_results = false; } // Executes a standard query if ($input_parameters === null) { // Explains the query - if ($explain) + if ($explain_results) { - $explain = $this->fetch('EXPLAIN ' . $sql); + $explain_results = $this->fetch('EXPLAIN ' . $sql, null, true); } $start_time = microtime(true); @@ -337,9 +337,9 @@ class Database extends Object else { // Explains the query - if ($explain) + if ($explain_results) { - $explain = $this->fetch('EXPLAIN ' . $sql, $input_parameters); + $explain_results = $this->fetch('EXPLAIN ' . $sql, $input_parameters, true); } $start_time = microtime(true); @@ -351,9 +351,15 @@ class Database extends Object $duration = $end_time - $start_time; // Logs the information to the profiler - if ($this->config['pickles']['profiler']) + if ($this->config['pickles']['profiler'] && !$explain) { - Profiler::logQuery($sql, $input_parameters, $explain, $duration); + Profiler::query( + $sql, + $input_parameters, + $this->results->fetchAll(\PDO::FETCH_ASSOC), + $duration, + $explain_results + ); } } else @@ -372,13 +378,13 @@ class Database extends Object * @param string $return_type optional type of return set * @return mixed based on return type */ - public function fetch($sql = null, $input_parameters = null) + public function fetch($sql = null, $input_parameters = null, $explain = false) { $this->open(); if ($sql !== null) { - $this->execute($sql, $input_parameters); + $this->execute($sql, $input_parameters, $explain); } // Pulls the results based on the type diff --git a/src/Profiler.php b/src/Profiler.php index dc7b1fa..a2d4017 100644 --- a/src/Profiler.php +++ b/src/Profiler.php @@ -73,10 +73,6 @@ class Profiler // Tidys the data by type switch ($data_type) { - case 'array': - $details = print_r($data, true); - break; - case 'object': $details['class'] = get_class($data); @@ -93,7 +89,6 @@ class Profiler $data_type = $data_type; break; - case 'string': default: if ($type != false) { @@ -114,17 +109,18 @@ class Profiler } /** - * Log Query + * Query * * Serves as a wrapper to get query data to the log function * * @static * @param string $query the query being executed * @param array $input_parameters optional prepared statement data - * @param array $explain EXPLAIN data for the query + * @param array $results optional results of the query * @param float $duration the speed of the query + * @param array $explain EXPLAIN data for the query */ - public static function logQuery($query, $input_parameters = false, $explain = false, $duration = false) + public static function query($query, $input_parameters = false, $results = false, $duration = false, $explain = false) { $log = []; @@ -156,7 +152,13 @@ class Profiler $log .= 'query_time: ' . $duration; */ - $log = [$query, $input_parameters, $explain, $duration]; + $log = [ + 'query' => $query, + 'parameters' => $input_parameters, + 'results' => $results, + 'execution_time' => $duration, + 'explain' => $explain, + ]; self::log($log, false, 'database'); } diff --git a/tests/ProfilerTest.php b/tests/ProfilerTest.php index 6dadbac..9653c3b 100644 --- a/tests/ProfilerTest.php +++ b/tests/ProfilerTest.php @@ -9,26 +9,6 @@ class ProfilerTest extends PHPUnit_Framework_TestCase Pickles\Profiler::report(); } - public function testDisabledType() - { - $config = Pickles\Config::getInstance(); - $config->data['pickles']['profiler'] = false; - - $this->assertFalse(Pickles\Profiler::enabled('timers')); - } - - public function testTimerDisabled() - { - $this->assertFalse(Pickles\Profiler::timer('disabled')); - } - - public function testReportNothing() - { - $this->expectOutputRegex('/There is nothing to profile/'); - - Pickles\Profiler::report(); - } - public function testEnabled() { $config = Pickles\Config::getInstance(); @@ -67,9 +47,9 @@ class ProfilerTest extends PHPUnit_Framework_TestCase ], ]; - Pickles\Profiler::logQuery('SELECT * FROM table;'); - Pickles\Profiler::logQuery('SELECT * FROM table WHERE column = ?;', ['foo']); - Pickles\Profiler::logQuery('SELECT * FROM table;', false, $explain); + Pickles\Profiler::query('SELECT * FROM table;'); + Pickles\Profiler::query('SELECT * FROM table WHERE column = ?;', ['foo']); + Pickles\Profiler::query('SELECT * FROM table;', false, $explain); } public function testTimer() From 173136ddce507e76dc917e9e261a99f585f67ac3 Mon Sep 17 00:00:00 2001 From: Josh Sherman Date: Thu, 2 Oct 2014 06:42:16 -0400 Subject: [PATCH 056/129] Finished up Profiler and test coverage --- src/Profiler.php | 42 ++++-------------- tests/ProfilerTest.php | 96 +++++++++++++++++++++--------------------- 2 files changed, 57 insertions(+), 81 deletions(-) diff --git a/src/Profiler.php b/src/Profiler.php index a2d4017..3277fe2 100644 --- a/src/Profiler.php +++ b/src/Profiler.php @@ -122,44 +122,18 @@ class Profiler */ public static function query($query, $input_parameters = false, $results = false, $duration = false, $explain = false) { - $log = []; - - /* - if ($input_parameters != 'false' && is_array($input_parameters)) - { - foreach ($input_parameters as $key => $value) - { - $log .= $key . ' => ' . $value; - - $query = str_replace($key, $key, $query); - } - } - - $log = $query . ' ' . $log; - - if (is_array($explain)) - { - foreach ($explain as $table) - { - $log .= 'Possible Keys => ' . ($table['possible_keys'] == '' ? 'NONE' : $table['possible_keys']) - . 'Key => ' . ($table['key'] == '' ? 'NONE' : $table['key']) - . 'Type => ' . $table['type'] - . 'Rows => ' . $table['rows'] - . ($table['Extra'] != '' ? 'Extra => ' . $table['Extra'] : ''); - } - } - - $log .= 'query_time: ' . $duration; - */ - $log = [ - 'query' => $query, - 'parameters' => $input_parameters, - 'results' => $results, + 'query' => $query, + 'parameters' => $input_parameters, + 'results' => $results, 'execution_time' => $duration, - 'explain' => $explain, ]; + if ($explain) + { + $log['explain'] = $explain; + } + self::log($log, false, 'database'); } diff --git a/tests/ProfilerTest.php b/tests/ProfilerTest.php index 9653c3b..140d240 100644 --- a/tests/ProfilerTest.php +++ b/tests/ProfilerTest.php @@ -2,60 +2,62 @@ class ProfilerTest extends PHPUnit_Framework_TestCase { - public function testReport() + public function testProfiler() { - $this->expectOutputRegex('//'); + $_SERVER['REQUEST_METHOD'] = 'GET'; + $_SERVER['SERVER_NAME'] = '127.0.0.1'; - Pickles\Profiler::report(); - } + file_put_contents('/tmp/pickles.php', ' [ + "local" => "127.0.0.1", + "production" => "123.456.789.0", + ], + "pickles" => [ + "profiler" => true, + ], + ]; + '); - public function testEnabled() - { - $config = Pickles\Config::getInstance(); - $config->data['pickles']['profiler'] = true; + new Pickles\Config('/tmp/pickles.php'); - $this->assertTrue(Pickles\Profiler::enabled()); - } - - public function testEnabledType() - { - $config = Pickles\Config::getInstance(); - $config->data['pickles']['profiler'] = 'timers'; - - $this->assertTrue(Pickles\Profiler::enabled('timers')); - } - - public function testLogAndTimer() - { - Pickles\Profiler::log('timer', 'timer-one'); + Pickles\Profiler::log('i am a string'); Pickles\Profiler::log(['foo' => 'bar']); - Pickles\Profiler::log(new Pickles\Object); - Pickles\Profiler::log('string'); - Pickles\Profiler::log(3.14, 'method', true); - Pickles\Profiler::log('timer', 'timer-one'); - } + Pickles\Profiler::log($this, 'testProfiler'); + Pickles\Profiler::timer('swatch'); + Pickles\Profiler::query('SELECT', ['foo' => 'bar'], ['results'], 1, 'EXPLAIN'); + Pickles\Profiler::timer('swatch'); + Pickles\Profiler::query('SELECT', ['foo' => 'bar'], ['results'], 1); - public function testLogQuery() - { - $explain = [ - [ - 'key' => '', - 'possible_keys' => '', - 'type' => '', - 'rows' => '', - 'Extra' => '', - ], - ]; + $report = Pickles\Profiler::report(); - Pickles\Profiler::query('SELECT * FROM table;'); - Pickles\Profiler::query('SELECT * FROM table WHERE column = ?;', ['foo']); - Pickles\Profiler::query('SELECT * FROM table;', false, $explain); - } - - public function testTimer() - { - Pickles\Profiler::timer('timer-two'); - Pickles\Profiler::timer('timer-two'); + $this->assertEquals(7, count($report)); + $this->assertEquals(7, count($report['logs'])); + $this->assertEquals(5, count($report['logs'][0])); + $this->assertEquals('string', $report['logs'][0]['type']); + $this->assertEquals('i am a string', $report['logs'][0]['details']); + $this->assertEquals('array', $report['logs'][1]['type']); + $this->assertEquals(['foo' => 'bar'], $report['logs'][1]['details']); + $this->assertEquals('object', $report['logs'][2]['type']); + $this->assertEquals(['class' => 'ProfilerTest', 'method' => 'testProfiler()'], $report['logs'][2]['details']); + $this->assertEquals('timer', $report['logs'][3]['type']); + $this->assertEquals('swatch', $report['logs'][3]['details']['name']); + $this->assertEquals('start', $report['logs'][3]['details']['action']); + $this->assertEquals('database', $report['logs'][4]['type']); + $this->assertEquals('SELECT', $report['logs'][4]['details']['query']); + $this->assertEquals(['foo' => 'bar'], $report['logs'][4]['details']['parameters']); + $this->assertEquals(['results'], $report['logs'][4]['details']['results']); + $this->assertEquals(1, $report['logs'][4]['details']['execution_time']); + $this->assertEquals('EXPLAIN', $report['logs'][4]['details']['explain']); + $this->assertEquals('timer', $report['logs'][5]['type']); + $this->assertEquals('swatch', $report['logs'][5]['details']['name']); + $this->assertEquals('stop', $report['logs'][5]['details']['action']); + $this->assertEquals('database', $report['logs'][6]['type']); + $this->assertEquals('SELECT', $report['logs'][6]['details']['query']); + $this->assertEquals(['foo' => 'bar'], $report['logs'][6]['details']['parameters']); + $this->assertEquals(['results'], $report['logs'][6]['details']['results']); + $this->assertEquals(1, $report['logs'][6]['details']['execution_time']); + $this->assertFalse(isset($report['logs'][6]['details']['explain'])); } } From 07ed22c58f01f60cebf512e08afc5758d93d84d1 Mon Sep 17 00:00:00 2001 From: Josh Sherman Date: Thu, 2 Oct 2014 07:02:29 -0400 Subject: [PATCH 057/129] Fixed up resource class' tests --- src/Config.php | 2 +- tests/ResourceTest.php | 57 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 1 deletion(-) diff --git a/src/Config.php b/src/Config.php index 35ce774..0d3f226 100644 --- a/src/Config.php +++ b/src/Config.php @@ -139,7 +139,7 @@ class Config extends \ArrayObject $config['environment'] = $environment; // Defaults expected Pickles variables to false - foreach (['cache', 'profiler'] as $variable) + foreach (['auth', 'cache', 'profiler'] as $variable) { if (!isset($config['pickles'][$variable])) { diff --git a/tests/ResourceTest.php b/tests/ResourceTest.php index d9704b9..eeb7d24 100644 --- a/tests/ResourceTest.php +++ b/tests/ResourceTest.php @@ -67,6 +67,32 @@ namespace { class ResourceTest extends PHPUnit_Framework_TestCase { + public function setUp() + { + $_SERVER['REQUEST_METHOD'] = 'GET'; + $_SERVER['SERVER_NAME'] = '127.0.0.1'; + + file_put_contents('/tmp/pickles.php', ' [ + "local" => "127.0.0.1", + "production" => "123.456.789.0", + ], + "pickles" => [ + "namespace" => "", + "datasource" => "mysql", + ], + "datasources" => [ + "mysql" => [ + "driver" => "pdo_mysql", + ], + ], + ]; + '); + + Pickles\Config::getInstance('/tmp/pickles.php'); + } + public function testFilterAndValidate() { $response = json_encode([ @@ -211,6 +237,37 @@ namespace new Pickles\Router(); } + + public function testProfiler() + { + $this->expectOutputRegex('/"profiler":{/'); + + file_put_contents('/tmp/pickles.php', ' [ + "local" => "127.0.0.1", + "production" => "123.456.789.0", + ], + "pickles" => [ + "namespace" => "", + "datasource" => "mysql", + "profiler" => true, + ], + "datasources" => [ + "mysql" => [ + "driver" => "pdo_mysql", + ], + ], + ]; + '); + + Pickles\Config::getInstance('/tmp/pickles.php'); + + $_SERVER['REQUEST_METHOD'] = 'PUT'; + $_REQUEST['request'] = 'v1/resource/1'; + + new Pickles\Router(); + } } } From c8d97aac26f556bb9597e8a8880d879c57996224 Mon Sep 17 00:00:00 2001 From: Josh Sherman Date: Thu, 2 Oct 2014 07:28:12 -0400 Subject: [PATCH 058/129] Reworking database tests --- tests/DatabaseTest.php | 378 ++++++++++++++++++++++++++++++++++------- 1 file changed, 312 insertions(+), 66 deletions(-) diff --git a/tests/DatabaseTest.php b/tests/DatabaseTest.php index 48783c5..c298078 100644 --- a/tests/DatabaseTest.php +++ b/tests/DatabaseTest.php @@ -2,8 +2,54 @@ class DatabaseTest extends PHPUnit_Framework_TestCase { + public function setUp() + { + $_SERVER['REQUEST_METHOD'] = 'GET'; + $_SERVER['SERVER_NAME'] = '127.0.0.1'; + + file_put_contents('/tmp/pickles.php', ' [ + "local" => "127.0.0.1", + "production" => "123.456.789.0", + ], + "pickles" => [ + "namespace" => "", + "datasource" => "mysql", + ], + "datasources" => [ + "mysql" => [ + "type" => "mysql", + "driver" => "pdo_mysql", + "database" => "test", + "hostname" => "localhost", + "username" => "root", + "password" => "", + "database" => "test", + ], + ], + ]; + '); + + Pickles\Config::getInstance('/tmp/pickles.php'); + } + public function testGetInstanceFalse() { + file_put_contents('/tmp/pickles.php', ' [ + "local" => "127.0.0.1", + "production" => "123.456.789.0", + ], + "datasources" => [ + + ], + ]; + '); + + Pickles\Config::getInstance('/tmp/pickles.php'); + $this->assertFalse(Pickles\Database::getInstance()); } @@ -13,8 +59,21 @@ class DatabaseTest extends PHPUnit_Framework_TestCase */ public function testGetInstanceDatasourceNotDefined() { - $config = Pickles\Config::getInstance(); - $config->data['pickles']['datasource'] = 'bad'; + file_put_contents('/tmp/pickles.php', ' [ + "local" => "127.0.0.1", + "production" => "123.456.789.0", + ], + "pickles" => [ + "namespace" => "", + "datasource" => "bad", + ], + ]; + '); + + Pickles\Config::getInstance('/tmp/pickles.php'); + Pickles\Database::getInstance(); } @@ -24,12 +83,26 @@ class DatabaseTest extends PHPUnit_Framework_TestCase */ public function testGetInstanceDatasourceLacksDriver() { - $config = Pickles\Config::getInstance(); - $config->data['datasources'] = [ - 'bad' => [ - 'type' => 'mysql', - ], - ]; + file_put_contents('/tmp/pickles.php', ' [ + "local" => "127.0.0.1", + "production" => "123.456.789.0", + ], + "pickles" => [ + "namespace" => "", + "datasource" => "bad", + ], + "datasources" => [ + "bad" => [ + "type" => "mysql", + ], + ], + ]; + '); + + Pickles\Config::getInstance('/tmp/pickles.php'); + $this->assertInstanceOf('Pickles\\Database', Pickles\Database::getInstance()); } @@ -39,46 +112,122 @@ class DatabaseTest extends PHPUnit_Framework_TestCase */ public function testOpenConfigError() { - $config = Pickles\Config::getInstance(); - $config->data['datasources'] = [ - 'bad' => [ - 'type' => 'mysql', - 'driver' => 'pdo_mysql', - 'database' => 'test', - ], - ]; + file_put_contents('/tmp/pickles.php', ' [ + "local" => "127.0.0.1", + "production" => "123.456.789.0", + ], + "pickles" => [ + "namespace" => "", + "datasource" => "bad", + ], + "datasources" => [ + "bad" => [ + "type" => "mysql", + "driver" => "pdo_mysql", + "database" => "test", + ], + ], + ]; + '); + + Pickles\Config::getInstance('/tmp/pickles.php'); + $db = Pickles\Database::getInstance(); $db->open(); } public function testGetInstanceDatasourcesArray() { - $config = Pickles\Config::getInstance(); - $config->data['datasources'] = [ - 'mysql' => [ - 'type' => 'mysql', - 'driver' => 'pdo_mysql', - 'hostname' => 'localhost', - 'username' => 'root', - 'password' => '', - 'database' => 'test', - ], - ]; + file_put_contents('/tmp/pickles.php', ' [ + "local" => "127.0.0.1", + "production" => "123.456.789.0", + ], + "pickles" => [ + "namespace" => "", + "datasource" => "bad", + ], + "datasources" => [ + "bad" => [ + "type" => "mysql", + "driver" => "pdo_mysql", + "database" => "test", + "hostname" => "localhost", + "username" => "root", + "password" => "", + "database" => "test", + ], + ], + ]; + '); + + Pickles\Config::getInstance('/tmp/pickles.php'); + $this->assertInstanceOf('Pickles\\Database', Pickles\Database::getInstance()); } // Also tests the datasource being missing and selecting the first one public function testGetInstanceMySQL() { - $config = Pickles\Config::getInstance(); - unset($config->data['pickles']['datasource']); + file_put_contents('/tmp/pickles.php', ' [ + "local" => "127.0.0.1", + "production" => "123.456.789.0", + ], + "pickles" => [ + "namespace" => "", + ], + "datasources" => [ + "bad" => [ + "type" => "mysql", + "driver" => "pdo_mysql", + "database" => "test", + "hostname" => "localhost", + "username" => "root", + "password" => "", + "database" => "test", + ], + ], + ]; + '); + + Pickles\Config::getInstance('/tmp/pickles.php'); + $this->assertInstanceOf('Pickles\\Database', Pickles\Database::getInstance()); } public function testOpenMySQL() { - $config = Pickles\Config::getInstance(); - $config->data['pickles']['datasource'] = 'mysql'; + file_put_contents('/tmp/pickles.php', ' [ + "local" => "127.0.0.1", + "production" => "123.456.789.0", + ], + "pickles" => [ + "namespace" => "", + "datasource" => "mysql", + ], + "datasources" => [ + "mysql" => [ + "type" => "mysql", + "driver" => "pdo_mysql", + "database" => "test", + "hostname" => "localhost", + "username" => "root", + "password" => "", + "database" => "test", + ], + ], + ]; + '); + + Pickles\Config::getInstance('/tmp/pickles.php'); + $db = Pickles\Database::getInstance(); $db->open(); } @@ -101,9 +250,33 @@ class DatabaseTest extends PHPUnit_Framework_TestCase public function testFetch() { - $config = Pickles\Config::getInstance(); - $config->data['pickles']['logging'] = true; - $config->data['pickles']['profiler'] = true; + file_put_contents('/tmp/pickles.php', ' [ + "local" => "127.0.0.1", + "production" => "123.456.789.0", + ], + "pickles" => [ + "namespace" => "", + "datasource" => "mysql", + "profiler" => true, + ], + "datasources" => [ + "mysql" => [ + "type" => "mysql", + "driver" => "pdo_mysql", + "database" => "test", + "hostname" => "localhost", + "username" => "root", + "password" => "", + "database" => "test", + ], + ], + ]; + '); + + Pickles\Config::getInstance('/tmp/pickles.php'); + $db = Pickles\Database::getInstance(); $this->assertEquals([], $db->fetch('SELECT * FROM pickles WHERE id != ?', ['0'])); } @@ -131,16 +304,32 @@ class DatabaseTest extends PHPUnit_Framework_TestCase public function testGetInstancePostgreSQL() { - $config = Pickles\Config::getInstance(); - $config->data['pickles']['datasource'] = 'pgsql'; - $config->data['datasources']['pgsql'] = [ - 'type' => 'pgsql', - 'driver' => 'pdo_pgsql', - 'hostname' => 'localhost', - 'username' => '', - 'password' => '', - 'database' => 'test', - ]; + file_put_contents('/tmp/pickles.php', ' [ + "local" => "127.0.0.1", + "production" => "123.456.789.0", + ], + "pickles" => [ + "namespace" => "", + "datasource" => "pgsql", + ], + "datasources" => [ + "pgsql" => [ + "type" => "pgsql", + "driver" => "pdo_pgsql", + "database" => "test", + "hostname" => "localhost", + "username" => "root", + "password" => "", + "database" => "test", + ], + ], + ]; + '); + + Pickles\Config::getInstance('/tmp/pickles.php'); + $this->assertInstanceOf('Pickles\\Database', Pickles\Database::getInstance()); } @@ -150,24 +339,65 @@ class DatabaseTest extends PHPUnit_Framework_TestCase */ public function testOpenPostgreSQL() { - // Also throws an exception since I don't have PostgreSQL set up - $config = Pickles\Config::getInstance(); + file_put_contents('/tmp/pickles.php', ' [ + "local" => "127.0.0.1", + "production" => "123.456.789.0", + ], + "pickles" => [ + "namespace" => "", + "datasource" => "pgsql", + ], + "datasources" => [ + "pgsql" => [ + "type" => "pgsql", + "driver" => "pdo_pgsql", + "database" => "test", + "hostname" => "localhost", + "username" => "root", + "password" => "", + "database" => "test", + ], + ], + ]; + '); + + Pickles\Config::getInstance('/tmp/pickles.php'); + + // Throws an error because I don't have PostgreSQL installed $db = Pickles\Database::getInstance(); $db->open(); } public function testGetInstanceSQLite() { - $config = Pickles\Config::getInstance(); - $config->data['pickles']['datasource'] = 'sqlite'; - $config->data['datasources']['sqlite'] = [ - 'type' => 'sqlite', - 'driver' => 'pdo_sqlite', - 'hostname' => 'localhost', - 'username' => '', - 'password' => '', - 'database' => 'test', - ]; + file_put_contents('/tmp/pickles.php', ' [ + "local" => "127.0.0.1", + "production" => "123.456.789.0", + ], + "pickles" => [ + "namespace" => "", + "datasource" => "sqlite", + ], + "datasources" => [ + "sqlite" => [ + "type" => "sqlite", + "driver" => "pdo_sqlite", + "database" => "test", + "hostname" => "localhost", + "username" => "root", + "password" => "", + "database" => "test", + ], + ], + ]; + '); + + Pickles\Config::getInstance('/tmp/pickles.php'); + $this->assertInstanceOf('Pickles\\Database', Pickles\Database::getInstance()); } @@ -177,16 +407,32 @@ class DatabaseTest extends PHPUnit_Framework_TestCase */ public function testGetInstanceInvalidDriver() { - $config = Pickles\Config::getInstance(); - $config->data['pickles']['datasource'] = 'invalid'; - $config->data['datasources']['invalid'] = [ - 'type' => 'invalid', - 'driver' => 'pdo_invalid', - 'hostname' => 'localhost', - 'username' => '', - 'password' => '', - 'database' => 'test', - ]; + file_put_contents('/tmp/pickles.php', ' [ + "local" => "127.0.0.1", + "production" => "123.456.789.0", + ], + "pickles" => [ + "namespace" => "", + "datasource" => "invalid", + ], + "datasources" => [ + "invalid" => [ + "type" => "invalid", + "driver" => "pdo_invalid", + "database" => "test", + "hostname" => "localhost", + "username" => "root", + "password" => "", + "database" => "test", + ], + ], + ]; + '); + + Pickles\Config::getInstance('/tmp/pickles.php'); + Pickles\Database::getInstance(); } } From df98b99440373dee21913fcb0f64e02cd360f929 Mon Sep 17 00:00:00 2001 From: Josh Sherman Date: Thu, 2 Oct 2014 07:28:58 -0400 Subject: [PATCH 059/129] Dropped uopz from travis config --- .travis.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 3c4c2e1..79e4ec4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,16 +25,11 @@ services: install: - composer install - - pecl install uopz before_script: - echo "extension = memcache.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini - echo "extension = memcached.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini - echo "extension = redis.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini - - echo -e "zend_$(cat ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini)" > ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini - - echo "zend_extension = /home/travis/.phpenv/versions/$(php -r 'echo phpversion();')/lib/php/extensions/no-debug-zts-20100525/uopz.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini - - echo "[uopz]" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini - - echo "uopz.overloads=1" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini - mysql -e "create database IF NOT EXISTS test;" -u root - mysql test < tests/schema.sql -u root - mkdir -p build/logs From a41ee7c2d26d2bab261b93e73e1b153d73b164db Mon Sep 17 00:00:00 2001 From: Josh Sherman Date: Thu, 2 Oct 2014 17:34:25 -0400 Subject: [PATCH 060/129] Fixed config loading --- tests/CacheTest.php | 33 ++++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/tests/CacheTest.php b/tests/CacheTest.php index 364d304..06cefa4 100644 --- a/tests/CacheTest.php +++ b/tests/CacheTest.php @@ -2,19 +2,34 @@ class CacheTest extends PHPUnit_Framework_TestCase { - private $config; private $cache; public function setUp() { - $this->config = Pickles\Config::getInstance(); - $this->config->data['pickles']['cache'] = 'mc'; - $this->config->data['datasources']['mc'] = [ - 'type' => 'memcache', - 'hostname' => 'localhost', - 'port' => 11211, - 'namespace' => 'ns', - ]; + $_SERVER['REQUEST_METHOD'] = 'GET'; + $_SERVER['SERVER_NAME'] = '127.0.0.1'; + + file_put_contents('/tmp/pickles.php', ' [ + "local" => "127.0.0.1", + "production" => "123.456.789.0", + ], + "pickles" => [ + "cache" => "mc", + ], + "datasources" => [ + "mc" => [ + "type" => "memcache", + "hostname" => "localhost", + "port" => 11211, + "namespace" => "ns", + ], + ], + ]; + '); + + Pickles\Config::getInstance('/tmp/pickles.php'); $this->cache = Pickles\Cache::getInstance(); } From b48b73d064739e4e3f457b52839a466793d3e978 Mon Sep 17 00:00:00 2001 From: Josh Sherman Date: Thu, 2 Oct 2014 17:41:06 -0400 Subject: [PATCH 061/129] Dropped SITE_PATH --- tests/FileTest.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/FileTest.php b/tests/FileTest.php index 838643e..182ef78 100644 --- a/tests/FileTest.php +++ b/tests/FileTest.php @@ -33,10 +33,10 @@ class FileTest extends PHPUnit_Framework_TestCase public function testMissingTrailingSlash() { - $directory = SITE_PATH . 'missing'; + $directory = '/tmp/missing'; mkdir($directory, 0777, true); - touch(SITE_PATH . 'missing/slash'); + touch('/tmp/missing/slash'); Pickles\File::removeDirectory($directory); @@ -45,8 +45,8 @@ class FileTest extends PHPUnit_Framework_TestCase public function testRemoveFileNotDirectory() { - $directory = SITE_PATH . 'dir'; - $file = SITE_PATH . 'dir/file'; + $directory = '/tmp/dir'; + $file = '/tmp/dir/file'; mkdir($directory, 0777, true); touch($file); From c9ffe4c8bf6d47884886fb18299ecc007c124139 Mon Sep 17 00:00:00 2001 From: Josh Sherman Date: Thu, 2 Oct 2014 17:44:00 -0400 Subject: [PATCH 062/129] Fixed config in tests --- tests/RouterTest.php | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests/RouterTest.php b/tests/RouterTest.php index 46346f2..850b868 100644 --- a/tests/RouterTest.php +++ b/tests/RouterTest.php @@ -24,6 +24,22 @@ namespace $this->expectOutputString($response); $_SERVER['REQUEST_METHOD'] = 'GET'; + $_SERVER['SERVER_NAME'] = '127.0.0.1'; + + file_put_contents('/tmp/pickles.php', ' [ + "local" => "127.0.0.1", + "production" => "123.456.789.0", + ], + "pickles" => [ + "namespace" => "", + ], + "datasources" => [], + ]; + '); + + Pickles\Config::getInstance('/tmp/pickles.php'); new Pickles\Router(); } From 90c4c53294b5d56b996e045bf53543c2caf79159 Mon Sep 17 00:00:00 2001 From: Josh Sherman Date: Thu, 2 Oct 2014 17:54:59 -0400 Subject: [PATCH 063/129] Fixed up the config again --- tests/ModelTest.php | 55 +++++++++++++++++++++++++++------------------ 1 file changed, 33 insertions(+), 22 deletions(-) diff --git a/tests/ModelTest.php b/tests/ModelTest.php index 600a73a..dbf6bcf 100644 --- a/tests/ModelTest.php +++ b/tests/ModelTest.php @@ -25,31 +25,42 @@ class ModelTest extends PHPUnit_Framework_TestCase { // Clears out the Config for ease of testing Pickles\Object::$instances = []; - $config = Pickles\Config::getInstance(); - $config->data = [ - 'pickles' => [ - 'datasource' => 'mysql', - 'cache' => 'memcache', - ], - 'datasources' => [ - 'mysql' => [ - 'type' => 'mysql', - 'driver' => 'pdo_mysql', - 'hostname' => 'localhost', - 'username' => 'root', - 'password' => '', - 'database' => 'test', - 'cache' => true, + $_SERVER['REQUEST_METHOD'] = 'GET'; + $_SERVER['SERVER_NAME'] = '127.0.0.1'; + + file_put_contents('/tmp/pickles.php', ' [ + "local" => "127.0.0.1", + "production" => "123.456.789.0", ], - 'memcache' => [ - 'type' => 'memcache', - 'hostname' => 'localhost', - 'port' => 11211, - 'namespace' => '', + "pickles" => [ + "namespace" => "", + "datasource" => "mysql", + "cache" => "memcache", ], - ], - ]; + "datasources" => [ + "mysql" => [ + "type" => "mysql", + "driver" => "pdo_mysql", + "hostname" => "localhost", + "username" => "root", + "password" => "", + "database" => "test", + "cache" => true, + ], + "memcache" => [ + "type" => "memcache", + "hostname" => "localhost", + "port" => 11211, + "namespace" => "", + ], + ], + ]; + '); + + $config = Pickles\Config::getInstance('/tmp/pickles.php'); for ($i = 0; $i < 5; $i++) { From 4167d9962311045a39985e716ee39e6c63ad52bb Mon Sep 17 00:00:00 2001 From: Josh Sherman Date: Thu, 2 Oct 2014 18:15:48 -0400 Subject: [PATCH 064/129] Fixed deleting a file Not sure when this became an issue, but attempting to delete a file that ends with / will result in an error on OS X --- src/File.php | 10 +++++----- tests/FileTest.php | 10 ++++++---- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/File.php b/src/File.php index af69806..e04c596 100644 --- a/src/File.php +++ b/src/File.php @@ -38,14 +38,14 @@ class File */ public static function removeDirectory($directory) { - if (substr($directory, -1) != '/') - { - $directory .= '/'; - } - // If directory is a directory, read in all the files if (is_dir($directory)) { + if (substr($directory, -1) != '/') + { + $directory .= '/'; + } + $files = scandir($directory); // Loop through said files, check for directories, and unlink files diff --git a/tests/FileTest.php b/tests/FileTest.php index 182ef78..37c9fad 100644 --- a/tests/FileTest.php +++ b/tests/FileTest.php @@ -33,10 +33,10 @@ class FileTest extends PHPUnit_Framework_TestCase public function testMissingTrailingSlash() { - $directory = '/tmp/missing'; + $directory = '/tmp/pickles-fs/missing'; mkdir($directory, 0777, true); - touch('/tmp/missing/slash'); + touch('/tmp/pickles-fs/missing/slash'); Pickles\File::removeDirectory($directory); @@ -45,8 +45,8 @@ class FileTest extends PHPUnit_Framework_TestCase public function testRemoveFileNotDirectory() { - $directory = '/tmp/dir'; - $file = '/tmp/dir/file'; + $directory = '/tmp/pickles-fs/dir'; + $file = '/tmp/pickles-fs/dir/file'; mkdir($directory, 0777, true); touch($file); @@ -56,6 +56,8 @@ class FileTest extends PHPUnit_Framework_TestCase $this->assertFalse(file_exists($file)); Pickles\File::removeDirectory($directory); + + $this->assertFalse(file_exists($directory)); } } From d884f5a3df280a83242c952cc81243e3bdfbcf33 Mon Sep 17 00:00:00 2001 From: Josh Sherman Date: Thu, 2 Oct 2014 20:54:12 -0400 Subject: [PATCH 065/129] Finished up tests --- tests/DatabaseTest.php | 87 ++++++++++++++++++++++++++++++++++++++++++ tests/ProfilerTest.php | 3 ++ tests/ResourceTest.php | 43 +++++++++++++++++++++ tests/schema.sql | 2 + 4 files changed, 135 insertions(+) diff --git a/tests/DatabaseTest.php b/tests/DatabaseTest.php index c298078..dd49291 100644 --- a/tests/DatabaseTest.php +++ b/tests/DatabaseTest.php @@ -435,5 +435,92 @@ class DatabaseTest extends PHPUnit_Framework_TestCase Pickles\Database::getInstance(); } + + public function testProfilerWithoutParameters() + { + // Clears out the Config for ease of testing + Pickles\Object::$instances = []; + + $_SERVER['REQUEST_METHOD'] = 'GET'; + $_SERVER['SERVER_NAME'] = '127.0.0.1'; + + file_put_contents('/tmp/pickles.php', ' [ + "local" => "127.0.0.1", + "production" => "123.456.789.0", + ], + "pickles" => [ + "datasource" => "mysql", + "profiler" => true, + ], + "datasources" => [ + "mysql" => [ + "type" => "mysql", + "driver" => "pdo_mysql", + "database" => "test", + "hostname" => "localhost", + "username" => "root", + "password" => "", + "database" => "test", + ], + ], + ]; + '); + + Pickles\Config::getInstance('/tmp/pickles.php'); + + $db = Pickles\Database::getInstance(); + $db->execute('SELECT * FROM `users`'); + + $report = Pickles\Profiler::report(); + $this->assertEquals(7, count($report)); + $this->assertEquals(2, count($report['logs'])); + $this->assertTrue(isset($report['logs'][1]['details']['explain'])); + } + + public function testProfilerWithParameters() + { + // Clears out the Config for ease of testing + Pickles\Object::$instances = []; + + $_SERVER['REQUEST_METHOD'] = 'GET'; + $_SERVER['SERVER_NAME'] = '127.0.0.1'; + + file_put_contents('/tmp/pickles.php', ' [ + "local" => "127.0.0.1", + "production" => "123.456.789.0", + ], + "pickles" => [ + "datasource" => "mysql", + "profiler" => true, + ], + "datasources" => [ + "mysql" => [ + "type" => "mysql", + "driver" => "pdo_mysql", + "database" => "test", + "hostname" => "localhost", + "username" => "root", + "password" => "", + "database" => "test", + ], + ], + ]; + '); + + Pickles\Config::getInstance('/tmp/pickles.php'); + + $db = Pickles\Database::getInstance(); + $db->execute('SELECT * FROM `users` WHERE id = ?', [1000000]); + + $report = Pickles\Profiler::report(); + $this->assertEquals(7, count($report)); + $this->assertEquals(3, count($report['logs'])); + $this->assertEquals([1000000], $report['logs'][2]['details']['parameters']); + $this->assertTrue(isset($report['logs'][2]['details']['explain'])); + } } diff --git a/tests/ProfilerTest.php b/tests/ProfilerTest.php index 140d240..8df03d4 100644 --- a/tests/ProfilerTest.php +++ b/tests/ProfilerTest.php @@ -4,6 +4,9 @@ class ProfilerTest extends PHPUnit_Framework_TestCase { public function testProfiler() { + // Clears out any previous logging + Pickles\Profiler::report(); + $_SERVER['REQUEST_METHOD'] = 'GET'; $_SERVER['SERVER_NAME'] = '127.0.0.1'; diff --git a/tests/ResourceTest.php b/tests/ResourceTest.php index eeb7d24..78cbfed 100644 --- a/tests/ResourceTest.php +++ b/tests/ResourceTest.php @@ -204,6 +204,49 @@ namespace new Pickles\Router(); } + public function testBasicAuth() + { + Pickles\Object::$instances = []; + + $_SERVER['REQUEST_METHOD'] = 'GET'; + $_SERVER['SERVER_NAME'] = '127.0.0.1'; + + file_put_contents('/tmp/pickles.php', ' [ + "local" => "127.0.0.1", + "production" => "123.456.789.0", + ], + "pickles" => [ + "namespace" => "", + "datasource" => "mysql", + "auth" => "basic", + ], + "datasources" => [ + "mysql" => [ + "driver" => "pdo_mysql", + ], + ], + ]; + '); + + Pickles\Config::getInstance('/tmp/pickles.php'); + + $response = json_encode([ + 'meta' => [ + 'status' => 405, + 'message' => 'Method not allowed.', + ], + ]); + + $this->expectOutputString($response); + + $_SERVER['REQUEST_METHOD'] = 'DELETE'; + $_REQUEST['request'] = 'v1/resource/1'; + + new Pickles\Router(); + } + public function testMethodNotAllowed() { $response = json_encode([ diff --git a/tests/schema.sql b/tests/schema.sql index 5d0a89a..52ee31b 100644 --- a/tests/schema.sql +++ b/tests/schema.sql @@ -53,3 +53,5 @@ CREATE TABLE `users` ( `is_deleted` tinyint(1) unsigned DEFAULT '0', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; + +INSERT INTO users (id, username, created_at) VALUES (1000000, 'test', NOW()); From 510b5d8edc7f270962e5f9746d1e428d2229d8e1 Mon Sep 17 00:00:00 2001 From: Josh Sherman Date: Thu, 2 Oct 2014 20:59:27 -0400 Subject: [PATCH 066/129] Added new branches to test --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 79e4ec4..5ca38a0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,6 +17,8 @@ matrix: branches: only: - master + - 2.0 + - 1.9 services: - memcache From 5ce36537a6556f737dec75e7ee1b0a5ae89201f0 Mon Sep 17 00:00:00 2001 From: Josh Sherman Date: Thu, 2 Oct 2014 21:11:23 -0400 Subject: [PATCH 067/129] Renamed bootstrap file --- tests/{bootstrap.php => Bootstrap.php} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tests/{bootstrap.php => Bootstrap.php} (100%) diff --git a/tests/bootstrap.php b/tests/Bootstrap.php similarity index 100% rename from tests/bootstrap.php rename to tests/Bootstrap.php From 1b365bcff01963699927ece8f9bd91cc93f9c102 Mon Sep 17 00:00:00 2001 From: Josh Sherman Date: Thu, 2 Oct 2014 21:18:36 -0400 Subject: [PATCH 068/129] Added root namespace to function Tests were failing on 5.4 out on travis. Was barking about the function not existing in the namespace. Hoping this resolves it. --- src/Resource.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Resource.php b/src/Resource.php index 0539031..78a49cf 100644 --- a/src/Resource.php +++ b/src/Resource.php @@ -177,7 +177,7 @@ class Resource extends Object if ($function == 'password_hash') { - $global[$variable] = password_hash($value, PASSWORD_DEFAULT); + $global[$variable] = \password_hash($value, PASSWORD_DEFAULT); } else { From 31f4c32fb0c7f288d83d1e2f1842b01ab1eb78d1 Mon Sep 17 00:00:00 2001 From: Josh Sherman Date: Thu, 2 Oct 2014 21:34:09 -0400 Subject: [PATCH 069/129] Reworked test to check for PHP version `password_hash` is PHP 5.5. Instead of adding the sanity check in the code, I've opted to put it in the test to ditch the overhead of having to make that check for every request. --- src/Resource.php | 2 +- tests/ResourceTest.php | 11 ++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/Resource.php b/src/Resource.php index 78a49cf..0539031 100644 --- a/src/Resource.php +++ b/src/Resource.php @@ -177,7 +177,7 @@ class Resource extends Object if ($function == 'password_hash') { - $global[$variable] = \password_hash($value, PASSWORD_DEFAULT); + $global[$variable] = password_hash($value, PASSWORD_DEFAULT); } else { diff --git a/tests/ResourceTest.php b/tests/ResourceTest.php index 78cbfed..42e156e 100644 --- a/tests/ResourceTest.php +++ b/tests/ResourceTest.php @@ -144,10 +144,19 @@ namespace 'regex' => 'abc', ]; + if (version_compare(PHP_VERSION, '5.5.0', '<')) + { + unset($_GET['bar']); + } + new Pickles\Router(); $this->assertEquals('bar', $_GET['foo']); - $this->assertFalse('unencrypted' == $_GET['bar']); + + if (version_compare(PHP_VERSION, '5.5.0', '<')) + { + $this->assertFalse('unencrypted' == $_GET['bar']); + } } public function testHTTPS() From 5b2f483fa5bc9094460355903b1d92fc473d730b Mon Sep 17 00:00:00 2001 From: Josh Sherman Date: Thu, 2 Oct 2014 21:47:01 -0400 Subject: [PATCH 070/129] Added script to set up extension As per https://github.com/travis-ci/travis-ci/issues/2523 it appears that the php.ini on hhvm is in a different place. Created a script to write the extensions to the right php.ini based on the environment. --- .travis.yml | 4 +--- tests/travis.sh | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 3 deletions(-) create mode 100644 tests/travis.sh diff --git a/.travis.yml b/.travis.yml index 5ca38a0..9d1530f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -29,9 +29,7 @@ install: - composer install before_script: - - echo "extension = memcache.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini - - echo "extension = memcached.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini - - echo "extension = redis.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini + - ./tests/travis.sh - mysql -e "create database IF NOT EXISTS test;" -u root - mysql test < tests/schema.sql -u root - mkdir -p build/logs diff --git a/tests/travis.sh b/tests/travis.sh new file mode 100644 index 0000000..25bd5d9 --- /dev/null +++ b/tests/travis.sh @@ -0,0 +1,14 @@ +#!/bin/sh + +VERSION=`phpenv version-name` + +if [ "$VERSION" -eq "hhvm" ] +then + PHPINI=/etc/hhvm/php.ini +else + PHPINI="~/.phpenv/versions/$VERSION/etc/php.ini" +fi + +echo "extension = memcache.so" >> $PHPINI +echo "extension = memcached.so" >> $PHPINI +echo "extension = redis.so" >> $PHPINI From cd05384a6da1f3f9b13e7c78e61385902be4b9de Mon Sep 17 00:00:00 2001 From: Josh Sherman Date: Thu, 2 Oct 2014 21:49:28 -0400 Subject: [PATCH 071/129] Made script executable. --- tests/travis.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 tests/travis.sh diff --git a/tests/travis.sh b/tests/travis.sh old mode 100644 new mode 100755 From c7a6852a3db1b61940d87f66ad444b0215c9943b Mon Sep 17 00:00:00 2001 From: Josh Sherman Date: Thu, 2 Oct 2014 21:55:16 -0400 Subject: [PATCH 072/129] Working on script. --- tests/travis.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/travis.sh b/tests/travis.sh index 25bd5d9..292bc4d 100755 --- a/tests/travis.sh +++ b/tests/travis.sh @@ -2,11 +2,11 @@ VERSION=`phpenv version-name` -if [ "$VERSION" -eq "hhvm" ] +if [ "$VERSION" == "hhvm" ] then PHPINI=/etc/hhvm/php.ini else - PHPINI="~/.phpenv/versions/$VERSION/etc/php.ini" + PHPINI=~/.phpenv/versions/$VERSION/etc/php.ini fi echo "extension = memcache.so" >> $PHPINI From bbf392f45f3b104b952d516296b405c447536d5b Mon Sep 17 00:00:00 2001 From: Josh Sherman Date: Thu, 2 Oct 2014 22:01:59 -0400 Subject: [PATCH 073/129] Still tweaking towards HHVM --- composer.json | 2 +- tests/travis.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index dfc79fa..ee67f21 100644 --- a/composer.json +++ b/composer.json @@ -22,7 +22,7 @@ "satooshi/php-coveralls": "dev-master" }, "require": { - "php": ">=5.4" + "php": ">=5.3" }, "autoload": { "psr-4": { diff --git a/tests/travis.sh b/tests/travis.sh index 292bc4d..89a9a52 100755 --- a/tests/travis.sh +++ b/tests/travis.sh @@ -2,7 +2,7 @@ VERSION=`phpenv version-name` -if [ "$VERSION" == "hhvm" ] +if [ "${VERSION}" = 'hhvm' ] then PHPINI=/etc/hhvm/php.ini else From 58e54bcf7be735de94e83a5f70c16de68051e31a Mon Sep 17 00:00:00 2001 From: Josh Sherman Date: Thu, 2 Oct 2014 22:56:42 -0400 Subject: [PATCH 074/129] Updated lock file. --- composer.lock | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/composer.lock b/composer.lock index f6db866..ef194b1 100644 --- a/composer.lock +++ b/composer.lock @@ -1,9 +1,10 @@ { "_readme": [ "This file locks the dependencies of your project to a known state", - "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file" + "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", + "This file is @generated automatically" ], - "hash": "16033f40d01b3ada4064b0b7488d4cb7", + "hash": "78d6d0f19f6e8766118df89454b9ba14", "packages": [], "packages-dev": [ { @@ -1010,12 +1011,12 @@ "source": { "type": "git", "url": "https://github.com/symfony/Console.git", - "reference": "700dbb7648e89d0c8ce5337a9febc18dc84bcb6e" + "reference": "7049924cf0b7f350e5a099ea988e713ebf7590fe" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/Console/zipball/700dbb7648e89d0c8ce5337a9febc18dc84bcb6e", - "reference": "700dbb7648e89d0c8ce5337a9febc18dc84bcb6e", + "url": "https://api.github.com/repos/symfony/Console/zipball/7049924cf0b7f350e5a099ea988e713ebf7590fe", + "reference": "7049924cf0b7f350e5a099ea988e713ebf7590fe", "shasum": "" }, "require": { @@ -1058,7 +1059,7 @@ ], "description": "Symfony Console Component", "homepage": "http://symfony.com", - "time": "2014-09-25 10:18:55" + "time": "2014-10-01 05:53:11" }, { "name": "symfony/event-dispatcher", @@ -1067,12 +1068,12 @@ "source": { "type": "git", "url": "https://github.com/symfony/EventDispatcher.git", - "reference": "ea86d91a10c5737a1e043aa29fe05837f942d163" + "reference": "93d237c3da7565a9ad2b4b6a74af73f4e3c0c305" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/EventDispatcher/zipball/ea86d91a10c5737a1e043aa29fe05837f942d163", - "reference": "ea86d91a10c5737a1e043aa29fe05837f942d163", + "url": "https://api.github.com/repos/symfony/EventDispatcher/zipball/93d237c3da7565a9ad2b4b6a74af73f4e3c0c305", + "reference": "93d237c3da7565a9ad2b4b6a74af73f4e3c0c305", "shasum": "" }, "require": { @@ -1116,7 +1117,7 @@ ], "description": "Symfony EventDispatcher Component", "homepage": "http://symfony.com", - "time": "2014-09-28 16:15:31" + "time": "2014-10-01 05:53:11" }, { "name": "symfony/filesystem", @@ -1219,12 +1220,12 @@ "source": { "type": "git", "url": "https://github.com/symfony/Yaml.git", - "reference": "6644c5c6b395e63a55186dea1817eed0ceb1c075" + "reference": "656e03eeedc827e4830ac43547eae10bacaa1bbb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/Yaml/zipball/6644c5c6b395e63a55186dea1817eed0ceb1c075", - "reference": "6644c5c6b395e63a55186dea1817eed0ceb1c075", + "url": "https://api.github.com/repos/symfony/Yaml/zipball/656e03eeedc827e4830ac43547eae10bacaa1bbb", + "reference": "656e03eeedc827e4830ac43547eae10bacaa1bbb", "shasum": "" }, "require": { @@ -1257,7 +1258,7 @@ ], "description": "Symfony Yaml Component", "homepage": "http://symfony.com", - "time": "2014-09-22 11:59:59" + "time": "2014-10-01 05:53:11" } ], "aliases": [], @@ -1266,8 +1267,9 @@ "phpunit/phpunit": 20, "satooshi/php-coveralls": 20 }, + "prefer-stable": false, "platform": { - "php": ">=5.4" + "php": ">=5.3" }, "platform-dev": [] } From e5270afed448be5afbf0c6c9fb30ed9506bb588c Mon Sep 17 00:00:00 2001 From: Josh Sherman Date: Thu, 2 Oct 2014 23:40:56 -0400 Subject: [PATCH 075/129] Dropped PHP 5.3 --- .travis.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 9d1530f..b89a1e1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,18 +1,14 @@ language: php php: - - 5.3 - 5.4 - 5.5 - 5.6 - hhvm - - hhvm-nightly matrix: allow_failures: - - php: 5.3 - php: hhvm - - php: hhvm-nightly branches: only: From c61a49f8a7a0635368951b179838ed9f1f3d9f87 Mon Sep 17 00:00:00 2001 From: Josh Sherman Date: Fri, 3 Oct 2014 06:03:19 -0400 Subject: [PATCH 076/129] Fixed version check --- tests/ResourceTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ResourceTest.php b/tests/ResourceTest.php index 42e156e..987ddb0 100644 --- a/tests/ResourceTest.php +++ b/tests/ResourceTest.php @@ -153,7 +153,7 @@ namespace $this->assertEquals('bar', $_GET['foo']); - if (version_compare(PHP_VERSION, '5.5.0', '<')) + if (version_compare(PHP_VERSION, '5.5.0', '>=')) { $this->assertFalse('unencrypted' == $_GET['bar']); } From ab6623d6fb21583b5cb401cf798ebd75d71abb71 Mon Sep 17 00:00:00 2001 From: Josh Sherman Date: Fri, 3 Oct 2014 06:10:45 -0400 Subject: [PATCH 077/129] Trying to debug hhvm Added php -i to see where the php.ini file lives, the one I read about on Github doesn't allow me to write to it. --- tests/travis.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/travis.sh b/tests/travis.sh index 89a9a52..e8e0790 100755 --- a/tests/travis.sh +++ b/tests/travis.sh @@ -1,5 +1,7 @@ #!/bin/sh +php -i + VERSION=`phpenv version-name` if [ "${VERSION}" = 'hhvm' ] From 823fe0759a5e6eef4b5bcdcb76eed19e91da38d6 Mon Sep 17 00:00:00 2001 From: Josh Sherman Date: Fri, 3 Oct 2014 06:17:54 -0400 Subject: [PATCH 078/129] Updated to only inject on non-HHVM Saw someone else's .travis.yml that opted to not put those extension lines into the INI when running on HHVM. Worth a shot! --- tests/travis.sh | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/travis.sh b/tests/travis.sh index e8e0790..834094c 100755 --- a/tests/travis.sh +++ b/tests/travis.sh @@ -9,8 +9,9 @@ then PHPINI=/etc/hhvm/php.ini else PHPINI=~/.phpenv/versions/$VERSION/etc/php.ini + + echo "extension = memcache.so" >> $PHPINI + echo "extension = memcached.so" >> $PHPINI + echo "extension = redis.so" >> $PHPINI fi -echo "extension = memcache.so" >> $PHPINI -echo "extension = memcached.so" >> $PHPINI -echo "extension = redis.so" >> $PHPINI From 908fff2193ce7e49784cc2ed8eaadb7e638c60b8 Mon Sep 17 00:00:00 2001 From: Josh Sherman Date: Fri, 3 Oct 2014 06:21:21 -0400 Subject: [PATCH 079/129] Dropped php -a as it is hanging HHVM --- tests/travis.sh | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/travis.sh b/tests/travis.sh index 834094c..a178657 100755 --- a/tests/travis.sh +++ b/tests/travis.sh @@ -1,7 +1,5 @@ #!/bin/sh -php -i - VERSION=`phpenv version-name` if [ "${VERSION}" = 'hhvm' ] From 1a589efe129f0a453bc2660042022f1f0213fb8d Mon Sep 17 00:00:00 2001 From: Josh Sherman Date: Fri, 3 Oct 2014 06:30:02 -0400 Subject: [PATCH 080/129] Added config loading --- tests/RouterTest.php | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tests/RouterTest.php b/tests/RouterTest.php index 850b868..405ed2a 100644 --- a/tests/RouterTest.php +++ b/tests/RouterTest.php @@ -65,6 +65,24 @@ namespace // work. That logic is off in ResourceTest public function testFoundWithUID() { + $_SERVER['REQUEST_METHOD'] = 'GET'; + $_SERVER['SERVER_NAME'] = '127.0.0.1'; + + file_put_contents('/tmp/pickles.php', ' [ + "local" => "127.0.0.1", + "production" => "123.456.789.0", + ], + "pickles" => [ + "namespace" => "", + ], + "datasources" => [], + ]; + '); + + Pickles\Config::getInstance('/tmp/pickles.php'); + $response = json_encode([ 'meta' => [ 'status' => 405, From 4c55c25f00b07b1a7beef375420f755b3313aed1 Mon Sep 17 00:00:00 2001 From: Josh Sherman Date: Fri, 3 Oct 2014 06:33:55 -0400 Subject: [PATCH 081/129] Cleared out instances --- tests/RouterTest.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/RouterTest.php b/tests/RouterTest.php index 405ed2a..57f7ab8 100644 --- a/tests/RouterTest.php +++ b/tests/RouterTest.php @@ -65,6 +65,8 @@ namespace // work. That logic is off in ResourceTest public function testFoundWithUID() { + Pickles\Object::$instances = []; + $_SERVER['REQUEST_METHOD'] = 'GET'; $_SERVER['SERVER_NAME'] = '127.0.0.1'; From 75951b90ef42f660dddd9c379b58e34e4fac5a2e Mon Sep 17 00:00:00 2001 From: Josh Sherman Date: Fri, 3 Oct 2014 06:41:42 -0400 Subject: [PATCH 082/129] Updated dependencies --- composer.json | 2 +- composer.lock | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/composer.json b/composer.json index ee67f21..dfc79fa 100644 --- a/composer.json +++ b/composer.json @@ -22,7 +22,7 @@ "satooshi/php-coveralls": "dev-master" }, "require": { - "php": ">=5.3" + "php": ">=5.4" }, "autoload": { "psr-4": { diff --git a/composer.lock b/composer.lock index ef194b1..0e4d847 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "78d6d0f19f6e8766118df89454b9ba14", + "hash": "16033f40d01b3ada4064b0b7488d4cb7", "packages": [], "packages-dev": [ { @@ -475,12 +475,12 @@ "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", - "reference": "3d40ae857a3941ede714be45079e85f9e956b4b3" + "reference": "cf12676fb7f4a5adf5289a833046c91cb6f9c5b9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/3d40ae857a3941ede714be45079e85f9e956b4b3", - "reference": "3d40ae857a3941ede714be45079e85f9e956b4b3", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/cf12676fb7f4a5adf5289a833046c91cb6f9c5b9", + "reference": "cf12676fb7f4a5adf5289a833046c91cb6f9c5b9", "shasum": "" }, "require": { @@ -489,7 +489,7 @@ "phpunit/php-text-template": "~1.2" }, "require-dev": { - "phpunit/phpunit": "4.3.*@dev" + "phpunit/phpunit": "4.4.*@dev" }, "suggest": { "ext-soap": "*" @@ -497,7 +497,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.3.x-dev" + "dev-master": "2.4.x-dev" } }, "autoload": { @@ -522,7 +522,7 @@ "mock", "xunit" ], - "time": "2014-09-10 14:10:18" + "time": "2014-10-03 05:13:28" }, { "name": "psr/log", @@ -1269,7 +1269,7 @@ }, "prefer-stable": false, "platform": { - "php": ">=5.3" + "php": ">=5.4" }, "platform-dev": [] } From e08c9a5aded6d944fc4349e8fcebd7f2a4d41d78 Mon Sep 17 00:00:00 2001 From: Josh Sherman Date: Fri, 3 Oct 2014 06:43:44 -0400 Subject: [PATCH 083/129] Tweaked readme a bit --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3fb75b6..3905f55 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,8 @@ [![Stories in Ready](https://badge.waffle.io/joshtronic/pickles.png?label=ready&title=Ready)](https://waffle.io/joshtronic/pickles) Pickles f/k/a PICKLES (PHP Interface Collection of Killer Libraries to Enhance -Stuff) is an open source framework for the rapid development of RESTful API -systems. The intention of this framework is to allow developers to a means to +Stuff) is an open source framework for the rapid development of RESTful +services. The intention of this framework is to allow developers to a means to develop service-oriented backend systems that are completely decoupled from the front end components. Thus allowing the freedom to build the front end implementation(s) using whichever tools they choose, be it Ember.js, Angular.js From 89fc175701491bc65d6fc63bf7d5b5abb86e69e3 Mon Sep 17 00:00:00 2001 From: Josh Sherman Date: Fri, 3 Oct 2014 06:50:55 -0400 Subject: [PATCH 084/129] Updated dependencies --- composer.lock | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/composer.lock b/composer.lock index 0e4d847..64198c9 100644 --- a/composer.lock +++ b/composer.lock @@ -471,16 +471,16 @@ }, { "name": "phpunit/phpunit-mock-objects", - "version": "dev-master", + "version": "2.3.x-dev", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", - "reference": "cf12676fb7f4a5adf5289a833046c91cb6f9c5b9" + "reference": "c63d2367247365f688544f0d500af90a11a44c65" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/cf12676fb7f4a5adf5289a833046c91cb6f9c5b9", - "reference": "cf12676fb7f4a5adf5289a833046c91cb6f9c5b9", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/c63d2367247365f688544f0d500af90a11a44c65", + "reference": "c63d2367247365f688544f0d500af90a11a44c65", "shasum": "" }, "require": { @@ -489,7 +489,7 @@ "phpunit/php-text-template": "~1.2" }, "require-dev": { - "phpunit/phpunit": "4.4.*@dev" + "phpunit/phpunit": "~4.3" }, "suggest": { "ext-soap": "*" @@ -497,7 +497,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.4.x-dev" + "dev-master": "2.3.x-dev" } }, "autoload": { @@ -522,7 +522,7 @@ "mock", "xunit" ], - "time": "2014-10-03 05:13:28" + "time": "2014-10-03 05:12:11" }, { "name": "psr/log", From 84a785d4c9764ca8f625300c2260e031c3c54c07 Mon Sep 17 00:00:00 2001 From: Josh Sherman Date: Fri, 3 Oct 2014 07:30:10 -0400 Subject: [PATCH 085/129] Added Basic Auth functionality --- src/Resource.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Resource.php b/src/Resource.php index 0539031..91b21e5 100644 --- a/src/Resource.php +++ b/src/Resource.php @@ -103,16 +103,17 @@ class Resource extends Object throw new \Exception('Authentication is not configured properly.', 401); } - /* // This class should be in the classes directory of the service - $auth = '\\' . $this->config['pickles']['namespace'] . '\\Auth'; - var_dump($auth); + $auth = '\\' . $this->config['pickles']['namespace'] . '\\Classes\\Auth'; $auth = new $auth(); switch ($this->config['pickles']['auth']) { case 'basic': - $auth->basic(); + if (!$auth->basic()) + { + throw new \Exception('Invalid authentication credentials.', 401); + } break; case 'oauth2': @@ -123,7 +124,6 @@ class Resource extends Object throw new \Exception('Invalid authentication scheme.', 401); break; } - */ } // Hack together some new globals From ae98b676832affd5735596fb5e64b3314f4f0e62 Mon Sep 17 00:00:00 2001 From: Josh Sherman Date: Fri, 3 Oct 2014 10:46:43 -0400 Subject: [PATCH 086/129] Cleaned up headers --- src/Auth.php | 23 +++++++++++++++++++++++ src/Browser.php | 7 ++----- src/Cache.php | 11 ++++------- src/Config.php | 11 ++++------- src/Convert.php | 7 ++----- src/Database.php | 9 +++------ src/Date.php | 7 ++----- src/Distance.php | 7 ++----- src/File.php | 7 ++----- src/Model.php | 15 ++++++--------- src/Number.php | 7 ++----- src/Object.php | 11 ++++------- src/Profiler.php | 7 ++----- src/Resource.php | 11 ++++------- src/Router.php | 11 ++++------- src/Sort.php | 7 ++----- src/String.php | 7 ++----- src/Time.php | 7 ++----- 18 files changed, 72 insertions(+), 100 deletions(-) diff --git a/src/Auth.php b/src/Auth.php index 147c0a3..315f62f 100644 --- a/src/Auth.php +++ b/src/Auth.php @@ -1,9 +1,32 @@ * @copyright Copyright 2007-2014, Josh Sherman * @license http://www.opensource.org/licenses/mit-license.html - * @package PICKLES - * @link https://github.com/joshtronic/pickles + * @link http://picklesphp.com + * @package Pickles */ namespace Pickles; diff --git a/src/Cache.php b/src/Cache.php index 28a862b..ecb1fe6 100644 --- a/src/Cache.php +++ b/src/Cache.php @@ -1,18 +1,15 @@ * @copyright Copyright 2007-2014, Josh Sherman * @license http://www.opensource.org/licenses/mit-license.html - * @package PICKLES - * @link https://github.com/joshtronic/pickles + * @link http://picklesphp.com + * @package Pickles */ namespace Pickles; @@ -26,7 +23,7 @@ namespace Pickles; * don't entirely remember specifics, but the reason for not using Memcached() * was due to an unexplainable bug in the version in the repository for Ubuntu * 10.04 LTS. Memcached() does support more of the memcached protocol and will - * eventually be what PICKLES uses. Keys are forced to be uppercase for + * eventually be what Pickles uses. Keys are forced to be uppercase for * consistencies sake as I've been burned by the case sensitivity due to typos * in my code. * diff --git a/src/Config.php b/src/Config.php index 0d3f226..c32d87b 100644 --- a/src/Config.php +++ b/src/Config.php @@ -1,18 +1,15 @@ * @copyright Copyright 2007-2014, Josh Sherman * @license http://www.opensource.org/licenses/mit-license.html - * @package PICKLES - * @link https://github.com/joshtronic/pickles + * @link http://picklesphp.com + * @package Pickles */ namespace Pickles; @@ -23,7 +20,7 @@ namespace Pickles; * Handles loading the site's configuration file (if available). At the moment * this class is a very skewed Singleton. The plan is to eventually extend this * out to support multiple configuration files, and the ability to load in - * custom config files on the fly as well. The core of PICKLES uses the class + * custom config files on the fly as well. The core of Pickles uses the class * as a Singleton so we're not loading the configuration multiple times per * page load. */ diff --git a/src/Convert.php b/src/Convert.php index e46d22a..87e3c81 100644 --- a/src/Convert.php +++ b/src/Convert.php @@ -3,16 +3,13 @@ /** * Converter * - * PHP version 5 - * * Licensed under The MIT License * Redistribution of these files must retain the above copyright notice. * - * @author Josh Sherman * @copyright Copyright 2007-2014, Josh Sherman * @license http://www.opensource.org/licenses/mit-license.html - * @package PICKLES - * @link https://github.com/joshtronic/pickles + * @link http://picklesphp.com + * @package Pickles */ namespace Pickles; diff --git a/src/Database.php b/src/Database.php index 9985df4..24f269c 100644 --- a/src/Database.php +++ b/src/Database.php @@ -1,18 +1,15 @@ * @copyright Copyright 2007-2014, Josh Sherman * @license http://www.opensource.org/licenses/mit-license.html - * @package PICKLES - * @link https://github.com/joshtronic/pickles + * @link http://picklesphp.com + * @package Pickles * @todo Drop driver, hardcode drivers based on the type * @todo More assumptions for the datasource variables */ diff --git a/src/Date.php b/src/Date.php index d38c75a..75af657 100644 --- a/src/Date.php +++ b/src/Date.php @@ -3,16 +3,13 @@ /** * Date Utility Collection * - * PHP version 5 - * * Licensed under The MIT License * Redistribution of these files must retain the above copyright notice. * - * @author Josh Sherman * @copyright Copyright 2007-2014, Josh Sherman * @license http://www.opensource.org/licenses/mit-license.html - * @package PICKLES - * @link https://github.com/joshtronic/pickles + * @link http://picklesphp.com + * @package Pickles */ namespace Pickles; diff --git a/src/Distance.php b/src/Distance.php index 3c65b7e..11ff121 100644 --- a/src/Distance.php +++ b/src/Distance.php @@ -3,16 +3,13 @@ /** * Distance * - * PHP version 5 - * * Licensed under The MIT License * Redistribution of these files must retain the above copyright notice. * - * @author Josh Sherman * @copyright Copyright 2007-2014, Josh Sherman * @license http://www.opensource.org/licenses/mit-license.html - * @package PICKLES - * @link https://github.com/joshtronic/pickles + * @link http://picklesphp.com + * @package Pickles */ namespace Pickles; diff --git a/src/File.php b/src/File.php index e04c596..4399b19 100644 --- a/src/File.php +++ b/src/File.php @@ -3,16 +3,13 @@ /** * File Utility Collection * - * PHP version 5 - * * Licensed under The MIT License * Redistribution of these files must retain the above copyright notice. * - * @author Josh Sherman * @copyright Copyright 2007-2014, Josh Sherman * @license http://www.opensource.org/licenses/mit-license.html - * @package PICKLES - * @link https://github.com/joshtronic/pickles + * @link http://picklesphp.com + * @package Pickles */ namespace Pickles; diff --git a/src/Model.php b/src/Model.php index f001893..83e4adb 100644 --- a/src/Model.php +++ b/src/Model.php @@ -1,18 +1,15 @@ * @copyright Copyright 2007-2014, Josh Sherman * @license http://www.opensource.org/licenses/mit-license.html - * @package PICKLES - * @link https://github.com/joshtronic/pickles + * @link http://picklesphp.com + * @package Pickles */ namespace Pickles; @@ -20,9 +17,9 @@ namespace Pickles; /** * Model Class * - * This is a parent class that all PICKLES data models should be extending. When - * using the class as designed, objects will function as active record pattern - * objects. + * This is a parent class that all Pickles data models should be extending. + * When using the class as designed, objects will function as active record + * pattern objects. */ class Model extends Object { diff --git a/src/Number.php b/src/Number.php index 5b7bc8f..59c9929 100644 --- a/src/Number.php +++ b/src/Number.php @@ -3,16 +3,13 @@ /** * Number Utility Collection * - * PHP version 5 - * * Licensed under The MIT License * Redistribution of these files must retain the above copyright notice. * - * @author Josh Sherman * @copyright Copyright 2007-2014, Josh Sherman * @license http://www.opensource.org/licenses/mit-license.html - * @package PICKLES - * @link https://github.com/joshtronic/pickles + * @link http://picklesphp.com + * @package Pickles */ namespace Pickles; diff --git a/src/Object.php b/src/Object.php index e9b9956..2ca484d 100644 --- a/src/Object.php +++ b/src/Object.php @@ -1,18 +1,15 @@ * @copyright Copyright 2007-2014, Josh Sherman * @license http://www.opensource.org/licenses/mit-license.html - * @package PICKLES - * @link https://github.com/joshtronic/pickles + * @link http://picklesphp.com + * @package Pickles */ namespace Pickles; @@ -20,7 +17,7 @@ namespace Pickles; /** * Object Class * - * Every instantiated class in PICKLES should be extending this class. By doing + * Every instantiated class in Pickles should be extending this class. By doing * so the class is automatically hooked into the profiler, and the object will * have access to some common components as well. */ diff --git a/src/Profiler.php b/src/Profiler.php index 3277fe2..184c8d4 100644 --- a/src/Profiler.php +++ b/src/Profiler.php @@ -3,16 +3,13 @@ /** * Profiler * - * PHP version 5 - * * Licensed under The MIT License * Redistribution of these files must retain the above copyright notice. * - * @author Josh Sherman * @copyright Copyright 2007-2014, Josh Sherman * @license http://www.opensource.org/licenses/mit-license.html - * @package PICKLES - * @link https://github.com/joshtronic/pickles + * @link http://picklesphp.com + * @package Pickles */ namespace Pickles; diff --git a/src/Resource.php b/src/Resource.php index 0539031..20beea3 100644 --- a/src/Resource.php +++ b/src/Resource.php @@ -1,18 +1,15 @@ * @copyright Copyright 2007-2014, Josh Sherman * @license http://www.opensource.org/licenses/mit-license.html - * @package PICKLES - * @link https://github.com/joshtronic/pickles + * @link http://picklesphp.com + * @package Pickles */ namespace Pickles; @@ -20,7 +17,7 @@ namespace Pickles; /** * Resource Class * - * This is a parent class that all PICKLES modules should be extending. Each + * This is a parent class that all Pickles modules should be extending. Each * module can specify it's own meta data and whether or not a user must be * properly authenticated to view the page. Currently any pages without a * template are treated as pages being requested via AJAX and the return will diff --git a/src/Router.php b/src/Router.php index 7630857..018e28d 100644 --- a/src/Router.php +++ b/src/Router.php @@ -1,18 +1,15 @@ * @copyright Copyright 2007-2014, Josh Sherman * @license http://www.opensource.org/licenses/mit-license.html - * @package PICKLES - * @link https://github.com/joshtronic/pickles + * @link http://picklesphp.com + * @package Pickles */ namespace Pickles; @@ -20,7 +17,7 @@ namespace Pickles; /** * Router Class * - * The heavy lifter of PICKLES, makes the calls to get the session and + * 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. diff --git a/src/Sort.php b/src/Sort.php index 92b9417..5c2a4d9 100644 --- a/src/Sort.php +++ b/src/Sort.php @@ -3,16 +3,13 @@ /** * Sorting Utility Collection * - * PHP version 5 - * * Licensed under The MIT License * Redistribution of these files must retain the above copyright notice. * - * @author Josh Sherman * @copyright Copyright 2007-2014, Josh Sherman * @license http://www.opensource.org/licenses/mit-license.html - * @package PICKLES - * @link https://github.com/joshtronic/pickles + * @link http://picklesphp.com + * @package Pickles */ namespace Pickles; diff --git a/src/String.php b/src/String.php index 62ca630..518b308 100644 --- a/src/String.php +++ b/src/String.php @@ -3,16 +3,13 @@ /** * String Utility Collection * - * PHP version 5 - * * Licensed under The MIT License * Redistribution of these files must retain the above copyright notice. * - * @author Josh Sherman * @copyright Copyright 2007-2014, Josh Sherman * @license http://www.opensource.org/licenses/mit-license.html - * @package PICKLES - * @link https://github.com/joshtronic/pickles + * @link http://picklesphp.com + * @package Pickles */ namespace Pickles; diff --git a/src/Time.php b/src/Time.php index af7e4e7..a1a0bb9 100644 --- a/src/Time.php +++ b/src/Time.php @@ -3,16 +3,13 @@ /** * Time Utility Collection * - * PHP version 5 - * * Licensed under The MIT License * Redistribution of these files must retain the above copyright notice. * - * @author Josh Sherman * @copyright Copyright 2007-2014, Josh Sherman * @license http://www.opensource.org/licenses/mit-license.html - * @package PICKLES - * @link https://github.com/joshtronic/pickles + * @link http://picklesphp.com + * @package Pickles */ namespace Pickles; From 2df1a5a162f7113ee2c7fa3773a29026135d9cea Mon Sep 17 00:00:00 2001 From: Josh Sherman Date: Fri, 3 Oct 2014 11:06:16 -0400 Subject: [PATCH 087/129] Updated project name --- .travis.yml | 2 +- README.md | 11 ++++++----- composer.json | 8 ++++---- src/Resource.php | 2 +- 4 files changed, 12 insertions(+), 11 deletions(-) diff --git a/.travis.yml b/.travis.yml index b89a1e1..281f474 100644 --- a/.travis.yml +++ b/.travis.yml @@ -32,7 +32,7 @@ before_script: - phpenv rehash script: - - phpunit --coverage-clover /home/travis/build/joshtronic/pickles/build/logs/clover.xml + - phpunit --coverage-clover /home/travis/build/picklesphp/pickles/build/logs/clover.xml after_success: - php vendor/bin/coveralls --config ../.coveralls.yml -v diff --git a/README.md b/README.md index 3905f55..8d9c0fc 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,9 @@ # Pickles -[![Build Status](https://travis-ci.org/joshtronic/pickles.png?branch=master)](https://travis-ci.org/joshtronic/pickles) [![Coverage Status](https://coveralls.io/repos/joshtronic/pickles/badge.png)](https://coveralls.io/r/joshtronic/pickles) [![Dependency Status](https://www.versioneye.com/user/projects/52d1bc1eec13751bde00002a/badge.png)](https://www.versioneye.com/user/projects/52d1bc1eec13751bde00002a) -[![Stories in Ready](https://badge.waffle.io/joshtronic/pickles.png?label=ready&title=Ready)](https://waffle.io/joshtronic/pickles) +[![Build Status](https://travis-ci.org/picklesphp/pickles.png?branch=master)](https://travis-ci.org/picklesphp/pickles) +[![Coverage Status](https://coveralls.io/repos/picklesphp/pickles/badge.png)](https://coveralls.io/r/picklesphp/pickles) +[![Dependency Status](https://www.versioneye.com/user/projects/52d1bc1eec13751bde00002a/badge.png)](https://www.versioneye.com/user/projects/52d1bc1eec13751bde00002a) +[![Stories in Ready](https://badge.waffle.io/picklesphp/pickles.png?label=ready&title=Ready)](https://waffle.io/picklesphp/pickles) Pickles f/k/a PICKLES (PHP Interface Collection of Killer Libraries to Enhance Stuff) is an open source framework for the rapid development of RESTful @@ -23,7 +25,6 @@ acronym. [JustinDavis]: http://justindavis.co [HolidayAPI]: https://github.com/gravityblvd/tools.gravityblvd.com -[MasterZip]: https://github.com/joshtronic/pickles/archive/master.zip +[MasterZip]: https://github.com/picklesphp/pickles/archive/master.zip [StackPost]: http://joshtronic.com/2014/01/13/your-stack-is-outdated/#.UuVzI3n0A18 -[UPOZ]: https://github.com/krakjoe/uopz -[v13.12]: https://github.com/joshtronic/pickles/tree/13.12 +[v13.12]: https://github.com/picklesphp/pickles/tree/13.12 diff --git a/composer.json b/composer.json index dfc79fa..31156bc 100644 --- a/composer.json +++ b/composer.json @@ -1,9 +1,9 @@ { - "name": "joshtronic/pickles", + "name": "picklesphp/pickles", "description": "The Pickles SOA Framework", "type": "library", "keywords": ["framework", "api", "soa", "oauth"], - "homepage": "https://github.com/joshtronic/pickles", + "homepage": "http://picklesphp.com", "license": "MIT", "authors": [ { @@ -13,8 +13,8 @@ } ], "support": { - "issues": "https://github.com/joshtronic/pickles/issues", - "source": "https://github.com/joshtronic/pickles" + "issues": "https://github.com/picklesphp/pickles/issues", + "source": "https://github.com/picklesphp/pickles" }, "minimum-stability" : "dev", "require-dev": { diff --git a/src/Resource.php b/src/Resource.php index 20beea3..f55900a 100644 --- a/src/Resource.php +++ b/src/Resource.php @@ -359,7 +359,7 @@ class Resource extends Object { http_response_code($this->status); header('Content-Type: application/json'); - header('X-Powered-By: Pickles v2 - https://github.com/joshtronic/pickles'); + header('X-Powered-By: Pickles v2 - https://picklesphp.com'); $meta = [ 'status' => $this->status, From e45e1251e1ef803c1309ee66796a6a82848b2f41 Mon Sep 17 00:00:00 2001 From: Josh Sherman Date: Sat, 4 Oct 2014 07:24:57 -0400 Subject: [PATCH 088/129] Added auth test, cleaned up directory --- composer.json | 2 +- composer.lock | 10 ++-- phpunit.xml | 6 +-- src/Auth.php | 10 ++-- src/Resource.php | 17 ++++++- src/Router.php | 1 + tests/Pickles/AuthTest.php | 38 +++++++++++++++ tests/{ => Pickles}/BrowserTest.php | 0 tests/{ => Pickles}/CacheTest.php | 0 tests/{ => Pickles}/ConfigTest.php | 0 tests/{ => Pickles}/ConvertTest.php | 0 tests/{ => Pickles}/DatabaseTest.php | 0 tests/{ => Pickles}/DateTest.php | 0 tests/{ => Pickles}/DistanceTest.php | 0 tests/{ => Pickles}/FileTest.php | 0 tests/{ => Pickles}/ModelTest.php | 0 tests/{ => Pickles}/NumberTest.php | 0 tests/{ => Pickles}/ObjectTest.php | 0 tests/{ => Pickles}/ProfilerTest.php | 0 tests/{ => Pickles}/ResourceTest.php | 65 ++++++++++++++++++++++++++ tests/{ => Pickles}/RouterTest.php | 0 tests/{ => Pickles}/SortTest.php | 0 tests/{ => Pickles}/StringTest.php | 0 tests/{ => Pickles}/TimeTest.php | 0 tests/{Bootstrap.php => bootstrap.php} | 0 25 files changed, 134 insertions(+), 15 deletions(-) create mode 100644 tests/Pickles/AuthTest.php rename tests/{ => Pickles}/BrowserTest.php (100%) rename tests/{ => Pickles}/CacheTest.php (100%) rename tests/{ => Pickles}/ConfigTest.php (100%) rename tests/{ => Pickles}/ConvertTest.php (100%) rename tests/{ => Pickles}/DatabaseTest.php (100%) rename tests/{ => Pickles}/DateTest.php (100%) rename tests/{ => Pickles}/DistanceTest.php (100%) rename tests/{ => Pickles}/FileTest.php (100%) rename tests/{ => Pickles}/ModelTest.php (100%) rename tests/{ => Pickles}/NumberTest.php (100%) rename tests/{ => Pickles}/ObjectTest.php (100%) rename tests/{ => Pickles}/ProfilerTest.php (100%) rename tests/{ => Pickles}/ResourceTest.php (85%) rename tests/{ => Pickles}/RouterTest.php (100%) rename tests/{ => Pickles}/SortTest.php (100%) rename tests/{ => Pickles}/StringTest.php (100%) rename tests/{ => Pickles}/TimeTest.php (100%) rename tests/{Bootstrap.php => bootstrap.php} (100%) diff --git a/composer.json b/composer.json index 31156bc..4bbdbbd 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { "name": "picklesphp/pickles", - "description": "The Pickles SOA Framework", + "description": "Pickles is a PHP framework for building kick-ass services", "type": "library", "keywords": ["framework", "api", "soa", "oauth"], "homepage": "http://picklesphp.com", diff --git a/composer.lock b/composer.lock index 64198c9..4f7de01 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "16033f40d01b3ada4064b0b7488d4cb7", + "hash": "90c06945853fd0c6910bfd5e6f9c1e13", "packages": [], "packages-dev": [ { @@ -1068,12 +1068,12 @@ "source": { "type": "git", "url": "https://github.com/symfony/EventDispatcher.git", - "reference": "93d237c3da7565a9ad2b4b6a74af73f4e3c0c305" + "reference": "e133748fd9165e24f8e9498ef5862f8bd37004e5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/EventDispatcher/zipball/93d237c3da7565a9ad2b4b6a74af73f4e3c0c305", - "reference": "93d237c3da7565a9ad2b4b6a74af73f4e3c0c305", + "url": "https://api.github.com/repos/symfony/EventDispatcher/zipball/e133748fd9165e24f8e9498ef5862f8bd37004e5", + "reference": "e133748fd9165e24f8e9498ef5862f8bd37004e5", "shasum": "" }, "require": { @@ -1117,7 +1117,7 @@ ], "description": "Symfony EventDispatcher Component", "homepage": "http://symfony.com", - "time": "2014-10-01 05:53:11" + "time": "2014-10-04 06:08:58" }, { "name": "symfony/filesystem", diff --git a/phpunit.xml b/phpunit.xml index 016e823..2d7ea10 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -2,13 +2,13 @@ - - ./tests/ + + ./tests/Pickles diff --git a/src/Auth.php b/src/Auth.php index 315f62f..aaf757e 100644 --- a/src/Auth.php +++ b/src/Auth.php @@ -1,7 +1,7 @@ config['pickles']['namespace'] . '\\Classes\\Auth'; + + // Strips preceding slashs when there is no namespace + if (strpos($auth, '\\\\') === 0) + { + $auth = substr($auth, 2); + } + $auth = new $auth(); + // @todo Remove when switch is implemented + if (!$auth->basic()) + { + throw new \Exception('Invalid authentication credentials.', 401); + } + + // @todo Not yet implemented + /* switch ($this->config['pickles']['auth']) { case 'basic': @@ -112,7 +127,6 @@ class Resource extends Object throw new \Exception('Invalid authentication credentials.', 401); } break; - case 'oauth2': $auth->oauth2(); break; @@ -121,6 +135,7 @@ class Resource extends Object throw new \Exception('Invalid authentication scheme.', 401); break; } + */ } // Hack together some new globals diff --git a/src/Router.php b/src/Router.php index 018e28d..5eb9e76 100644 --- a/src/Router.php +++ b/src/Router.php @@ -63,6 +63,7 @@ class Router extends Object array_unshift($nouns, '', $this->config['pickles']['namespace'], 'Resources', $version); $class = implode('\\', $nouns); + // @todo Make namespace mandatory // Strips preceding slashs when there is no namespace if (strpos($class, '\\\\') === 0) { diff --git a/tests/Pickles/AuthTest.php b/tests/Pickles/AuthTest.php new file mode 100644 index 0000000..c399202 --- /dev/null +++ b/tests/Pickles/AuthTest.php @@ -0,0 +1,38 @@ + [ + "local" => "127.0.0.1", + "production" => "123.456.789.0", + ], + ]; + '); + + Pickles\Config::getInstance('/tmp/pickles.php'); + + $this->auth = new Pickles\Auth(); + } + + public function testBasic() + { + $this->assertFalse($this->auth->basic()); + } + + public function testOAuth2() + { + $this->assertFalse($this->auth->oauth2()); + } +} + diff --git a/tests/BrowserTest.php b/tests/Pickles/BrowserTest.php similarity index 100% rename from tests/BrowserTest.php rename to tests/Pickles/BrowserTest.php diff --git a/tests/CacheTest.php b/tests/Pickles/CacheTest.php similarity index 100% rename from tests/CacheTest.php rename to tests/Pickles/CacheTest.php diff --git a/tests/ConfigTest.php b/tests/Pickles/ConfigTest.php similarity index 100% rename from tests/ConfigTest.php rename to tests/Pickles/ConfigTest.php diff --git a/tests/ConvertTest.php b/tests/Pickles/ConvertTest.php similarity index 100% rename from tests/ConvertTest.php rename to tests/Pickles/ConvertTest.php diff --git a/tests/DatabaseTest.php b/tests/Pickles/DatabaseTest.php similarity index 100% rename from tests/DatabaseTest.php rename to tests/Pickles/DatabaseTest.php diff --git a/tests/DateTest.php b/tests/Pickles/DateTest.php similarity index 100% rename from tests/DateTest.php rename to tests/Pickles/DateTest.php diff --git a/tests/DistanceTest.php b/tests/Pickles/DistanceTest.php similarity index 100% rename from tests/DistanceTest.php rename to tests/Pickles/DistanceTest.php diff --git a/tests/FileTest.php b/tests/Pickles/FileTest.php similarity index 100% rename from tests/FileTest.php rename to tests/Pickles/FileTest.php diff --git a/tests/ModelTest.php b/tests/Pickles/ModelTest.php similarity index 100% rename from tests/ModelTest.php rename to tests/Pickles/ModelTest.php diff --git a/tests/NumberTest.php b/tests/Pickles/NumberTest.php similarity index 100% rename from tests/NumberTest.php rename to tests/Pickles/NumberTest.php diff --git a/tests/ObjectTest.php b/tests/Pickles/ObjectTest.php similarity index 100% rename from tests/ObjectTest.php rename to tests/Pickles/ObjectTest.php diff --git a/tests/ProfilerTest.php b/tests/Pickles/ProfilerTest.php similarity index 100% rename from tests/ProfilerTest.php rename to tests/Pickles/ProfilerTest.php diff --git a/tests/ResourceTest.php b/tests/Pickles/ResourceTest.php similarity index 85% rename from tests/ResourceTest.php rename to tests/Pickles/ResourceTest.php index 987ddb0..4a23713 100644 --- a/tests/ResourceTest.php +++ b/tests/Pickles/ResourceTest.php @@ -63,6 +63,28 @@ namespace Resources\v1 } } +namespace Classes +{ + class Auth extends \Pickles\Auth + { + private static $count = 0; + + public function basic() + { + self::$count++; + + if (self::$count % 2) + { + return false; + } + else + { + return true; + } + } + } +} + namespace { class ResourceTest extends PHPUnit_Framework_TestCase @@ -213,6 +235,49 @@ namespace new Pickles\Router(); } + public function testBasicAuthBadCredentials() + { + Pickles\Object::$instances = []; + + $_SERVER['REQUEST_METHOD'] = 'GET'; + $_SERVER['SERVER_NAME'] = '127.0.0.1'; + + file_put_contents('/tmp/pickles.php', ' [ + "local" => "127.0.0.1", + "production" => "123.456.789.0", + ], + "pickles" => [ + "namespace" => "", + "datasource" => "mysql", + "auth" => "basic", + ], + "datasources" => [ + "mysql" => [ + "driver" => "pdo_mysql", + ], + ], + ]; + '); + + Pickles\Config::getInstance('/tmp/pickles.php'); + + $response = json_encode([ + 'meta' => [ + 'status' => 401, + 'message' => 'Invalid authentication credentials.', + ], + ]); + + $this->expectOutputString($response); + + $_SERVER['REQUEST_METHOD'] = 'DELETE'; + $_REQUEST['request'] = 'v1/resource/1'; + + new Pickles\Router(); + } + public function testBasicAuth() { Pickles\Object::$instances = []; diff --git a/tests/RouterTest.php b/tests/Pickles/RouterTest.php similarity index 100% rename from tests/RouterTest.php rename to tests/Pickles/RouterTest.php diff --git a/tests/SortTest.php b/tests/Pickles/SortTest.php similarity index 100% rename from tests/SortTest.php rename to tests/Pickles/SortTest.php diff --git a/tests/StringTest.php b/tests/Pickles/StringTest.php similarity index 100% rename from tests/StringTest.php rename to tests/Pickles/StringTest.php diff --git a/tests/TimeTest.php b/tests/Pickles/TimeTest.php similarity index 100% rename from tests/TimeTest.php rename to tests/Pickles/TimeTest.php diff --git a/tests/Bootstrap.php b/tests/bootstrap.php similarity index 100% rename from tests/Bootstrap.php rename to tests/bootstrap.php From 77473c5b31306c7205bb2b2c3dd91476463bb958 Mon Sep 17 00:00:00 2001 From: Josh Sherman Date: Sat, 4 Oct 2014 11:04:21 -0400 Subject: [PATCH 089/129] Dropped sort class from the core --- src/Sort.php | 70 -------------------------------------- tests/Pickles/SortTest.php | 61 --------------------------------- 2 files changed, 131 deletions(-) delete mode 100644 src/Sort.php delete mode 100644 tests/Pickles/SortTest.php diff --git a/src/Sort.php b/src/Sort.php deleted file mode 100644 index 5c2a4d9..0000000 --- a/src/Sort.php +++ /dev/null @@ -1,70 +0,0 @@ -' : '<') .' $b) ? -1 : 1; - ')); - - return true; - } -} - diff --git a/tests/Pickles/SortTest.php b/tests/Pickles/SortTest.php deleted file mode 100644 index ed916f2..0000000 --- a/tests/Pickles/SortTest.php +++ /dev/null @@ -1,61 +0,0 @@ - 'epsilon'], - ['name' => 'gamma'], - ['name' => 'alpha'], - ['name' => 'delta'], - ['name' => 'beta'], - ]; - - $sorted = [ - ['name' => 'alpha'], - ['name' => 'beta'], - ['name' => 'delta'], - ['name' => 'epsilon'], - ['name' => 'gamma'], - ]; - - Pickles\Sort::by('name', $shuffled); - - $this->assertEquals($sorted, $shuffled); - } - - public function testByNameDESC() - { - $shuffled = [ - ['name' => 'epsilon'], - ['name' => 'gamma'], - ['name' => 'alpha'], - ['name' => 'delta'], - ['name' => 'beta'], - ]; - - $sorted = [ - ['name' => 'gamma'], - ['name' => 'epsilon'], - ['name' => 'delta'], - ['name' => 'beta'], - ['name' => 'alpha'], - ]; - - Pickles\Sort::by('name', $shuffled, Pickles\Sort::DESC); - - $this->assertEquals($sorted, $shuffled); - } - - public function testMissingField() - { - $shuffled = [['foo' => 'bar', 'bar' => 'foo']]; - $sorted = [['foo' => 'bar', 'bar' => 'foo']]; - - Pickles\Sort::by('name', $shuffled); - - $this->assertEquals($sorted, $shuffled); - } -} - From ee34d8aff9e935915519c6c7350e619bb6d40fd8 Mon Sep 17 00:00:00 2001 From: Josh Sherman Date: Sat, 4 Oct 2014 11:08:01 -0400 Subject: [PATCH 090/129] Standardized the badges --- README.md | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 8d9c0fc..5354487 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ # Pickles -[![Build Status](https://travis-ci.org/picklesphp/pickles.png?branch=master)](https://travis-ci.org/picklesphp/pickles) -[![Coverage Status](https://coveralls.io/repos/picklesphp/pickles/badge.png)](https://coveralls.io/r/picklesphp/pickles) -[![Dependency Status](https://www.versioneye.com/user/projects/52d1bc1eec13751bde00002a/badge.png)](https://www.versioneye.com/user/projects/52d1bc1eec13751bde00002a) -[![Stories in Ready](https://badge.waffle.io/picklesphp/pickles.png?label=ready&title=Ready)](https://waffle.io/picklesphp/pickles) +[![License](http://img.shields.io/packagist/l/picklesphp/pickles.svg?style=flat)][packagist] +[![Build](http://img.shields.io/travis/picklesphp/pickles.svg?style=flat)][travis] +[![Coverage](http://img.shields.io/coveralls/picklesphp/pickles.svg?style=flat)][coveralls] +[![Downloads](http://img.shields.io/packagist/dt/picklesphp/pickles.svg?style=flat)][packagist] Pickles f/k/a PICKLES (PHP Interface Collection of Killer Libraries to Enhance Stuff) is an open source framework for the rapid development of RESTful @@ -20,11 +20,9 @@ contributor, [Justin Davis][JustinDavis] for romanticizing the v2 reimagining and [Dean Jones][DeanJones] for helping to come up with the original PICKLES v1 acronym. +[coveralls]: https://coveralls.io/r/picklesphp/pickles +[packagist]: https://packagist.org/packages/picklesphp/pickles +[travis]: http://travis-ci.org/picklesphp/pickles [DeanJones]: https://github.com/deanproxy [GeoffOliver]: https://github.com/geoffoliver [JustinDavis]: http://justindavis.co - -[HolidayAPI]: https://github.com/gravityblvd/tools.gravityblvd.com -[MasterZip]: https://github.com/picklesphp/pickles/archive/master.zip -[StackPost]: http://joshtronic.com/2014/01/13/your-stack-is-outdated/#.UuVzI3n0A18 -[v13.12]: https://github.com/picklesphp/pickles/tree/13.12 From 6c148c124ee7343f5df50453551ab660a7d2dac2 Mon Sep 17 00:00:00 2001 From: Josh Sherman Date: Sat, 4 Oct 2014 11:10:59 -0400 Subject: [PATCH 091/129] Added self-update to the travis build Making this a standard thing in my travis scripts because it seems they let the version go stale pretty regularly. --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 281f474..61a281d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -22,6 +22,7 @@ services: - redis install: + - composer self-update - composer install before_script: From c5d39db63bc6f12d0bcbdebe1d4dacf13d5d6edd Mon Sep 17 00:00:00 2001 From: Josh Sherman Date: Mon, 6 Oct 2014 18:27:53 -0400 Subject: [PATCH 092/129] Brought Sort back into the repo Decided that having a bunch of external dependencies would end up being more trouble than it's worth --- src/Sort.php | 70 ++++++++++++++++++++++++++++++++++++++ tests/Pickles/SortTest.php | 61 +++++++++++++++++++++++++++++++++ 2 files changed, 131 insertions(+) create mode 100644 src/Sort.php create mode 100644 tests/Pickles/SortTest.php diff --git a/src/Sort.php b/src/Sort.php new file mode 100644 index 0000000..5c2a4d9 --- /dev/null +++ b/src/Sort.php @@ -0,0 +1,70 @@ +' : '<') .' $b) ? -1 : 1; + ')); + + return true; + } +} + diff --git a/tests/Pickles/SortTest.php b/tests/Pickles/SortTest.php new file mode 100644 index 0000000..ed916f2 --- /dev/null +++ b/tests/Pickles/SortTest.php @@ -0,0 +1,61 @@ + 'epsilon'], + ['name' => 'gamma'], + ['name' => 'alpha'], + ['name' => 'delta'], + ['name' => 'beta'], + ]; + + $sorted = [ + ['name' => 'alpha'], + ['name' => 'beta'], + ['name' => 'delta'], + ['name' => 'epsilon'], + ['name' => 'gamma'], + ]; + + Pickles\Sort::by('name', $shuffled); + + $this->assertEquals($sorted, $shuffled); + } + + public function testByNameDESC() + { + $shuffled = [ + ['name' => 'epsilon'], + ['name' => 'gamma'], + ['name' => 'alpha'], + ['name' => 'delta'], + ['name' => 'beta'], + ]; + + $sorted = [ + ['name' => 'gamma'], + ['name' => 'epsilon'], + ['name' => 'delta'], + ['name' => 'beta'], + ['name' => 'alpha'], + ]; + + Pickles\Sort::by('name', $shuffled, Pickles\Sort::DESC); + + $this->assertEquals($sorted, $shuffled); + } + + public function testMissingField() + { + $shuffled = [['foo' => 'bar', 'bar' => 'foo']]; + $sorted = [['foo' => 'bar', 'bar' => 'foo']]; + + Pickles\Sort::by('name', $shuffled); + + $this->assertEquals($sorted, $shuffled); + } +} + From de12028b357f3b8782a0aceacde4a1c77dd5f552 Mon Sep 17 00:00:00 2001 From: Josh Sherman Date: Mon, 6 Oct 2014 19:53:09 -0400 Subject: [PATCH 093/129] Added oauth server as a dependency --- composer.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 4bbdbbd..99bd01e 100644 --- a/composer.json +++ b/composer.json @@ -22,7 +22,8 @@ "satooshi/php-coveralls": "dev-master" }, "require": { - "php": ">=5.4" + "php": ">=5.4", + "league/oauth2-server": "3.*" }, "autoload": { "psr-4": { From 8e60e9d553526ebd1429f80890b8f9e7eaddad02 Mon Sep 17 00:00:00 2001 From: Josh Sherman Date: Tue, 7 Oct 2014 09:33:21 -0400 Subject: [PATCH 094/129] Dropped date utility class Only had one function and it was referencing the time class. Moved date tests to the time tests because it seemed like a more comprehensive set of tests. --- src/Date.php | 39 -------------------------------------- tests/Pickles/DateTest.php | 29 ---------------------------- tests/Pickles/TimeTest.php | 22 +++++++++++++++++++++ 3 files changed, 22 insertions(+), 68 deletions(-) delete mode 100644 src/Date.php delete mode 100644 tests/Pickles/DateTest.php diff --git a/src/Date.php b/src/Date.php deleted file mode 100644 index 75af657..0000000 --- a/src/Date.php +++ /dev/null @@ -1,39 +0,0 @@ -assertEquals(Pickles\Date::age($a), $b); - } - - public function providerAge() - { - ini_set('date.timezone', 'America/New_York'); - - $time = strtotime('-25 years'); - - return [ - [date('Y-m-d', $time), '25'], - [date('m/d/Y', $time), '25'], - [date('r', $time), '25'], - ['today', '0'], - ['400 days ago', '1'], - [true, Pickles\Date::age('1969-12-31')], - ]; - } -} - diff --git a/tests/Pickles/TimeTest.php b/tests/Pickles/TimeTest.php index cdcc040..d4dbf86 100644 --- a/tests/Pickles/TimeTest.php +++ b/tests/Pickles/TimeTest.php @@ -7,6 +7,28 @@ class TimeTest extends PHPUnit_Framework_TestCase date_default_timezone_set('GMT'); } + /** + * @dataProvider providerAge + */ + public function testAge($a, $b) + { + $this->assertEquals(Pickles\Time::age($a), $b); + } + + public function providerAge() + { + $time = strtotime('-25 years'); + + return [ + [date('Y-m-d', $time), '25'], + [date('m/d/Y', $time), '25'], + [date('r', $time), '25'], + ['today', '0'], + ['400 days ago', '1'], + [true, Pickles\Date::age('1969-12-31')], + ]; + } + public function testAgePastTime() { $this->assertEquals(18, Pickles\Time::age(date('Y-m-d', strtotime('-18 years')))); From 846f75d0f376f1ddbecb241884bf5c64b4bef2e5 Mon Sep 17 00:00:00 2001 From: Josh Sherman Date: Tue, 7 Oct 2014 09:37:58 -0400 Subject: [PATCH 095/129] Updated dependencies --- composer.json | 2 +- composer.lock | 183 +++++++++++++++++++++++++++++++++----------------- 2 files changed, 123 insertions(+), 62 deletions(-) diff --git a/composer.json b/composer.json index 99bd01e..33c66af 100644 --- a/composer.json +++ b/composer.json @@ -22,7 +22,7 @@ "satooshi/php-coveralls": "dev-master" }, "require": { - "php": ">=5.4", + "php": ">=5.4", "league/oauth2-server": "3.*" }, "autoload": { diff --git a/composer.lock b/composer.lock index 4f7de01..937cd04 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,71 @@ "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "90c06945853fd0c6910bfd5e6f9c1e13", - "packages": [], + "hash": "c9883c463f5dad3fc400327e41287cc0", + "packages": [ + { + "name": "league/oauth2-server", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/oauth2-server.git", + "reference": "13cd0cacdf1e9ae23fa073d4408a336246babb8e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/oauth2-server/zipball/13cd0cacdf1e9ae23fa073d4408a336246babb8e", + "reference": "13cd0cacdf1e9ae23fa073d4408a336246babb8e", + "shasum": "" + }, + "require": { + "php": ">=5.4.0" + }, + "replace": { + "lncd/oauth2": "*" + }, + "require-dev": { + "league/phpunit-coverage-listener": "~1.0", + "mockery/mockery": "~0.8" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.2.x-dev" + } + }, + "autoload": { + "psr-0": { + "League\\OAuth2\\Server": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Alex Bilbie", + "email": "hello@alexbilbie.com", + "homepage": "http://www.alexbilbie.com", + "role": "Developer" + } + ], + "description": "A lightweight and powerful OAuth 2.0 authorization and resource server library with support for all the core specification grants. This library will allow you to secure your API with OAuth and allow your applications users to approve apps that want to access their data from your API.", + "keywords": [ + "Authentication", + "api", + "auth", + "authorization", + "oauth", + "oauth2", + "protect", + "resource", + "secure", + "server" + ], + "time": "2014-09-08 21:01:33" + } + ], "packages-dev": [ { "name": "doctrine/instantiator", @@ -13,12 +76,12 @@ "source": { "type": "git", "url": "https://github.com/doctrine/instantiator.git", - "reference": "26404e0c90565b614ee76b988b9bc8790d77f590" + "reference": "4bdc0421209a00e6f425f4c3554d1b0df3a7e897" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/26404e0c90565b614ee76b988b9bc8790d77f590", - "reference": "26404e0c90565b614ee76b988b9bc8790d77f590", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/4bdc0421209a00e6f425f4c3554d1b0df3a7e897", + "reference": "4bdc0421209a00e6f425f4c3554d1b0df3a7e897", "shasum": "" }, "require": { @@ -59,7 +122,7 @@ "constructor", "instantiate" ], - "time": "2014-08-25 15:09:25" + "time": "2014-10-05 00:20:37" }, { "name": "guzzle/guzzle", @@ -159,21 +222,21 @@ "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "0f0063f283597359ea5cb4e1afdf1a14b0ac5038" + "reference": "28d21b57c189cb72829056353de603c4d4da55a0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/0f0063f283597359ea5cb4e1afdf1a14b0ac5038", - "reference": "0f0063f283597359ea5cb4e1afdf1a14b0ac5038", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/28d21b57c189cb72829056353de603c4d4da55a0", + "reference": "28d21b57c189cb72829056353de603c4d4da55a0", "shasum": "" }, "require": { "php": ">=5.3.3", - "phpunit/php-file-iterator": "~1.3.1", - "phpunit/php-text-template": "~1.2.0", - "phpunit/php-token-stream": "~1.2.2", - "sebastian/environment": "~1.0.0", - "sebastian/version": "~1.0.3" + "phpunit/php-file-iterator": "~1.3", + "phpunit/php-text-template": "~1.2", + "phpunit/php-token-stream": "~1.3", + "sebastian/environment": "~1.0", + "sebastian/version": "~1.0" }, "require-dev": { "ext-xdebug": ">=2.1.4", @@ -213,7 +276,7 @@ "testing", "xunit" ], - "time": "2014-09-01 22:04:32" + "time": "2014-10-05 10:46:54" }, { "name": "phpunit/php-file-iterator", @@ -350,45 +413,44 @@ }, { "name": "phpunit/php-token-stream", - "version": "1.2.2", + "version": "dev-master", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-token-stream.git", - "reference": "ad4e1e23ae01b483c16f600ff1bebec184588e32" + "reference": "f8d5d08c56de5cfd592b3340424a81733259a876" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/ad4e1e23ae01b483c16f600ff1bebec184588e32", - "reference": "ad4e1e23ae01b483c16f600ff1bebec184588e32", + "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/f8d5d08c56de5cfd592b3340424a81733259a876", + "reference": "f8d5d08c56de5cfd592b3340424a81733259a876", "shasum": "" }, "require": { "ext-tokenizer": "*", "php": ">=5.3.3" }, + "require-dev": { + "phpunit/phpunit": "~4.2" + }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.2-dev" + "dev-master": "1.3-dev" } }, "autoload": { "classmap": [ - "PHP/" + "src/" ] }, "notification-url": "https://packagist.org/downloads/", - "include-path": [ - "" - ], "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", - "role": "lead" + "email": "sebastian@phpunit.de" } ], "description": "Wrapper around PHP's tokenizer extension.", @@ -396,7 +458,7 @@ "keywords": [ "tokenizer" ], - "time": "2014-03-03 05:10:30" + "time": "2014-08-31 06:12:13" }, { "name": "phpunit/phpunit", @@ -404,12 +466,12 @@ "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "5ee7af96d62a3fe9cf466fab1b505ac8dc1796c6" + "reference": "f75e6b2e0764bcf4faa5082f4b60d5e7cf0959bb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/5ee7af96d62a3fe9cf466fab1b505ac8dc1796c6", - "reference": "5ee7af96d62a3fe9cf466fab1b505ac8dc1796c6", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/f75e6b2e0764bcf4faa5082f4b60d5e7cf0959bb", + "reference": "f75e6b2e0764bcf4faa5082f4b60d5e7cf0959bb", "shasum": "" }, "require": { @@ -423,10 +485,10 @@ "phpunit/php-file-iterator": "~1.3.1", "phpunit/php-text-template": "~1.2", "phpunit/php-timer": "~1.0.2", - "phpunit/phpunit-mock-objects": "2.3.*@dev", + "phpunit/phpunit-mock-objects": "2.4.*@dev", "sebastian/comparator": "~1.0", "sebastian/diff": "~1.1", - "sebastian/environment": "~1.0", + "sebastian/environment": "~1.1", "sebastian/exporter": "~1.0", "sebastian/global-state": "1.0.*@dev", "sebastian/version": "~1.0", @@ -467,29 +529,29 @@ "testing", "xunit" ], - "time": "2014-09-19 08:44:28" + "time": "2014-10-07 09:30:07" }, { "name": "phpunit/phpunit-mock-objects", - "version": "2.3.x-dev", + "version": "dev-master", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", - "reference": "c63d2367247365f688544f0d500af90a11a44c65" + "reference": "96c5b81f9842f38fe6c73ad0020306cc4862a9e3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/c63d2367247365f688544f0d500af90a11a44c65", - "reference": "c63d2367247365f688544f0d500af90a11a44c65", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/96c5b81f9842f38fe6c73ad0020306cc4862a9e3", + "reference": "96c5b81f9842f38fe6c73ad0020306cc4862a9e3", "shasum": "" }, "require": { - "doctrine/instantiator": "~1.0,>=1.0.1", + "doctrine/instantiator": "~1.0,>=1.0.2", "php": ">=5.3.3", "phpunit/php-text-template": "~1.2" }, "require-dev": { - "phpunit/phpunit": "~4.3" + "phpunit/phpunit": "4.4.*@dev" }, "suggest": { "ext-soap": "*" @@ -497,7 +559,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.3.x-dev" + "dev-master": "2.4.x-dev" } }, "autoload": { @@ -522,7 +584,7 @@ "mock", "xunit" ], - "time": "2014-10-03 05:12:11" + "time": "2014-10-04 10:04:20" }, { "name": "psr/log", @@ -760,24 +822,24 @@ "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "10c7467da0622f7848cc5cadc0828c3359254df4" + "reference": "6288ebbf6fa3ed2b2ff2d69c356fbaaf4f0971a7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/10c7467da0622f7848cc5cadc0828c3359254df4", - "reference": "10c7467da0622f7848cc5cadc0828c3359254df4", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/6288ebbf6fa3ed2b2ff2d69c356fbaaf4f0971a7", + "reference": "6288ebbf6fa3ed2b2ff2d69c356fbaaf4f0971a7", "shasum": "" }, "require": { "php": ">=5.3.3" }, "require-dev": { - "phpunit/phpunit": "~4.0" + "phpunit/phpunit": "~4.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "1.1.x-dev" } }, "autoload": { @@ -792,8 +854,7 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "email": "sebastian@phpunit.de" } ], "description": "Provides functionality to handle HHVM/PHP environments", @@ -803,7 +864,7 @@ "environment", "hhvm" ], - "time": "2014-05-04 17:56:05" + "time": "2014-10-07 09:23:16" }, { "name": "sebastian/exporter", @@ -876,12 +937,12 @@ "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "cd2f406aa90b591e1c45023c3a8d77edcb417bac" + "reference": "231d48620efde984fd077ee92916099a3ece9a59" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/cd2f406aa90b591e1c45023c3a8d77edcb417bac", - "reference": "cd2f406aa90b591e1c45023c3a8d77edcb417bac", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/231d48620efde984fd077ee92916099a3ece9a59", + "reference": "231d48620efde984fd077ee92916099a3ece9a59", "shasum": "" }, "require": { @@ -919,7 +980,7 @@ "keywords": [ "global state" ], - "time": "2014-09-04 07:21:11" + "time": "2014-10-06 09:49:11" }, { "name": "sebastian/version", @@ -1011,12 +1072,12 @@ "source": { "type": "git", "url": "https://github.com/symfony/Console.git", - "reference": "7049924cf0b7f350e5a099ea988e713ebf7590fe" + "reference": "771649efa94246e63a6ab2726ba908a358bdd403" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/Console/zipball/7049924cf0b7f350e5a099ea988e713ebf7590fe", - "reference": "7049924cf0b7f350e5a099ea988e713ebf7590fe", + "url": "https://api.github.com/repos/symfony/Console/zipball/771649efa94246e63a6ab2726ba908a358bdd403", + "reference": "771649efa94246e63a6ab2726ba908a358bdd403", "shasum": "" }, "require": { @@ -1059,7 +1120,7 @@ ], "description": "Symfony Console Component", "homepage": "http://symfony.com", - "time": "2014-10-01 05:53:11" + "time": "2014-10-05 13:59:22" }, { "name": "symfony/event-dispatcher", @@ -1220,12 +1281,12 @@ "source": { "type": "git", "url": "https://github.com/symfony/Yaml.git", - "reference": "656e03eeedc827e4830ac43547eae10bacaa1bbb" + "reference": "499f7d7aa96747ad97940089bd7a1fb24ad8182a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/Yaml/zipball/656e03eeedc827e4830ac43547eae10bacaa1bbb", - "reference": "656e03eeedc827e4830ac43547eae10bacaa1bbb", + "url": "https://api.github.com/repos/symfony/Yaml/zipball/499f7d7aa96747ad97940089bd7a1fb24ad8182a", + "reference": "499f7d7aa96747ad97940089bd7a1fb24ad8182a", "shasum": "" }, "require": { @@ -1258,7 +1319,7 @@ ], "description": "Symfony Yaml Component", "homepage": "http://symfony.com", - "time": "2014-10-01 05:53:11" + "time": "2014-10-05 13:53:50" } ], "aliases": [], From 15993194781c5c7a89c4af439d96d16889989eb5 Mon Sep 17 00:00:00 2001 From: Josh Sherman Date: Tue, 7 Oct 2014 09:48:07 -0400 Subject: [PATCH 096/129] Updated class name --- tests/Pickles/TimeTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Pickles/TimeTest.php b/tests/Pickles/TimeTest.php index d4dbf86..414f906 100644 --- a/tests/Pickles/TimeTest.php +++ b/tests/Pickles/TimeTest.php @@ -25,7 +25,7 @@ class TimeTest extends PHPUnit_Framework_TestCase [date('r', $time), '25'], ['today', '0'], ['400 days ago', '1'], - [true, Pickles\Date::age('1969-12-31')], + [true, Pickles\Time::age('1969-12-31')], ]; } From 840a68961c2b28172ba84d7dd3f955efbe3ea399 Mon Sep 17 00:00:00 2001 From: Josh Sherman Date: Tue, 7 Oct 2014 12:10:46 -0400 Subject: [PATCH 097/129] Cleaned up some double ;; --- src/String.php | 2 +- tests/Pickles/ModelTest.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/String.php b/src/String.php index 518b308..a66d463 100644 --- a/src/String.php +++ b/src/String.php @@ -62,7 +62,7 @@ class String $string = strtolower(trim($string)); $string = preg_replace('/[^a-z0-9-]/', '-', $string); $string = preg_replace('/-+/', '-', $string); - return trim($string, '-');; + return trim($string, '-'); } // }}} diff --git a/tests/Pickles/ModelTest.php b/tests/Pickles/ModelTest.php index dbf6bcf..dd0fd64 100644 --- a/tests/Pickles/ModelTest.php +++ b/tests/Pickles/ModelTest.php @@ -483,7 +483,7 @@ class ModelTest extends PHPUnit_Framework_TestCase public function testCommitIncrement() { $model = new MockModelWithoutColumns(1); - $model->record['field1'] = 100;; + $model->record['field1'] = 100; $model->commit(); $model = new MockModelWithoutColumns(1); $model->record['field1'] = '++'; From aa8c86e5c22879d73c9d1b4524a627d638209aa8 Mon Sep 17 00:00:00 2001 From: Josh Sherman Date: Wed, 8 Oct 2014 21:37:22 -0400 Subject: [PATCH 098/129] Working on oauth --- composer.json | 2 +- src/Auth.php | 11 ++++++++--- src/Resource.php | 49 ++++++++++++++++++++++++------------------------ src/Router.php | 2 ++ 4 files changed, 35 insertions(+), 29 deletions(-) diff --git a/composer.json b/composer.json index 33c66af..dd8cc94 100644 --- a/composer.json +++ b/composer.json @@ -23,7 +23,7 @@ }, "require": { "php": ">=5.4", - "league/oauth2-server": "3.*" + "league/oauth2-server": "4.*" }, "autoload": { "psr-4": { diff --git a/src/Auth.php b/src/Auth.php index aaf757e..8bbadfd 100644 --- a/src/Auth.php +++ b/src/Auth.php @@ -27,14 +27,19 @@ class Auth extends Object * Rather use an API key and not worry about the password? Do it. Return * true when authentication is successful and false when it is not. */ - public function basic() + public static function basic() { return false; } - public function oauth2() + /** + * OAuth2 + * + * Handles authentication of the access token. + */ + final static public function oauth2() { - return false; + } } diff --git a/src/Resource.php b/src/Resource.php index 9a5dcd2..7bb02d1 100644 --- a/src/Resource.php +++ b/src/Resource.php @@ -95,47 +95,46 @@ class Resource extends Object if ($this->auth === true || (isset($this->auth[$method]) && $this->auth[$method])) { - if (!$this->config['pickles']['auth']) + if (!$this->config['auth'][$_SERVER['__version']]) { throw new \Exception('Authentication is not configured properly.', 401); } - // This class should be in the classes directory of the service - $auth = '\\' . $this->config['pickles']['namespace'] . '\\Classes\\Auth'; - - // Strips preceding slashs when there is no namespace - if (strpos($auth, '\\\\') === 0) - { - $auth = substr($auth, 2); - } - - $auth = new $auth(); - - // @todo Remove when switch is implemented - if (!$auth->basic()) - { - throw new \Exception('Invalid authentication credentials.', 401); - } - - // @todo Not yet implemented - /* - switch ($this->config['pickles']['auth']) + switch ($this->config['auth'][$_SERVER['__version']]['strategy']) { case 'basic': - if (!$auth->basic()) + // @todo Check if Auth class has been implemented, if + // not, fallback to the parent + + // This class should be in the classes directory of the service + $auth = '\\' . $this->config['pickles']['namespace'] . '\\Classes\\Auth'; + + // Strips preceding slashs when there is no namespace + if (strpos($auth, '\\\\') === 0) + { + $auth = substr($auth, 2); + } + + // @todo Custom method + if (!$auth::basic()) { throw new \Exception('Invalid authentication credentials.', 401); } break; + case 'oauth2': - $auth->oauth2(); + /* + if (!Auth::oauth2()) + { + throw new \Exception('Invalid access token.', 401); + } + */ break; default: - throw new \Exception('Invalid authentication scheme.', 401); + throw new \Exception('Invalid authentication strategy.', 401); break; } - */ } // Hack together some new globals diff --git a/src/Router.php b/src/Router.php index 5eb9e76..45cb0e6 100644 --- a/src/Router.php +++ b/src/Router.php @@ -46,6 +46,8 @@ class Router extends Object $nouns = []; $uids = []; + $_SERVER['__version'] = substr($version, 1); + // Loops through the components to determine nouns and IDs foreach ($components as $index => $component) { From cb4dac157de6c51988193cd745dba437c3e385e0 Mon Sep 17 00:00:00 2001 From: Josh Sherman Date: Wed, 8 Oct 2014 21:42:42 -0400 Subject: [PATCH 099/129] Updated dependencies --- composer.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/composer.lock b/composer.lock index 937cd04..d89d047 100644 --- a/composer.lock +++ b/composer.lock @@ -822,12 +822,12 @@ "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "6288ebbf6fa3ed2b2ff2d69c356fbaaf4f0971a7" + "reference": "205fcef5998953ec69cb79bc1ea9fee1277c8714" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/6288ebbf6fa3ed2b2ff2d69c356fbaaf4f0971a7", - "reference": "6288ebbf6fa3ed2b2ff2d69c356fbaaf4f0971a7", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/205fcef5998953ec69cb79bc1ea9fee1277c8714", + "reference": "205fcef5998953ec69cb79bc1ea9fee1277c8714", "shasum": "" }, "require": { @@ -864,7 +864,7 @@ "environment", "hhvm" ], - "time": "2014-10-07 09:23:16" + "time": "2014-10-08 05:30:43" }, { "name": "sebastian/exporter", From a8346922350cb31af5fe95bf9a4e46f5d3f75119 Mon Sep 17 00:00:00 2001 From: Josh Sherman Date: Sat, 11 Oct 2014 07:40:09 -0400 Subject: [PATCH 100/129] Stubbed out storage classes --- src/OAuth2/AccessTokenStorage.php | 37 +++++++++++++++++++++++++++++ src/OAuth2/ClientStorage.php | 21 +++++++++++++++++ src/OAuth2/ScopeStorage.php | 15 ++++++++++++ src/OAuth2/SessionStorage.php | 39 +++++++++++++++++++++++++++++++ src/Resource.php | 18 ++++++++++++++ 5 files changed, 130 insertions(+) create mode 100644 src/OAuth2/AccessTokenStorage.php create mode 100644 src/OAuth2/ClientStorage.php create mode 100644 src/OAuth2/ScopeStorage.php create mode 100644 src/OAuth2/SessionStorage.php diff --git a/src/OAuth2/AccessTokenStorage.php b/src/OAuth2/AccessTokenStorage.php new file mode 100644 index 0000000..2b66d4f --- /dev/null +++ b/src/OAuth2/AccessTokenStorage.php @@ -0,0 +1,37 @@ +setSessionStorage(new OAuth2\SessionStorage); + $server->setAccessTokenStorage(new OAuth2\AccessTokenStorage); + $server->setClientStorage(new OAuth2\ClientStorage); + $server->setScopeStorage(new OAuth2\ScopeStorage); + + $passwordGrant = new \League\OAuth2\Server\Grant\PasswordGrant(); + $passwordGrant->setVerifyCredentialsCallback(function ($username, $password) + { + // implement logic here to validate a username and + // password, return an ID if valid, otherwise return false + return false; + }); + + var_dump(microtime()); + exit('EOF'); break; default: From 2ec85c469b208d4b3c64107875b885419866440f Mon Sep 17 00:00:00 2001 From: Josh Sherman Date: Sun, 12 Oct 2014 21:20:48 -0400 Subject: [PATCH 101/129] Working on that OAuth2 --- sql/oauth2.sql | 95 +++++++++++++++++++++++++++++++ src/Auth.php | 45 --------------- src/OAuth2/AccessTokenStorage.php | 3 +- src/OAuth2/ClientStorage.php | 60 ++++++++++++++++++- src/OAuth2/Resource.php | 55 ++++++++++++++++++ src/OAuth2/ScopeStorage.php | 2 +- src/OAuth2/SessionStorage.php | 2 +- src/OAuth2/StorageAdapter.php | 20 +++++++ src/Resource.php | 29 +--------- src/Router.php | 56 ++++++++++-------- 10 files changed, 266 insertions(+), 101 deletions(-) create mode 100644 sql/oauth2.sql delete mode 100644 src/Auth.php create mode 100644 src/OAuth2/Resource.php create mode 100644 src/OAuth2/StorageAdapter.php diff --git a/sql/oauth2.sql b/sql/oauth2.sql new file mode 100644 index 0000000..0d708e8 --- /dev/null +++ b/sql/oauth2.sql @@ -0,0 +1,95 @@ +CREATE TABLE `oauth_clients` ( + `id` CHAR(40) NOT NULL, + `secret` CHAR(40) NOT NULL, + `name` VARCHAR(255) NOT NULL, + `auto_approve` TINYINT(1) NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + UNIQUE KEY `u_oacl_clse_clid` (`secret`,`id`) +) ENGINE=INNODB DEFAULT CHARSET=utf8 COLLATE utf8_unicode_ci; + +CREATE TABLE `oauth_client_endpoints` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `client_id` char(40) NOT NULL, + `redirect_uri` varchar(255) NOT NULL, + PRIMARY KEY (`id`), + KEY `i_oaclen_clid` (`client_id`), + CONSTRAINT `f_oaclen_clid` FOREIGN KEY (`client_id`) REFERENCES `oauth_clients` (`id`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE utf8_unicode_ci; + +CREATE TABLE `oauth_sessions` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `client_id` char(40) NOT NULL, + `owner_type` enum('user','client') NOT NULL DEFAULT 'user', + `owner_id` varchar(255) NOT NULL, + PRIMARY KEY (`id`), + KEY `i_uase_clid_owty_owid` (`client_id`,`owner_type`,`owner_id`), + CONSTRAINT `f_oase_clid` FOREIGN KEY (`client_id`) REFERENCES `oauth_clients` (`id`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE utf8_unicode_ci; + +CREATE TABLE `oauth_session_access_tokens` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `session_id` int(10) unsigned NOT NULL, + `access_token` char(40) NOT NULL, + `access_token_expires` int(10) unsigned NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `u_oaseacto_acto_seid` (`access_token`,`session_id`), + KEY `f_oaseto_seid` (`session_id`), + CONSTRAINT `f_oaseto_seid` FOREIGN KEY (`session_id`) REFERENCES `oauth_sessions` (`id`) ON DELETE CASCADE ON UPDATE NO ACTION +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE utf8_unicode_ci; + +CREATE TABLE `oauth_session_authcodes` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `session_id` int(10) unsigned NOT NULL, + `auth_code` char(40) NOT NULL, + `auth_code_expires` int(10) unsigned NOT NULL, + PRIMARY KEY (`id`), + KEY `session_id` (`session_id`), + CONSTRAINT `oauth_session_authcodes_ibfk_1` FOREIGN KEY (`session_id`) REFERENCES `oauth_sessions` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE utf8_unicode_ci; + +CREATE TABLE `oauth_session_redirects` ( + `session_id` int(10) unsigned NOT NULL, + `redirect_uri` varchar(255) NOT NULL, + PRIMARY KEY (`session_id`), + CONSTRAINT `f_oasere_seid` FOREIGN KEY (`session_id`) REFERENCES `oauth_sessions` (`id`) ON DELETE CASCADE ON UPDATE NO ACTION +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE utf8_unicode_ci; + +CREATE TABLE `oauth_session_refresh_tokens` ( + `session_access_token_id` int(10) unsigned NOT NULL, + `refresh_token` char(40) NOT NULL, + `refresh_token_expires` int(10) unsigned NOT NULL, + `client_id` char(40) NOT NULL, + PRIMARY KEY (`session_access_token_id`), + KEY `client_id` (`client_id`), + CONSTRAINT `oauth_session_refresh_tokens_ibfk_1` FOREIGN KEY (`client_id`) REFERENCES `oauth_clients` (`id`) ON DELETE CASCADE, + CONSTRAINT `f_oasetore_setoid` FOREIGN KEY (`session_access_token_id`) REFERENCES `oauth_session_access_tokens` (`id`) ON DELETE CASCADE ON UPDATE NO ACTION +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE utf8_unicode_ci; + +CREATE TABLE `oauth_scopes` ( + `id` smallint(5) unsigned NOT NULL AUTO_INCREMENT, + `scope` varchar(255) NOT NULL, + `name` varchar(255) NOT NULL, + `description` varchar(255) DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `u_oasc_sc` (`scope`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE utf8_unicode_ci; + +CREATE TABLE `oauth_session_token_scopes` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `session_access_token_id` int(10) unsigned DEFAULT NULL, + `scope_id` smallint(5) unsigned NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `u_setosc_setoid_scid` (`session_access_token_id`,`scope_id`), + KEY `f_oasetosc_scid` (`scope_id`), + CONSTRAINT `f_oasetosc_scid` FOREIGN KEY (`scope_id`) REFERENCES `oauth_scopes` (`id`) ON DELETE CASCADE ON UPDATE NO ACTION, + CONSTRAINT `f_oasetosc_setoid` FOREIGN KEY (`session_access_token_id`) REFERENCES `oauth_session_access_tokens` (`id`) ON DELETE CASCADE ON UPDATE NO ACTION +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE utf8_unicode_ci; + +CREATE TABLE `oauth_session_authcode_scopes` ( + `oauth_session_authcode_id` int(10) unsigned NOT NULL, + `scope_id` smallint(5) unsigned NOT NULL, + KEY `oauth_session_authcode_id` (`oauth_session_authcode_id`), + KEY `scope_id` (`scope_id`), + CONSTRAINT `oauth_session_authcode_scopes_ibfk_2` FOREIGN KEY (`scope_id`) REFERENCES `oauth_scopes` (`id`) ON DELETE CASCADE, + CONSTRAINT `oauth_session_authcode_scopes_ibfk_1` FOREIGN KEY (`oauth_session_authcode_id`) REFERENCES `oauth_session_authcodes` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE utf8_unicode_ci; diff --git a/src/Auth.php b/src/Auth.php deleted file mode 100644 index 8bbadfd..0000000 --- a/src/Auth.php +++ /dev/null @@ -1,45 +0,0 @@ -db->fetch($sql, $parameters); + + if (count($results) === 1) + { + $client = new ClientEntity($this->server); + + $client->hydrate([ + 'id' => $results[0]['id'], + 'name' => $results[0]['name'] + ]); + + return $client; + } + + return null; } public function getBySession(SessionEntity $session) { + /* + $result = Capsule::table('oauth_clients') + ->select(['oauth_clients.id', 'oauth_clients.name']) + ->join('oauth_sessions', 'oauth_clients.id', '=', 'oauth_sessions.client_id') + ->where('oauth_sessions.id', $session->getId()) + ->get(); + if (count($result) === 1) { + $client = new ClientEntity($this->server); + $client->hydrate([ + 'id' => $result[0]['id'], + 'name' => $result[0]['name'] + ]); + + return $client; + } + + return null; + */ } } diff --git a/src/OAuth2/Resource.php b/src/OAuth2/Resource.php new file mode 100644 index 0000000..6c1cea3 --- /dev/null +++ b/src/OAuth2/Resource.php @@ -0,0 +1,55 @@ +setSessionStorage(new SessionStorage); + $server->setAccessTokenStorage(new AccessTokenStorage); + $server->setClientStorage(new ClientStorage); + $server->setScopeStorage(new ScopeStorage); + + $passwordGrant = new PasswordGrant; + $passwordGrant->setVerifyCredentialsCallback(function ($username, $password) + { + $user = new User(['email' => $username]); + + return $user->count() + && password_verify($password, $user->record['password']); + }); + + $server->addGrantType($passwordGrant); + + // @todo Add grant types listed in the config. Password is always added + + $response = $server->issueAccessToken(); + } + catch (\Exception $e) + { + // @todo Set error code's accordingly. + + throw new \Exception($e->getMessage(), $e->httpStatusCode); + } + + break; + + default: + throw new \Exception('Not Found.', 404); + break; + } + } +} + diff --git a/src/OAuth2/ScopeStorage.php b/src/OAuth2/ScopeStorage.php index d6c15aa..6cf7532 100644 --- a/src/OAuth2/ScopeStorage.php +++ b/src/OAuth2/ScopeStorage.php @@ -5,7 +5,7 @@ namespace Pickles\OAuth2; use \League\OAuth2\Server\Storage\Adapter; use \League\OAuth2\Server\Storage\ScopeInterface; -class ScopeStorage extends Adapter implements ScopeInterface +class ScopeStorage extends StorageAdapter implements ScopeInterface { public function get($scope, $grant_type = null, $client_id = null) { diff --git a/src/OAuth2/SessionStorage.php b/src/OAuth2/SessionStorage.php index 313d68b..17b1639 100644 --- a/src/OAuth2/SessionStorage.php +++ b/src/OAuth2/SessionStorage.php @@ -9,7 +9,7 @@ use \League\OAuth2\Server\Entity\SessionEntity; use \League\OAuth2\Server\Storage\Adapter; use \League\OAuth2\Server\Storage\SessionInterface; -class SessionStorage extends Adapter implements SessionInterface +class SessionStorage extends StorageAdapter implements SessionInterface { public function getByAccessToken(AccessTokenEntity $access_token) { diff --git a/src/OAuth2/StorageAdapter.php b/src/OAuth2/StorageAdapter.php new file mode 100644 index 0000000..27ab436 --- /dev/null +++ b/src/OAuth2/StorageAdapter.php @@ -0,0 +1,20 @@ +config = Config::getInstance(); + $this->db = Database::getInstance(); + } +} + diff --git a/src/Resource.php b/src/Resource.php index b22d163..a8e04ad 100644 --- a/src/Resource.php +++ b/src/Resource.php @@ -92,6 +92,7 @@ class Resource extends Object } // Check auth if flag is explicitly true or is true for the method + /* if ($this->auth === true || (isset($this->auth[$method]) && $this->auth[$method])) { @@ -122,38 +123,12 @@ class Resource extends Object } break; - case 'oauth2': - /* - if (!Auth::oauth2()) - { - throw new \Exception('Invalid access token.', 401); - } - */ - - $server = new \League\OAuth2\Server\AuthorizationServer; - - $server->setSessionStorage(new OAuth2\SessionStorage); - $server->setAccessTokenStorage(new OAuth2\AccessTokenStorage); - $server->setClientStorage(new OAuth2\ClientStorage); - $server->setScopeStorage(new OAuth2\ScopeStorage); - - $passwordGrant = new \League\OAuth2\Server\Grant\PasswordGrant(); - $passwordGrant->setVerifyCredentialsCallback(function ($username, $password) - { - // implement logic here to validate a username and - // password, return an ID if valid, otherwise return false - return false; - }); - - var_dump(microtime()); - exit('EOF'); - break; - default: throw new \Exception('Invalid authentication strategy.', 401); break; } } + */ // Hack together some new globals if (in_array($method, ['PUT', 'DELETE'])) diff --git a/src/Router.php b/src/Router.php index 45cb0e6..d737f87 100644 --- a/src/Router.php +++ b/src/Router.php @@ -22,7 +22,7 @@ namespace Pickles; * 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(); + * @usage new Pickles\Router; */ class Router extends Object { @@ -42,34 +42,42 @@ class Router extends Object // Grabs the requested page $request = $_REQUEST['request']; $components = explode('/', $request); - $version = array_shift($components); $nouns = []; $uids = []; - $_SERVER['__version'] = substr($version, 1); - - // Loops through the components to determine nouns and IDs - foreach ($components as $index => $component) + // Checks if we're trying to rock some OAuth + if ($components[0] == 'oauth') { - if ($index % 2) - { - $uids[end($nouns)] = $component; - } - else - { - $nouns[] = $component; - } + $class = 'Pickles\OAuth2\Resource'; } - - // Creates our class name - array_unshift($nouns, '', $this->config['pickles']['namespace'], 'Resources', $version); - $class = implode('\\', $nouns); - - // @todo Make namespace mandatory - // Strips preceding slashs when there is no namespace - if (strpos($class, '\\\\') === 0) + else { - $class = substr($class, 2); + $version = array_shift($components); + $_SERVER['__version'] = substr($version, 1); + + // Loops through the components to determine nouns and IDs + foreach ($components as $index => $component) + { + if ($index % 2) + { + $uids[end($nouns)] = $component; + } + else + { + $nouns[] = $component; + } + } + + // Creates our class name + array_unshift($nouns, '', $this->config['pickles']['namespace'], 'Resources', $version); + $class = implode('\\', $nouns); + + // @todo Make namespace mandatory + // Strips preceding slashs when there is no namespace + if (strpos($class, '\\\\') === 0) + { + $class = substr($class, 2); + } } // Checks that the file is present and contains our class @@ -86,7 +94,7 @@ class Router extends Object // Creates a resource object if we don't have one if (!isset($resource)) { - $resource = new Resource(); + $resource = new Resource; } $code = $e->getCode(); From 75596ed725aca8bcd2470ef06467afd1bce884d5 Mon Sep 17 00:00:00 2001 From: Josh Sherman Date: Sun, 12 Oct 2014 21:53:15 -0400 Subject: [PATCH 102/129] Moved to new namespace --- src/OAuth2/Resource.php | 2 +- src/Resource.php | 20 -------------------- src/Router.php | 9 +-------- 3 files changed, 2 insertions(+), 29 deletions(-) diff --git a/src/OAuth2/Resource.php b/src/OAuth2/Resource.php index 6c1cea3..c8bfb13 100644 --- a/src/OAuth2/Resource.php +++ b/src/OAuth2/Resource.php @@ -4,7 +4,7 @@ namespace Pickles\OAuth2; use \League\OAuth2\Server\AuthorizationServer; use \League\OAuth2\Server\Grant\PasswordGrant; -use \Pickles\App\Model\User; +use \Pickles\App\Models\User; class Resource extends \Pickles\Resource { diff --git a/src/Resource.php b/src/Resource.php index a8e04ad..51e6565 100644 --- a/src/Resource.php +++ b/src/Resource.php @@ -103,26 +103,6 @@ class Resource extends Object switch ($this->config['auth'][$_SERVER['__version']]['strategy']) { - case 'basic': - // @todo Check if Auth class has been implemented, if - // not, fallback to the parent - - // This class should be in the classes directory of the service - $auth = '\\' . $this->config['pickles']['namespace'] . '\\Classes\\Auth'; - - // Strips preceding slashs when there is no namespace - if (strpos($auth, '\\\\') === 0) - { - $auth = substr($auth, 2); - } - - // @todo Custom method - if (!$auth::basic()) - { - throw new \Exception('Invalid authentication credentials.', 401); - } - break; - default: throw new \Exception('Invalid authentication strategy.', 401); break; diff --git a/src/Router.php b/src/Router.php index d737f87..31ea2e8 100644 --- a/src/Router.php +++ b/src/Router.php @@ -69,15 +69,8 @@ class Router extends Object } // Creates our class name - array_unshift($nouns, '', $this->config['pickles']['namespace'], 'Resources', $version); + array_unshift($nouns, '', 'Pickles', 'App', 'Resources', $version); $class = implode('\\', $nouns); - - // @todo Make namespace mandatory - // Strips preceding slashs when there is no namespace - if (strpos($class, '\\\\') === 0) - { - $class = substr($class, 2); - } } // Checks that the file is present and contains our class From 8e9c64482243de36ed5db1ad794150abca0c8210 Mon Sep 17 00:00:00 2001 From: Josh Sherman Date: Mon, 13 Oct 2014 21:27:19 -0400 Subject: [PATCH 103/129] Working out the routing --- src/OAuth2/Resource.php | 48 +++++++++++++++++++++++++++++++---------- src/Router.php | 13 ++++++----- 2 files changed, 43 insertions(+), 18 deletions(-) diff --git a/src/OAuth2/Resource.php b/src/OAuth2/Resource.php index c8bfb13..7f949b9 100644 --- a/src/OAuth2/Resource.php +++ b/src/OAuth2/Resource.php @@ -10,7 +10,14 @@ class Resource extends \Pickles\Resource { public function __construct() { - switch ($_REQUEST['request']) + parent::__construct(); + + if (!isset($this->config['oauth'][$_SERVER['__version']])) + { + throw new \Exception('Forbidden.', 403); + } + + switch (substr($_REQUEST['request'], strlen($_SERVER['__version']) + 2)) { case 'oauth/access_token': try @@ -22,25 +29,44 @@ class Resource extends \Pickles\Resource $server->setClientStorage(new ClientStorage); $server->setScopeStorage(new ScopeStorage); - $passwordGrant = new PasswordGrant; - $passwordGrant->setVerifyCredentialsCallback(function ($username, $password) + switch ($_REQUEST['grant_type']) { - $user = new User(['email' => $username]); + case 'authorization_code': + throw new \Exception('Not Implemented', 501); + break; - return $user->count() - && password_verify($password, $user->record['password']); - }); + case 'client_credentials': + throw new \Exception('Not Implemented', 501); + break; - $server->addGrantType($passwordGrant); + case 'implicit': + throw new \Exception('Not Implemented', 501); + break; - // @todo Add grant types listed in the config. Password is always added + case 'password': + $grant = new PasswordGrant; + + $grant->setVerifyCredentialsCallback(function ($username, $password) + { + $user = new User(['email' => $username]); + + return $user->count() + && password_verify($password, $user->record['password']); + }); + + break; + + case 'refresh_token': + throw new \Exception('Not Implemented', 501); + break; + } + + $server->addGrantType($grant); $response = $server->issueAccessToken(); } catch (\Exception $e) { - // @todo Set error code's accordingly. - throw new \Exception($e->getMessage(), $e->httpStatusCode); } diff --git a/src/Router.php b/src/Router.php index 31ea2e8..c41baf7 100644 --- a/src/Router.php +++ b/src/Router.php @@ -40,10 +40,12 @@ class Router extends Object try { // Grabs the requested page - $request = $_REQUEST['request']; - $components = explode('/', $request); - $nouns = []; - $uids = []; + $request = $_REQUEST['request']; + $components = explode('/', $request); + $nouns = []; + $uids = []; + $version = array_shift($components); + $_SERVER['__version'] = substr($version, 1); // Checks if we're trying to rock some OAuth if ($components[0] == 'oauth') @@ -52,9 +54,6 @@ class Router extends Object } else { - $version = array_shift($components); - $_SERVER['__version'] = substr($version, 1); - // Loops through the components to determine nouns and IDs foreach ($components as $index => $component) { From 1cc1595e305cc31cb76e113b02d68609e372720d Mon Sep 17 00:00:00 2001 From: Josh Sherman Date: Mon, 13 Oct 2014 21:30:57 -0400 Subject: [PATCH 104/129] Updated dependencies --- composer.lock | 143 ++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 126 insertions(+), 17 deletions(-) diff --git a/composer.lock b/composer.lock index d89d047..ff3f1b9 100644 --- a/composer.lock +++ b/composer.lock @@ -4,41 +4,93 @@ "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "c9883c463f5dad3fc400327e41287cc0", + "hash": "76708e9b1bd8a87135b6c5b4c0e38a2a", "packages": [ { - "name": "league/oauth2-server", - "version": "dev-master", + "name": "league/event", + "version": "1.0.0", "source": { "type": "git", - "url": "https://github.com/thephpleague/oauth2-server.git", - "reference": "13cd0cacdf1e9ae23fa073d4408a336246babb8e" + "url": "https://github.com/thephpleague/event.git", + "reference": "06adb7ce55b93346be43a3ba677ac613bbf288a2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/oauth2-server/zipball/13cd0cacdf1e9ae23fa073d4408a336246babb8e", - "reference": "13cd0cacdf1e9ae23fa073d4408a336246babb8e", + "url": "https://api.github.com/repos/thephpleague/event/zipball/06adb7ce55b93346be43a3ba677ac613bbf288a2", + "reference": "06adb7ce55b93346be43a3ba677ac613bbf288a2", "shasum": "" }, "require": { "php": ">=5.4.0" }, + "require-dev": { + "henrikbjorn/phpspec-code-coverage": "1.0.*@dev", + "phpspec/phpspec": "2.0.*@dev" + }, + "type": "library", + "autoload": { + "psr-4": { + "League\\Event\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frank de Jonge", + "email": "info@frenky.net" + } + ], + "description": "Event package", + "keywords": [ + "emitter", + "event", + "listener" + ], + "time": "2014-09-09 14:40:43" + }, + { + "name": "league/oauth2-server", + "version": "dev-develop", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/oauth2-server.git", + "reference": "6333a975f8fb51111b447a7e85806e4519fb52b9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/oauth2-server/zipball/6333a975f8fb51111b447a7e85806e4519fb52b9", + "reference": "6333a975f8fb51111b447a7e85806e4519fb52b9", + "shasum": "" + }, + "require": { + "league/event": "1.0.*", + "php": ">=5.4.0", + "symfony/http-foundation": "~2.1" + }, "replace": { + "league/oauth2server": "*", "lncd/oauth2": "*" }, "require-dev": { + "alexbilbie/fizzfuzz": "dev-develop", + "codeception/codeception": "2.0.*", "league/phpunit-coverage-listener": "~1.0", - "mockery/mockery": "~0.8" + "mockery/mockery": "~0.9", + "phpunit/phpunit": "~4.0", + "squizlabs/php_codesniffer": "~1.5" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.2.x-dev" + "dev-develop": "4.0.x-dev" } }, "autoload": { - "psr-0": { - "League\\OAuth2\\Server": "src/" + "psr-4": { + "League\\OAuth2\\Server\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -54,19 +106,76 @@ } ], "description": "A lightweight and powerful OAuth 2.0 authorization and resource server library with support for all the core specification grants. This library will allow you to secure your API with OAuth and allow your applications users to approve apps that want to access their data from your API.", + "homepage": "http://oauth2.thephpleague.com/", "keywords": [ "Authentication", "api", "auth", + "authorisation", "authorization", "oauth", + "oauth 2", + "oauth 2.0", "oauth2", "protect", "resource", "secure", "server" ], - "time": "2014-09-08 21:01:33" + "time": "2014-10-03 13:42:01" + }, + { + "name": "symfony/http-foundation", + "version": "dev-master", + "target-dir": "Symfony/Component/HttpFoundation", + "source": { + "type": "git", + "url": "https://github.com/symfony/HttpFoundation.git", + "reference": "c24942a7ec2d8409d1f60d02c4460ca8317e885a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/HttpFoundation/zipball/c24942a7ec2d8409d1f60d02c4460ca8317e885a", + "reference": "c24942a7ec2d8409d1f60d02c4460ca8317e885a", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "symfony/expression-language": "~2.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.6-dev" + } + }, + "autoload": { + "psr-0": { + "Symfony\\Component\\HttpFoundation\\": "" + }, + "classmap": [ + "Symfony/Component/HttpFoundation/Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + }, + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "description": "Symfony HttpFoundation Component", + "homepage": "http://symfony.com", + "time": "2014-10-07 14:06:18" } ], "packages-dev": [ @@ -76,16 +185,16 @@ "source": { "type": "git", "url": "https://github.com/doctrine/instantiator.git", - "reference": "4bdc0421209a00e6f425f4c3554d1b0df3a7e897" + "reference": "f976e5de371104877ebc89bd8fecb0019ed9c119" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/4bdc0421209a00e6f425f4c3554d1b0df3a7e897", - "reference": "4bdc0421209a00e6f425f4c3554d1b0df3a7e897", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/f976e5de371104877ebc89bd8fecb0019ed9c119", + "reference": "f976e5de371104877ebc89bd8fecb0019ed9c119", "shasum": "" }, "require": { - "php": "~5.3" + "php": ">=5.3,<8.0-DEV" }, "require-dev": { "athletic/athletic": "~0.1.8", @@ -122,7 +231,7 @@ "constructor", "instantiate" ], - "time": "2014-10-05 00:20:37" + "time": "2014-10-13 12:58:55" }, { "name": "guzzle/guzzle", From ec14621e7c48c5c6146b58ca505f49375da2d625 Mon Sep 17 00:00:00 2001 From: Josh Sherman Date: Mon, 13 Oct 2014 21:46:34 -0400 Subject: [PATCH 105/129] Tweaked unit tests --- src/Resource.php | 13 +--- tests/Pickles/AuthTest.php | 38 ------------ tests/Pickles/ResourceTest.php | 110 +-------------------------------- tests/Pickles/RouterTest.php | 5 +- 4 files changed, 4 insertions(+), 162 deletions(-) delete mode 100644 tests/Pickles/AuthTest.php diff --git a/src/Resource.php b/src/Resource.php index 51e6565..9857cb2 100644 --- a/src/Resource.php +++ b/src/Resource.php @@ -91,24 +91,15 @@ class Resource extends Object throw new \Exception('HTTPS is required.', 400); } - // Check auth if flag is explicitly true or is true for the method - /* + // Check auth if flag is explicitly true or is true for the method if ($this->auth === true || (isset($this->auth[$method]) && $this->auth[$method])) { - if (!$this->config['auth'][$_SERVER['__version']]) + if (!isset($this->config['oauth2'][$_SERVER['__version']])) { throw new \Exception('Authentication is not configured properly.', 401); } - - switch ($this->config['auth'][$_SERVER['__version']]['strategy']) - { - default: - throw new \Exception('Invalid authentication strategy.', 401); - break; - } } - */ // Hack together some new globals if (in_array($method, ['PUT', 'DELETE'])) diff --git a/tests/Pickles/AuthTest.php b/tests/Pickles/AuthTest.php deleted file mode 100644 index c399202..0000000 --- a/tests/Pickles/AuthTest.php +++ /dev/null @@ -1,38 +0,0 @@ - [ - "local" => "127.0.0.1", - "production" => "123.456.789.0", - ], - ]; - '); - - Pickles\Config::getInstance('/tmp/pickles.php'); - - $this->auth = new Pickles\Auth(); - } - - public function testBasic() - { - $this->assertFalse($this->auth->basic()); - } - - public function testOAuth2() - { - $this->assertFalse($this->auth->oauth2()); - } -} - diff --git a/tests/Pickles/ResourceTest.php b/tests/Pickles/ResourceTest.php index 4a23713..a80c4a3 100644 --- a/tests/Pickles/ResourceTest.php +++ b/tests/Pickles/ResourceTest.php @@ -1,6 +1,6 @@ [ - "local" => "127.0.0.1", - "production" => "123.456.789.0", - ], - "pickles" => [ - "namespace" => "", - "datasource" => "mysql", - "auth" => "basic", - ], - "datasources" => [ - "mysql" => [ - "driver" => "pdo_mysql", - ], - ], - ]; - '); - - Pickles\Config::getInstance('/tmp/pickles.php'); - - $response = json_encode([ - 'meta' => [ - 'status' => 401, - 'message' => 'Invalid authentication credentials.', - ], - ]); - - $this->expectOutputString($response); - - $_SERVER['REQUEST_METHOD'] = 'DELETE'; - $_REQUEST['request'] = 'v1/resource/1'; - - new Pickles\Router(); - } - - public function testBasicAuth() - { - Pickles\Object::$instances = []; - - $_SERVER['REQUEST_METHOD'] = 'GET'; - $_SERVER['SERVER_NAME'] = '127.0.0.1'; - - file_put_contents('/tmp/pickles.php', ' [ - "local" => "127.0.0.1", - "production" => "123.456.789.0", - ], - "pickles" => [ - "namespace" => "", - "datasource" => "mysql", - "auth" => "basic", - ], - "datasources" => [ - "mysql" => [ - "driver" => "pdo_mysql", - ], - ], - ]; - '); - - Pickles\Config::getInstance('/tmp/pickles.php'); - - $response = json_encode([ - 'meta' => [ - 'status' => 405, - 'message' => 'Method not allowed.', - ], - ]); - - $this->expectOutputString($response); - - $_SERVER['REQUEST_METHOD'] = 'DELETE'; - $_REQUEST['request'] = 'v1/resource/1'; - - new Pickles\Router(); - } - public function testMethodNotAllowed() { $response = json_encode([ diff --git a/tests/Pickles/RouterTest.php b/tests/Pickles/RouterTest.php index 57f7ab8..b7993d1 100644 --- a/tests/Pickles/RouterTest.php +++ b/tests/Pickles/RouterTest.php @@ -1,6 +1,6 @@ "127.0.0.1", "production" => "123.456.789.0", ], - "pickles" => [ - "namespace" => "", - ], "datasources" => [], ]; '); From 49a713eb35dd69f9fdc98c151d0fb70a39cd7cad Mon Sep 17 00:00:00 2001 From: Josh Sherman Date: Mon, 13 Oct 2014 22:50:43 -0400 Subject: [PATCH 106/129] Finished up password grant --- src/OAuth2/Resource.php | 13 +++++++++---- src/Resource.php | 21 ++------------------- src/Router.php | 6 ++++++ 3 files changed, 17 insertions(+), 23 deletions(-) diff --git a/src/OAuth2/Resource.php b/src/OAuth2/Resource.php index 7f949b9..68a3a73 100644 --- a/src/OAuth2/Resource.php +++ b/src/OAuth2/Resource.php @@ -5,13 +5,12 @@ namespace Pickles\OAuth2; use \League\OAuth2\Server\AuthorizationServer; use \League\OAuth2\Server\Grant\PasswordGrant; use \Pickles\App\Models\User; +use \Pickles\Config; class Resource extends \Pickles\Resource { - public function __construct() + public function POST() { - parent::__construct(); - if (!isset($this->config['oauth'][$_SERVER['__version']])) { throw new \Exception('Forbidden.', 403); @@ -48,7 +47,11 @@ class Resource extends \Pickles\Resource $grant->setVerifyCredentialsCallback(function ($username, $password) { - $user = new User(['email' => $username]); + $user = new User([ + 'conditions' => [ + 'email' => $username, + ], + ]); return $user->count() && password_verify($password, $user->record['password']); @@ -64,6 +67,8 @@ class Resource extends \Pickles\Resource $server->addGrantType($grant); $response = $server->issueAccessToken(); + + return $response; } catch (\Exception $e) { diff --git a/src/Resource.php b/src/Resource.php index 9857cb2..1b94fa2 100644 --- a/src/Resource.php +++ b/src/Resource.php @@ -26,15 +26,6 @@ namespace Pickles; */ class Resource extends Object { - /** - * HTTPS - * - * Whether or not the page should be loaded via HTTP Secure. - * - * @var boolean defaults to false - */ - public $https = false; - /** * Filter * @@ -83,14 +74,6 @@ class Resource extends Object try { - // Determines if we need to serve over HTTP or HTTPS - if (($this->https === true - || (isset($this->https[$method]) && $this->https[$method])) - && (!isset($_SERVER['HTTPS']) || $_SERVER['HTTPS'] == false)) - { - throw new \Exception('HTTPS is required.', 400); - } - // Check auth if flag is explicitly true or is true for the method if ($this->auth === true || (isset($this->auth[$method]) && $this->auth[$method])) @@ -101,7 +84,7 @@ class Resource extends Object } } - // Hack together some new globals + // Hacks together some new globals if (in_array($method, ['PUT', 'DELETE'])) { $GLOBALS['_' . $method] = []; @@ -337,7 +320,7 @@ class Resource extends Object { http_response_code($this->status); header('Content-Type: application/json'); - header('X-Powered-By: Pickles v2 - https://picklesphp.com'); + header('X-Powered-By: Pickles (http://picklesphp.com)'); $meta = [ 'status' => $this->status, diff --git a/src/Router.php b/src/Router.php index c41baf7..c9b0a90 100644 --- a/src/Router.php +++ b/src/Router.php @@ -39,6 +39,12 @@ class Router extends Object try { + // Secure by default + if (!isset($_SERVER['HTTPS']) || $_SERVER['HTTPS'] == false) + { + throw new \Exception('HTTPS is required.', 400); + } + // Grabs the requested page $request = $_REQUEST['request']; $components = explode('/', $request); From c244e02d464d93f35606691c65ad1c434dee7356 Mon Sep 17 00:00:00 2001 From: Josh Sherman Date: Tue, 14 Oct 2014 07:11:03 -0400 Subject: [PATCH 107/129] Implementing storage interfaces --- src/OAuth2/ClientStorage.php | 22 ++++++++++++---------- src/Resource.php | 21 +++++++++++++++++++-- 2 files changed, 31 insertions(+), 12 deletions(-) diff --git a/src/OAuth2/ClientStorage.php b/src/OAuth2/ClientStorage.php index fe02603..dddfeda 100644 --- a/src/OAuth2/ClientStorage.php +++ b/src/OAuth2/ClientStorage.php @@ -55,25 +55,27 @@ class ClientStorage extends StorageAdapter implements ClientInterface public function getBySession(SessionEntity $session) { - /* - $result = Capsule::table('oauth_clients') - ->select(['oauth_clients.id', 'oauth_clients.name']) - ->join('oauth_sessions', 'oauth_clients.id', '=', 'oauth_sessions.client_id') - ->where('oauth_sessions.id', $session->getId()) - ->get(); + $sql = 'SELECT oauth_clients.id, oauth_clients.name' + . ' FROM oauth_clients' + . ' JOIN oauth_sessions' + . ' ON oauth_clients.id = oauth_sessions.client_id' + . ' WHERE oauth_sessions.id = ?'; - if (count($result) === 1) { + $results = $this->db->fetch($sql, [$session->getId()]); + + if (count($results) === 1) + { $client = new ClientEntity($this->server); + $client->hydrate([ - 'id' => $result[0]['id'], - 'name' => $result[0]['name'] + 'id' => $results[0]['id'], + 'name' => $results[0]['name'] ]); return $client; } return null; - */ } } diff --git a/src/Resource.php b/src/Resource.php index 1b94fa2..4ca265f 100644 --- a/src/Resource.php +++ b/src/Resource.php @@ -14,6 +14,12 @@ namespace Pickles; +use \League\OAuth2\Server\ResourceServer; +use Pickles\OAuth2\AccessTokenStorage; +use Pickles\OAuth2\ClientStorage; +use Pickles\OAuth2\ScopeStorage; +use Pickles\OAuth2\SessionStorage; + /** * Resource Class * @@ -74,11 +80,22 @@ class Resource extends Object try { - // Check auth if flag is explicitly true or is true for the method + // Checks if auth flag is explicity true or true for the method if ($this->auth === true || (isset($this->auth[$method]) && $this->auth[$method])) { - if (!isset($this->config['oauth2'][$_SERVER['__version']])) + if (isset($this->config['oauth'][$_SERVER['__version']])) + { + $server = new ResourceServer( + new SessionStorage, + new AccessTokenStorage, + new ClientStorage, + new ScopeStorage + ); + + $server->isValidRequest(); + } + else { throw new \Exception('Authentication is not configured properly.', 401); } From dc06f3732074b1b14746069507511484c8a3ac28 Mon Sep 17 00:00:00 2001 From: Josh Sherman Date: Tue, 14 Oct 2014 22:05:52 -0400 Subject: [PATCH 108/129] Updated the interfaces and dropped oauth version Seems the oauth lib's stable version is 3.2, dropped down from the 4 version to that. --- composer.json | 2 +- src/OAuth2/AccessTokenStorage.php | 48 ++++++++++++++++++++++ src/OAuth2/ClientStorage.php | 2 +- src/OAuth2/ScopeStorage.php | 11 +++++ src/OAuth2/SessionStorage.php | 67 +++++++++++++++++++++++++++++++ 5 files changed, 128 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index dd8cc94..da9531c 100644 --- a/composer.json +++ b/composer.json @@ -23,7 +23,7 @@ }, "require": { "php": ">=5.4", - "league/oauth2-server": "4.*" + "league/oauth2-server": "3.2.*" }, "autoload": { "psr-4": { diff --git a/src/OAuth2/AccessTokenStorage.php b/src/OAuth2/AccessTokenStorage.php index d6af720..7ecd943 100644 --- a/src/OAuth2/AccessTokenStorage.php +++ b/src/OAuth2/AccessTokenStorage.php @@ -3,6 +3,7 @@ namespace Pickles\OAuth2; use \League\OAuth2\Server\Entity\AbstractTokenEntity; +use \League\OAuth2\Server\Entity\AccessTokenEntity; use \League\OAuth2\Server\Entity\ScopeEntity; use \League\OAuth2\Server\Storage\AccessTokenInterface; @@ -10,27 +11,74 @@ class AccessTokenStorage extends StorageAdapter implements AccessTokenInterface { public function get($token) { + $sql = 'SELECT oauth_session_access_tokens.*' + . ' FROM oauth_session_access_tokens' + . ' WHERE access_token = ?' + . ' AND access_token_expires >= ?;'; + $results = $this->db->fetch($sql, [$token, time()]); + + if (count($results) === 1) + { + return (new AccessTokenEntity($this->server)) + ->setId($results[0]['access_token']) + ->setExpireTime($results[0]['access_token_expires']); + } + + return null; } public function getScopes(AbstractTokenEntity $token) { + $sql = 'SELECT oauth_scopes.id, oauth_scopes.description' + . ' FROM oauth_session_token_scopes' + . ' INNER JOIN oauth_scopes' + . ' ON oauth_session_token_scopes.scope_id = oauth_scopes.id' + . ' WHERE oauth_session_token_scopes.session_access_token_id = ?;'; + $results = $this->db->fetch($sql, [$token->getId()]); + $response = []; + + if (count($results) > 0) + { + foreach ($results as $row) + { + $response[] = (new ScopeEntity($this->server))->hydrate([ + 'id' => $row['id'], + 'description' => $row['description'] + ]); + } + } + + return $response; } public function create($token, $expiration, $session_id) { + $sql = 'INSERT INTO oauth_session_access_tokens' + . ' (access_token, session_id, access_token_expires)' + . ' VALUES' + . ' (?, ?, ?);'; + $this->db->execute($sql, [$token, $session_id, $expiration]); } public function associateScope(AbstractTokenEntity $token, ScopeEntity $scope) { + $sql = 'INSERT INTO oauth_session_token_scopes' + . ' (access_token, scope)' + . ' VALUES' + . ' (?, ?);'; + $this->db->execute($sql, [$token->getId(), $scope->getId()]); } public function delete(AbstractTokenEntity $token) { + $sql = 'DELETE FROM oauth_session_token_scopes' + . ' WHERE access_token = ?;'; + $this->db->execute($sql, [$token->getId()]); } } diff --git a/src/OAuth2/ClientStorage.php b/src/OAuth2/ClientStorage.php index dddfeda..8a38ffc 100644 --- a/src/OAuth2/ClientStorage.php +++ b/src/OAuth2/ClientStorage.php @@ -57,7 +57,7 @@ class ClientStorage extends StorageAdapter implements ClientInterface { $sql = 'SELECT oauth_clients.id, oauth_clients.name' . ' FROM oauth_clients' - . ' JOIN oauth_sessions' + . ' INNER JOIN oauth_sessions' . ' ON oauth_clients.id = oauth_sessions.client_id' . ' WHERE oauth_sessions.id = ?'; diff --git a/src/OAuth2/ScopeStorage.php b/src/OAuth2/ScopeStorage.php index 6cf7532..c614eb7 100644 --- a/src/OAuth2/ScopeStorage.php +++ b/src/OAuth2/ScopeStorage.php @@ -9,7 +9,18 @@ class ScopeStorage extends StorageAdapter implements ScopeInterface { public function get($scope, $grant_type = null, $client_id = null) { + $sql = 'SELECT * FROM oauth_scopes WHERE id = ?;'; + $results = $this->db->fetch($sql, [$scope]); + if (count($results) === 0) + { + return null; + } + + return (new ScopeEntity($this->server))->hydrate([ + 'id' => $result[0]['id'], + 'description' => $result[0]['description'], + ]); } } diff --git a/src/OAuth2/SessionStorage.php b/src/OAuth2/SessionStorage.php index 17b1639..68b015a 100644 --- a/src/OAuth2/SessionStorage.php +++ b/src/OAuth2/SessionStorage.php @@ -13,27 +13,94 @@ class SessionStorage extends StorageAdapter implements SessionInterface { public function getByAccessToken(AccessTokenEntity $access_token) { + $sql = 'SELECT oauth_sessions.id, oauth_sessions.owner_type,' + . ' oauth_sessions.owner_id, oauth_sessions.client_id,' + . ' oauth_sessions.client_redirect_uri' + . ' FROM oauth_sessions' + . ' INNER JOIN oauth_session_access_tokens' + . ' ON oauth_session_access_tokens.session_id = oauth_sessions.id' + . ' WHERE oauth_session_access_tokens.access_token = ?;'; + $results = $this->db->fetch($sql, [$access_token->getId()]); + + if (count($results) === 1) + { + $session = new SessionEntity($this->server); + $session->setId($result[0]['id']); + $session->setOwner($result[0]['owner_type'], $result[0]['owner_id']); + + return $session; + } + + return null; } public function getByAuthCode(AuthCodeEntity $auth_code) { + $sql = 'SELECT oauth_sessions.id, oauth_sessions.owner_type,' + . ' oauth_sessions.owner_id, oauth_sessions.client_id,' + . ' oauth_sessions.client_redirect_uri' + . ' FROM oauth_sessions' + . ' INNER JOIN oauth_authcodes' + . ' ON oauth_auth_codes.session_id = oauth_sessions.id' + . ' WHERE oauth_auth_codes.auth_code = ?;'; + $results = $this->db->fetch($sql, [$auth_code->getId()]); + + if (count($results) === 1) + { + $session = new SessionEntity($this->server); + $session->setId($result[0]['id']); + $session->setOwner($result[0]['owner_type'], $result[0]['owner_id']); + + return $session; + } + + return null; } public function getScopes(SessionEntity $session) { + $sql = 'SELECT oauth_sessions.*' + . ' FROM oauth_sessions' + . ' INNER JOIN oauth_session_token_scopes' + . ' ON oauth_sessions.id = oauth_session_token_scopes.session_access_token_id' + . ' INNER JOIN oauth_scopes' + . ' ON oauth_scopes.id = oauth_session_token_scopes.scope_id' + . ' WHERE oauth_sessions.id = ?;'; + $results = $this->db->fetch($sql, [$session->getId()]); + $scopes = []; + + foreach ($results as $scope) + { + $scopes[] = (new ScopeEntity($this->server))->hydrate([ + 'id' => $scope['id'], + 'description' => $scope['description'], + ]); + } + + return $scopes; } public function create($owner_type, $owner_id, $client_id, $client_redirect_uri = null) { + $sql = 'INSERT INTO oauth_sessions' + . ' (owner_type, owner_id, client_id)' + . ' VALUES' + . ' (?, ?, ?);'; + return $this->db->execute($sql, [$owner_type, $owner_id, $client_id]); } public function associateScope(SessionEntity $session, ScopeEntity $scope) { + $sql = 'INSERT INTO oauth_session_token_scopes' + . ' (session_access_token_id, scope_id)' + . ' VALUES' + . ' (?, ?);'; + $this->db->execute($sql, [$session->getId(), $scope->getId()]); } } From 84231d943429371206d3b6fed60298d723441487 Mon Sep 17 00:00:00 2001 From: Josh Sherman Date: Tue, 14 Oct 2014 22:24:46 -0400 Subject: [PATCH 109/129] Swapped oauth lib --- composer.json | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/composer.json b/composer.json index da9531c..dd82443 100644 --- a/composer.json +++ b/composer.json @@ -1,14 +1,14 @@ { - "name": "picklesphp/pickles", + "name": "picklesphp/pickles", "description": "Pickles is a PHP framework for building kick-ass services", - "type": "library", - "keywords": ["framework", "api", "soa", "oauth"], - "homepage": "http://picklesphp.com", - "license": "MIT", + "type": "library", + "keywords": ["framework", "api", "soa", "oauth"], + "homepage": "http://picklesphp.com", + "license": "MIT", "authors": [ { - "name": "Josh Sherman", - "email": "josh@gravityblvd.com", + "name": "Josh Sherman", + "email": "josh@gravityblvd.com", "homepage": "http://joshtronic.com" } ], @@ -18,12 +18,12 @@ }, "minimum-stability" : "dev", "require-dev": { - "phpunit/phpunit": "dev-master", + "phpunit/phpunit": "dev-master", "satooshi/php-coveralls": "dev-master" }, "require": { - "php": ">=5.4", - "league/oauth2-server": "3.2.*" + "php": ">=5.4", + "bshaffer/oauth2-server-php": "v1.5" }, "autoload": { "psr-4": { From 32e7ae5f0fcc3206163d87abf17f815bb2b3be02 Mon Sep 17 00:00:00 2001 From: Josh Sherman Date: Wed, 15 Oct 2014 07:08:39 -0400 Subject: [PATCH 110/129] Switched oauth lib Wasn't pleased to find that the new lib used the username as the primary key across a bunch of tables. Not ideal IMO. --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index dd82443..51764cd 100644 --- a/composer.json +++ b/composer.json @@ -23,7 +23,7 @@ }, "require": { "php": ">=5.4", - "bshaffer/oauth2-server-php": "v1.5" + "thephpleague/oauth2-server": "4.0.*" }, "autoload": { "psr-4": { From 08284b0f350f8bfb92c463739cb393ea6fc1f371 Mon Sep 17 00:00:00 2001 From: Josh Sherman Date: Wed, 15 Oct 2014 07:18:09 -0400 Subject: [PATCH 111/129] Fixed dependency version --- composer.json | 2 +- composer.lock | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index 51764cd..fe830d1 100644 --- a/composer.json +++ b/composer.json @@ -23,7 +23,7 @@ }, "require": { "php": ">=5.4", - "thephpleague/oauth2-server": "4.0.*" + "league/oauth2-server": "4.0.x-dev" }, "autoload": { "psr-4": { diff --git a/composer.lock b/composer.lock index ff3f1b9..5adfeaf 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "76708e9b1bd8a87135b6c5b4c0e38a2a", + "hash": "f919c496ec07285f990ccb4efab8cf18", "packages": [ { "name": "league/event", @@ -1434,6 +1434,7 @@ "aliases": [], "minimum-stability": "dev", "stability-flags": { + "league/oauth2-server": 20, "phpunit/phpunit": 20, "satooshi/php-coveralls": 20 }, From f235f4a520f787b8905a5d5a2d50387d41cb4342 Mon Sep 17 00:00:00 2001 From: Josh Sherman Date: Wed, 15 Oct 2014 07:48:34 -0400 Subject: [PATCH 112/129] Tweaking the schema --- sql/oauth2.sql | 90 +++++++++++++++++++++---------- src/OAuth2/AccessTokenStorage.php | 22 ++++---- src/OAuth2/ClientStorage.php | 6 +-- src/OAuth2/SessionStorage.php | 20 +++---- 4 files changed, 86 insertions(+), 52 deletions(-) diff --git a/sql/oauth2.sql b/sql/oauth2.sql index 0d708e8..5c4fbc1 100644 --- a/sql/oauth2.sql +++ b/sql/oauth2.sql @@ -7,13 +7,16 @@ CREATE TABLE `oauth_clients` ( UNIQUE KEY `u_oacl_clse_clid` (`secret`,`id`) ) ENGINE=INNODB DEFAULT CHARSET=utf8 COLLATE utf8_unicode_ci; -CREATE TABLE `oauth_client_endpoints` ( +CREATE TABLE `oauth_endpoints` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `client_id` char(40) NOT NULL, `redirect_uri` varchar(255) NOT NULL, PRIMARY KEY (`id`), KEY `i_oaclen_clid` (`client_id`), - CONSTRAINT `f_oaclen_clid` FOREIGN KEY (`client_id`) REFERENCES `oauth_clients` (`id`) ON DELETE CASCADE ON UPDATE CASCADE + CONSTRAINT `f_oaclen_clid` + FOREIGN KEY (`client_id`) + REFERENCES `oauth_clients` (`id`) + ON DELETE CASCADE ON UPDATE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE utf8_unicode_ci; CREATE TABLE `oauth_sessions` ( @@ -23,46 +26,64 @@ CREATE TABLE `oauth_sessions` ( `owner_id` varchar(255) NOT NULL, PRIMARY KEY (`id`), KEY `i_uase_clid_owty_owid` (`client_id`,`owner_type`,`owner_id`), - CONSTRAINT `f_oase_clid` FOREIGN KEY (`client_id`) REFERENCES `oauth_clients` (`id`) ON DELETE CASCADE ON UPDATE CASCADE + CONSTRAINT `f_oase_clid` + FOREIGN KEY (`client_id`) + REFERENCES `oauth_clients` (`id`) + ON DELETE CASCADE ON UPDATE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE utf8_unicode_ci; -CREATE TABLE `oauth_session_access_tokens` ( +CREATE TABLE `oauth_access_tokens` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `session_id` int(10) unsigned NOT NULL, `access_token` char(40) NOT NULL, - `access_token_expires` int(10) unsigned NOT NULL, + `expires_at` int(10) unsigned NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `u_oaseacto_acto_seid` (`access_token`,`session_id`), KEY `f_oaseto_seid` (`session_id`), - CONSTRAINT `f_oaseto_seid` FOREIGN KEY (`session_id`) REFERENCES `oauth_sessions` (`id`) ON DELETE CASCADE ON UPDATE NO ACTION + CONSTRAINT `f_oaseto_seid` + FOREIGN KEY (`session_id`) + REFERENCES `oauth_sessions` (`id`) + ON DELETE CASCADE ON UPDATE NO ACTION ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE utf8_unicode_ci; -CREATE TABLE `oauth_session_authcodes` ( +CREATE TABLE `oauth_authorization_codes` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `session_id` int(10) unsigned NOT NULL, - `auth_code` char(40) NOT NULL, - `auth_code_expires` int(10) unsigned NOT NULL, + `authorization_code` char(40) NOT NULL, + `expires_at` int(10) unsigned NOT NULL, PRIMARY KEY (`id`), KEY `session_id` (`session_id`), - CONSTRAINT `oauth_session_authcodes_ibfk_1` FOREIGN KEY (`session_id`) REFERENCES `oauth_sessions` (`id`) ON DELETE CASCADE + CONSTRAINT `oauth_authorization_codes_ibfk_1` + FOREIGN KEY (`session_id`) + REFERENCES `oauth_sessions` (`id`) + ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE utf8_unicode_ci; -CREATE TABLE `oauth_session_redirects` ( +CREATE TABLE `oauth_redirect_uris` ( `session_id` int(10) unsigned NOT NULL, `redirect_uri` varchar(255) NOT NULL, PRIMARY KEY (`session_id`), - CONSTRAINT `f_oasere_seid` FOREIGN KEY (`session_id`) REFERENCES `oauth_sessions` (`id`) ON DELETE CASCADE ON UPDATE NO ACTION + CONSTRAINT `f_oasere_seid` + FOREIGN KEY (`session_id`) + REFERENCES `oauth_sessions` (`id`) + ON DELETE CASCADE ON UPDATE NO ACTION ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE utf8_unicode_ci; -CREATE TABLE `oauth_session_refresh_tokens` ( - `session_access_token_id` int(10) unsigned NOT NULL, +CREATE TABLE `oauth_refresh_tokens` ( + `access_token_id` int(10) unsigned NOT NULL, `refresh_token` char(40) NOT NULL, - `refresh_token_expires` int(10) unsigned NOT NULL, + `expires_at` int(10) unsigned NOT NULL, `client_id` char(40) NOT NULL, - PRIMARY KEY (`session_access_token_id`), + PRIMARY KEY (`access_token_id`), KEY `client_id` (`client_id`), - CONSTRAINT `oauth_session_refresh_tokens_ibfk_1` FOREIGN KEY (`client_id`) REFERENCES `oauth_clients` (`id`) ON DELETE CASCADE, - CONSTRAINT `f_oasetore_setoid` FOREIGN KEY (`session_access_token_id`) REFERENCES `oauth_session_access_tokens` (`id`) ON DELETE CASCADE ON UPDATE NO ACTION + CONSTRAINT `oauth_refresh_tokens_ibfk_1` + FOREIGN KEY (`client_id`) + REFERENCES `oauth_clients` (`id`) + ON DELETE CASCADE, + CONSTRAINT `f_oasetore_setoid` + FOREIGN KEY (`access_token_id`) + REFERENCES `oauth_access_tokens` (`id`) + ON DELETE CASCADE ON UPDATE NO ACTION ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE utf8_unicode_ci; CREATE TABLE `oauth_scopes` ( @@ -74,22 +95,35 @@ CREATE TABLE `oauth_scopes` ( UNIQUE KEY `u_oasc_sc` (`scope`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE utf8_unicode_ci; -CREATE TABLE `oauth_session_token_scopes` ( +CREATE TABLE `oauth_access_token_scopes` ( `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, - `session_access_token_id` int(10) unsigned DEFAULT NULL, + `access_token_id` int(10) unsigned DEFAULT NULL, `scope_id` smallint(5) unsigned NOT NULL, PRIMARY KEY (`id`), - UNIQUE KEY `u_setosc_setoid_scid` (`session_access_token_id`,`scope_id`), + UNIQUE KEY `u_setosc_setoid_scid` (`access_token_id`,`scope_id`), KEY `f_oasetosc_scid` (`scope_id`), - CONSTRAINT `f_oasetosc_scid` FOREIGN KEY (`scope_id`) REFERENCES `oauth_scopes` (`id`) ON DELETE CASCADE ON UPDATE NO ACTION, - CONSTRAINT `f_oasetosc_setoid` FOREIGN KEY (`session_access_token_id`) REFERENCES `oauth_session_access_tokens` (`id`) ON DELETE CASCADE ON UPDATE NO ACTION + CONSTRAINT `f_oasetosc_scid` + FOREIGN KEY (`scope_id`) + REFERENCES `oauth_scopes` (`id`) + ON DELETE CASCADE ON UPDATE NO ACTION, + CONSTRAINT `f_oasetosc_setoid` + FOREIGN KEY (`access_token_id`) + REFERENCES `oauth_access_tokens` (`id`) + ON DELETE CASCADE ON UPDATE NO ACTION ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE utf8_unicode_ci; -CREATE TABLE `oauth_session_authcode_scopes` ( - `oauth_session_authcode_id` int(10) unsigned NOT NULL, +CREATE TABLE `oauth_authorization_code_scopes` ( + `authorization_code_id` int(10) unsigned NOT NULL, `scope_id` smallint(5) unsigned NOT NULL, - KEY `oauth_session_authcode_id` (`oauth_session_authcode_id`), + KEY `authorization_code_id` (`authorization_code_id`), KEY `scope_id` (`scope_id`), - CONSTRAINT `oauth_session_authcode_scopes_ibfk_2` FOREIGN KEY (`scope_id`) REFERENCES `oauth_scopes` (`id`) ON DELETE CASCADE, - CONSTRAINT `oauth_session_authcode_scopes_ibfk_1` FOREIGN KEY (`oauth_session_authcode_id`) REFERENCES `oauth_session_authcodes` (`id`) ON DELETE CASCADE + CONSTRAINT `oauth_authorization_code_scopes_ibfk_2` + FOREIGN KEY (`scope_id`) + REFERENCES `oauth_scopes` (`id`) + ON DELETE CASCADE, + CONSTRAINT `oauth_authorization_code_scopes_ibfk_1` + FOREIGN KEY (`authorization_code_id`) + REFERENCES `oauth_authorization_codes` (`id`) + ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE utf8_unicode_ci; + diff --git a/src/OAuth2/AccessTokenStorage.php b/src/OAuth2/AccessTokenStorage.php index 7ecd943..04d8dc0 100644 --- a/src/OAuth2/AccessTokenStorage.php +++ b/src/OAuth2/AccessTokenStorage.php @@ -11,10 +11,10 @@ class AccessTokenStorage extends StorageAdapter implements AccessTokenInterface { public function get($token) { - $sql = 'SELECT oauth_session_access_tokens.*' - . ' FROM oauth_session_access_tokens' + $sql = 'SELECT oauth_access_tokens.*' + . ' FROM oauth_access_tokens' . ' WHERE access_token = ?' - . ' AND access_token_expires >= ?;'; + . ' AND expires_at >= ?;'; $results = $this->db->fetch($sql, [$token, time()]); @@ -22,7 +22,7 @@ class AccessTokenStorage extends StorageAdapter implements AccessTokenInterface { return (new AccessTokenEntity($this->server)) ->setId($results[0]['access_token']) - ->setExpireTime($results[0]['access_token_expires']); + ->setExpireTime($results[0]['expires_at']); } return null; @@ -31,10 +31,10 @@ class AccessTokenStorage extends StorageAdapter implements AccessTokenInterface public function getScopes(AbstractTokenEntity $token) { $sql = 'SELECT oauth_scopes.id, oauth_scopes.description' - . ' FROM oauth_session_token_scopes' + . ' FROM oauth_access_token_scopes' . ' INNER JOIN oauth_scopes' - . ' ON oauth_session_token_scopes.scope_id = oauth_scopes.id' - . ' WHERE oauth_session_token_scopes.session_access_token_id = ?;'; + . ' ON oauth_access_token_scopes.scope_id = oauth_scopes.id' + . ' WHERE oauth_access_token_scopes.access_token_id = ?;'; $results = $this->db->fetch($sql, [$token->getId()]); $response = []; @@ -55,8 +55,8 @@ class AccessTokenStorage extends StorageAdapter implements AccessTokenInterface public function create($token, $expiration, $session_id) { - $sql = 'INSERT INTO oauth_session_access_tokens' - . ' (access_token, session_id, access_token_expires)' + $sql = 'INSERT INTO oauth_access_tokens' + . ' (access_token, session_id, expires_at)' . ' VALUES' . ' (?, ?, ?);'; @@ -65,7 +65,7 @@ class AccessTokenStorage extends StorageAdapter implements AccessTokenInterface public function associateScope(AbstractTokenEntity $token, ScopeEntity $scope) { - $sql = 'INSERT INTO oauth_session_token_scopes' + $sql = 'INSERT INTO oauth_access_token_scopes' . ' (access_token, scope)' . ' VALUES' . ' (?, ?);'; @@ -75,7 +75,7 @@ class AccessTokenStorage extends StorageAdapter implements AccessTokenInterface public function delete(AbstractTokenEntity $token) { - $sql = 'DELETE FROM oauth_session_token_scopes' + $sql = 'DELETE FROM oauth_access_token_scopes' . ' WHERE access_token = ?;'; $this->db->execute($sql, [$token->getId()]); diff --git a/src/OAuth2/ClientStorage.php b/src/OAuth2/ClientStorage.php index 8a38ffc..1a32583 100644 --- a/src/OAuth2/ClientStorage.php +++ b/src/OAuth2/ClientStorage.php @@ -16,8 +16,8 @@ class ClientStorage extends StorageAdapter implements ClientInterface if ($redirect_uri) { $sql .= ', oauth_client_redirect_uris.*' - . ' INNER JOIN oauth_client_redirect_uris' - . ' ON oauth_clients.id = oauth_client_redirect_uris.client_id'; + . ' INNER JOIN oauth_redirect_uris' + . ' ON oauth_clients.id = oauth_redirect_uris.client_id'; } $sql .= ' FROM oauth_clients WHERE oauth_clients.id = ?'; @@ -32,7 +32,7 @@ class ClientStorage extends StorageAdapter implements ClientInterface if ($redirect_uri) { - $sql .= 'AND oauth_client_redirect_uris.redirect_uri = ?'; + $sql .= 'AND oauth_redirect_uris.redirect_uri = ?'; $parameters[] = $redirect_uri; } diff --git a/src/OAuth2/SessionStorage.php b/src/OAuth2/SessionStorage.php index 68b015a..22a6786 100644 --- a/src/OAuth2/SessionStorage.php +++ b/src/OAuth2/SessionStorage.php @@ -17,9 +17,9 @@ class SessionStorage extends StorageAdapter implements SessionInterface . ' oauth_sessions.owner_id, oauth_sessions.client_id,' . ' oauth_sessions.client_redirect_uri' . ' FROM oauth_sessions' - . ' INNER JOIN oauth_session_access_tokens' - . ' ON oauth_session_access_tokens.session_id = oauth_sessions.id' - . ' WHERE oauth_session_access_tokens.access_token = ?;'; + . ' INNER JOIN oauth_access_tokens' + . ' ON oauth_access_tokens.session_id = oauth_sessions.id' + . ' WHERE oauth_access_tokens.access_token = ?;'; $results = $this->db->fetch($sql, [$access_token->getId()]); @@ -41,9 +41,9 @@ class SessionStorage extends StorageAdapter implements SessionInterface . ' oauth_sessions.owner_id, oauth_sessions.client_id,' . ' oauth_sessions.client_redirect_uri' . ' FROM oauth_sessions' - . ' INNER JOIN oauth_authcodes' - . ' ON oauth_auth_codes.session_id = oauth_sessions.id' - . ' WHERE oauth_auth_codes.auth_code = ?;'; + . ' INNER JOIN oauth_authorization_codes' + . ' ON oauth_authorization_codes.session_id = oauth_sessions.id' + . ' WHERE oauth_authorization_codes.authorization_code = ?;'; $results = $this->db->fetch($sql, [$auth_code->getId()]); @@ -63,10 +63,10 @@ class SessionStorage extends StorageAdapter implements SessionInterface { $sql = 'SELECT oauth_sessions.*' . ' FROM oauth_sessions' - . ' INNER JOIN oauth_session_token_scopes' - . ' ON oauth_sessions.id = oauth_session_token_scopes.session_access_token_id' + . ' INNER JOIN oauth_access_token_scopes' + . ' ON oauth_sessions.id = oauth_access_token_scopes.access_token_id' . ' INNER JOIN oauth_scopes' - . ' ON oauth_scopes.id = oauth_session_token_scopes.scope_id' + . ' ON oauth_scopes.id = oauth_access_token_scopes.scope_id' . ' WHERE oauth_sessions.id = ?;'; $results = $this->db->fetch($sql, [$session->getId()]); @@ -95,7 +95,7 @@ class SessionStorage extends StorageAdapter implements SessionInterface public function associateScope(SessionEntity $session, ScopeEntity $scope) { - $sql = 'INSERT INTO oauth_session_token_scopes' + $sql = 'INSERT INTO oauth_access_token_scopes' . ' (session_access_token_id, scope_id)' . ' VALUES' . ' (?, ?);'; From 9e2e4f75f392f54ad167fdd254f639833645d6c9 Mon Sep 17 00:00:00 2001 From: Josh Sherman Date: Wed, 15 Oct 2014 07:56:25 -0400 Subject: [PATCH 113/129] Tweaked schema some more. --- src/OAuth2/AccessTokenStorage.php | 22 +++++++++++----------- src/OAuth2/SessionStorage.php | 16 ++++++++-------- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/OAuth2/AccessTokenStorage.php b/src/OAuth2/AccessTokenStorage.php index 7ecd943..04d8dc0 100644 --- a/src/OAuth2/AccessTokenStorage.php +++ b/src/OAuth2/AccessTokenStorage.php @@ -11,10 +11,10 @@ class AccessTokenStorage extends StorageAdapter implements AccessTokenInterface { public function get($token) { - $sql = 'SELECT oauth_session_access_tokens.*' - . ' FROM oauth_session_access_tokens' + $sql = 'SELECT oauth_access_tokens.*' + . ' FROM oauth_access_tokens' . ' WHERE access_token = ?' - . ' AND access_token_expires >= ?;'; + . ' AND expires_at >= ?;'; $results = $this->db->fetch($sql, [$token, time()]); @@ -22,7 +22,7 @@ class AccessTokenStorage extends StorageAdapter implements AccessTokenInterface { return (new AccessTokenEntity($this->server)) ->setId($results[0]['access_token']) - ->setExpireTime($results[0]['access_token_expires']); + ->setExpireTime($results[0]['expires_at']); } return null; @@ -31,10 +31,10 @@ class AccessTokenStorage extends StorageAdapter implements AccessTokenInterface public function getScopes(AbstractTokenEntity $token) { $sql = 'SELECT oauth_scopes.id, oauth_scopes.description' - . ' FROM oauth_session_token_scopes' + . ' FROM oauth_access_token_scopes' . ' INNER JOIN oauth_scopes' - . ' ON oauth_session_token_scopes.scope_id = oauth_scopes.id' - . ' WHERE oauth_session_token_scopes.session_access_token_id = ?;'; + . ' ON oauth_access_token_scopes.scope_id = oauth_scopes.id' + . ' WHERE oauth_access_token_scopes.access_token_id = ?;'; $results = $this->db->fetch($sql, [$token->getId()]); $response = []; @@ -55,8 +55,8 @@ class AccessTokenStorage extends StorageAdapter implements AccessTokenInterface public function create($token, $expiration, $session_id) { - $sql = 'INSERT INTO oauth_session_access_tokens' - . ' (access_token, session_id, access_token_expires)' + $sql = 'INSERT INTO oauth_access_tokens' + . ' (access_token, session_id, expires_at)' . ' VALUES' . ' (?, ?, ?);'; @@ -65,7 +65,7 @@ class AccessTokenStorage extends StorageAdapter implements AccessTokenInterface public function associateScope(AbstractTokenEntity $token, ScopeEntity $scope) { - $sql = 'INSERT INTO oauth_session_token_scopes' + $sql = 'INSERT INTO oauth_access_token_scopes' . ' (access_token, scope)' . ' VALUES' . ' (?, ?);'; @@ -75,7 +75,7 @@ class AccessTokenStorage extends StorageAdapter implements AccessTokenInterface public function delete(AbstractTokenEntity $token) { - $sql = 'DELETE FROM oauth_session_token_scopes' + $sql = 'DELETE FROM oauth_access_token_scopes' . ' WHERE access_token = ?;'; $this->db->execute($sql, [$token->getId()]); diff --git a/src/OAuth2/SessionStorage.php b/src/OAuth2/SessionStorage.php index 68b015a..e9e72f6 100644 --- a/src/OAuth2/SessionStorage.php +++ b/src/OAuth2/SessionStorage.php @@ -17,9 +17,9 @@ class SessionStorage extends StorageAdapter implements SessionInterface . ' oauth_sessions.owner_id, oauth_sessions.client_id,' . ' oauth_sessions.client_redirect_uri' . ' FROM oauth_sessions' - . ' INNER JOIN oauth_session_access_tokens' - . ' ON oauth_session_access_tokens.session_id = oauth_sessions.id' - . ' WHERE oauth_session_access_tokens.access_token = ?;'; + . ' INNER JOIN oauth_access_tokens' + . ' ON oauth_access_tokens.session_id = oauth_sessions.id' + . ' WHERE oauth_access_tokens.access_token = ?;'; $results = $this->db->fetch($sql, [$access_token->getId()]); @@ -63,10 +63,10 @@ class SessionStorage extends StorageAdapter implements SessionInterface { $sql = 'SELECT oauth_sessions.*' . ' FROM oauth_sessions' - . ' INNER JOIN oauth_session_token_scopes' - . ' ON oauth_sessions.id = oauth_session_token_scopes.session_access_token_id' + . ' INNER JOIN oauth_access_token_scopes' + . ' ON oauth_sessions.id = oauth_access_token_scopes.access_token_id' . ' INNER JOIN oauth_scopes' - . ' ON oauth_scopes.id = oauth_session_token_scopes.scope_id' + . ' ON oauth_scopes.id = oauth_access_token_scopes.scope_id' . ' WHERE oauth_sessions.id = ?;'; $results = $this->db->fetch($sql, [$session->getId()]); @@ -95,8 +95,8 @@ class SessionStorage extends StorageAdapter implements SessionInterface public function associateScope(SessionEntity $session, ScopeEntity $scope) { - $sql = 'INSERT INTO oauth_session_token_scopes' - . ' (session_access_token_id, scope_id)' + $sql = 'INSERT INTO oauth_access_token_scopes' + . ' (access_token_id, scope_id)' . ' VALUES' . ' (?, ?);'; From a40041acc689f735ae799c47e2b93fcda6c68b83 Mon Sep 17 00:00:00 2001 From: Josh Sherman Date: Thu, 16 Oct 2014 07:30:32 -0400 Subject: [PATCH 114/129] Implemented refresh tokens Right now it's hardcoded to always return a refresh token when you issue an access token. Should think about making this an optional workflow or committing to it being turned on indefinitely. --- src/OAuth2/RefreshTokenStorage.php | 56 ++++++++++++++++++++++++++++++ src/OAuth2/Resource.php | 7 ++++ 2 files changed, 63 insertions(+) create mode 100644 src/OAuth2/RefreshTokenStorage.php diff --git a/src/OAuth2/RefreshTokenStorage.php b/src/OAuth2/RefreshTokenStorage.php new file mode 100644 index 0000000..4bd6182 --- /dev/null +++ b/src/OAuth2/RefreshTokenStorage.php @@ -0,0 +1,56 @@ += ?;'; + + $results = $this->db->fetch($sql, [$token, time()]); + + if (count($results) === 1) + { + return (new RefreshTokenEntity($this->server)) + ->setId($result[0]['refresh_token']) + ->setExpireTime($result[0]['expires_at']) + ->setAccessTokenId($result[0]['access_token_id']); + } + + return null; + } + + public function create($token, $expiration, $access_token) + { + $sql = 'SELECT id FROM oauth_access_tokens WHERE access_token = ?;'; + $results = $this->db->fetch($sql, [$access_token]); + $token_id = $results[0]['id']; + + $sql = 'INSERT INTO oauth_refresh_tokens' + . ' (refresh_token, access_token_id, expires_at, client_id)' + . ' VALUES' + . ' (?, ?, ?, ?);'; + + $this->db->execute($sql, [ + $token, + $token_id, + $expiration, + $this->server->getRequest()->request->get('client_id', null), + ]); + } + + public function delete(RefreshTokenEntity $token) + { + $sql = 'DELETE FROM oauth_refresh_tokens WHERE refresh_token = ?;'; + + $this->db->execute($sql, [$token->getId()]); + } +} + diff --git a/src/OAuth2/Resource.php b/src/OAuth2/Resource.php index 68a3a73..e34b9b8 100644 --- a/src/OAuth2/Resource.php +++ b/src/OAuth2/Resource.php @@ -4,6 +4,7 @@ namespace Pickles\OAuth2; use \League\OAuth2\Server\AuthorizationServer; use \League\OAuth2\Server\Grant\PasswordGrant; +use \League\OAuth2\Server\Grant\RefreshTokenGrant; use \Pickles\App\Models\User; use \Pickles\Config; @@ -27,6 +28,7 @@ class Resource extends \Pickles\Resource $server->setAccessTokenStorage(new AccessTokenStorage); $server->setClientStorage(new ClientStorage); $server->setScopeStorage(new ScopeStorage); + $server->setRefreshTokenStorage(new RefreshTokenStorage); switch ($_REQUEST['grant_type']) { @@ -44,6 +46,8 @@ class Resource extends \Pickles\Resource case 'password': $grant = new PasswordGrant; + $grant->setAccessTokenTTL(3600); + // @todo ^^^ check config and use that value $grant->setVerifyCredentialsCallback(function ($username, $password) { @@ -66,6 +70,9 @@ class Resource extends \Pickles\Resource $server->addGrantType($grant); + $refreshTokenGrant = new RefreshTokenGrant; + $server->addGrantType($refreshTokenGrant); + $response = $server->issueAccessToken(); return $response; From 59777fe20696601fa6815b76dd390cede036e838 Mon Sep 17 00:00:00 2001 From: Josh Sherman Date: Thu, 16 Oct 2014 10:29:49 -0400 Subject: [PATCH 115/129] Update README.md --- README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 5354487..6bf603c 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ # Pickles -[![License](http://img.shields.io/packagist/l/picklesphp/pickles.svg?style=flat)][packagist] -[![Build](http://img.shields.io/travis/picklesphp/pickles.svg?style=flat)][travis] -[![Coverage](http://img.shields.io/coveralls/picklesphp/pickles.svg?style=flat)][coveralls] -[![Downloads](http://img.shields.io/packagist/dt/picklesphp/pickles.svg?style=flat)][packagist] +[![License](http://img.shields.io/packagist/l/joshtronic/pickles.svg?style=flat)][packagist] +[![Build](http://img.shields.io/travis/joshtronic/pickles.svg?style=flat)][travis] +[![Coverage](http://img.shields.io/coveralls/joshtronic/pickles.svg?style=flat)][coveralls] +[![Downloads](http://img.shields.io/packagist/dt/joshtronic/pickles.svg?style=flat)][packagist] Pickles f/k/a PICKLES (PHP Interface Collection of Killer Libraries to Enhance Stuff) is an open source framework for the rapid development of RESTful @@ -20,9 +20,9 @@ contributor, [Justin Davis][JustinDavis] for romanticizing the v2 reimagining and [Dean Jones][DeanJones] for helping to come up with the original PICKLES v1 acronym. -[coveralls]: https://coveralls.io/r/picklesphp/pickles -[packagist]: https://packagist.org/packages/picklesphp/pickles -[travis]: http://travis-ci.org/picklesphp/pickles +[coveralls]: https://coveralls.io/r/joshtronic/pickles +[packagist]: https://packagist.org/packages/joshtronic/pickles +[travis]: http://travis-ci.org/joshtronic/pickles [DeanJones]: https://github.com/deanproxy [GeoffOliver]: https://github.com/geoffoliver [JustinDavis]: http://justindavis.co From c824d3d5e67d6d0f9201f5a7f8667e01a6f9b4aa Mon Sep 17 00:00:00 2001 From: Josh Sherman Date: Thu, 16 Oct 2014 10:40:11 -0400 Subject: [PATCH 116/129] Cleaned up the links a bit. --- .travis.yml | 2 +- composer.json | 8 ++++---- src/Browser.php | 2 +- src/Cache.php | 2 +- src/Config.php | 2 +- src/Convert.php | 2 +- src/Database.php | 2 +- src/Distance.php | 2 +- src/File.php | 2 +- src/Model.php | 2 +- src/Number.php | 2 +- src/Object.php | 2 +- src/Profiler.php | 2 +- src/Resource.php | 2 +- src/Router.php | 2 +- src/Sort.php | 2 +- src/String.php | 2 +- src/Time.php | 2 +- 18 files changed, 21 insertions(+), 21 deletions(-) diff --git a/.travis.yml b/.travis.yml index 61a281d..ac3f6aa 100644 --- a/.travis.yml +++ b/.travis.yml @@ -33,7 +33,7 @@ before_script: - phpenv rehash script: - - phpunit --coverage-clover /home/travis/build/picklesphp/pickles/build/logs/clover.xml + - phpunit --coverage-clover /home/travis/build/joshtronic/pickles/build/logs/clover.xml after_success: - php vendor/bin/coveralls --config ../.coveralls.yml -v diff --git a/composer.json b/composer.json index dd8cc94..85105fb 100644 --- a/composer.json +++ b/composer.json @@ -1,9 +1,9 @@ { - "name": "picklesphp/pickles", + "name": "joshtronic/pickles", "description": "Pickles is a PHP framework for building kick-ass services", "type": "library", "keywords": ["framework", "api", "soa", "oauth"], - "homepage": "http://picklesphp.com", + "homepage": "https://github.com/joshtronic/pickles", "license": "MIT", "authors": [ { @@ -13,8 +13,8 @@ } ], "support": { - "issues": "https://github.com/picklesphp/pickles/issues", - "source": "https://github.com/picklesphp/pickles" + "issues": "https://github.com/joshtronic/pickles/issues", + "source": "https://github.com/joshtronic/pickles" }, "minimum-stability" : "dev", "require-dev": { diff --git a/src/Browser.php b/src/Browser.php index 18f46ae..8a767c1 100644 --- a/src/Browser.php +++ b/src/Browser.php @@ -8,7 +8,7 @@ * * @copyright Copyright 2007-2014, Josh Sherman * @license http://www.opensource.org/licenses/mit-license.html - * @link http://picklesphp.com + * @link https://github.com/joshtronic/pickles * @package Pickles */ diff --git a/src/Cache.php b/src/Cache.php index ecb1fe6..d7c6c72 100644 --- a/src/Cache.php +++ b/src/Cache.php @@ -8,7 +8,7 @@ * * @copyright Copyright 2007-2014, Josh Sherman * @license http://www.opensource.org/licenses/mit-license.html - * @link http://picklesphp.com + * @link https://github.com/joshtronic/pickles * @package Pickles */ diff --git a/src/Config.php b/src/Config.php index c32d87b..5ae4e79 100644 --- a/src/Config.php +++ b/src/Config.php @@ -8,7 +8,7 @@ * * @copyright Copyright 2007-2014, Josh Sherman * @license http://www.opensource.org/licenses/mit-license.html - * @link http://picklesphp.com + * @link https://github.com/joshtronic/pickles * @package Pickles */ diff --git a/src/Convert.php b/src/Convert.php index 87e3c81..b9caa97 100644 --- a/src/Convert.php +++ b/src/Convert.php @@ -8,7 +8,7 @@ * * @copyright Copyright 2007-2014, Josh Sherman * @license http://www.opensource.org/licenses/mit-license.html - * @link http://picklesphp.com + * @link https://github.com/joshtronic/pickles * @package Pickles */ diff --git a/src/Database.php b/src/Database.php index 24f269c..1379ec8 100644 --- a/src/Database.php +++ b/src/Database.php @@ -8,7 +8,7 @@ * * @copyright Copyright 2007-2014, Josh Sherman * @license http://www.opensource.org/licenses/mit-license.html - * @link http://picklesphp.com + * @link https://github.com/joshtronic/pickles * @package Pickles * @todo Drop driver, hardcode drivers based on the type * @todo More assumptions for the datasource variables diff --git a/src/Distance.php b/src/Distance.php index 11ff121..4bb841a 100644 --- a/src/Distance.php +++ b/src/Distance.php @@ -8,7 +8,7 @@ * * @copyright Copyright 2007-2014, Josh Sherman * @license http://www.opensource.org/licenses/mit-license.html - * @link http://picklesphp.com + * @link https://github.com/joshtronic/pickles * @package Pickles */ diff --git a/src/File.php b/src/File.php index 4399b19..6f83c5d 100644 --- a/src/File.php +++ b/src/File.php @@ -8,7 +8,7 @@ * * @copyright Copyright 2007-2014, Josh Sherman * @license http://www.opensource.org/licenses/mit-license.html - * @link http://picklesphp.com + * @link https://github.com/joshtronic/pickles * @package Pickles */ diff --git a/src/Model.php b/src/Model.php index 83e4adb..037427e 100644 --- a/src/Model.php +++ b/src/Model.php @@ -8,7 +8,7 @@ * * @copyright Copyright 2007-2014, Josh Sherman * @license http://www.opensource.org/licenses/mit-license.html - * @link http://picklesphp.com + * @link https://github.com/joshtronic/pickles * @package Pickles */ diff --git a/src/Number.php b/src/Number.php index 59c9929..92e87ca 100644 --- a/src/Number.php +++ b/src/Number.php @@ -8,7 +8,7 @@ * * @copyright Copyright 2007-2014, Josh Sherman * @license http://www.opensource.org/licenses/mit-license.html - * @link http://picklesphp.com + * @link https://github.com/joshtronic/pickles * @package Pickles */ diff --git a/src/Object.php b/src/Object.php index 2ca484d..c7809b9 100644 --- a/src/Object.php +++ b/src/Object.php @@ -8,7 +8,7 @@ * * @copyright Copyright 2007-2014, Josh Sherman * @license http://www.opensource.org/licenses/mit-license.html - * @link http://picklesphp.com + * @link https://github.com/joshtronic/pickles * @package Pickles */ diff --git a/src/Profiler.php b/src/Profiler.php index 184c8d4..969961f 100644 --- a/src/Profiler.php +++ b/src/Profiler.php @@ -8,7 +8,7 @@ * * @copyright Copyright 2007-2014, Josh Sherman * @license http://www.opensource.org/licenses/mit-license.html - * @link http://picklesphp.com + * @link https://github.com/joshtronic/pickles * @package Pickles */ diff --git a/src/Resource.php b/src/Resource.php index 9857cb2..9473fbe 100644 --- a/src/Resource.php +++ b/src/Resource.php @@ -8,7 +8,7 @@ * * @copyright Copyright 2007-2014, Josh Sherman * @license http://www.opensource.org/licenses/mit-license.html - * @link http://picklesphp.com + * @link https://github.com/joshtronic/pickles * @package Pickles */ diff --git a/src/Router.php b/src/Router.php index c41baf7..96be948 100644 --- a/src/Router.php +++ b/src/Router.php @@ -8,7 +8,7 @@ * * @copyright Copyright 2007-2014, Josh Sherman * @license http://www.opensource.org/licenses/mit-license.html - * @link http://picklesphp.com + * @link https://github.com/joshtronic/pickles * @package Pickles */ diff --git a/src/Sort.php b/src/Sort.php index 5c2a4d9..6d37de6 100644 --- a/src/Sort.php +++ b/src/Sort.php @@ -8,7 +8,7 @@ * * @copyright Copyright 2007-2014, Josh Sherman * @license http://www.opensource.org/licenses/mit-license.html - * @link http://picklesphp.com + * @link https://github.com/joshtronic/pickles * @package Pickles */ diff --git a/src/String.php b/src/String.php index a66d463..3b8dc8d 100644 --- a/src/String.php +++ b/src/String.php @@ -8,7 +8,7 @@ * * @copyright Copyright 2007-2014, Josh Sherman * @license http://www.opensource.org/licenses/mit-license.html - * @link http://picklesphp.com + * @link https://github.com/joshtronic/pickles * @package Pickles */ diff --git a/src/Time.php b/src/Time.php index a1a0bb9..4b538e0 100644 --- a/src/Time.php +++ b/src/Time.php @@ -8,7 +8,7 @@ * * @copyright Copyright 2007-2014, Josh Sherman * @license http://www.opensource.org/licenses/mit-license.html - * @link http://picklesphp.com + * @link https://github.com/joshtronic/pickles * @package Pickles */ From 1aa39f3a8d51195127be18d2d8f5cab3d02a61e5 Mon Sep 17 00:00:00 2001 From: Josh Sherman Date: Thu, 16 Oct 2014 19:25:57 -0400 Subject: [PATCH 117/129] Made refresh_token grant configurable --- src/OAuth2/RefreshTokenStorage.php | 6 +-- src/OAuth2/Resource.php | 60 +++++++++++++++++++++++++++--- 2 files changed, 57 insertions(+), 9 deletions(-) diff --git a/src/OAuth2/RefreshTokenStorage.php b/src/OAuth2/RefreshTokenStorage.php index 4bd6182..bceea31 100644 --- a/src/OAuth2/RefreshTokenStorage.php +++ b/src/OAuth2/RefreshTokenStorage.php @@ -19,9 +19,9 @@ class RefreshTokenStorage extends StorageAdapter implements RefreshTokenInterfac if (count($results) === 1) { return (new RefreshTokenEntity($this->server)) - ->setId($result[0]['refresh_token']) - ->setExpireTime($result[0]['expires_at']) - ->setAccessTokenId($result[0]['access_token_id']); + ->setId($results[0]['refresh_token']) + ->setExpireTime($results[0]['expires_at']) + ->setAccessTokenId($results[0]['access_token_id']); } return null; diff --git a/src/OAuth2/Resource.php b/src/OAuth2/Resource.php index e34b9b8..2ff78ae 100644 --- a/src/OAuth2/Resource.php +++ b/src/OAuth2/Resource.php @@ -2,6 +2,7 @@ namespace Pickles\OAuth2; +use \League\OAuth2\Exception\OAuthException; use \League\OAuth2\Server\AuthorizationServer; use \League\OAuth2\Server\Grant\PasswordGrant; use \League\OAuth2\Server\Grant\RefreshTokenGrant; @@ -16,6 +17,12 @@ class Resource extends \Pickles\Resource { throw new \Exception('Forbidden.', 403); } + elseif (!isset($_REQUEST['grant_type'])) + { + throw new \Exception('Bad Request.', 400); + } + + $config = $this->config['oauth'][$_SERVER['__version']]; switch (substr($_REQUEST['request'], strlen($_SERVER['__version']) + 2)) { @@ -30,7 +37,29 @@ class Resource extends \Pickles\Resource $server->setScopeStorage(new ScopeStorage); $server->setRefreshTokenStorage(new RefreshTokenStorage); - switch ($_REQUEST['grant_type']) + $grant_type = $_REQUEST['grant_type']; + $grants = ['password']; + + if (isset($config['grants'])) + { + $grants = array_unique(array_merge($grants, $config['grants'])); + } + + if (!in_array($grant_type, $grants)) + { + throw new \Exception('Unsupported grant type.', 403); + } + + // Defaults TTLs to 1 day and 1 week respectively + $token_ttl = 3600; + $refresh_ttl = 604800; + + if (isset($config['ttl']['access_token'])) + { + $token_ttl = $config['ttl']['access_token']; + } + + switch ($grant_type) { case 'authorization_code': throw new \Exception('Not Implemented', 501); @@ -46,8 +75,7 @@ class Resource extends \Pickles\Resource case 'password': $grant = new PasswordGrant; - $grant->setAccessTokenTTL(3600); - // @todo ^^^ check config and use that value + $grant->setAccessTokenTTL($token_ttl); $grant->setVerifyCredentialsCallback(function ($username, $password) { @@ -65,22 +93,42 @@ class Resource extends \Pickles\Resource case 'refresh_token': throw new \Exception('Not Implemented', 501); + + // @todo Need to work through this, appears lib is busted + $grant = new RefreshTokenGrant; + //$grant->setAccessTokenTTL($refresh_ttl); + $server->addGrantType($grant); break; } $server->addGrantType($grant); - $refreshTokenGrant = new RefreshTokenGrant; - $server->addGrantType($refreshTokenGrant); + // Adds the refresh token grant if enabled + if ($grant_type != 'refresh_token' + && in_array('refresh_token', $grants)) + { + if (isset($config['ttl']['refresh_token'])) + { + $refresh_ttl = $config['ttl']['refresh_token']; + } + + $grant = new RefreshTokenGrant; + $grant->setAccessTokenTTL($refresh_ttl); + $server->addGrantType($grant); + } $response = $server->issueAccessToken(); return $response; } - catch (\Exception $e) + catch (OAuthException $e) { throw new \Exception($e->getMessage(), $e->httpStatusCode); } + catch (\Exception $e) + { + throw new \Exception($e->getMessage(), $e->getCode()); + } break; From 9d5dac05c3bcd038f91e9faf8841adc97f471ec8 Mon Sep 17 00:00:00 2001 From: Josh Sherman Date: Fri, 17 Oct 2014 06:48:03 -0400 Subject: [PATCH 118/129] Dropped browser class Actually moved it off to joshtronic/php-remoteaddr --- src/Browser.php | 54 ----------------------------------- src/Time.php | 3 -- tests/Pickles/BrowserTest.php | 36 ----------------------- 3 files changed, 93 deletions(-) delete mode 100644 src/Browser.php delete mode 100644 tests/Pickles/BrowserTest.php diff --git a/src/Browser.php b/src/Browser.php deleted file mode 100644 index 8a767c1..0000000 --- a/src/Browser.php +++ /dev/null @@ -1,54 +0,0 @@ -assertFalse(Pickles\Browser::remoteIP()); - } - - public function testRemoteIPRemoteAddress() - { - $_SERVER['REMOTE_ADDR'] = '1.2.3.4'; - - $this->assertEquals('1.2.3.4', Pickles\Browser::remoteIP()); - } - - public function testRemoteIPHTTPXForwardedFor() - { - $_SERVER['HTTP_X_FORWARDED_FOR'] = '2.3.4.5'; - - $this->assertEquals('2.3.4.5', Pickles\Browser::remoteIP()); - } - - public function testRemoteIPHTTPClientIP() - { - $_SERVER['HTTP_CLIENT_IP'] = '3.4.5.6'; - - $this->assertEquals('3.4.5.6', Pickles\Browser::remoteIP()); - } - - public function testRemoteIPWithComma() - { - - } -} - From 68b2f83379f3946717e14542344f8965a756592a Mon Sep 17 00:00:00 2001 From: Josh Sherman Date: Fri, 17 Oct 2014 06:53:08 -0400 Subject: [PATCH 119/129] Dropped Number class Actually just moved it to joshtronic/php-ordinalindicator --- src/Number.php | 69 ------------------------------------ tests/Pickles/NumberTest.php | 49 ------------------------- 2 files changed, 118 deletions(-) delete mode 100644 src/Number.php delete mode 100644 tests/Pickles/NumberTest.php diff --git a/src/Number.php b/src/Number.php deleted file mode 100644 index 92e87ca..0000000 --- a/src/Number.php +++ /dev/null @@ -1,69 +0,0 @@ - tags - * @return string formatted number - */ - public static function ordinalIndicator($number, $superscript = false) - { - if (!in_array(($number % 100), [11, 12, 13])) - { - switch ($number % 10) - { - case 1: - $suffix = 'st'; - break; - - case 2: - $suffix = 'nd'; - break; - - case 3: - $suffix = 'rd'; - break; - - default: - $suffix = 'th'; - break; - } - } - - if ($superscript) - { - $suffix = '' . $suffix . ''; - } - - return $number . $suffix; - } -} - diff --git a/tests/Pickles/NumberTest.php b/tests/Pickles/NumberTest.php deleted file mode 100644 index b54dbb4..0000000 --- a/tests/Pickles/NumberTest.php +++ /dev/null @@ -1,49 +0,0 @@ -assertEquals($b, Pickles\Number::ordinalIndicator($a)); - } - - public function providerOrginalIndicatorNoSuper() - { - return [ - [1, '1st'], - [2, '2nd'], - [3, '3rd'], - [4, '4th'], - [51, '51st'], - [52, '52nd'], - [53, '53rd'], - [54, '54th'], - ]; - } - - /** - * @dataProvider providerOrginalIndicatorSuper - */ - public function testOrdinalIndicatorSuper($a, $b) - { - $this->assertEquals($b, Pickles\Number::ordinalIndicator($a, true)); - } - - public function providerOrginalIndicatorSuper() - { - return [ - [1, '1st'], - [2, '2nd'], - [3, '3rd'], - [4, '4th'], - [51, '51st'], - [52, '52nd'], - [53, '53rd'], - [54, '54th'], - ]; - } -} - From ea28dbbc5e84c689a2399ab35b9eaf0af830386e Mon Sep 17 00:00:00 2001 From: Josh Sherman Date: Fri, 17 Oct 2014 07:15:06 -0400 Subject: [PATCH 120/129] Dropped file class All it contained was a method to remove a directory recursively. Moved files over to joshtronic/php-rmr (rm -r) --- src/File.php | 72 -------------------------------------- tests/Pickles/FileTest.php | 63 --------------------------------- 2 files changed, 135 deletions(-) delete mode 100644 src/File.php delete mode 100644 tests/Pickles/FileTest.php diff --git a/src/File.php b/src/File.php deleted file mode 100644 index 6f83c5d..0000000 --- a/src/File.php +++ /dev/null @@ -1,72 +0,0 @@ -assertFalse(file_exists($directory)); - } - - public function testMissingTrailingSlash() - { - $directory = '/tmp/pickles-fs/missing'; - - mkdir($directory, 0777, true); - touch('/tmp/pickles-fs/missing/slash'); - - Pickles\File::removeDirectory($directory); - - $this->assertFalse(file_exists($directory)); - } - - public function testRemoveFileNotDirectory() - { - $directory = '/tmp/pickles-fs/dir'; - $file = '/tmp/pickles-fs/dir/file'; - - mkdir($directory, 0777, true); - touch($file); - - Pickles\File::removeDirectory($file); - - $this->assertFalse(file_exists($file)); - - Pickles\File::removeDirectory($directory); - - $this->assertFalse(file_exists($directory)); - } -} - From ce45dc0dbe05f31630a50c7ebd87fdb35bc3e429 Mon Sep 17 00:00:00 2001 From: Josh Sherman Date: Fri, 17 Oct 2014 07:18:47 -0400 Subject: [PATCH 121/129] Dropped Sort class Moved it off to joshtronic/php-sort --- src/Sort.php | 70 -------------------------------------- tests/Pickles/SortTest.php | 61 --------------------------------- 2 files changed, 131 deletions(-) delete mode 100644 src/Sort.php delete mode 100644 tests/Pickles/SortTest.php diff --git a/src/Sort.php b/src/Sort.php deleted file mode 100644 index 6d37de6..0000000 --- a/src/Sort.php +++ /dev/null @@ -1,70 +0,0 @@ -' : '<') .' $b) ? -1 : 1; - ')); - - return true; - } -} - diff --git a/tests/Pickles/SortTest.php b/tests/Pickles/SortTest.php deleted file mode 100644 index ed916f2..0000000 --- a/tests/Pickles/SortTest.php +++ /dev/null @@ -1,61 +0,0 @@ - 'epsilon'], - ['name' => 'gamma'], - ['name' => 'alpha'], - ['name' => 'delta'], - ['name' => 'beta'], - ]; - - $sorted = [ - ['name' => 'alpha'], - ['name' => 'beta'], - ['name' => 'delta'], - ['name' => 'epsilon'], - ['name' => 'gamma'], - ]; - - Pickles\Sort::by('name', $shuffled); - - $this->assertEquals($sorted, $shuffled); - } - - public function testByNameDESC() - { - $shuffled = [ - ['name' => 'epsilon'], - ['name' => 'gamma'], - ['name' => 'alpha'], - ['name' => 'delta'], - ['name' => 'beta'], - ]; - - $sorted = [ - ['name' => 'gamma'], - ['name' => 'epsilon'], - ['name' => 'delta'], - ['name' => 'beta'], - ['name' => 'alpha'], - ]; - - Pickles\Sort::by('name', $shuffled, Pickles\Sort::DESC); - - $this->assertEquals($sorted, $shuffled); - } - - public function testMissingField() - { - $shuffled = [['foo' => 'bar', 'bar' => 'foo']]; - $sorted = [['foo' => 'bar', 'bar' => 'foo']]; - - Pickles\Sort::by('name', $shuffled); - - $this->assertEquals($sorted, $shuffled); - } -} - From ecb075b344410ade662f36d5dc308fb41f4d56e5 Mon Sep 17 00:00:00 2001 From: Josh Sherman Date: Fri, 17 Oct 2014 07:22:15 -0400 Subject: [PATCH 122/129] Dropped Convert class All it contained was an array to XML function. Moved off to joshtronic/php-array2xml --- src/Convert.php | 121 ---------------------------------- tests/Pickles/ConvertTest.php | 27 -------- 2 files changed, 148 deletions(-) delete mode 100644 src/Convert.php delete mode 100644 tests/Pickles/ConvertTest.php diff --git a/src/Convert.php b/src/Convert.php deleted file mode 100644 index b9caa97..0000000 --- a/src/Convert.php +++ /dev/null @@ -1,121 +0,0 @@ - [ - * 'child' => [ - * ['name' => 'Wendy Darling'], - * ['name' => 'John Darling'], - * ['name' => 'Michael Darling'], - * ], - * ]] - * - * Output XML = - * - * Wendy Darling - * John Darling - * Michael Darling - * - * - * @static - * @param array $array array to convert into XML - * @return string generated XML - */ - public static function arrayToXML($array, $format = false, $level = 0) - { - $xml = ''; - - if (is_array($array)) - { - foreach ($array as $node => $value) - { - // Checks if the value is an array - if (is_array($value)) - { - foreach ($value as $node2 => $value2) - { - if (is_array($value2)) - { - // Nest the value if the node is an integer - $new_value = (is_int($node2) ? $value2 : [$node2 => $value2]); - - $xml .= ($format ? str_repeat("\t", $level) : ''); - $xml .= '<' . $node . '>' . ($format ? "\n" : ''); - $xml .= self::arrayToXML($new_value, $format, $level + 1); - $xml .= ($format ? str_repeat("\t", $level) : ''); - $xml .= '' . ($format ? "\n" : ''); - } - else - { - if (is_int($node2)) - { - $node2 = $node; - } - - // Checks for special characters - if (htmlspecialchars($value2) != $value2) - { - $xml .= ($format ? str_repeat("\t", $level) : ''); - $xml .= '<' . $node2 . '>' . ($format ? "\n" : ''); - } - else - { - $xml .= ($format ? str_repeat("\t", $level) : ''); - $xml .= '<' . $node2 . '>' . $value2 . '' . ($format ? "\n" : ''); - } - } - } - } - else - { - // Checks for special characters - if (htmlspecialchars($value) != $value) - { - $xml .= ($format ? str_repeat("\t", $level) : ''); - $xml .= '<' . $node . '>' . ($format ? "\n" : ''); - } - else - { - $xml .= ($format ? str_repeat("\t", $level) : ''); - $xml .= '<' . $node . '>' . $value . '' . ($format ? "\n" : ''); - } - } - } - } - - return $xml; - } - - // }}} -} - diff --git a/tests/Pickles/ConvertTest.php b/tests/Pickles/ConvertTest.php deleted file mode 100644 index 36314a6..0000000 --- a/tests/Pickles/ConvertTest.php +++ /dev/null @@ -1,27 +0,0 @@ -assertEquals(Pickles\Convert::arrayToXML($a, $b), $c); - } - - public function providerArrayToXML() - { - return [ - ['foo', false, ''], - [['foo', 'bar'], false, '<0>foo<1>bar'], - [['foo', 'bar'], true, "<0>foo\n<1>bar\n"], - [['foo' => 'bar'], false, 'bar'], - [['foo' => 'b & r'], false, ''], - [['children' => ['child' => ['foo', 'bar']]], false, 'foobar'], - [['children' => ['child' => ['foo & bar']]], false, ''], - [['children' => ['child' => ['foo', 'bar']]], true, "\n\tfoo\n\tbar\n\n"], - ]; - } -} - From 91579b3be422887f14a2b6b343525f52fa3e26e0 Mon Sep 17 00:00:00 2001 From: Josh Sherman Date: Fri, 17 Oct 2014 07:26:29 -0400 Subject: [PATCH 123/129] Dropped Distance class Moved off to joshtronic/php-distance --- src/Distance.php | 142 --------------------------------- tests/Pickles/DistanceTest.php | 75 ----------------- 2 files changed, 217 deletions(-) delete mode 100644 src/Distance.php delete mode 100644 tests/Pickles/DistanceTest.php diff --git a/src/Distance.php b/src/Distance.php deleted file mode 100644 index 4bb841a..0000000 --- a/src/Distance.php +++ /dev/null @@ -1,142 +0,0 @@ -assertEquals(0.621371, Pickles\Distance::kilometersToMiles(1)); - } - - public function testConvertKilometersToMeters() - { - $this->assertEquals(1000, Pickles\Distance::kilometersToMeters(1)); - } - - public function testConvertKilometersToYards() - { - $this->assertEquals(1093.61, Pickles\Distance::kilometersToYards(1)); - } - - public function testConvertMilesToKilometers() - { - $this->assertEquals(1.60934, Pickles\Distance::milesToKilometers(1)); - } - - public function testConvertMilesToMeters() - { - $this->assertEquals(1609.34, Pickles\Distance::milesToMeters(1)); - } - - public function testConvertMilesToYards() - { - $this->assertEquals(1760, Pickles\Distance::milesToYards(1)); - } - - public function testConvertMetersToKilometers() - { - $this->assertEquals(0.001, Pickles\Distance::metersToKilometers(1)); - } - - public function testConvertMetersToMiles() - { - $this->assertEquals(0.000621371, Pickles\Distance::metersToMiles(1)); - } - - public function testConvertMetersToYards() - { - $this->assertEquals(1.09361, Pickles\Distance::metersToYards(1)); - } - - public function testCalculateDistanceMiles() - { - $this->assertEquals(1003.2646776326, Pickles\Distance::calculateDistance(27.947222, -82.458611, 40.67, -73.94)); - } - - public function testCalculateDistanceKilometers() - { - $this->assertEquals(1614.5939763012, Pickles\Distance::calculateDistance(27.947222, -82.458611, 40.67, -73.94, 'kilometers')); - } - - public function testCalculateDistanceMeters() - { - $this->assertEquals(1614593.9763012, Pickles\Distance::calculateDistance(27.947222, -82.458611, 40.67, -73.94, 'meters'), '', 0.2); - } - - public function testCalculateDistanceYards() - { - $this->assertEquals(1765745.8326334, Pickles\Distance::calculateDistance(27.947222, -82.458611, 40.67, -73.94, 'yards'), '', 0.2); - } - - public function testNotEnoughUnits() - { - $this->assertFalse(Pickles\Distance::milesTo(123)); - } -} - From 76611eb7da9976bd79544b53bd798c5601e58179 Mon Sep 17 00:00:00 2001 From: Josh Sherman Date: Sat, 18 Oct 2014 12:54:33 -0400 Subject: [PATCH 124/129] Bye bye Model. --- src/Model.php | 1559 ------------------------------------------------- 1 file changed, 1559 deletions(-) delete mode 100644 src/Model.php diff --git a/src/Model.php b/src/Model.php deleted file mode 100644 index 037427e..0000000 --- a/src/Model.php +++ /dev/null @@ -1,1559 +0,0 @@ -table == false) - { - throw new \Exception('You must set the table variable.'); - } - - // Runs the parent constructor so we have the config - parent::__construct(['cache', 'db']); - - // Interrogates our database object - $this->use_cache = $this->db->cache; - $this->mysql = ($this->db->driver == 'pdo_mysql'); - $this->postgresql = ($this->db->driver == 'pdo_pgsql'); - - // Grabs the class name to use in our cache keys - $this->model = get_class($this); - - // Default column mapping - $columns = [ - 'id' => 'id', - 'created_at' => 'created_at', - 'created_id' => 'created_id', - 'updated_at' => 'updated_at', - 'updated_id' => 'updated_id', - 'deleted_at' => 'deleted_at', - 'deleted_id' => 'deleted_id', - 'is_deleted' => 'is_deleted', - ]; - - // Sets all but the `id` column to false - if ($this->columns === false) - { - foreach ($columns as $column => $field) - { - if ($column != 'id') - { - $columns[$column] = false; - } - } - } - // Merges the model's columns with the defaults - elseif (is_array($this->columns)) - { - foreach ($this->columns as $column => $field) - { - $columns[$column] = $field; - } - } - - $this->columns = $columns; - - // Takes a snapshot of the [non-object] object properties - foreach ($this as $variable => $value) - { - if (!in_array($variable, ['db', 'cache', 'config', 'snapshot'])) - { - $this->snapshot[$variable] = $value; - } - } - - return $this->execute($type_or_parameters, $parameters, $passed_key); - } - - // }}} - // {{{ Database Execution Methods - - /** - * Execute - * - * Potentially populates the record set from the passed arguments. - * - * @param mixed $type_or_parameters optional type of query or parameters - * @param mixed $parameter_or_key optional data to create query or cache key - * @param string $passed_key optional key to use for caching - */ - public function execute($type_or_parameters = null, $parameters_or_key = null, $passed_key = null) - { - // Resets internal properties - foreach ($this->snapshot as $variable => $value) - { - $this->$variable = $value; - } - - // Builds out the query - if ($type_or_parameters != null) - { - // Loads the parameters into the object - if (is_array($type_or_parameters)) - { - if (is_array($parameters_or_key)) - { - throw new \Exception('You cannot pass in 2 query parameter arrays.'); - } - - $this->prepareParameters($type_or_parameters); - - if ($this->use_cache - && isset($type_or_parameters['conditions'][$this->columns['id']]) - && count($type_or_parameters) == 1 - && count($type_or_parameters['conditions']) == 1) - { - $cache_keys = []; - $sorted_records = []; - - if (!is_array($type_or_parameters['conditions'][$this->columns['id']])) - { - $type_or_parameters['conditions'][$this->columns['id']] = [$type_or_parameters['conditions'][$this->columns['id']]]; - } - - foreach ($type_or_parameters['conditions'][$this->columns['id']] as $id) - { - $cache_keys[] = strtoupper($this->model) . '-' . $id; - $sorted_records[$id] = true; - } - - $cached = $this->cache->get($cache_keys); - $partial_cache = []; - - if ($cached !== false) - { - foreach ($cached as $record) - { - $partial_cache[$record['id']] = $record; - } - } - - unset($cached); - - foreach ($type_or_parameters['conditions'][$this->columns['id']] as $key => $id) - { - if (isset($partial_cache[$id])) - { - unset($type_or_parameters['conditions'][$this->columns['id']][$key]); - } - } - - if (count($type_or_parameters['conditions'][$this->columns['id']]) == 0) - { - $cache_key = true; - $cached = array_values($partial_cache); - } - } - - if ($this->columns['is_deleted']) - { - $type_or_parameters['conditions'][$this->columns['is_deleted']] = '0'; - } - - $this->loadParameters($type_or_parameters); - } - elseif (is_array($parameters_or_key)) - { - $this->prepareParameters($parameters_or_key); - - // @todo Fix cache merging to allow for this - /* - if ($this->use_cache - && isset($parameters_or_key['conditions'][$this->columns['id']]) - && count($parameters_or_key) == 1 - && count($parameters_or_key['conditions']) == 1) - { - $cache_keys = []; - $sorted_records = []; - - foreach ($parameters_or_key['conditions'][$this->columns['id']] as $id) - { - $cache_keys[] = strtoupper($this->model) . '-' . $id; - $sorted_records[$id] = true; - } - - $cached = $this->cache->get($cache_keys); - $partial_cache = []; - - if ($cached !== false) - { - foreach ($cached as $record) - { - $partial_cache[$record['id']] = $record; - } - } - - unset($cached); - - foreach ($parameters_or_key['conditions'][$this->columns['id']] as $key => $id) - { - if (isset($partial_cache[$id])) - { - unset($parameters_or_key['conditions'][$this->columns['id']][$key]); - } - } - - if (count($parameters_or_key['conditions'][$this->columns['id']]) == 0) - { - $cache_key = true; - $cached = array_values($partial_cache); - } - } - */ - - if ($this->columns['is_deleted']) - { - $parameters_or_key['conditions'][$this->columns['is_deleted']] = '0'; - } - - $this->loadParameters($parameters_or_key); - } - elseif (ctype_digit((string)$type_or_parameters)) - { - $cache_key = strtoupper($this->model) . '-' . $type_or_parameters; - $parameters_or_key = [$this->columns['id'] => $type_or_parameters]; - - if ($this->columns['is_deleted']) - { - $parameters_or_key[$this->columns['is_deleted']] = '0'; - } - - $this->loadParameters($parameters_or_key); - } - elseif (ctype_digit((string)$parameters_or_key)) - { - // @todo Fix cache merging to allow for this - //$cache_key = strtoupper($this->model) . '-' . $parameters_or_key; - - $parameters_or_key = [$this->columns['id'] => $parameters_or_key]; - - if ($this->columns['is_deleted']) - { - $parameters_or_key[$this->columns['is_deleted']] = '0'; - } - - $this->loadParameters($parameters_or_key); - } - elseif ($this->columns['is_deleted']) - { - $this->loadParameters([$this->columns['is_deleted'] => '0']); - } - - if (is_string($passed_key)) - { - $cache_key = $passed_key; - } - - // Starts with a basic SELECT ... FROM - $this->sql = [ - 'SELECT ' . (is_array($this->fields) ? implode(', ', $this->fields) : $this->fields), - 'FROM ' . $this->table, - ]; - - // Updates query to use COUNT syntax - if ($type_or_parameters == 'count') - { - $this->sql[0] = 'SELECT COUNT(*) AS count'; - $this->generateQuery(); - } - // Adds the rest of the query - elseif (!isset($cache_key) || $cache_key !== true) - { - $this->generateQuery(); - } - - if (isset($cache_key) && $this->use_cache && !isset($cached)) - { - $cached = $this->cache->get($cache_key); - } - - if (isset($cached) && $cached !== false) - { - $this->records = $cached; - } - else - { - $this->records = $this->db->fetch( - implode(' ', $this->sql), - (count($this->input_parameters) == 0 ? null : $this->input_parameters) - ); - - if (isset($partial_cache) && count($this->records)) - { - $records = array_merge($partial_cache, $this->records); - - if (isset($sorted_records)) - { - foreach ($records as $record) - { - $sorted_records[$record['id']] = $record; - } - - $records = $sorted_records; - } - - $this->records = $records; - } - - if ($this->use_cache) - { - if (isset($cache_key)) - { - if ($passed_key) - { - $cache_value = $this->records; - } - elseif (isset($this->records[0])) - { - $cache_value = $this->records[0]; - } - - // Only set the value for non-empty records. Caching - // values that are empty could be caused by querying - // records that don't exist at the moment, but could - // exist in the future. INSERTs do not do any sort of - // cache invalidation at this time. - if (isset($cache_value)) - { - $this->cache->set($cache_key, $cache_value); - } - } - elseif (isset($cache_keys)) - { - // @todo Move to Memcached extension and switch to use setMulti() - foreach ($this->records as $record) - { - if (isset($record['id'])) - { - $this->cache->set(strtoupper($this->model) . '-' . $record['id'], $record); - } - } - } - } - } - - $index_records = in_array($type_or_parameters, ['list', 'indexed']); - - // Flattens the data into a list - if ($index_records == true) - { - $list = []; - - foreach ($this->records as $record) - { - // Uses the first value as the key and the second as the value - if ($type_or_parameters == 'list') - { - $list[array_shift($record)] = array_shift($record); - } - // Uses the first value as the key - else - { - $list[current($record)] = $record; - } - } - - $this->records = $list; - } - - // Sets up the current record - if (isset($this->records[0])) - { - $this->record = $this->records[0]; - } - else - { - if ($index_records == true) - { - $this->record[key($this->records)] = current($this->records); - } - else - { - $this->record = $this->records; - } - } - - if (!preg_match('/^[0-9]+$/', implode('', array_keys($this->records)))) - { - $this->records = [$this->records]; - } - - $this->index = 0; - $this->original = $this->records; - } - - return true; - } - - // }}} - // {{{ SQL Generation Methods - - /** - * Generate Query - * - * Goes through all of the object variables that correspond with parts of - * the query and adds them to the master SQL array. - * - * @return array $this->sql an array of SQL parts - */ - public function generateQuery() - { - // Adds the WHERE conditionals - if ($this->conditions != false) - { - $use_id = true; - - foreach ($this->conditions as $column => $value) - { - if (!is_int($column)) - { - $use_id = false; - } - } - - if ($use_id) - { - $this->conditions = [$this->columns['id'] => $this->conditions]; - } - - $this->sql[] = 'WHERE ' . (is_array($this->conditions) ? $this->generateConditions($this->conditions) : $this->conditions); - } - - // Adds the GROUP BY syntax - if ($this->group != false) - { - $this->sql[] = 'GROUP BY ' . (is_array($this->group) ? implode(', ', $this->group) : $this->group); - } - - // Adds the HAVING conditions - if ($this->having != false) - { - $this->sql[] = 'HAVING ' . (is_array($this->having) ? $this->generateConditions($this->having) : $this->having); - } - - // Adds the ORDER BY syntax - if ($this->order != false) - { - $this->sql[] = 'ORDER BY ' . (is_array($this->order) ? implode(', ', $this->order) : $this->order); - } - - // Adds the LIMIT syntax - if ($this->limit != false) - { - $this->sql[] = 'LIMIT ' . (is_array($this->limit) ? implode(', ', $this->limit) : $this->limit); - } - - // Adds the OFFSET syntax - if ($this->offset != false) - { - $this->sql[] = 'OFFSET ' . $this->offset; - } - - return $this->sql; - } - - /** - * Generate Conditions - * - * Generates the conditional blocks of SQL from the passed array of - * conditions. Supports as much as I could remember to implement. This - * method is utilized by both the WHERE and HAVING clauses. - * - * @param array $conditions array of potentially nested conditions - * @param boolean $inject_values whether or not to use input parameters - * @param string $conditional syntax to use between conditions - * @return string $sql generated SQL for the conditions - */ - public function generateConditions($conditions, $inject_values = false, $conditional = 'AND') - { - $sql = ''; - - foreach ($conditions as $key => $value) - { - $key = trim($key); - - if ($sql != '') - { - if (preg_match('/^(AND|&&|OR|\|\||XOR)( NOT)?/i', $key)) - { - $sql .= ' '; - } - else - { - $sql .= ' ' . $conditional . ' '; - } - } - - // Checks for our keywords to control the flow - $operator = preg_match('/(<|<=|=|>=|>|!=|!|<>| LIKE)$/i', $key); - $between = preg_match('/ BETWEEN$/i', $key); - $is_is_not = preg_match('/( IS| IS NOT)$/i', $key); - - // Checks for boolean and null - $is_true = ($value === true); - $is_false = ($value === false); - $is_null = ($value === null); - - // Generates an in statement - if (is_array($value) && $between == false) - { - $sql .= $key . ' in ('; - - if ($inject_values == true) - { - $sql .= implode(', ', $value); - } - else - { - $sql .= implode(', ', array_fill(1, count($value), '?')); - $this->input_parameters = array_merge($this->input_parameters, $value); - } - - $sql .= ')'; - } - else - { - // If the key is numeric it wasn't set, so don't use it - if (is_numeric($key)) - { - $sql .= $value; - } - else - { - // Omits the operator as the operator is there - if ($operator == true || $is_is_not == true) - { - if ($is_true || $is_false || $is_null) - { - // Scrubs the operator if someone doesn't use IS / IS NOT - if ($operator == true) - { - $key = preg_replace('/ ?(!=|!|<>)$/i', ' IS NOT', $key); - $key = preg_replace('/ ?(<|<=|=|>=| LIKE)$/i', ' IS', $key); - } - - $sql .= $key . ' '; - - if ($is_true) - { - $sql .= 'TRUE'; - } - elseif ($is_false) - { - $sql .= 'FALSE'; - } - else - { - $sql .= 'NULL'; - } - } - else - { - $sql .= $key . ' '; - - if ($inject_values == true) - { - $sql .= $value; - } - else - { - $sql .= '?'; - $this->input_parameters[] = $value; - } - } - } - // Generates a between statement - elseif ($between == true) - { - if (is_array($value)) - { - // Checks the number of values, between expects 2 - if (count($value) != 2) - { - throw new \Exception('BETWEEN expects an array with 2 values.'); - } - else - { - $sql .= $key . ' '; - - if ($inject_values == true) - { - $sql .= $value[0] . ' AND ' . $value[1]; - } - else - { - $sql .= '? AND ?'; - $this->input_parameters = array_merge($this->input_parameters, $value); - } - } - } - else - { - throw new \Exception('BETWEEN expects an array.'); - } - } - else - { - $sql .= $key . ' '; - - // Checks if we're working with constants - if ($is_true) - { - $sql .= 'IS TRUE'; - } - elseif ($is_false) - { - $sql .= 'IS FALSE'; - } - elseif ($is_null) - { - $sql .= 'IS NULL'; - } - else - { - if ($inject_values == true) - { - $sql .= '= ' . $value; - } - else - { - $sql .= '= ?'; - $this->input_parameters[] = $value; - } - } - } - } - } - } - - return $sql; - } - - // }}} - // {{{ Record Interaction Methods - - /** - * Count Records - * - * Counts the records - */ - public function count() - { - if (isset($this->records[0]) && $this->records[0] == []) - { - return 0; - } - else - { - return count($this->records); - } - } - - /** - * Sort Records - * - * Sorts the records by the specified index in the specified order. - * - * @param string $index the index to be sorted on - * @param string $order the direction to order - * @return boolean true - * @todo Implement this method - */ - public function sort($index, $order = 'asc') - { - return true; - } - - /** - * Shuffle Records - * - * Sorts the records in a pseudo-random order. - * - * @return boolean true - * @todo Implement this method - */ - public function shuffle() - { - return true; - } - - /** - * Next Record - * - * Increment the record array to the next member of the record set. - * - * @return boolean whether or not there was next element - */ - public function next() - { - $return = (boolean)($this->record = next($this->records)); - - if ($return == true) - { - $this->index++; - } - - return $return; - } - - /** - * Previous Record - * - * Decrement the record array to the next member of the record set. - * - * @return boolean whether or not there was previous element - */ - public function prev() - { - $return = (boolean)($this->record = prev($this->records)); - - if ($return == true) - { - $this->index--; - } - - return $return; - } - - /** - * Reset Record - * - * Set the pointer to the first element of the record set. - * - * @return boolean whether or not records is an array (and could be reset) - */ - public function reset() - { - $return = (boolean)($this->record = reset($this->records)); - - if ($return == true) - { - $this->index = 0; - } - - return $return; - } - - /** - * First Record - * - * Alias of reset(). "first" is more intuitive to me, but reset stays in - * line with the built in PHP functions. Not sure why I'd want to add some - * consistency to one of the most inconsistent languages. - * - * @return boolean whether or not records is an array (and could be reset) - */ - public function first() - { - return $this->reset(); - } - - /** - * End Record - * - * Set the pointer to the last element of the record set. - * - * @return boolean whether or not records is an array (and end() worked) - */ - public function end() - { - $return = (boolean)($this->record = end($this->records)); - - if ($return == true) - { - $this->index = $this->count() - 1; - } - - return $return; - } - - /** - * Last record - * - * Alias of end(). "last" is more intuitive to me, but end stays in line - * with the built in PHP functions. - * - * @return boolean whether or not records is an array (and end() worked) - */ - public function last() - { - return $this->end(); - } - - /** - * Walk Records - * - * Returns the current record and advances to the next. Built to allow for - * simplified code when looping through a record set. - * - * @return mixed either an array of the current record or false - * @todo does not currently support "indexed" or "list" return types - */ - public function walk() - { - // checks if we should start iterating, solves off by one issues with next() - if ($this->iterate == false) - { - $this->iterate = true; - - // resets the records, saves calling reset() when walking multiple times - $this->reset(); - } - else - { - $this->next(); - } - - return $this->record; - } - - /** - * Queue Record - * - * Stashes the current record and creates an empty record ready to be - * manipulated. Eliminates looping through records and inserting each one - * separately and/or the need for helper methods in the models. - */ - public function queue() - { - $this->commit_type = 'queue'; - $this->records[] = $this->record; - $this->record = null; - } - - // }}} - // {{{ Record Manipulation Methods - - /** - * Commit - * - * INSERTs or UPDATEs a record in the database. - * - * @return boolean results of the query - */ - public function commit() - { - // Multiple row query / queries - if ($this->commit_type == 'queue') - { - $update = false; - $cache_keys = []; - - /** - * @todo I outta loop through twice to determine if it's an INSERT - * or an UPDATE. As it stands, you could run into a scenario - * where you could have a mixed lot that would attempt to - * build out a query with both INSERT and UPDATE syntax and - * would probably cause a doomsday scenario for our universe. - * @todo Doesn't play nice with ->walk() at all. Ends up stuck in - * an infinite loop and never executes. Could be part of the - * aforementioned doomsday scenario and fortunately PHP isn't - * letting it happen thanks to memory constraints. - */ - foreach ($this->records as $record) - { - // Performs an UPDATE with multiple queries - if (array_key_exists($this->columns['id'], $record)) - { - $update = true; - - if (!isset($sql)) - { - $sql = ''; - $input_parameters = []; - } - - $update_fields = []; - - foreach ($record as $field => $value) - { - if ($field != $this->columns['id']) - { - $update_fields[] = $field . ' = ?'; - $input_parameters[] = (is_array($value) ? json_encode($value) : $value); - } - else - { - $cache_keys[] = strtoupper($this->model) . '-' . $value; - } - } - - // @todo Check if the column was passed in - if ($this->columns['updated_at'] != false) - { - $update_fields[] = $this->columns['updated_at'] . ' = ?'; - $input_parameters[] = Time::timestamp(); - } - - // @todo Check if the column was passed in - if ($this->columns['updated_id'] != false && isset($_SESSION['__pickles']['security']['user_id'])) - { - $update_fields[] = $this->columns['updated_id'] . ' = ?'; - $input_parameters[] = $_SESSION['__pickles']['security']['user_id']; - } - - if ($sql != '') - { - $sql .= '; '; - } - - $sql .= 'UPDATE ' . $this->table - . ' SET ' . implode(', ', $update_fields) - . ' WHERE ' . $this->columns['id'] . ' = ?'; - - $input_parameters[] = $record[$this->columns['id']]; - } - // Performs a multiple row INSERT - else - { - if (!isset($sql)) - { - $field_count = count($record); - $insert_fields = array_keys($record); - - if ($this->columns['created_at'] != false) - { - $insert_fields[] = $this->columns['created_at']; - $field_count++; - } - - if ($this->columns['created_id'] != false && isset($_SESSION['__pickles']['security']['user_id'])) - { - $insert_fields[] = $this->columns['created_id']; - $field_count++; - } - - $values = '(' . implode(', ', array_fill(0, $field_count, '?')) . ')'; - $input_parameters = []; - - // INSERT INTO ... - $sql = 'INSERT INTO ' . $this->table . ' (' . implode(', ', $insert_fields) . ') VALUES ' . $values; - } - else - { - $sql .= ', ' . $values; - } - - foreach ($record as $variable => $value) - { - $input_parameters[] = (is_array($value) ? json_encode($value) : $value); - } - - // @todo Check if the column was passed in - if ($this->columns['created_at'] != false) - { - $input_parameters[] = Time::timestamp(); - } - - // @todo Check if the column was passed in - if ($this->columns['created_id'] != false && isset($_SESSION['__pickles']['security']['user_id'])) - { - $input_parameters[] = $_SESSION['__pickles']['security']['user_id']; - } - } - } - - $results = $this->db->execute($sql . ';', $input_parameters); - - // Clears the cache - if ($update && $this->use_cache) - { - $this->cache->delete($cache_keys); - } - - return $results; - } - // Single row INSERT or UPDATE - else - { - // Determines if it's an UPDATE or INSERT - $update = (isset($this->record[$this->columns['id']]) && trim($this->record[$this->columns['id']]) != ''); - - // Starts to build the query, optionally sets PRIORITY, DELAYED and IGNORE syntax - if ($this->replace === true && $this->mysql) - { - $sql = 'REPLACE'; - - if (strtoupper($this->priority) == 'LOW') - { - $sql .= ' LOW_PRIORITY'; - } - elseif ($this->delayed == true) - { - $sql .= ' DELAYED'; - } - - $sql .= ' INTO ' . $this->table; - } - else - { - if ($update == true) - { - $sql = 'UPDATE'; - } - else - { - $sql = 'INSERT'; - - // priority syntax takes priority over delayed - if ($this->mysql) - { - if ($this->priority !== false - && in_array(strtoupper($this->priority), ['LOW', 'HIGH'])) - { - $sql .= ' ' . strtoupper($this->priority) . '_PRIORITY'; - } - elseif ($this->delayed == true) - { - $sql .= ' DELAYED'; - } - - if ($this->ignore == true) - { - $sql .= ' IGNORE'; - } - } - - $sql .= ' INTO'; - } - - $sql .= ' ' . $this->table . ($update ? ' SET ' : ' '); - } - - $input_parameters = null; - - // Limits the columns being updated - $record = ($update ? array_diff_assoc( - $this->record, - isset($this->original[$this->index]) ? $this->original[$this->index] : [] - ) : $this->record); - - // Makes sure there's something to INSERT or UPDATE - if (count($record) > 0) - { - if ($this->replace && $update) - { - $update = false; - } - - $insert_fields = []; - - // Loops through all the columns and assembles the query - foreach ($record as $column => $value) - { - if ($column != $this->columns['id']) - { - if ($update == true) - { - if ($input_parameters != null) - { - $sql .= ', '; - } - - $sql .= $column . ' = '; - - if (in_array($value, ['++', '--'])) - { - $sql .= $column . ' ' . substr($value, 0, 1) . ' ?'; - $value = 1; - } - else - { - $sql .= '?'; - } - } - else - { - $insert_fields[] = $column; - } - - $input_parameters[] = (is_array($value) ? json_encode($value) : $value); - } - } - - // If it's an UPDATE tack on the ID - if ($update == true) - { - if ($this->columns['updated_at'] != false) - { - if ($input_parameters != null) - { - $sql .= ', '; - } - - $sql .= $this->columns['updated_at'] . ' = ?'; - $input_parameters[] = Time::timestamp(); - } - - if ($this->columns['updated_id'] != false && isset($_SESSION['__pickles']['security']['user_id'])) - { - if ($input_parameters != null) - { - $sql .= ', '; - } - - $sql .= $this->columns['updated_id'] . ' = ?'; - - $input_parameters[] = $_SESSION['__pickles']['security']['user_id']; - } - - $sql .= ' WHERE ' . $this->columns['id'] . ' = ?' . ($this->mysql ? ' LIMIT 1' : '') . ';'; - $input_parameters[] = $this->record[$this->columns['id']]; - } - else - { - // @todo REPLACE should be grabbing the previous values so - // that we're not wiping out pertinent data when the - // internal columns are in use. This includes the - // `id` column that is needed to keep it from doing - // an INSERT instead of an UPDATE - if ($this->columns['created_at'] != false || $this->replace) - { - $insert_fields[] = $this->columns['created_at']; - $input_parameters[] = Time::timestamp(); - } - - if ($this->columns['created_id'] != false && isset($_SESSION['__pickles']['security']['user_id'])) - { - $insert_fields[] = $this->columns['created_id']; - $input_parameters[] = $_SESSION['__pickles']['security']['user_id']; - } - - $sql .= '(' . implode(', ', $insert_fields) . ') VALUES (' . implode(', ', array_fill(0, count($input_parameters), '?')) . ')'; - - // PDO::lastInsertID() doesn't work so we return the ID with the query - if ($this->postgresql) - { - $sql .= ' RETURNING ' . $this->columns['id']; - } - - $sql .= ';'; - } - - // Executes the query - if ($this->postgresql && $update == false) - { - return $this->db->fetch($sql, $input_parameters); - } - else - { - $results = $this->db->execute($sql, $input_parameters); - - // Clears the cache - if ($update && $this->use_cache) - { - $this->cache->delete(strtoupper($this->model) . '-' . $this->record[$this->columns['id']]); - } - - return $results; - } - } - } - - return false; - } - - /** - * Delete Record - * - * DELETEs the current record from the database. - * - * @return boolean status of the query - */ - public function delete() - { - if (isset($this->record[$this->columns['id']])) - { - // Logical deletion - if ($this->columns['is_deleted']) - { - $sql = 'UPDATE ' . $this->table . ' SET ' . $this->columns['is_deleted'] . ' = ?'; - $input_parameters = ['1']; - - if ($this->columns['deleted_at']) - { - $sql .= ', ' . $this->columns['deleted_at'] . ' = ?'; - $input_parameters[] = Time::timestamp(); - } - - if ($this->columns['deleted_id'] && isset($_SESSION['__pickles']['security']['user_id'])) - { - $sql .= ', ' . $this->columns['deleted_id'] . ' = ?'; - $input_parameters[] = $_SESSION['__pickles']['security']['user_id']; - } - - $sql .= ' WHERE ' . $this->columns['id'] . ' = ?'; - } - // For reals deletion - else - { - $sql = 'DELETE FROM ' . $this->table . ' WHERE ' . $this->columns['id'] . ' = ?' . ($this->mysql ? ' LIMIT 1' : '') . ';'; - } - - $input_parameters[] = $this->record[$this->columns['id']]; - $results = $this->db->execute($sql, $input_parameters); - - // Clears the cache - if ($this->use_cache) - { - $this->cache->delete(strtoupper($this->model) . '-' . $this->record[$this->columns['id']]); - } - - return $results; - } - else - { - return false; - } - } - - // }}} - // {{{ Utility Methods - - /** - * Prepare Parameters - * - * Checks if the parameters array is only integers and reconstructs the - * array with the proper conditions format. - * - * @param array $array parameters array, passed by reference - */ - public function prepareParameters(&$parameters) - { - $all_integers = true; - - foreach ($parameters as $key => $value) - { - if (!ctype_digit((string)$key) || !ctype_digit((string)$value)) - { - $all_integers = false; - } - } - - if ($all_integers) - { - $parameters = ['conditions' => [$this->columns['id'] => $parameters]]; - } - } - - /** - * Load Parameters - * - * Loads the passed parameters back into the object. - * - * @param array $parameters key / value list - * @param boolean whether or not the parameters were loaded - */ - public function loadParameters($parameters) - { - if (is_array($parameters)) - { - $conditions = true; - - // Adds the parameters to the object - foreach ($parameters as $key => $value) - { - // Clean up the variable just in case - $key = trim(strtolower($key)); - - // Assigns valid keys to the appropriate class property - if (in_array($key, ['fields', 'table', 'conditions', 'group', 'having', 'order', 'limit', 'offset'])) - { - $this->$key = $value; - $conditions = false; - } - } - - // If no valid properties were found, assume it's the conditionals - if ($conditions == true) - { - $this->conditions = $parameters; - } - - return true; - } - - return false; - } - - /** - * Field Values - * - * Pulls the value from a single field and returns an array without any - * duplicates. Perfect for extracting foreign keys to use in later queries. - * - * @param string $field field we want the values for - * @return array values for the passed field - */ - public function fieldvalues($field) - { - $values = []; - - foreach ($this->records as $record) - { - $values[] = $record[$field]; - } - - return array_unique($values); - } - - // }}} -} - From 6414644f35de32ffc411680a651759e95f183fc1 Mon Sep 17 00:00:00 2001 From: Josh Sherman Date: Sun, 19 Oct 2014 09:16:11 -0400 Subject: [PATCH 125/129] Shifting to that NoSQL life Dropped the database abstraction layer, added a rudimentary MongoDB class, dopped all of the interface classes for the OAuth2 library and updated the dependencies to use @bshaffer's OAuth2 class as it has MongoDB support out of the box. --- composer.json | 2 +- src/Cache.php | 221 ---------- src/Config.php | 4 +- src/Database.php | 393 ------------------ src/Mongo.php | 50 +++ src/OAuth2/AccessTokenStorage.php | 84 ---- src/OAuth2/ClientStorage.php | 81 ---- src/OAuth2/RefreshTokenStorage.php | 56 --- src/OAuth2/ScopeStorage.php | 26 -- src/OAuth2/SessionStorage.php | 106 ----- src/OAuth2/StorageAdapter.php | 20 - src/Object.php | 34 +- src/Resource.php | 8 +- tests/Pickles/CacheTest.php | 102 ----- tests/Pickles/DatabaseTest.php | 526 ------------------------ tests/Pickles/ModelTest.php | 633 ----------------------------- 16 files changed, 67 insertions(+), 2279 deletions(-) delete mode 100644 src/Cache.php delete mode 100644 src/Database.php create mode 100644 src/Mongo.php delete mode 100644 src/OAuth2/AccessTokenStorage.php delete mode 100644 src/OAuth2/ClientStorage.php delete mode 100644 src/OAuth2/RefreshTokenStorage.php delete mode 100644 src/OAuth2/ScopeStorage.php delete mode 100644 src/OAuth2/SessionStorage.php delete mode 100644 src/OAuth2/StorageAdapter.php delete mode 100644 tests/Pickles/CacheTest.php delete mode 100644 tests/Pickles/DatabaseTest.php delete mode 100644 tests/Pickles/ModelTest.php diff --git a/composer.json b/composer.json index 3495418..1204abf 100644 --- a/composer.json +++ b/composer.json @@ -23,7 +23,7 @@ }, "require": { "php": ">=5.4", - "league/oauth2-server": "4.0.x-dev" + "bshaffer/oauth2-server-php": "v1.5" }, "autoload": { "psr-4": { diff --git a/src/Cache.php b/src/Cache.php deleted file mode 100644 index d7c6c72..0000000 --- a/src/Cache.php +++ /dev/null @@ -1,221 +0,0 @@ -config['pickles']['cache']) && $this->config['pickles']['cache']) - { - $datasources = $this->config['pickles']['cache']; - - if (!is_array($datasources)) - { - $datasources = [$datasources]; - } - - $this->connection = new \Memcache(); - - foreach ($datasources as $name) - { - if (isset($this->config['datasources'][$name])) - { - $datasource = $this->config['datasources'][$name]; - - $this->connection->addServer($datasource['hostname'], $datasource['port']); - $this->servers++; - - if (isset($datasource['namespace'])) - { - $this->namespace = $datasource['namespace']; - } - } - } - } - - if ($this->namespace != '') - { - $this->namespace .= '-'; - } - } - - /** - * Destructor - * - * Closes the connection when the object dies. - */ - public function __destruct() - { - if ($this->servers) - { - $this->connection->close(); - } - } - - /** - * Get Instance - * - * Let's the parent class do all the work. - * - * @static - * @param string $class name of the class to instantiate - * @return object self::$instance instance of the Cache class - */ - public static function getInstance($class = 'Cache') - { - return parent::getInstance($class); - } - - /** - * Get Key - * - * Gets the value of the key(s) and returns it. - * - * @param mixed $keys key(s) to retrieve - * @return mixed value(s) of the requested key(s), false if not set - */ - public function get($keys) - { - if (is_array($keys)) - { - foreach ($keys as $index => $key) - { - $keys[$index] = strtoupper($this->namespace . $key); - } - } - else - { - $keys = strtoupper($this->namespace . $keys); - } - - return $this->connection->get($keys); - } - - /** - * Set Key - * - * Sets key to the specified value. I've found that compression can lead to - * issues with integers and can slow down the storage and retrieval of data - * (defeats the purpose of caching if you ask me) and isn't supported. I've - * also been burned by data inadvertantly being cached for infinity, but - * have had great success caching data for a full day, hence defaulting the - * expiration to a full day. - * - * @param string $key key to set - * @param mixed $value value to set - * @param integer $expiration optional expiration, defaults to 1 day - * @return boolean status of writing the data to the key - */ - public function set($key, $value, $expire = Time::DAY) - { - $key = strtoupper($key); - - return $this->connection->set(strtoupper($this->namespace . $key), $value, 0, $expire); - } - - /** - * Delete Key - * - * Deletes the specified key(s). - * - * @param mixed $keys key(s) to delete - * @return boolean status of deleting the key - */ - public function delete($keys) - { - if (!is_array($keys)) - { - $keys = [$keys]; - } - - // Memcache() doesn't let you pass an array to delete all records the same way you can with get() - foreach ($keys as $key) - { - $this->connection->delete(strtoupper($this->namespace . $key)); - } - - return true; - } - - /** - * Increment Key - * - * Increments the value of an existing key. - * - * @param string $key key to increment - * @return boolean status of incrementing the key - * @todo Check if it's set as Memcache() doesn't and won't inc if it doesn't exist - */ - public function increment($key) - { - return $this->connection->increment(strtoupper($this->namespace . $key)); - } -} - diff --git a/src/Config.php b/src/Config.php index 5ae4e79..1ef241e 100644 --- a/src/Config.php +++ b/src/Config.php @@ -138,9 +138,9 @@ class Config extends \ArrayObject // Defaults expected Pickles variables to false foreach (['auth', 'cache', 'profiler'] as $variable) { - if (!isset($config['pickles'][$variable])) + if (!isset($config[$variable])) { - $config['pickles'][$variable] = false; + $config[$variable] = false; } } diff --git a/src/Database.php b/src/Database.php deleted file mode 100644 index 1379ec8..0000000 --- a/src/Database.php +++ /dev/null @@ -1,393 +0,0 @@ - true, - \PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION, - \PDO::NULL_EMPTY_STRING => true, - ]; - - /** - * Driver - * - * @var string - */ - public $driver = null; - - /** - * Hostname for the server - * - * @var string - */ - public $hostname = 'localhost'; - - /** - * Port number for the server - * - * @var integer - */ - public $port = null; - - /** - * UNIX socket for the server - * - * @var integer - */ - public $socket = null; - - /** - * Username for the server - * - * @var string - */ - public $username = null; - - /** - * Password for the server - * - * @var string - */ - public $password = null; - - /** - * Database name for the server - * - * @var string - */ - public $database = null; - - /** - * Whether or not to use caching - * - * @var boolean - */ - public $cache = false; - - /** - * Connection resource - * - * @var object - */ - public $connection = null; - - /** - * Results object for the executed statement - * - * @var object - */ - public $results = null; - - /** - * Get Instance - * - * Instantiates a new instance of the Database class or returns the - * previously instantiated copy. - * - * @static - * @param string $datasource_name name of the datasource - * @return object instance of the class - */ - public static function getInstance($datasource_name = false) - { - $config = Config::getInstance(); - - // Tries to load a datasource if one wasn't specified - if (!$datasource_name) - { - if (isset($config['pickles']['datasource'])) - { - $datasource_name = $config['pickles']['datasource']; - } - elseif (is_array($config['datasources'])) - { - $datasources = $config['datasources']; - - foreach ($datasources as $name => $datasource) - { - if (isset($datasource['driver'])) - { - $datasource_name = $name; - } - } - } - } - - // Attempts to validate the datasource - if ($datasource_name) - { - if (!isset(self::$instances['Database'][$datasource_name])) - { - if (!isset($config['datasources'][$datasource_name])) - { - throw new \Exception('The specified datasource is not defined in the config.'); - } - - $datasource = $config['datasources'][$datasource_name]; - - if (!isset($datasource['driver'])) - { - throw new \Exception('The specified datasource lacks a driver.'); - } - - $datasource['driver'] = strtolower($datasource['driver']); - - // Checks the driver is legit and scrubs the name - switch ($datasource['driver']) - { - case 'pdo_mysql': - $attributes = [ - 'dsn' => 'mysql:host=[[hostname]];port=[[port]];unix_socket=[[socket]];dbname=[[database]]', - 'port' => 3306, - ]; - break; - - case 'pdo_pgsql': - $attributes = [ - 'dsn' => 'pgsql:host=[[hostname]];port=[[port]];dbname=[[database]];user=[[username]];password=[[password]]', - 'port' => 5432, - ]; - break; - - case 'pdo_sqlite': - $attributes = ['dsn' => 'sqlite:[[hostname]]']; - break; - - default: - throw new \Exception('Datasource driver "' . $datasource['driver'] . '" is invalid'); - break; - } - - // Instantiates our database class - $instance = new Database(); - - // Sets our database parameters - if (is_array($datasource)) - { - $datasource = array_merge($attributes, $datasource); - - foreach ($datasource as $variable => $value) - { - $instance->$variable = $value; - } - } - - // Caches the instance for possible reuse later - self::$instances['Database'][$datasource_name] = $instance; - } - - // Returns the instance - return self::$instances['Database'][$datasource_name]; - } - - return false; - } - - /** - * Opens database connection - * - * Establishes a connection to the database based on the set configuration - * options. - * - * @return boolean true on success, throws an exception overwise - */ - public function open() - { - if ($this->connection === null) - { - switch ($this->driver) - { - case 'pdo_mysql': - // Resolves "Invalid UTF-8 sequence" issues when encoding as JSON - // @todo Didn't resolve that issue, borked some other characters though - //$this->attributes[\PDO::MYSQL_ATTR_INIT_COMMAND] = 'SET NAMES utf8'; - break; - - case 'pdo_pgsql': - // This combats a bug: https://bugs.php.net/bug.php?id=62571&edit=1 - $this->attributes[\PDO::ATTR_PERSISTENT] = false; - - // This allows for multiple prepared queries - $this->attributes[\PDO::ATTR_EMULATE_PREPARES] = true; - break; - } - - if (isset($this->username, $this->password, $this->database)) - { - // Swaps out any variables with values in the DSN - $this->dsn = str_replace( - ['[[hostname]]', '[[port]]', '[[socket]]', '[[username]]', '[[password]]', '[[database]]'], - [$this->hostname, $this->port, $this->socket, $this->username, $this->password, $this->database], - $this->dsn - ); - - // Strips any empty parameters in the DSN - $this->dsn = str_replace(['host=;', 'port=;', 'unix_socket=;'], '', $this->dsn); - - // Attempts to establish a connection - $this->connection = new \PDO( - $this->dsn, $this->username, $this->password, $this->attributes - ); - } - else - { - throw new \Exception('There was an error loading the database configuration.'); - } - } - - return true; - } - - /** - * Closes database connection - * - * Sets the connection to null regardless of state. - * - * @return boolean always true - */ - public function close() - { - $this->connection = null; - return true; - } - - /** - * Executes an SQL Statement - * - * Executes a standard or prepared query based on passed parameters. All - * queries are logged to a file as well as timed and logged in the - * execution time is over 1 second. - * - * @param string $sql statement to execute - * @param array $input_parameters optional key/values to be bound - * @return integer ID of the last inserted row or sequence number - */ - public function execute($sql, $input_parameters = null, $explain = false) - { - $this->open(); - - $sql = trim($sql); - - // Checks if the query is blank - if ($sql != '') - { - // Establishes if we're working on an EXPLAIN - if ($this->config['pickles']['profiler']) - { - $explain_results = preg_match('/^SELECT /i', $sql); - } - else - { - $explain_results = false; - } - - // Executes a standard query - if ($input_parameters === null) - { - // Explains the query - if ($explain_results) - { - $explain_results = $this->fetch('EXPLAIN ' . $sql, null, true); - } - - $start_time = microtime(true); - $this->results = $this->connection->query($sql); - } - // Executes a prepared statement - else - { - // Explains the query - if ($explain_results) - { - $explain_results = $this->fetch('EXPLAIN ' . $sql, $input_parameters, true); - } - - $start_time = microtime(true); - $this->results = $this->connection->prepare($sql); - $this->results->execute($input_parameters); - } - - $end_time = microtime(true); - $duration = $end_time - $start_time; - - // Logs the information to the profiler - if ($this->config['pickles']['profiler'] && !$explain) - { - Profiler::query( - $sql, - $input_parameters, - $this->results->fetchAll(\PDO::FETCH_ASSOC), - $duration, - $explain_results - ); - } - } - else - { - throw new \Exception('No query to execute.'); - } - - return $this->connection->lastInsertId(); - } - - /** - * Fetch records from the database - * - * @param string $sql statement to be executed - * @param array $input_parameters optional key/values to be bound - * @param string $return_type optional type of return set - * @return mixed based on return type - */ - public function fetch($sql = null, $input_parameters = null, $explain = false) - { - $this->open(); - - if ($sql !== null) - { - $this->execute($sql, $input_parameters, $explain); - } - - // Pulls the results based on the type - $results = $this->results->fetchAll(\PDO::FETCH_ASSOC); - - return $results; - } -} - diff --git a/src/Mongo.php b/src/Mongo.php new file mode 100644 index 0000000..2ffcc50 --- /dev/null +++ b/src/Mongo.php @@ -0,0 +1,50 @@ +selectDB($mongo['database']); + + // Caches the instance for possible reuse later + self::$instances['Mongo'] = $instance; + } + + // Returns the instance + return self::$instances['Mongo']; + } +} + diff --git a/src/OAuth2/AccessTokenStorage.php b/src/OAuth2/AccessTokenStorage.php deleted file mode 100644 index 04d8dc0..0000000 --- a/src/OAuth2/AccessTokenStorage.php +++ /dev/null @@ -1,84 +0,0 @@ -= ?;'; - - $results = $this->db->fetch($sql, [$token, time()]); - - if (count($results) === 1) - { - return (new AccessTokenEntity($this->server)) - ->setId($results[0]['access_token']) - ->setExpireTime($results[0]['expires_at']); - } - - return null; - } - - public function getScopes(AbstractTokenEntity $token) - { - $sql = 'SELECT oauth_scopes.id, oauth_scopes.description' - . ' FROM oauth_access_token_scopes' - . ' INNER JOIN oauth_scopes' - . ' ON oauth_access_token_scopes.scope_id = oauth_scopes.id' - . ' WHERE oauth_access_token_scopes.access_token_id = ?;'; - - $results = $this->db->fetch($sql, [$token->getId()]); - $response = []; - - if (count($results) > 0) - { - foreach ($results as $row) - { - $response[] = (new ScopeEntity($this->server))->hydrate([ - 'id' => $row['id'], - 'description' => $row['description'] - ]); - } - } - - return $response; - } - - public function create($token, $expiration, $session_id) - { - $sql = 'INSERT INTO oauth_access_tokens' - . ' (access_token, session_id, expires_at)' - . ' VALUES' - . ' (?, ?, ?);'; - - $this->db->execute($sql, [$token, $session_id, $expiration]); - } - - public function associateScope(AbstractTokenEntity $token, ScopeEntity $scope) - { - $sql = 'INSERT INTO oauth_access_token_scopes' - . ' (access_token, scope)' - . ' VALUES' - . ' (?, ?);'; - - $this->db->execute($sql, [$token->getId(), $scope->getId()]); - } - - public function delete(AbstractTokenEntity $token) - { - $sql = 'DELETE FROM oauth_access_token_scopes' - . ' WHERE access_token = ?;'; - - $this->db->execute($sql, [$token->getId()]); - } -} - diff --git a/src/OAuth2/ClientStorage.php b/src/OAuth2/ClientStorage.php deleted file mode 100644 index 1a32583..0000000 --- a/src/OAuth2/ClientStorage.php +++ /dev/null @@ -1,81 +0,0 @@ -db->fetch($sql, $parameters); - - if (count($results) === 1) - { - $client = new ClientEntity($this->server); - - $client->hydrate([ - 'id' => $results[0]['id'], - 'name' => $results[0]['name'] - ]); - - return $client; - } - - return null; - } - - public function getBySession(SessionEntity $session) - { - $sql = 'SELECT oauth_clients.id, oauth_clients.name' - . ' FROM oauth_clients' - . ' INNER JOIN oauth_sessions' - . ' ON oauth_clients.id = oauth_sessions.client_id' - . ' WHERE oauth_sessions.id = ?'; - - $results = $this->db->fetch($sql, [$session->getId()]); - - if (count($results) === 1) - { - $client = new ClientEntity($this->server); - - $client->hydrate([ - 'id' => $results[0]['id'], - 'name' => $results[0]['name'] - ]); - - return $client; - } - - return null; - } -} - diff --git a/src/OAuth2/RefreshTokenStorage.php b/src/OAuth2/RefreshTokenStorage.php deleted file mode 100644 index bceea31..0000000 --- a/src/OAuth2/RefreshTokenStorage.php +++ /dev/null @@ -1,56 +0,0 @@ -= ?;'; - - $results = $this->db->fetch($sql, [$token, time()]); - - if (count($results) === 1) - { - return (new RefreshTokenEntity($this->server)) - ->setId($results[0]['refresh_token']) - ->setExpireTime($results[0]['expires_at']) - ->setAccessTokenId($results[0]['access_token_id']); - } - - return null; - } - - public function create($token, $expiration, $access_token) - { - $sql = 'SELECT id FROM oauth_access_tokens WHERE access_token = ?;'; - $results = $this->db->fetch($sql, [$access_token]); - $token_id = $results[0]['id']; - - $sql = 'INSERT INTO oauth_refresh_tokens' - . ' (refresh_token, access_token_id, expires_at, client_id)' - . ' VALUES' - . ' (?, ?, ?, ?);'; - - $this->db->execute($sql, [ - $token, - $token_id, - $expiration, - $this->server->getRequest()->request->get('client_id', null), - ]); - } - - public function delete(RefreshTokenEntity $token) - { - $sql = 'DELETE FROM oauth_refresh_tokens WHERE refresh_token = ?;'; - - $this->db->execute($sql, [$token->getId()]); - } -} - diff --git a/src/OAuth2/ScopeStorage.php b/src/OAuth2/ScopeStorage.php deleted file mode 100644 index c614eb7..0000000 --- a/src/OAuth2/ScopeStorage.php +++ /dev/null @@ -1,26 +0,0 @@ -db->fetch($sql, [$scope]); - - if (count($results) === 0) - { - return null; - } - - return (new ScopeEntity($this->server))->hydrate([ - 'id' => $result[0]['id'], - 'description' => $result[0]['description'], - ]); - } -} - diff --git a/src/OAuth2/SessionStorage.php b/src/OAuth2/SessionStorage.php deleted file mode 100644 index 2754b1d..0000000 --- a/src/OAuth2/SessionStorage.php +++ /dev/null @@ -1,106 +0,0 @@ -db->fetch($sql, [$access_token->getId()]); - - if (count($results) === 1) - { - $session = new SessionEntity($this->server); - $session->setId($result[0]['id']); - $session->setOwner($result[0]['owner_type'], $result[0]['owner_id']); - - return $session; - } - - return null; - } - - public function getByAuthCode(AuthCodeEntity $auth_code) - { - $sql = 'SELECT oauth_sessions.id, oauth_sessions.owner_type,' - . ' oauth_sessions.owner_id, oauth_sessions.client_id,' - . ' oauth_sessions.client_redirect_uri' - . ' FROM oauth_sessions' - . ' INNER JOIN oauth_authorization_codes' - . ' ON oauth_authorization_codes.session_id = oauth_sessions.id' - . ' WHERE oauth_authorization_codes.authorization_code = ?;'; - - $results = $this->db->fetch($sql, [$auth_code->getId()]); - - if (count($results) === 1) - { - $session = new SessionEntity($this->server); - $session->setId($result[0]['id']); - $session->setOwner($result[0]['owner_type'], $result[0]['owner_id']); - - return $session; - } - - return null; - } - - public function getScopes(SessionEntity $session) - { - $sql = 'SELECT oauth_sessions.*' - . ' FROM oauth_sessions' - . ' INNER JOIN oauth_access_token_scopes' - . ' ON oauth_sessions.id = oauth_access_token_scopes.access_token_id' - . ' INNER JOIN oauth_scopes' - . ' ON oauth_scopes.id = oauth_access_token_scopes.scope_id' - . ' WHERE oauth_sessions.id = ?;'; - - $results = $this->db->fetch($sql, [$session->getId()]); - $scopes = []; - - foreach ($results as $scope) - { - $scopes[] = (new ScopeEntity($this->server))->hydrate([ - 'id' => $scope['id'], - 'description' => $scope['description'], - ]); - } - - return $scopes; - } - - public function create($owner_type, $owner_id, $client_id, $client_redirect_uri = null) - { - $sql = 'INSERT INTO oauth_sessions' - . ' (owner_type, owner_id, client_id)' - . ' VALUES' - . ' (?, ?, ?);'; - - return $this->db->execute($sql, [$owner_type, $owner_id, $client_id]); - } - - public function associateScope(SessionEntity $session, ScopeEntity $scope) - { - $sql = 'INSERT INTO oauth_access_token_scopes' - . ' (access_token_id, scope_id)' - . ' VALUES' - . ' (?, ?);'; - - $this->db->execute($sql, [$session->getId(), $scope->getId()]); - } -} - diff --git a/src/OAuth2/StorageAdapter.php b/src/OAuth2/StorageAdapter.php deleted file mode 100644 index 27ab436..0000000 --- a/src/OAuth2/StorageAdapter.php +++ /dev/null @@ -1,20 +0,0 @@ -config = Config::getInstance(); - $this->db = Database::getInstance(); - } -} - diff --git a/src/Object.php b/src/Object.php index c7809b9..42bb6c0 100644 --- a/src/Object.php +++ b/src/Object.php @@ -39,47 +39,33 @@ class Object public $config = null; /** - * Instance of the Cache object + * Instance of the Mongo object * * @var object */ - public $cache = null; + public $mongo = null; /** - * Instance of the Database object + * Instance of the Redis object * * @var object */ - public $db = null; + public $redis = null; /** * Constructor * * Establishes a Config instance for all children to enjoy */ - public function __construct($objects = null) + public function __construct() { + // @todo Lazy load these so we're not loading them on every instance $this->config = Config::getInstance(); - - if ($objects) - { - if (!is_array($objects)) - { - $objects = [$objects]; - } - - foreach ($objects as $object) - { - switch ($object) - { - case 'cache': $this->cache = Cache::getInstance(); break; - case 'db': $this->db = Database::getInstance(); break; - } - } - } + $this->mongo = Mongo::getInstance(); + //$this->redis = Redis::getInstance(); // Optionally logs the constructor to the profiler - if ($this->config['pickles']['profiler']) + if ($this->config['profiler']) { Profiler::log($this, '__construct'); } @@ -119,7 +105,7 @@ class Object public function __destruct() { // Optionally logs the destructor to the profiler - if ($this->config['pickles']['profiler']) + if ($this->config['profiler']) { Profiler::log($this, '__destruct'); } diff --git a/src/Resource.php b/src/Resource.php index 9d9863c..fadd62f 100644 --- a/src/Resource.php +++ b/src/Resource.php @@ -290,7 +290,7 @@ class Resource extends Object throw new \Exception('Missing or invalid parameters.', 400); } - parent::__construct(['cache', 'db']); + parent::__construct(); // Checks if the request method has been implemented if (get_class($this) != 'Pickles\\Resource') @@ -302,7 +302,7 @@ class Resource extends Object else { // Starts a timer before the resource is executed - if ($this->config['pickles']['profiler']) + if ($this->config['profiler']) { $timer = get_class($this) . '->' . $method . '()'; Profiler::timer($timer); @@ -311,7 +311,7 @@ class Resource extends Object $this->response = $this->$method(); // Stops the resource timer - if ($this->config['pickles']['profiler']) + if ($this->config['profiler']) { Profiler::timer($timer); } @@ -374,7 +374,7 @@ class Resource extends Object } } - if ($this->config['pickles']['profiler']) + if ($this->config['profiler']) { $response['profiler'] = Profiler::report(); } diff --git a/tests/Pickles/CacheTest.php b/tests/Pickles/CacheTest.php deleted file mode 100644 index 06cefa4..0000000 --- a/tests/Pickles/CacheTest.php +++ /dev/null @@ -1,102 +0,0 @@ - [ - "local" => "127.0.0.1", - "production" => "123.456.789.0", - ], - "pickles" => [ - "cache" => "mc", - ], - "datasources" => [ - "mc" => [ - "type" => "memcache", - "hostname" => "localhost", - "port" => 11211, - "namespace" => "ns", - ], - ], - ]; - '); - - Pickles\Config::getInstance('/tmp/pickles.php'); - - $this->cache = Pickles\Cache::getInstance(); - } - - public function testGetInstance() - { - $this->assertInstanceOf('Pickles\\Cache', $this->cache); - } - - public function testSetAndGet() - { - $key = Pickles\String::random(); - $value = Pickles\String::random(); - - $this->cache->set($key, $value); - - $this->assertEquals($value, $this->cache->get($key)); - } - - public function testSetAndGetMultiple() - { - $keys = $values = $expected = []; - - for ($i = 0; $i < 5; $i++) - { - $keys[] = Pickles\String::random(); - $values[] = Pickles\String::random(); - } - - foreach ($keys as $key => $key_name) - { - $value = $values[$key]; - $expected['NS-' . strtoupper($key_name)] = $value; - $this->cache->set($key_name, $value); - } - - $this->assertEquals($expected, $this->cache->get($keys)); - } - - public function testDelete() - { - $key = Pickles\String::random(); - $value = Pickles\String::random(); - - $this->cache->set($key, $value); - $this->cache->delete($key); - - $this->assertFalse($this->cache->get($key)); - } - - public function testIncrement() - { - $key = Pickles\String::random(); - - $this->assertFalse($this->cache->increment($key)); - - $this->cache->set($key, 1); - - $this->assertEquals(2, $this->cache->increment($key)); - $this->assertEquals(3, $this->cache->increment($key)); - $this->assertEquals(4, $this->cache->increment($key)); - } - - // Doesn't do much but test that the destructor doesn't explode - public function testDestructor() - { - $this->cache->__destruct(); - } -} - diff --git a/tests/Pickles/DatabaseTest.php b/tests/Pickles/DatabaseTest.php deleted file mode 100644 index dd49291..0000000 --- a/tests/Pickles/DatabaseTest.php +++ /dev/null @@ -1,526 +0,0 @@ - [ - "local" => "127.0.0.1", - "production" => "123.456.789.0", - ], - "pickles" => [ - "namespace" => "", - "datasource" => "mysql", - ], - "datasources" => [ - "mysql" => [ - "type" => "mysql", - "driver" => "pdo_mysql", - "database" => "test", - "hostname" => "localhost", - "username" => "root", - "password" => "", - "database" => "test", - ], - ], - ]; - '); - - Pickles\Config::getInstance('/tmp/pickles.php'); - } - - public function testGetInstanceFalse() - { - file_put_contents('/tmp/pickles.php', ' [ - "local" => "127.0.0.1", - "production" => "123.456.789.0", - ], - "datasources" => [ - - ], - ]; - '); - - Pickles\Config::getInstance('/tmp/pickles.php'); - - $this->assertFalse(Pickles\Database::getInstance()); - } - - /** - * @expectedException Exception - * @expectedExceptionMessage The specified datasource is not defined in the config. - */ - public function testGetInstanceDatasourceNotDefined() - { - file_put_contents('/tmp/pickles.php', ' [ - "local" => "127.0.0.1", - "production" => "123.456.789.0", - ], - "pickles" => [ - "namespace" => "", - "datasource" => "bad", - ], - ]; - '); - - Pickles\Config::getInstance('/tmp/pickles.php'); - - Pickles\Database::getInstance(); - } - - /** - * @expectedException Exception - * @expectedExceptionMessage The specified datasource lacks a driver. - */ - public function testGetInstanceDatasourceLacksDriver() - { - file_put_contents('/tmp/pickles.php', ' [ - "local" => "127.0.0.1", - "production" => "123.456.789.0", - ], - "pickles" => [ - "namespace" => "", - "datasource" => "bad", - ], - "datasources" => [ - "bad" => [ - "type" => "mysql", - ], - ], - ]; - '); - - Pickles\Config::getInstance('/tmp/pickles.php'); - - $this->assertInstanceOf('Pickles\\Database', Pickles\Database::getInstance()); - } - - /** - * @expectedException Exception - * @expectedExceptionMessage There was an error loading the database configuration. - */ - public function testOpenConfigError() - { - file_put_contents('/tmp/pickles.php', ' [ - "local" => "127.0.0.1", - "production" => "123.456.789.0", - ], - "pickles" => [ - "namespace" => "", - "datasource" => "bad", - ], - "datasources" => [ - "bad" => [ - "type" => "mysql", - "driver" => "pdo_mysql", - "database" => "test", - ], - ], - ]; - '); - - Pickles\Config::getInstance('/tmp/pickles.php'); - - $db = Pickles\Database::getInstance(); - $db->open(); - } - - public function testGetInstanceDatasourcesArray() - { - file_put_contents('/tmp/pickles.php', ' [ - "local" => "127.0.0.1", - "production" => "123.456.789.0", - ], - "pickles" => [ - "namespace" => "", - "datasource" => "bad", - ], - "datasources" => [ - "bad" => [ - "type" => "mysql", - "driver" => "pdo_mysql", - "database" => "test", - "hostname" => "localhost", - "username" => "root", - "password" => "", - "database" => "test", - ], - ], - ]; - '); - - Pickles\Config::getInstance('/tmp/pickles.php'); - - $this->assertInstanceOf('Pickles\\Database', Pickles\Database::getInstance()); - } - - // Also tests the datasource being missing and selecting the first one - public function testGetInstanceMySQL() - { - file_put_contents('/tmp/pickles.php', ' [ - "local" => "127.0.0.1", - "production" => "123.456.789.0", - ], - "pickles" => [ - "namespace" => "", - ], - "datasources" => [ - "bad" => [ - "type" => "mysql", - "driver" => "pdo_mysql", - "database" => "test", - "hostname" => "localhost", - "username" => "root", - "password" => "", - "database" => "test", - ], - ], - ]; - '); - - Pickles\Config::getInstance('/tmp/pickles.php'); - - $this->assertInstanceOf('Pickles\\Database', Pickles\Database::getInstance()); - } - - public function testOpenMySQL() - { - file_put_contents('/tmp/pickles.php', ' [ - "local" => "127.0.0.1", - "production" => "123.456.789.0", - ], - "pickles" => [ - "namespace" => "", - "datasource" => "mysql", - ], - "datasources" => [ - "mysql" => [ - "type" => "mysql", - "driver" => "pdo_mysql", - "database" => "test", - "hostname" => "localhost", - "username" => "root", - "password" => "", - "database" => "test", - ], - ], - ]; - '); - - Pickles\Config::getInstance('/tmp/pickles.php'); - - $db = Pickles\Database::getInstance(); - $db->open(); - } - - public function testExecute() - { - $db = Pickles\Database::getInstance(); - $this->assertEquals('0', $db->execute('SHOW TABLES')); - } - - /** - * @expectedException Exception - * @expectedExceptionMessage No query to execute. - */ - public function testExecuteNoQuery() - { - $db = Pickles\Database::getInstance(); - $db->execute(' '); - } - - public function testFetch() - { - file_put_contents('/tmp/pickles.php', ' [ - "local" => "127.0.0.1", - "production" => "123.456.789.0", - ], - "pickles" => [ - "namespace" => "", - "datasource" => "mysql", - "profiler" => true, - ], - "datasources" => [ - "mysql" => [ - "type" => "mysql", - "driver" => "pdo_mysql", - "database" => "test", - "hostname" => "localhost", - "username" => "root", - "password" => "", - "database" => "test", - ], - ], - ]; - '); - - Pickles\Config::getInstance('/tmp/pickles.php'); - - $db = Pickles\Database::getInstance(); - $this->assertEquals([], $db->fetch('SELECT * FROM pickles WHERE id != ?', ['0'])); - } - - public function testExplainNoInput() - { - $config = Pickles\Config::getInstance(); - $db = Pickles\Database::getInstance(); - $this->assertEquals([], $db->fetch('SELECT * FROM pickles WHERE id != 0')); - } - - public function testSlowQuery() - { - $db = Pickles\Database::getInstance(); - $this->assertEquals('0', $db->execute('SHOW DATABASES', null, true)); - } - - public function testCloseMySQL() - { - $db = Pickles\Database::getInstance(); - $db->open(); - - $this->assertTrue($db->close()); - } - - public function testGetInstancePostgreSQL() - { - file_put_contents('/tmp/pickles.php', ' [ - "local" => "127.0.0.1", - "production" => "123.456.789.0", - ], - "pickles" => [ - "namespace" => "", - "datasource" => "pgsql", - ], - "datasources" => [ - "pgsql" => [ - "type" => "pgsql", - "driver" => "pdo_pgsql", - "database" => "test", - "hostname" => "localhost", - "username" => "root", - "password" => "", - "database" => "test", - ], - ], - ]; - '); - - Pickles\Config::getInstance('/tmp/pickles.php'); - - $this->assertInstanceOf('Pickles\\Database', Pickles\Database::getInstance()); - } - - /** - * @expectedException PDOException - * @expectedExceptionCode 7 - */ - public function testOpenPostgreSQL() - { - file_put_contents('/tmp/pickles.php', ' [ - "local" => "127.0.0.1", - "production" => "123.456.789.0", - ], - "pickles" => [ - "namespace" => "", - "datasource" => "pgsql", - ], - "datasources" => [ - "pgsql" => [ - "type" => "pgsql", - "driver" => "pdo_pgsql", - "database" => "test", - "hostname" => "localhost", - "username" => "root", - "password" => "", - "database" => "test", - ], - ], - ]; - '); - - Pickles\Config::getInstance('/tmp/pickles.php'); - - // Throws an error because I don't have PostgreSQL installed - $db = Pickles\Database::getInstance(); - $db->open(); - } - - public function testGetInstanceSQLite() - { - file_put_contents('/tmp/pickles.php', ' [ - "local" => "127.0.0.1", - "production" => "123.456.789.0", - ], - "pickles" => [ - "namespace" => "", - "datasource" => "sqlite", - ], - "datasources" => [ - "sqlite" => [ - "type" => "sqlite", - "driver" => "pdo_sqlite", - "database" => "test", - "hostname" => "localhost", - "username" => "root", - "password" => "", - "database" => "test", - ], - ], - ]; - '); - - Pickles\Config::getInstance('/tmp/pickles.php'); - - $this->assertInstanceOf('Pickles\\Database', Pickles\Database::getInstance()); - } - - /** - * @expectedException Exception - * @expectedExceptionMessage Datasource driver "pdo_invalid" is invalid - */ - public function testGetInstanceInvalidDriver() - { - file_put_contents('/tmp/pickles.php', ' [ - "local" => "127.0.0.1", - "production" => "123.456.789.0", - ], - "pickles" => [ - "namespace" => "", - "datasource" => "invalid", - ], - "datasources" => [ - "invalid" => [ - "type" => "invalid", - "driver" => "pdo_invalid", - "database" => "test", - "hostname" => "localhost", - "username" => "root", - "password" => "", - "database" => "test", - ], - ], - ]; - '); - - Pickles\Config::getInstance('/tmp/pickles.php'); - - Pickles\Database::getInstance(); - } - - public function testProfilerWithoutParameters() - { - // Clears out the Config for ease of testing - Pickles\Object::$instances = []; - - $_SERVER['REQUEST_METHOD'] = 'GET'; - $_SERVER['SERVER_NAME'] = '127.0.0.1'; - - file_put_contents('/tmp/pickles.php', ' [ - "local" => "127.0.0.1", - "production" => "123.456.789.0", - ], - "pickles" => [ - "datasource" => "mysql", - "profiler" => true, - ], - "datasources" => [ - "mysql" => [ - "type" => "mysql", - "driver" => "pdo_mysql", - "database" => "test", - "hostname" => "localhost", - "username" => "root", - "password" => "", - "database" => "test", - ], - ], - ]; - '); - - Pickles\Config::getInstance('/tmp/pickles.php'); - - $db = Pickles\Database::getInstance(); - $db->execute('SELECT * FROM `users`'); - - $report = Pickles\Profiler::report(); - $this->assertEquals(7, count($report)); - $this->assertEquals(2, count($report['logs'])); - $this->assertTrue(isset($report['logs'][1]['details']['explain'])); - } - - public function testProfilerWithParameters() - { - // Clears out the Config for ease of testing - Pickles\Object::$instances = []; - - $_SERVER['REQUEST_METHOD'] = 'GET'; - $_SERVER['SERVER_NAME'] = '127.0.0.1'; - - file_put_contents('/tmp/pickles.php', ' [ - "local" => "127.0.0.1", - "production" => "123.456.789.0", - ], - "pickles" => [ - "datasource" => "mysql", - "profiler" => true, - ], - "datasources" => [ - "mysql" => [ - "type" => "mysql", - "driver" => "pdo_mysql", - "database" => "test", - "hostname" => "localhost", - "username" => "root", - "password" => "", - "database" => "test", - ], - ], - ]; - '); - - Pickles\Config::getInstance('/tmp/pickles.php'); - - $db = Pickles\Database::getInstance(); - $db->execute('SELECT * FROM `users` WHERE id = ?', [1000000]); - - $report = Pickles\Profiler::report(); - $this->assertEquals(7, count($report)); - $this->assertEquals(3, count($report['logs'])); - $this->assertEquals([1000000], $report['logs'][2]['details']['parameters']); - $this->assertTrue(isset($report['logs'][2]['details']['explain'])); - } -} - diff --git a/tests/Pickles/ModelTest.php b/tests/Pickles/ModelTest.php deleted file mode 100644 index dd0fd64..0000000 --- a/tests/Pickles/ModelTest.php +++ /dev/null @@ -1,633 +0,0 @@ - 'created_at']; -} - -// MyISAM -class MyMockModel extends Pickles\Model -{ - public $table = 'mypickles'; -} - -class ModelTest extends PHPUnit_Framework_TestCase -{ - public static function setUpBeforeClass() - { - // Clears out the Config for ease of testing - Pickles\Object::$instances = []; - - $_SERVER['REQUEST_METHOD'] = 'GET'; - $_SERVER['SERVER_NAME'] = '127.0.0.1'; - - file_put_contents('/tmp/pickles.php', ' [ - "local" => "127.0.0.1", - "production" => "123.456.789.0", - ], - "pickles" => [ - "namespace" => "", - "datasource" => "mysql", - "cache" => "memcache", - ], - "datasources" => [ - "mysql" => [ - "type" => "mysql", - "driver" => "pdo_mysql", - "hostname" => "localhost", - "username" => "root", - "password" => "", - "database" => "test", - "cache" => true, - ], - "memcache" => [ - "type" => "memcache", - "hostname" => "localhost", - "port" => 11211, - "namespace" => "", - ], - ], - ]; - '); - - $config = Pickles\Config::getInstance('/tmp/pickles.php'); - - for ($i = 0; $i < 5; $i++) - { - $model = new MockModel(); - $model->record['field1'] = 'one'; - $model->record['field2'] = 'two'; - $model->record['field3'] = 'three'; - $model->record['field4'] = 'four'; - $model->record['field5'] = 'five'; - $model->commit(); - } - } - - /** - * @expectedException Exception - * @expectedExceptionMessage You must set the table variable - */ - public function testNoTable() - { - new Pickles\Model(); - } - - public function testWithoutColumns() - { - $model = new MockModelWithoutColumns(); - $columns = PHPUnit_Framework_Assert::readAttribute($model, 'columns'); - - $this->assertFalse($columns['is_deleted']); - } - - public function testWithColumns() - { - $model = new MockModel(); - $columns = PHPUnit_Framework_Assert::readAttribute($model, 'columns'); - - $this->assertEquals('is_deleted', $columns['is_deleted']); - } - - /** - * @expectedException Exception - * @expectedExceptionMessage You cannot pass in 2 query parameter arrays - */ - public function testDoubleArray() - { - $model = new MockModel(['foo' => 'bar'], ['test' => 'ing']); - } - - public function testFetchInt() - { - $model = new MockModel(1); - $this->assertEquals(1, $model->count()); - $this->assertEquals(1, $model->record['id']); - } - - public function testFetchIntArray() - { - $model = new MockModel([1, 2, 3]); - $this->assertEquals(3, $model->count()); - } - - /* - @todo Acting wonky, passes tests on just this class, fails on all - public function testFetchConditionsID() - { - $model = new MockModel(['conditions' => ['id' => 1]]); - var_dump($model->record); - $this->assertEquals(1, $model->count()); - $this->assertEquals(1, $model->record['id']); - } - */ - - public function testFetchCount() - { - $model = new MockModel('count'); - $this->assertEquals(5, $model->record['count']); - } - - public function testFetchCountConditions() - { - $model = new MockModel('count', ['conditions' => ['id' => [1, 3, 5]]]); - $this->assertEquals(3, $model->record['count']); - } - - public function testFetchIndexed() - { - $model = new MockModel('indexed', ['conditions' => ['id' => [2, 4]]]); - $this->assertEquals(2, $model->count()); - $this->assertEquals([2, 4], array_keys($model->records)); - } - - // Also tests against a full cache - public function testFetchList() - { - $model = new MockModel('list', ['conditions' => ['id' => [2, 4]]]); - $this->assertEquals(2, $model->count()); - $this->assertEquals([2, 4], array_keys($model->records)); - } - - public function testFetchCountWithID() - { - $model = new MockModel('count', 3); - $this->assertEquals(1, $model->record['count']); - } - - public function testFetchListWithID() - { - $model = new MockModel('list', 2); - $this->assertEquals(1, $model->count()); - $this->assertEquals([2 => 'one'], $model->records); - } - - public function testFieldValues() - { - $model = new MockModel('all'); - - $fields = $model->fieldValues('id'); - - $this->assertEquals('5', count($fields)); - - foreach ($fields as $value) - { - $this->assertTrue(ctype_digit($value)); - } - } - - public function testSort() - { - $model = new MockModel(); - $this->assertTrue($model->sort('id')); - } - - public function testShuffle() - { - $model = new MockModel(); - $this->assertTrue($model->shuffle()); - } - - public function testNextPrev() - { - $model = new MockModel('all'); - $model->next(); - $this->assertEquals(2, $model->record['id']); - $model->prev(); - $this->assertEquals(1, $model->record['id']); - } - - public function testLastFirst() - { - $model = new MockModel('all'); - $model->last(); - $this->assertEquals(5, $model->record['id']); - $model->first(); - $this->assertEquals(1, $model->record['id']); - } - - public function testEndReset() - { - $model = new MockModel('all'); - $model->end(); - $this->assertEquals(5, $model->record['id']); - $model->reset(); - $this->assertEquals(1, $model->record['id']); - } - - public function testWalk() - { - $model = new MockModel('all'); - $expected = 0; - - while ($model->walk()) - { - $expected++; - $this->assertEquals($expected, $model->record['id']); - } - } - - public function testInsert() - { - $model = new MockModel(); - - for ($i = 1; $i <= 5; $i++) - { - $model->record['field' . $i] = Pickles\String::random(); - } - - $model->commit(); - } - - public function testInsertMultiple() - { - $model = new MockModel(); - - for ($i = 1; $i <= 5; $i++) - { - for ($j = 1; $j <= 5; $j++) - { - $model->record['field' . $j] = Pickles\String::random(); - } - - $model->queue(); - } - - $model->commit(); - } - - public function testGetFromCache() - { - $model = new MockModel(1); - $this->assertEquals('1', $model->record['id']); - } - - public function testGetFromCacheConditionals() - { - $model = new MockModel(['conditions' => ['id' => 1]]); - $this->assertEquals('1', $model->record['id']); - } - - public function testCacheKey() - { - $model = new MockModel('indexed', 1, 'cache-key'); - $this->assertEquals([1], array_keys($model->records)); - } - - public function testGenerateQuery() - { - $model = new MockModelWithoutColumns([ - 'conditions' => [1, 2, 3], - 'group' => 'id', - 'having' => '1 = 1', - 'order' => 'id DESC', - 'limit' => 5, - 'offset' => 1, - ]); - $this->assertEquals('2', $model->record['id']); - } - - public function testGenerateConditions() - { - $model = new MockModel(); - $conditions = $model->generateConditions([ - 'id' => [1, 2, 3], - 'NOT' => 5, - 'OR id !=' => 10, - 'OR NOT' => [15, 20, 25], - 'id != 30', - 'id IS NOT' => null, - 'id !=' => false, - 'id <' => true, - 'id >' => null, - 'id BETWEEN' => [35, 40], - ]); - $this->assertEquals('id in (?, ?, ?) AND NOT = ? OR id != ? OR NOT in (?, ?, ?) AND id != 30 AND id IS NOT NULL AND id IS NOT FALSE AND id IS TRUE AND id > NULL AND id BETWEEN ? AND ?', $conditions); - } - - public function testGenerateConditionsInjectValues() - { - $model = new MockModel(); - $conditions = $model->generateConditions([ - 'id' => [1, 2, 3], - 'NOT' => 5, - 'OR id !=' => 10, - 'OR NOT' => [15, 20, 25], - 'id != 30', - 'id IS NOT' => null, - 'id !=' => false, - 'id <' => true, - 'id >' => null, - 'id BETWEEN' => [35, 40], - ], true); - $this->assertEquals('id in (1, 2, 3) AND NOT = 5 OR id != 10 OR NOT in (15, 20, 25) AND id != 30 AND id IS NOT NULL AND id IS NOT FALSE AND id IS TRUE AND id > NULL AND id BETWEEN 35 AND 40', $conditions); - } - - public function testGenerateConditionsNoOperatorTrue() - { - $model = new MockModel(); - $conditions = $model->generateConditions(['id' => true]); - $this->assertEquals('id IS TRUE', $conditions); - } - - public function testGenerateConditionsNoOperatorFalse() - { - $model = new MockModel(); - $conditions = $model->generateConditions(['id' => false]); - $this->assertEquals('id IS FALSE', $conditions); - } - - public function testGenerateConditionsNoOperatorNull() - { - $model = new MockModel(); - $conditions = $model->generateConditions(['id' => null]); - $this->assertEquals('id IS NULL', $conditions); - } - - /** - * @expectedException Exception - * @expectedExceptionMessage BETWEEN expects an array with 2 values. - */ - public function testGenerateConditionsBetweenMissingValue() - { - $model = new MockModel(); - $conditions = $model->generateConditions(['id BETWEEN' => [1]]); - } - - /** - * @expectedException Exception - * @expectedExceptionMessage BETWEEN expects an array. - */ - public function testGenerateConditionsBetweenNotArray() - { - $model = new MockModel(); - $conditions = $model->generateConditions(['id BETWEEN' => '1']); - } - - public function testCommitSingleRecord() - { - $value = Pickles\String::random(); - $model = new MockModel(1); - $model->record['field1'] = $value; - $model->commit(); - - $model = new MockModel(1); - $this->assertEquals($value, $model->record['field1']); - } - - // Handles filling coverage gaps but isn't a reliable test. Would need to - // test against a table without a UID column so we can see this in action, - // else it just takes a shit because the ID isn't injected back in. - public function testCommitSingleRecordReplace() - { - $value = Pickles\String::random(); - $model = new MockModel(1); - $model->replace = true; - $model->record['field1'] = $value; - $model->commit(); - $model = new MockModel(1); - } - - public function testCommitInsertPriority() - { - $value = Pickles\String::random(); - $model = new MockModel(); - $model->priority = 'low'; - $model->record['field1'] = $value; - $id = $model->commit(); - $model = new MockModel($id); - $this->assertEquals($value, $model->record['field1']); - } - - public function testCommitInsertDelayed() - { - $value = Pickles\String::random(); - $model = new MyMockModel(); - $model->delayed = true; - $model->record['field1'] = $value; - $model->commit(); - $model = new MyMockModel(1); - $this->assertEquals($value, $model->record['field1']); - } - - public function testCommitInsertIgnore() - { - $value = Pickles\String::random(); - $model = new MockModel(); - $model->ignore = true; - $model->record['field1'] = $value; - $id = $model->commit(); - $model = new MockModel($id); - $this->assertEquals($value, $model->record['field1']); - } - - public function testCommitReplacePriority() - { - $value = Pickles\String::random(); - $model = new MockModel(); - $model->replace = true; - $model->priority = 'low'; - $model->record['field1'] = $value; - $id = $model->commit(); - $model = new MockModel($id); - $this->assertEquals($value, $model->record['field1']); - } - - public function testCommitReplaceDelayed() - { - $value = Pickles\String::random(); - $model = new MyMockModel(); - $model->replace = true; - $model->delayed = true; - $model->record['field1'] = $value; - $model->commit(); - $model = new MyMockModel(2); - $this->assertEquals($value, $model->record['field1']); - } - - public function testCommitReplaceIgnore() - { - $value = Pickles\String::random(); - $model = new MockModel(); - $model->replace = true; - $model->ignore = true; - $model->record['field1'] = $value; - $id = $model->commit(); - $model = new MockModel($id); - $this->assertEquals($value, $model->record['field1']); - } - - public function testCommitMultipleFields() - { - $value1 = Pickles\String::random(); - $value2 = Pickles\String::random(); - $model = new MockModelWithoutColumns(1); - $model->record['field1'] = $value1; - $model->record['field2'] = $value2; - $model->commit(); - $model = new MockModelWithoutColumns(1); - $this->assertEquals($value1, $model->record['field1']); - $this->assertEquals($value2, $model->record['field2']); - } - - public function testCommitIncrement() - { - $model = new MockModelWithoutColumns(1); - $model->record['field1'] = 100; - $model->commit(); - $model = new MockModelWithoutColumns(1); - $model->record['field1'] = '++'; - $model->commit(); - $model = new MockModelWithoutColumns(1); - $this->assertEquals(101, $model->record['field1']); - } - - public function testCommitUpdatedID() - { - $_SESSION['__pickles']['security']['user_id'] = 1; - $value = Pickles\String::random(); - $model = new MockModel(1); - $model->record['field1'] = $value; - $model->commit(); - $model = new MockModel(1); - $this->assertEquals($value, $model->record['field1']); - $this->assertEquals(1, $model->record['updated_id']); - } - - public function testCommitCreatedID() - { - $_SESSION['__pickles']['security']['user_id'] = 1; - $value = Pickles\String::random(); - $model = new MockModel(); - $model->record['field1'] = $value; - $id = $model->commit(); - $model = new MockModel($id); - $this->assertEquals(1, $model->record['created_id']); - } - - // Doesn't test against actual PostgreSQL instance, just for valid syntax - public function testCommitInsertPostgreSQL() - { - $_SESSION['__pickles']['security']['user_id'] = 1; - $value = Pickles\String::random(); - $model = new MockModel(); - $model->mysql = false; - $model->postgresql = true; - $model->record['field1'] = $value; - - try - { - $model->commit(); - } - catch (Exception $e) - { - - } - - $this->assertRegExp('/RETURNING id/', $model->db->results->queryString); - } - - // Doesn't test against actual PostgreSQL instance, just for valid syntax - public function testCommitUpdatePostgreSQL() - { - $_SESSION['__pickles']['security']['user_id'] = 1; - $value = Pickles\String::random(); - $model = new MockModel(1); - $model->mysql = false; - $model->postgresql = true; - $model->record['field1'] = $value; - - try - { - $model->commit(); - } - catch (Exception $e) - { - - } - - $model = new MockModel(1); - $this->assertEquals($value, $model->record['field1']); - } - - public function testCommitNothing() - { - $model = new MockModel(); - $this->assertFalse($model->commit()); - } - - public function testDeleteLogical() - { - $_SESSION['__pickles']['security']['user_id'] = 1; - $model = new MockModel(1); - $model->delete(); - $model = new MockModel(1); - $this->assertEquals([], $model->record); - } - - public function testDeleteActual() - { - $model = new MockModelWithoutColumns(2); - $model->delete(); - $model = new MockModelWithoutColumns(2); - $this->assertEquals(0, $model->count()); - } - - public function testDeleteNothing() - { - $model = new MockModelWithoutColumns(100); - $this->assertFalse($model->delete()); - } - - public function testLoadParametersWithString() - { - $model = new MockModel(); - $this->assertFalse($model->loadParameters('')); - } - - public function testMultipleQueueInsert() - { - $_SESSION['__pickles']['security']['user_id'] = 1; - $model = new MockModel('count'); - $count = $model->record['count']; - $model = new MockModel(); - - for ($i = 0; $i < 5; $i++) - { - $model->record['field1'] = Pickles\String::random(); - $model->record['updated_id'] = 1; - $model->queue(); - } - - $model->commit(); - $model = new MockModel('count'); - $this->assertEquals($count + 5, $model->record['count']); - } - - public function testMultipleQueueUpdate() - { - $_SESSION['__pickles']['security']['user_id'] = 1; - $model = new MockModel(); - - for ($i = 3; $i <= 5; $i++) - { - $model->record['id'] = $i; - $model->record['field1'] = Pickles\String::random(); - $model->record['updated_id'] = 1; - $model->queue(); - } - - $model->commit(); - } -} - From a4242fd61167e538b6f061c15eaa50a67d5dbeeb Mon Sep 17 00:00:00 2001 From: Josh Sherman Date: Sun, 19 Oct 2014 09:31:01 -0400 Subject: [PATCH 126/129] Updated dependencies --- composer.lock | 198 ++++++++++---------------------------------------- 1 file changed, 37 insertions(+), 161 deletions(-) diff --git a/composer.lock b/composer.lock index 5adfeaf..f189d7c 100644 --- a/composer.lock +++ b/composer.lock @@ -4,160 +4,35 @@ "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "f919c496ec07285f990ccb4efab8cf18", + "hash": "5c522617d27e476602ea1c1b1622b9ed", "packages": [ { - "name": "league/event", - "version": "1.0.0", + "name": "bshaffer/oauth2-server-php", + "version": "v1.5", "source": { "type": "git", - "url": "https://github.com/thephpleague/event.git", - "reference": "06adb7ce55b93346be43a3ba677ac613bbf288a2" + "url": "https://github.com/bshaffer/oauth2-server-php.git", + "reference": "74fcc75e47614c1417c750e907a44567d9ceee1f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/event/zipball/06adb7ce55b93346be43a3ba677ac613bbf288a2", - "reference": "06adb7ce55b93346be43a3ba677ac613bbf288a2", + "url": "https://api.github.com/repos/bshaffer/oauth2-server-php/zipball/74fcc75e47614c1417c750e907a44567d9ceee1f", + "reference": "74fcc75e47614c1417c750e907a44567d9ceee1f", "shasum": "" }, "require": { - "php": ">=5.4.0" + "php": ">=5.3.9" }, - "require-dev": { - "henrikbjorn/phpspec-code-coverage": "1.0.*@dev", - "phpspec/phpspec": "2.0.*@dev" + "suggest": { + "aws/aws-sdk-php": "Required to use the DynamoDB storage engine", + "predis/predis": "Required to use the Redis storage engine", + "thobbs/phpcassa": "Required to use the Cassandra storage engine" }, "type": "library", - "autoload": { - "psr-4": { - "League\\Event\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Frank de Jonge", - "email": "info@frenky.net" - } - ], - "description": "Event package", - "keywords": [ - "emitter", - "event", - "listener" - ], - "time": "2014-09-09 14:40:43" - }, - { - "name": "league/oauth2-server", - "version": "dev-develop", - "source": { - "type": "git", - "url": "https://github.com/thephpleague/oauth2-server.git", - "reference": "6333a975f8fb51111b447a7e85806e4519fb52b9" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/thephpleague/oauth2-server/zipball/6333a975f8fb51111b447a7e85806e4519fb52b9", - "reference": "6333a975f8fb51111b447a7e85806e4519fb52b9", - "shasum": "" - }, - "require": { - "league/event": "1.0.*", - "php": ">=5.4.0", - "symfony/http-foundation": "~2.1" - }, - "replace": { - "league/oauth2server": "*", - "lncd/oauth2": "*" - }, - "require-dev": { - "alexbilbie/fizzfuzz": "dev-develop", - "codeception/codeception": "2.0.*", - "league/phpunit-coverage-listener": "~1.0", - "mockery/mockery": "~0.9", - "phpunit/phpunit": "~4.0", - "squizlabs/php_codesniffer": "~1.5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-develop": "4.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "League\\OAuth2\\Server\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Alex Bilbie", - "email": "hello@alexbilbie.com", - "homepage": "http://www.alexbilbie.com", - "role": "Developer" - } - ], - "description": "A lightweight and powerful OAuth 2.0 authorization and resource server library with support for all the core specification grants. This library will allow you to secure your API with OAuth and allow your applications users to approve apps that want to access their data from your API.", - "homepage": "http://oauth2.thephpleague.com/", - "keywords": [ - "Authentication", - "api", - "auth", - "authorisation", - "authorization", - "oauth", - "oauth 2", - "oauth 2.0", - "oauth2", - "protect", - "resource", - "secure", - "server" - ], - "time": "2014-10-03 13:42:01" - }, - { - "name": "symfony/http-foundation", - "version": "dev-master", - "target-dir": "Symfony/Component/HttpFoundation", - "source": { - "type": "git", - "url": "https://github.com/symfony/HttpFoundation.git", - "reference": "c24942a7ec2d8409d1f60d02c4460ca8317e885a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/HttpFoundation/zipball/c24942a7ec2d8409d1f60d02c4460ca8317e885a", - "reference": "c24942a7ec2d8409d1f60d02c4460ca8317e885a", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "require-dev": { - "symfony/expression-language": "~2.4" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.6-dev" - } - }, "autoload": { "psr-0": { - "Symfony\\Component\\HttpFoundation\\": "" - }, - "classmap": [ - "Symfony/Component/HttpFoundation/Resources/stubs" - ] + "OAuth2": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -165,17 +40,19 @@ ], "authors": [ { - "name": "Symfony Community", - "homepage": "http://symfony.com/contributors" - }, - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" + "name": "Brent Shaffer", + "email": "bshafs@gmail.com", + "homepage": "http://brentertainment.com" } ], - "description": "Symfony HttpFoundation Component", - "homepage": "http://symfony.com", - "time": "2014-10-07 14:06:18" + "description": "OAuth2 Server for PHP", + "homepage": "http://github.com/bshaffer/oauth2-server-php", + "keywords": [ + "auth", + "oauth", + "oauth2" + ], + "time": "2014-08-28 02:13:42" } ], "packages-dev": [ @@ -239,12 +116,12 @@ "source": { "type": "git", "url": "https://github.com/guzzle/guzzle3.git", - "reference": "d27346b6c8cb6a42092e763f8a2f674dfc560956" + "reference": "3c0ca2255751631f1dd64eb16bbe3b9440258297" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle3/zipball/d27346b6c8cb6a42092e763f8a2f674dfc560956", - "reference": "d27346b6c8cb6a42092e763f8a2f674dfc560956", + "url": "https://api.github.com/repos/guzzle/guzzle3/zipball/3c0ca2255751631f1dd64eb16bbe3b9440258297", + "reference": "3c0ca2255751631f1dd64eb16bbe3b9440258297", "shasum": "" }, "require": { @@ -323,7 +200,7 @@ "rest", "web service" ], - "time": "2014-09-16 21:19:27" + "time": "2014-10-15 19:36:56" }, { "name": "phpunit/php-code-coverage", @@ -575,12 +452,12 @@ "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "f75e6b2e0764bcf4faa5082f4b60d5e7cf0959bb" + "reference": "cd4014775069d7d39d20f617037c2c0f9b4bc25b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/f75e6b2e0764bcf4faa5082f4b60d5e7cf0959bb", - "reference": "f75e6b2e0764bcf4faa5082f4b60d5e7cf0959bb", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/cd4014775069d7d39d20f617037c2c0f9b4bc25b", + "reference": "cd4014775069d7d39d20f617037c2c0f9b4bc25b", "shasum": "" }, "require": { @@ -638,7 +515,7 @@ "testing", "xunit" ], - "time": "2014-10-07 09:30:07" + "time": "2014-10-17 09:28:50" }, { "name": "phpunit/phpunit-mock-objects", @@ -879,12 +756,12 @@ "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "5843509fed39dee4b356a306401e9dd1a931fec7" + "reference": "92d423df43b160006907ea4297b916fdf00415d8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/5843509fed39dee4b356a306401e9dd1a931fec7", - "reference": "5843509fed39dee4b356a306401e9dd1a931fec7", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/92d423df43b160006907ea4297b916fdf00415d8", + "reference": "92d423df43b160006907ea4297b916fdf00415d8", "shasum": "" }, "require": { @@ -923,7 +800,7 @@ "keywords": [ "diff" ], - "time": "2014-08-15 10:29:00" + "time": "2014-10-19 13:19:30" }, { "name": "sebastian/environment", @@ -1434,7 +1311,6 @@ "aliases": [], "minimum-stability": "dev", "stability-flags": { - "league/oauth2-server": 20, "phpunit/phpunit": 20, "satooshi/php-coveralls": 20 }, From 1ef2adae122ad9c907460b5f3b83209d610f952c Mon Sep 17 00:00:00 2001 From: Josh Sherman Date: Sun, 19 Oct 2014 10:23:48 -0400 Subject: [PATCH 127/129] Added suggestions --- composer.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/composer.json b/composer.json index 1204abf..8227c05 100644 --- a/composer.json +++ b/composer.json @@ -25,6 +25,10 @@ "php": ">=5.4", "bshaffer/oauth2-server-php": "v1.5" }, + "suggest": { + "mongodb/mongo-php-driver": "Required to use the Mongo storage engine", + "predis/predis": "Required to use the Redis storage engine", + } "autoload": { "psr-4": { "Pickles\\" : "src/" From 11e4fee71152d61ca07da60c7a106c03339f23c9 Mon Sep 17 00:00:00 2001 From: Josh Sherman Date: Sun, 19 Oct 2014 10:26:15 -0400 Subject: [PATCH 128/129] Switching the OAuth2 lib back again Couldn't handle the fact that the errors were being echoed from the library and not thrown or at the very least passed back so I could use them. --- composer.json | 2 +- src/OAuth2/AccessTokenStorage.php | 84 +++++++++++++++++++++++ src/OAuth2/ClientStorage.php | 81 ++++++++++++++++++++++ src/OAuth2/RefreshTokenStorage.php | 56 +++++++++++++++ src/OAuth2/ScopeStorage.php | 26 +++++++ src/OAuth2/SessionStorage.php | 106 +++++++++++++++++++++++++++++ src/OAuth2/StorageAdapter.php | 20 ++++++ 7 files changed, 374 insertions(+), 1 deletion(-) create mode 100644 src/OAuth2/AccessTokenStorage.php create mode 100644 src/OAuth2/ClientStorage.php create mode 100644 src/OAuth2/RefreshTokenStorage.php create mode 100644 src/OAuth2/ScopeStorage.php create mode 100644 src/OAuth2/SessionStorage.php create mode 100644 src/OAuth2/StorageAdapter.php diff --git a/composer.json b/composer.json index 8227c05..7638c3d 100644 --- a/composer.json +++ b/composer.json @@ -23,7 +23,7 @@ }, "require": { "php": ">=5.4", - "bshaffer/oauth2-server-php": "v1.5" + "league/oauth2-server": "4.0.x-dev" }, "suggest": { "mongodb/mongo-php-driver": "Required to use the Mongo storage engine", diff --git a/src/OAuth2/AccessTokenStorage.php b/src/OAuth2/AccessTokenStorage.php new file mode 100644 index 0000000..04d8dc0 --- /dev/null +++ b/src/OAuth2/AccessTokenStorage.php @@ -0,0 +1,84 @@ += ?;'; + + $results = $this->db->fetch($sql, [$token, time()]); + + if (count($results) === 1) + { + return (new AccessTokenEntity($this->server)) + ->setId($results[0]['access_token']) + ->setExpireTime($results[0]['expires_at']); + } + + return null; + } + + public function getScopes(AbstractTokenEntity $token) + { + $sql = 'SELECT oauth_scopes.id, oauth_scopes.description' + . ' FROM oauth_access_token_scopes' + . ' INNER JOIN oauth_scopes' + . ' ON oauth_access_token_scopes.scope_id = oauth_scopes.id' + . ' WHERE oauth_access_token_scopes.access_token_id = ?;'; + + $results = $this->db->fetch($sql, [$token->getId()]); + $response = []; + + if (count($results) > 0) + { + foreach ($results as $row) + { + $response[] = (new ScopeEntity($this->server))->hydrate([ + 'id' => $row['id'], + 'description' => $row['description'] + ]); + } + } + + return $response; + } + + public function create($token, $expiration, $session_id) + { + $sql = 'INSERT INTO oauth_access_tokens' + . ' (access_token, session_id, expires_at)' + . ' VALUES' + . ' (?, ?, ?);'; + + $this->db->execute($sql, [$token, $session_id, $expiration]); + } + + public function associateScope(AbstractTokenEntity $token, ScopeEntity $scope) + { + $sql = 'INSERT INTO oauth_access_token_scopes' + . ' (access_token, scope)' + . ' VALUES' + . ' (?, ?);'; + + $this->db->execute($sql, [$token->getId(), $scope->getId()]); + } + + public function delete(AbstractTokenEntity $token) + { + $sql = 'DELETE FROM oauth_access_token_scopes' + . ' WHERE access_token = ?;'; + + $this->db->execute($sql, [$token->getId()]); + } +} + diff --git a/src/OAuth2/ClientStorage.php b/src/OAuth2/ClientStorage.php new file mode 100644 index 0000000..1a32583 --- /dev/null +++ b/src/OAuth2/ClientStorage.php @@ -0,0 +1,81 @@ +db->fetch($sql, $parameters); + + if (count($results) === 1) + { + $client = new ClientEntity($this->server); + + $client->hydrate([ + 'id' => $results[0]['id'], + 'name' => $results[0]['name'] + ]); + + return $client; + } + + return null; + } + + public function getBySession(SessionEntity $session) + { + $sql = 'SELECT oauth_clients.id, oauth_clients.name' + . ' FROM oauth_clients' + . ' INNER JOIN oauth_sessions' + . ' ON oauth_clients.id = oauth_sessions.client_id' + . ' WHERE oauth_sessions.id = ?'; + + $results = $this->db->fetch($sql, [$session->getId()]); + + if (count($results) === 1) + { + $client = new ClientEntity($this->server); + + $client->hydrate([ + 'id' => $results[0]['id'], + 'name' => $results[0]['name'] + ]); + + return $client; + } + + return null; + } +} + diff --git a/src/OAuth2/RefreshTokenStorage.php b/src/OAuth2/RefreshTokenStorage.php new file mode 100644 index 0000000..bceea31 --- /dev/null +++ b/src/OAuth2/RefreshTokenStorage.php @@ -0,0 +1,56 @@ += ?;'; + + $results = $this->db->fetch($sql, [$token, time()]); + + if (count($results) === 1) + { + return (new RefreshTokenEntity($this->server)) + ->setId($results[0]['refresh_token']) + ->setExpireTime($results[0]['expires_at']) + ->setAccessTokenId($results[0]['access_token_id']); + } + + return null; + } + + public function create($token, $expiration, $access_token) + { + $sql = 'SELECT id FROM oauth_access_tokens WHERE access_token = ?;'; + $results = $this->db->fetch($sql, [$access_token]); + $token_id = $results[0]['id']; + + $sql = 'INSERT INTO oauth_refresh_tokens' + . ' (refresh_token, access_token_id, expires_at, client_id)' + . ' VALUES' + . ' (?, ?, ?, ?);'; + + $this->db->execute($sql, [ + $token, + $token_id, + $expiration, + $this->server->getRequest()->request->get('client_id', null), + ]); + } + + public function delete(RefreshTokenEntity $token) + { + $sql = 'DELETE FROM oauth_refresh_tokens WHERE refresh_token = ?;'; + + $this->db->execute($sql, [$token->getId()]); + } +} + diff --git a/src/OAuth2/ScopeStorage.php b/src/OAuth2/ScopeStorage.php new file mode 100644 index 0000000..c614eb7 --- /dev/null +++ b/src/OAuth2/ScopeStorage.php @@ -0,0 +1,26 @@ +db->fetch($sql, [$scope]); + + if (count($results) === 0) + { + return null; + } + + return (new ScopeEntity($this->server))->hydrate([ + 'id' => $result[0]['id'], + 'description' => $result[0]['description'], + ]); + } +} + diff --git a/src/OAuth2/SessionStorage.php b/src/OAuth2/SessionStorage.php new file mode 100644 index 0000000..2754b1d --- /dev/null +++ b/src/OAuth2/SessionStorage.php @@ -0,0 +1,106 @@ +db->fetch($sql, [$access_token->getId()]); + + if (count($results) === 1) + { + $session = new SessionEntity($this->server); + $session->setId($result[0]['id']); + $session->setOwner($result[0]['owner_type'], $result[0]['owner_id']); + + return $session; + } + + return null; + } + + public function getByAuthCode(AuthCodeEntity $auth_code) + { + $sql = 'SELECT oauth_sessions.id, oauth_sessions.owner_type,' + . ' oauth_sessions.owner_id, oauth_sessions.client_id,' + . ' oauth_sessions.client_redirect_uri' + . ' FROM oauth_sessions' + . ' INNER JOIN oauth_authorization_codes' + . ' ON oauth_authorization_codes.session_id = oauth_sessions.id' + . ' WHERE oauth_authorization_codes.authorization_code = ?;'; + + $results = $this->db->fetch($sql, [$auth_code->getId()]); + + if (count($results) === 1) + { + $session = new SessionEntity($this->server); + $session->setId($result[0]['id']); + $session->setOwner($result[0]['owner_type'], $result[0]['owner_id']); + + return $session; + } + + return null; + } + + public function getScopes(SessionEntity $session) + { + $sql = 'SELECT oauth_sessions.*' + . ' FROM oauth_sessions' + . ' INNER JOIN oauth_access_token_scopes' + . ' ON oauth_sessions.id = oauth_access_token_scopes.access_token_id' + . ' INNER JOIN oauth_scopes' + . ' ON oauth_scopes.id = oauth_access_token_scopes.scope_id' + . ' WHERE oauth_sessions.id = ?;'; + + $results = $this->db->fetch($sql, [$session->getId()]); + $scopes = []; + + foreach ($results as $scope) + { + $scopes[] = (new ScopeEntity($this->server))->hydrate([ + 'id' => $scope['id'], + 'description' => $scope['description'], + ]); + } + + return $scopes; + } + + public function create($owner_type, $owner_id, $client_id, $client_redirect_uri = null) + { + $sql = 'INSERT INTO oauth_sessions' + . ' (owner_type, owner_id, client_id)' + . ' VALUES' + . ' (?, ?, ?);'; + + return $this->db->execute($sql, [$owner_type, $owner_id, $client_id]); + } + + public function associateScope(SessionEntity $session, ScopeEntity $scope) + { + $sql = 'INSERT INTO oauth_access_token_scopes' + . ' (access_token_id, scope_id)' + . ' VALUES' + . ' (?, ?);'; + + $this->db->execute($sql, [$session->getId(), $scope->getId()]); + } +} + diff --git a/src/OAuth2/StorageAdapter.php b/src/OAuth2/StorageAdapter.php new file mode 100644 index 0000000..27ab436 --- /dev/null +++ b/src/OAuth2/StorageAdapter.php @@ -0,0 +1,20 @@ +config = Config::getInstance(); + $this->db = Database::getInstance(); + } +} + From 8e88ffb4409b5260d569620130ef1316e2b337cc Mon Sep 17 00:00:00 2001 From: Josh Sherman Date: Sun, 19 Oct 2014 10:29:29 -0400 Subject: [PATCH 129/129] Fixed composer.json and updated dependencies --- composer.json | 4 +- composer.lock | 170 +++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 149 insertions(+), 25 deletions(-) diff --git a/composer.json b/composer.json index 7638c3d..24f7380 100644 --- a/composer.json +++ b/composer.json @@ -27,8 +27,8 @@ }, "suggest": { "mongodb/mongo-php-driver": "Required to use the Mongo storage engine", - "predis/predis": "Required to use the Redis storage engine", - } + "predis/predis": "Required to use the Redis storage engine" + }, "autoload": { "psr-4": { "Pickles\\" : "src/" diff --git a/composer.lock b/composer.lock index f189d7c..dfb8338 100644 --- a/composer.lock +++ b/composer.lock @@ -4,34 +4,33 @@ "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "5c522617d27e476602ea1c1b1622b9ed", + "hash": "65e330b4eb2873d61093fc36aa624d0f", "packages": [ { - "name": "bshaffer/oauth2-server-php", - "version": "v1.5", + "name": "league/event", + "version": "1.0.0", "source": { "type": "git", - "url": "https://github.com/bshaffer/oauth2-server-php.git", - "reference": "74fcc75e47614c1417c750e907a44567d9ceee1f" + "url": "https://github.com/thephpleague/event.git", + "reference": "06adb7ce55b93346be43a3ba677ac613bbf288a2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/bshaffer/oauth2-server-php/zipball/74fcc75e47614c1417c750e907a44567d9ceee1f", - "reference": "74fcc75e47614c1417c750e907a44567d9ceee1f", + "url": "https://api.github.com/repos/thephpleague/event/zipball/06adb7ce55b93346be43a3ba677ac613bbf288a2", + "reference": "06adb7ce55b93346be43a3ba677ac613bbf288a2", "shasum": "" }, "require": { - "php": ">=5.3.9" + "php": ">=5.4.0" }, - "suggest": { - "aws/aws-sdk-php": "Required to use the DynamoDB storage engine", - "predis/predis": "Required to use the Redis storage engine", - "thobbs/phpcassa": "Required to use the Cassandra storage engine" + "require-dev": { + "henrikbjorn/phpspec-code-coverage": "1.0.*@dev", + "phpspec/phpspec": "2.0.*@dev" }, "type": "library", "autoload": { - "psr-0": { - "OAuth2": "src/" + "psr-4": { + "League\\Event\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -40,19 +39,143 @@ ], "authors": [ { - "name": "Brent Shaffer", - "email": "bshafs@gmail.com", - "homepage": "http://brentertainment.com" + "name": "Frank de Jonge", + "email": "info@frenky.net" } ], - "description": "OAuth2 Server for PHP", - "homepage": "http://github.com/bshaffer/oauth2-server-php", + "description": "Event package", "keywords": [ - "auth", - "oauth", - "oauth2" + "emitter", + "event", + "listener" ], - "time": "2014-08-28 02:13:42" + "time": "2014-09-09 14:40:43" + }, + { + "name": "league/oauth2-server", + "version": "dev-develop", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/oauth2-server.git", + "reference": "6333a975f8fb51111b447a7e85806e4519fb52b9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/oauth2-server/zipball/6333a975f8fb51111b447a7e85806e4519fb52b9", + "reference": "6333a975f8fb51111b447a7e85806e4519fb52b9", + "shasum": "" + }, + "require": { + "league/event": "1.0.*", + "php": ">=5.4.0", + "symfony/http-foundation": "~2.1" + }, + "replace": { + "league/oauth2server": "*", + "lncd/oauth2": "*" + }, + "require-dev": { + "alexbilbie/fizzfuzz": "dev-develop", + "codeception/codeception": "2.0.*", + "league/phpunit-coverage-listener": "~1.0", + "mockery/mockery": "~0.9", + "phpunit/phpunit": "~4.0", + "squizlabs/php_codesniffer": "~1.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-develop": "4.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "League\\OAuth2\\Server\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Alex Bilbie", + "email": "hello@alexbilbie.com", + "homepage": "http://www.alexbilbie.com", + "role": "Developer" + } + ], + "description": "A lightweight and powerful OAuth 2.0 authorization and resource server library with support for all the core specification grants. This library will allow you to secure your API with OAuth and allow your applications users to approve apps that want to access their data from your API.", + "homepage": "http://oauth2.thephpleague.com/", + "keywords": [ + "Authentication", + "api", + "auth", + "authorisation", + "authorization", + "oauth", + "oauth 2", + "oauth 2.0", + "oauth2", + "protect", + "resource", + "secure", + "server" + ], + "time": "2014-10-03 13:42:01" + }, + { + "name": "symfony/http-foundation", + "version": "dev-master", + "target-dir": "Symfony/Component/HttpFoundation", + "source": { + "type": "git", + "url": "https://github.com/symfony/HttpFoundation.git", + "reference": "c24942a7ec2d8409d1f60d02c4460ca8317e885a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/HttpFoundation/zipball/c24942a7ec2d8409d1f60d02c4460ca8317e885a", + "reference": "c24942a7ec2d8409d1f60d02c4460ca8317e885a", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "symfony/expression-language": "~2.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.6-dev" + } + }, + "autoload": { + "psr-0": { + "Symfony\\Component\\HttpFoundation\\": "" + }, + "classmap": [ + "Symfony/Component/HttpFoundation/Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + }, + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "description": "Symfony HttpFoundation Component", + "homepage": "http://symfony.com", + "time": "2014-10-07 14:06:18" } ], "packages-dev": [ @@ -1311,6 +1434,7 @@ "aliases": [], "minimum-stability": "dev", "stability-flags": { + "league/oauth2-server": 20, "phpunit/phpunit": 20, "satooshi/php-coveralls": 20 },