From 4e4ca091d96127751e81dca6e674468c9feb1854 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 1 Jul 2011 06:18:15 -0700 Subject: [PATCH 001/206] Fix link to documentation on welcome page. --- application/views/home/index.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/views/home/index.php b/application/views/home/index.php index 4e17bf50..f745a06c 100644 --- a/application/views/home/index.php +++ b/application/views/home/index.php @@ -73,7 +73,7 @@

Ready to dig in? Start building your application in the application/routes.php file.

-

Need to learn more? Peruse our wonderful documentation.

+

Need to learn more? Peruse our wonderful documentation.

\ No newline at end of file From a9029ddfe9dac9fc59f93abbdb93b7e228fafe9b Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Sat, 2 Jul 2011 07:17:17 -0500 Subject: [PATCH 002/206] change form and html class to use html5 style elements. --- system/form.php | 2 +- system/html.php | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/system/form.php b/system/form.php index 49d60b36..56439455 100644 --- a/system/form.php +++ b/system/form.php @@ -317,7 +317,7 @@ class Form { $attributes['value'] = $value; $attributes['id'] = static::id($name, $attributes); - return ''.PHP_EOL; + return ''.PHP_EOL; } /** diff --git a/system/html.php b/system/html.php index 89968130..c562de09 100644 --- a/system/html.php +++ b/system/html.php @@ -32,7 +32,7 @@ class HTML { */ public static function style($url, $media = 'all') { - return ''.PHP_EOL; + return ''.PHP_EOL; } /** @@ -146,7 +146,7 @@ class HTML { public static function image($url, $alt = '', $attributes = array()) { $attributes['alt'] = static::entities($alt); - return ''; + return ''; } /** @@ -157,7 +157,7 @@ class HTML { */ public static function breaks($count = 1) { - return str_repeat('
', $count); + return str_repeat('
', $count); } /** From cd33e81702bf5adfc2a20f8e72dcf2c7bf2ee59c Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Sat, 2 Jul 2011 07:37:37 -0500 Subject: [PATCH 003/206] added several of the new html5 form elements. added support for attributes such as required, autofocus, etc. --- application/routes.php | 1 + system/form.php | 68 +++++++++++++++++++++++++++++++++++++++++- system/html.php | 7 +++++ 3 files changed, 75 insertions(+), 1 deletion(-) diff --git a/application/routes.php b/application/routes.php index e9f03c02..c457010e 100644 --- a/application/routes.php +++ b/application/routes.php @@ -19,6 +19,7 @@ return array( 'GET /' => function() { + return Form::input('email', 'email', '', array('required', 'class' => 'awesome')); return View::make('home/index'); }, diff --git a/system/form.php b/system/form.php index 56439455..ad0b69f2 100644 --- a/system/form.php +++ b/system/form.php @@ -146,6 +146,7 @@ class Form { * Create a HTML hidden input element. * * @param string $name + * @param string $value * @param array $attributes * @return string */ @@ -154,6 +155,71 @@ class Form { return static::input('hidden', $name, $value, $attributes); } + /** + * Create a HTML email input element. + * + * @param string $name + * @param string $value + * @param array $attributes + * @return string + */ + public static function email($name, $value = null, $attributes = array()) + { + return static::input('email', $name, $value, $attributes); + } + + /** + * Create a HTML URL input element. + * + * @param string $name + * @param string $value + * @param array $attributes + * @return string + */ + public static function url($name, $value = null, $attributes = array()) + { + return static::input('url', $name, $value, $attributes); + } + + /** + * Create a HTML search input element. + * + * @param string $name + * @param string $value + * @param array $attributes + * @return string + */ + public static function search($name, $value = null, $attributes = array()) + { + return static::input('search', $name, $value, $attributes); + } + + /** + * Create a HTML number input element. + * + * @param string $name + * @param string $value + * @param array $attributes + * @return string + */ + public static function number($name, $value = null, $attributes = array()) + { + return static::input('number', $name, $value, $attributes); + } + + /** + * Create a HTML telephone input element. + * + * @param string $name + * @param string $value + * @param array $attributes + * @return string + */ + public static function tel($name, $value = null, $attributes = array()) + { + return static::input('tel', $name, $value, $attributes); + } + /** * Create a HTML file input element. * @@ -310,7 +376,7 @@ class Form { * @param array $attributes * @return string */ - private static function input($type, $name, $value = null, $attributes = array()) + public static function input($type, $name, $value = null, $attributes = array()) { $attributes['type'] = $type; $attributes['name'] = $name; diff --git a/system/html.php b/system/html.php index c562de09..aba7a563 100644 --- a/system/html.php +++ b/system/html.php @@ -227,6 +227,11 @@ class HTML { foreach ($attributes as $key => $value) { + if (is_numeric($key)) + { + $key = $value; + } + if ( ! is_null($value)) { $html[] = $key.'="'.static::entities($value).'"'; @@ -300,6 +305,8 @@ class HTML { array_unshift($parameters, substr($method, 8)); return forward_static_call_array('HTML::link_to_route', $parameters); } + + throw new \Exception("Static method [$method] is not defined on the HTML class."); } } \ No newline at end of file From 2da478cc38d304ca27a7e0506e68df90ce54cd87 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Sat, 2 Jul 2011 08:43:12 -0500 Subject: [PATCH 004/206] added more html5 form elements. --- system/form.php | 53 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) diff --git a/system/form.php b/system/form.php index ad0b69f2..1a4d7847 100644 --- a/system/form.php +++ b/system/form.php @@ -194,6 +194,19 @@ class Form { return static::input('search', $name, $value, $attributes); } + /** + * Create a HTML color input element. + * + * @param string $name + * @param string $value + * @param array $attributes + * @return string + */ + public static function color($name, $value = null, $attributes = array()) + { + return static::input('color', $name, $value, $attributes); + } + /** * Create a HTML number input element. * @@ -207,6 +220,19 @@ class Form { return static::input('number', $name, $value, $attributes); } + /** + * Create a HTML range input element. + * + * @param string $name + * @param string $value + * @param array $attributes + * @return string + */ + public static function range($name, $value = null, $attributes = array()) + { + return static::input('range', $name, $value, $attributes); + } + /** * Create a HTML telephone input element. * @@ -220,6 +246,19 @@ class Form { return static::input('tel', $name, $value, $attributes); } + /** + * Create a HTML date input element. + * + * @param string $name + * @param string $value + * @param array $attributes + * @return string + */ + public static function date($name, $value = null, $attributes = array()) + { + return static::input('date', $name, $value, $attributes); + } + /** * Create a HTML file input element. * @@ -235,7 +274,7 @@ class Form { /** * Create a HTML submit input element. * - * @param string $name + * @param string $value * @param array $attributes * @return string */ @@ -244,6 +283,18 @@ class Form { return static::input('submit', null, $value, $attributes); } + /** + * Create a HTML reset input element. + * + * @param string $value + * @param array $attributes + * @return string + */ + public static function reset($value, $attributes = array()) + { + return static::input('reset', null, $value, $attributes); + } + /** * Create a HTML button element. * From f7dcb057990b1f00051729f4d0ebb09253e9255f Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Sat, 2 Jul 2011 08:51:14 -0500 Subject: [PATCH 005/206] added more html5 form elements. --- application/routes.php | 1 - system/form.php | 51 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/application/routes.php b/application/routes.php index c457010e..e9f03c02 100644 --- a/application/routes.php +++ b/application/routes.php @@ -19,7 +19,6 @@ return array( 'GET /' => function() { - return Form::input('email', 'email', '', array('required', 'class' => 'awesome')); return View::make('home/index'); }, diff --git a/system/form.php b/system/form.php index 1a4d7847..219d5d00 100644 --- a/system/form.php +++ b/system/form.php @@ -259,6 +259,45 @@ class Form { return static::input('date', $name, $value, $attributes); } + /** + * Create a HTML time input element. + * + * @param string $name + * @param string $value + * @param array $attributes + * @return string + */ + public static function time($name, $value = null, $attributes = array()) + { + return static::input('time', $name, $value, $attributes); + } + + /** + * Create a HTML datetime input element. + * + * @param string $name + * @param string $value + * @param array $attributes + * @return string + */ + public static function datetime($name, $value = null, $attributes = array()) + { + return static::input('datetime', $name, $value, $attributes); + } + + /** + * Create a HTML local datetime input element. + * + * @param string $name + * @param string $value + * @param array $attributes + * @return string + */ + public static function datetime_local($name, $value = null, $attributes = array()) + { + return static::input('datetime-local', $name, $value, $attributes); + } + /** * Create a HTML file input element. * @@ -295,6 +334,18 @@ class Form { return static::input('reset', null, $value, $attributes); } + /** + * Create a HTML image input element. + * + * @param string $value + * @param array $attributes + * @return string + */ + public static function image($value, $attributes = array()) + { + return static::input('image', null, $value, $attributes); + } + /** * Create a HTML button element. * From 4fe64b3a73f26e8f90db1b9e435dbb44b7d2fc07 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Sun, 3 Jul 2011 12:51:11 -0500 Subject: [PATCH 006/206] remove broken link from routes file. --- application/routes.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/application/routes.php b/application/routes.php index e9f03c02..229a673a 100644 --- a/application/routes.php +++ b/application/routes.php @@ -13,8 +13,6 @@ return array( | in this file. It's a breeze. Just tell Laravel the request method and | URI a function should respond to. | - | To learn more, check out: http://laravel.com/docs/basics/routes - | */ 'GET /' => function() From 1375ff98f218248e889d8613f7f1f5b7e2690648 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Sun, 3 Jul 2011 12:55:23 -0500 Subject: [PATCH 007/206] cleaning up form class. --- system/form.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/system/form.php b/system/form.php index 219d5d00..5d3c9d53 100644 --- a/system/form.php +++ b/system/form.php @@ -24,11 +24,13 @@ class Form { $action = Request::uri(); } - $action = URL::to($action); - - $attributes['action'] = HTML::entities($action); + $attributes['action'] = HTML::entities(URL::to($action)); $attributes['method'] = ($method == 'GET' or $method == 'POST') ? $method : 'POST'; + // ------------------------------------------------------- + // Set the default character set if it hasn't already been + // set in the attributes. + // ------------------------------------------------------- if ( ! array_key_exists('accept-charset', $attributes)) { $attributes['accept-charset'] = Config::get('application.encoding'); From c3ea6e656d1bd4d6c51a9d08f45354ccb0f2cac0 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Sun, 3 Jul 2011 12:58:41 -0500 Subject: [PATCH 008/206] adding some comments to the form class. --- system/form.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/system/form.php b/system/form.php index 5d3c9d53..85335d38 100644 --- a/system/form.php +++ b/system/form.php @@ -25,6 +25,11 @@ class Form { } $attributes['action'] = HTML::entities(URL::to($action)); + + // ------------------------------------------------------- + // If the request method is PUT or DELETE, we'll default + // the request method to POST. + // ------------------------------------------------------- $attributes['method'] = ($method == 'GET' or $method == 'POST') ? $method : 'POST'; // ------------------------------------------------------- From c3f418b13c25c065e6da621d2e8f7edb5d568af7 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Sun, 3 Jul 2011 13:03:00 -0500 Subject: [PATCH 009/206] cleaning up form class. --- system/form.php | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/system/form.php b/system/form.php index 85335d38..d4264636 100644 --- a/system/form.php +++ b/system/form.php @@ -464,13 +464,12 @@ class Form { $html_options = array(); + // ------------------------------------------------------- + // Build the HTML options for the drop-down. + // ------------------------------------------------------- foreach ($options as $value => $display) { - $option_attributes = array(); - - $option_attributes['value'] = HTML::entities($value); - $option_attributes['selected'] = ($value == $selected) ? 'selected' : null; - + $option_attributes = array('value' => HTML::entities($value), 'selected' => ($value == $selected) ? 'selected' : null); $html_options[] = ''.HTML::entities($display).''; } From a4ca806081c7b81f4224a1d5b9b2360b9ab3fab3 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Sun, 3 Jul 2011 15:34:31 -0500 Subject: [PATCH 010/206] removed spaces and br function from html class. --- system/html.php | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/system/html.php b/system/html.php index aba7a563..13c88fa7 100644 --- a/system/html.php +++ b/system/html.php @@ -149,28 +149,6 @@ class HTML { return ''; } - /** - * Generate HTML breaks. - * - * @param int $count - * @return string - */ - public static function breaks($count = 1) - { - return str_repeat('
', $count); - } - - /** - * Generate non-breaking spaces. - * - * @param int $count - * @return string - */ - public static function spaces($count = 1) - { - return str_repeat(' ', $count); - } - /** * Generate an ordered list. * From 181237e9efa2201cb6d5d51da62173350911eb6b Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 5 Jul 2011 06:57:42 -0700 Subject: [PATCH 011/206] Refactoring Form class. --- system/form.php | 361 +++++++++++++++++------------------------------- 1 file changed, 130 insertions(+), 231 deletions(-) diff --git a/system/form.php b/system/form.php index d4264636..5783a79b 100644 --- a/system/form.php +++ b/system/form.php @@ -19,23 +19,15 @@ class Form { */ public static function open($action = null, $method = 'POST', $attributes = array()) { - if (is_null($action)) - { - $action = Request::uri(); - } - - $attributes['action'] = HTML::entities(URL::to($action)); + $attributes['action'] = HTML::entities(URL::to((is_null($action)) ? Request::uri() : $action)); // ------------------------------------------------------- // If the request method is PUT or DELETE, we'll default - // the request method to POST. + // the request method to POST since the reqeust method + // is being spoofed by the form. // ------------------------------------------------------- - $attributes['method'] = ($method == 'GET' or $method == 'POST') ? $method : 'POST'; + $attributes['method'] = ($method == 'PUT' or $method == 'DELETE') ? 'POST' : $method; - // ------------------------------------------------------- - // Set the default character set if it hasn't already been - // set in the attributes. - // ------------------------------------------------------- if ( ! array_key_exists('accept-charset', $attributes)) { $attributes['accept-charset'] = Config::get('application.encoding'); @@ -51,7 +43,7 @@ class Form { // ------------------------------------------------------- if ($method == 'PUT' or $method == 'DELETE') { - $html .= PHP_EOL.static::hidden('REQUEST_METHOD', $method); + $html .= PHP_EOL.static::input('hidden', 'REQUEST_METHOD', $method); } return $html.PHP_EOL; @@ -65,20 +57,11 @@ class Form { * @param array $attributes * @return string */ - public static function open_multipart($action = null, $method = 'POST', $attributes = array()) + public static function open_for_files($action = null, $method = 'POST', $attributes = array()) { $attributes['enctype'] = 'multipart/form-data'; - return static::open($action, $method, $attributes); - } - /** - * Close a HTML form. - * - * @return string - */ - public static function close() - { - return ''.PHP_EOL; + return static::open($action, $method, $attributes); } /** @@ -88,7 +71,7 @@ class Form { */ public static function token() { - return static::hidden('csrf_token', static::raw_token()); + return static::input('hidden', 'csrf_token', static::raw_token()); } /** @@ -121,20 +104,21 @@ class Form { public static function label($name, $value, $attributes = array()) { static::$labels[] = $name; + return ''.PHP_EOL; } /** - * Create a HTML text input element. + * Create a HTML input element. * * @param string $name - * @param string $value + * @param mixed $value * @param array $attributes * @return string */ - public static function text($name, $value = null, $attributes = array()) + public static function input($type, $name, $value = null, $attributes = array()) { - return static::input('text', $name, $value, $attributes); + return ' $type, 'name' => $name, 'value' => $value, 'id' => static::id($name, $attributes)))).'>'.PHP_EOL; } /** @@ -143,25 +127,12 @@ class Form { * @param string $name * @param array $attributes * @return string - */ + */ public static function password($name, $attributes = array()) { return static::input('password', $name, null, $attributes); } - /** - * Create a HTML hidden input element. - * - * @param string $name - * @param string $value - * @param array $attributes - * @return string - */ - public static function hidden($name, $value = null, $attributes = array()) - { - return static::input('hidden', $name, $value, $attributes); - } - /** * Create a HTML email input element. * @@ -169,23 +140,23 @@ class Form { * @param string $value * @param array $attributes * @return string - */ + */ public static function email($name, $value = null, $attributes = array()) { return static::input('email', $name, $value, $attributes); } /** - * Create a HTML URL input element. + * Create a HTML telephone input element. * * @param string $name * @param string $value * @param array $attributes * @return string - */ - public static function url($name, $value = null, $attributes = array()) + */ + public static function telephone($name, $value = null, $attributes = array()) { - return static::input('url', $name, $value, $attributes); + return static::input('tel', $name, $value, $attributes); } /** @@ -195,12 +166,25 @@ class Form { * @param string $value * @param array $attributes * @return string - */ + */ public static function search($name, $value = null, $attributes = array()) { return static::input('search', $name, $value, $attributes); } + /** + * Create a HTML URL input element. + * + * @param string $name + * @param string $value + * @param array $attributes + * @return string + */ + public static function url($name, $value = null, $attributes = array()) + { + return static::input('url', $name, $value, $attributes); + } + /** * Create a HTML color input element. * @@ -208,12 +192,25 @@ class Form { * @param string $value * @param array $attributes * @return string - */ + */ public static function color($name, $value = null, $attributes = array()) { return static::input('color', $name, $value, $attributes); } + /** + * Create a HTML date input element. + * + * @param string $name + * @param string $value + * @param array $attributes + * @return string + */ + public static function date($name, $value = null, $attributes = array()) + { + return static::input('date', $name, $value, $attributes); + } + /** * Create a HTML number input element. * @@ -221,7 +218,7 @@ class Form { * @param string $value * @param array $attributes * @return string - */ + */ public static function number($name, $value = null, $attributes = array()) { return static::input('number', $name, $value, $attributes); @@ -234,77 +231,12 @@ class Form { * @param string $value * @param array $attributes * @return string - */ + */ public static function range($name, $value = null, $attributes = array()) { return static::input('range', $name, $value, $attributes); } - /** - * Create a HTML telephone input element. - * - * @param string $name - * @param string $value - * @param array $attributes - * @return string - */ - public static function tel($name, $value = null, $attributes = array()) - { - return static::input('tel', $name, $value, $attributes); - } - - /** - * Create a HTML date input element. - * - * @param string $name - * @param string $value - * @param array $attributes - * @return string - */ - public static function date($name, $value = null, $attributes = array()) - { - return static::input('date', $name, $value, $attributes); - } - - /** - * Create a HTML time input element. - * - * @param string $name - * @param string $value - * @param array $attributes - * @return string - */ - public static function time($name, $value = null, $attributes = array()) - { - return static::input('time', $name, $value, $attributes); - } - - /** - * Create a HTML datetime input element. - * - * @param string $name - * @param string $value - * @param array $attributes - * @return string - */ - public static function datetime($name, $value = null, $attributes = array()) - { - return static::input('datetime', $name, $value, $attributes); - } - - /** - * Create a HTML local datetime input element. - * - * @param string $name - * @param string $value - * @param array $attributes - * @return string - */ - public static function datetime_local($name, $value = null, $attributes = array()) - { - return static::input('datetime-local', $name, $value, $attributes); - } - /** * Create a HTML file input element. * @@ -318,52 +250,51 @@ class Form { } /** - * Create a HTML submit input element. - * - * @param string $value - * @param array $attributes - * @return string - */ - public static function submit($value, $attributes = array()) - { - return static::input('submit', null, $value, $attributes); - } - - /** - * Create a HTML reset input element. - * - * @param string $value - * @param array $attributes - * @return string - */ - public static function reset($value, $attributes = array()) - { - return static::input('reset', null, $value, $attributes); - } - - /** - * Create a HTML image input element. - * - * @param string $value - * @param array $attributes - * @return string - */ - public static function image($value, $attributes = array()) - { - return static::input('image', null, $value, $attributes); - } - - /** - * Create a HTML button element. + * Create a HTML textarea element. * * @param string $name * @param string $value * @param array $attributes * @return string */ - public static function button($value, $attributes = array()) + public static function textarea($name, $value = '', $attributes = array()) { - return ''.HTML::entities($value).''.PHP_EOL; + $attributes = array_merge($attributes, array('id' => static::id($name, $attributes), 'name' => $name)); + + if ( ! isset($attributes['rows'])) + { + $attributes['rows'] = 10; + } + + if ( ! isset($attributes['cols'])) + { + $attributes['cols'] = 50; + } + + return ''.HTML::entities($value).''.PHP_EOL; + } + + /** + * Create a HTML select element. + * + * @param string $name + * @param array $options + * @param string $selected + * @param array $attributes + * @return string + */ + public static function select($name, $options = array(), $selected = null, $attributes = array()) + { + $attributes = array_merge($attributes, array('id' => static::id($name, $attributes), 'name' => $name)); + + $html = array(); + + foreach ($options as $value => $display) + { + $html[] = ' HTML::entities($value), 'selected' => ($value == $selected) ? 'selected' : null)).'>'.HTML::entities($display).''; + } + + return ''.implode('', $html).''.PHP_EOL; } /** @@ -406,92 +337,60 @@ class Form { */ private static function checkable($type, $name, $value, $checked, $attributes) { - if ($checked === true) - { - $attributes['checked'] = 'checked'; - } - - $attributes['id'] = static::id($name, $attributes); + $attributes = array_merge($attributes, array('id' => static::id($name, $attributes), 'checked' => ($checked) ? 'checked' : null)); return static::input($type, $name, $value, $attributes); } /** - * Create a HTML textarea element. + * Create a HTML submit input element. + * + * @param string $value + * @param array $attributes + * @return string + */ + public static function submit($value, $attributes = array()) + { + return static::input('submit', null, $value, $attributes); + } + + /** + * Create a HTML reset input element. + * + * @param string $value + * @param array $attributes + * @return string + */ + public static function reset($value, $attributes = array()) + { + return static::input('reset', null, $value, $attributes); + } + + /** + * Create a HTML image input element. + * + * @param string $url + * @param array $attributes + * @return string + */ + public static function image($url, $name = null, $attributes = array()) + { + $attributes['src'] = URL::to_asset($url); + + return static::input('image', $name, null, $attributes); + } + + /** + * Create a HTML button element. * * @param string $name * @param string $value * @param array $attributes * @return string */ - public static function textarea($name, $value = '', $attributes = array()) + public static function button($value, $attributes = array()) { - $attributes['name'] = $name; - $attributes['id'] = static::id($name, $attributes); - - // ------------------------------------------------------- - // Set the default number of rows. - // ------------------------------------------------------- - if ( ! isset($attributes['rows'])) - { - $attributes['rows'] = 10; - } - - // ------------------------------------------------------- - // Set the default number of columns. - // ------------------------------------------------------- - if ( ! isset($attributes['cols'])) - { - $attributes['cols'] = 50; - } - - return ''.HTML::entities($value).''.PHP_EOL; - } - - /** - * Create a HTML select element. - * - * @param string $name - * @param array $options - * @param string $selected - * @param array $attributes - * @return string - */ - public static function select($name, $options = array(), $selected = null, $attributes = array()) - { - $attributes['name'] = $name; - $attributes['id'] = static::id($name, $attributes); - - $html_options = array(); - - // ------------------------------------------------------- - // Build the HTML options for the drop-down. - // ------------------------------------------------------- - foreach ($options as $value => $display) - { - $option_attributes = array('value' => HTML::entities($value), 'selected' => ($value == $selected) ? 'selected' : null); - $html_options[] = ''.HTML::entities($display).''; - } - - return ''.implode('', $html_options).''.PHP_EOL; - } - - /** - * Create a HTML input element. - * - * @param string $name - * @param mixed $value - * @param array $attributes - * @return string - */ - public static function input($type, $name, $value = null, $attributes = array()) - { - $attributes['type'] = $type; - $attributes['name'] = $name; - $attributes['value'] = $value; - $attributes['id'] = static::id($name, $attributes); - - return ''.PHP_EOL; + return ''.HTML::entities($value).''.PHP_EOL; } /** @@ -505,7 +404,7 @@ class Form { { // ------------------------------------------------------- // If an ID attribute was already explicitly specified - // for the element, just use that. + // for the element in its attributes, use that ID. // ------------------------------------------------------- if (array_key_exists('id', $attributes)) { From 64704cf5c46539688e473dc41b2dc625e2d2bb9b Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 5 Jul 2011 06:58:27 -0700 Subject: [PATCH 012/206] Added comments to HTML class. --- system/html.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/system/html.php b/system/html.php index 13c88fa7..18a680bd 100644 --- a/system/html.php +++ b/system/html.php @@ -146,6 +146,7 @@ class HTML { public static function image($url, $alt = '', $attributes = array()) { $attributes['alt'] = static::entities($alt); + return ''; } @@ -205,6 +206,11 @@ class HTML { foreach ($attributes as $key => $value) { + // ------------------------------------------------------- + // If the attribute key is numeric, assign the attribute + // value to the key. This allows for attributes such as + // "required", "checked", etc. + // ------------------------------------------------------- if (is_numeric($key)) { $key = $value; From 4ace7c8f80514b4b646c7d952eb2e4193480a436 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 5 Jul 2011 08:29:03 -0700 Subject: [PATCH 013/206] Cleaning up Config class comments. --- system/config.php | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/system/config.php b/system/config.php index d0105ac1..c726bc26 100644 --- a/system/config.php +++ b/system/config.php @@ -30,11 +30,8 @@ class Config { public static function get($key, $default = null) { // ----------------------------------------------------- - // If a dot is not present, we will just return the + // If no dot is in the key, we will just return the // entire configuration array. - // - // If the configuration file does not exist, the default - // value will be returned. // ----------------------------------------------------- if(strpos($key, '.') === false) { @@ -90,9 +87,6 @@ class Config { // The left side of the dot is the file name, while // the right side of the dot is the item within that // file being requested. - // - // This syntax allows for the easy retrieval and setting - // of configuration items. // ----------------------------------------------------- $segments = explode('.', $key); From c480e19b6c3e6189eded303deb2fdc7da488a644 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 5 Jul 2011 09:49:38 -0700 Subject: [PATCH 014/206] Added better support for aliases column to Query class. --- system/db/query.php | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/system/db/query.php b/system/db/query.php index 85e02b2f..5f0a0bd3 100644 --- a/system/db/query.php +++ b/system/db/query.php @@ -120,8 +120,41 @@ class Query { public function select() { $this->select = ($this->distinct) ? 'SELECT DISTINCT ' : 'SELECT '; - $this->select .= implode(', ', array_map(array($this, 'wrap'), func_get_args())); + $columns = array(); + + foreach (func_get_args() as $column) + { + // --------------------------------------------------------- + // If the column name is being aliases, we will need to + // wrap the column name and its alias. + // --------------------------------------------------------- + if (strpos(strtolower($column), ' as ') !== false) + { + $segments = explode(' ', $column); + + $columns[] = $this->wrap($segments[0]).' AS '.$this->wrap($segments[2]); + } + else + { + $columns[] = $this->wrap($column); + } + } + + $this->select .= implode(', ', $columns); + + return $this; + } + + /** + * Set the FROM clause. + * + * @param string $from + * @return Query + */ + public function from($from) + { + $this->from = $from; return $this; } From 622635d5cfe35f77638d4958535f303b6640cc25 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 5 Jul 2011 09:54:31 -0700 Subject: [PATCH 015/206] Fixed typo in Query::select comment. --- system/db/query.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/db/query.php b/system/db/query.php index 5f0a0bd3..d2a7a1d9 100644 --- a/system/db/query.php +++ b/system/db/query.php @@ -126,7 +126,7 @@ class Query { foreach (func_get_args() as $column) { // --------------------------------------------------------- - // If the column name is being aliases, we will need to + // If the column name is being aliased, we will need to // wrap the column name and its alias. // --------------------------------------------------------- if (strpos(strtolower($column), ' as ') !== false) From f8b12b5daa12994b00392d74c841f8f11c443dda Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 6 Jul 2011 11:19:04 -0700 Subject: [PATCH 016/206] Added support for retrieving specific elements from the Input::file method. Ex: Input::file('picture.size')... --- system/input.php | 44 ++++++++++++++++++++++++++------------------ 1 file changed, 26 insertions(+), 18 deletions(-) diff --git a/system/input.php b/system/input.php index ffcc911c..0c414af9 100644 --- a/system/input.php +++ b/system/input.php @@ -34,7 +34,7 @@ class Input { static::hydrate(); } - return Arr::get(static::$input, $key, $default); + return (array_key_exists($key, static::$input)) ? static::$input[$key] : $default; } /** @@ -51,39 +51,57 @@ class Input { /** * Get input data from the previous request. * + * Since input data is flashed to the session, a session driver must be specified + * in order to use this method. + * * @param string $key * @param mixed $default * @return string */ public static function old($key = null, $default = null) { - // ---------------------------------------------------------- - // Since old input data is flashed to the session, we need - // to make sure a session driver has been specified. - // ---------------------------------------------------------- if (Config::get('session.driver') == '') { throw new \Exception("Sessions must be enabled to retrieve old input data."); } - return Arr::get(Session::get('laravel_old_input', array()), $key, $default); + return (array_key_exists($key, $old = Session::get('laravel_old_input', array()))) ? $old[$key] : $default; } /** * Get an item from the uploaded file data. * + * If a "dot" is present in the key. A specific element will be returned from + * the specified file array. + * + * Example: Input::file('picture.size'); + * + * The statement above will return the value of $_FILES['picture']['size']. + * * @param string $key * @param mixed $default * @return array */ public static function file($key = null, $default = null) { - return Arr::get($_FILES, $key, $default); + if (strpos($key, '.') !== false) + { + list($file, $key) = explode('.', $key); + + return (isset($_FILES[$file][$key])) ? $_FILES[$file][$key] : $default; + } + + return (array_key_exists($key, $_FILES)) ? $_FILES[$key] : $default; } /** * Hydrate the input data for the request. * + * Typically, browsers do not support PUT and DELETE methods on HTML forms. So, they are simulated + * by Laravel using a hidden POST element. If the request method is being "spoofed", the POST + * array will be moved into the PUT / DELETE array. True "PUT" or "DELETE" rqeuests will be read + * from the php://input file. + * * @return void */ public static function hydrate() @@ -100,20 +118,10 @@ class Input { case 'PUT': case 'DELETE': - // ---------------------------------------------------------------------- - // Typically, browsers do not support PUT and DELETE methods on HTML - // forms. So, we simulate them using a hidden POST variable. - // - // If the request method is being "spoofed", we'll move the POST array - // into the PUT / DELETE array. - // ---------------------------------------------------------------------- - if (isset($_POST['request_method']) and ($_POST['request_method'] == 'PUT' or $_POST['request_method'] == 'DELETE')) + if (isset($_POST['REQUEST_METHOD']) and in_array($_POST['REQUEST_METHOD'], array('PUT', 'DELETE'))) { static::$input =& $_POST; } - // ---------------------------------------------------------------------- - // If the request is a true PUT request, read the php://input file. - // ---------------------------------------------------------------------- else { parse_str(file_get_contents('php://input'), static::$input); From 1a82d9c54fb1c08e6a28d90576e3026d8d5bb431 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 6 Jul 2011 11:23:14 -0700 Subject: [PATCH 017/206] Removed usage of Arr from Request class. --- system/request.php | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/system/request.php b/system/request.php index 70986c41..0f65959a 100644 --- a/system/request.php +++ b/system/request.php @@ -28,16 +28,10 @@ class Request { return static::$uri; } - // ------------------------------------------------------- - // Use the PATH_INFO variable if it is available. - // ------------------------------------------------------- if (isset($_SERVER['PATH_INFO'])) { $uri = $_SERVER['PATH_INFO']; } - // ------------------------------------------------------- - // No PATH_INFO? Let's try REQUEST_URI. - // ------------------------------------------------------- elseif (isset($_SERVER['REQUEST_URI'])) { $uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH); @@ -84,15 +78,14 @@ class Request { /** * Get the request method. * + * The request method may be spoofed if a hidden "REQUEST_METHOD" POST element + * is present, allowing HTML forms to simulate PUT and DELETE requests. + * * @return string */ public static function method() { - // -------------------------------------------------------------- - // The method can be spoofed using a POST variable, allowing HTML - // forms to simulate PUT and DELETE requests. - // -------------------------------------------------------------- - return Arr::get($_POST, 'REQUEST_METHOD', $_SERVER['REQUEST_METHOD']); + return (array_key_exists('REQUEST_METHOD', $_POST)) ? $_POST['REQUEST_METHOD'] : $_SERVER['REQUEST_METHOD']; } /** @@ -162,11 +155,6 @@ class Request { */ public static function __callStatic($method, $parameters) { - // -------------------------------------------------------------- - // Dynamically call the "is" method using the given name. - // - // Example: Request::is_login() - // -------------------------------------------------------------- if (strpos($method, 'route_is_') === 0) { return static::route_is(substr($method, 9)); From 629122a5a965b0734251494f54ce57fac0e8f09e Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 6 Jul 2011 11:24:14 -0700 Subject: [PATCH 018/206] Remove usage of Arr from Cookie class. --- system/cookie.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/cookie.php b/system/cookie.php index 3b01226f..90dacc4d 100644 --- a/system/cookie.php +++ b/system/cookie.php @@ -107,7 +107,7 @@ class Cookie { */ public static function get($name, $default = null) { - return Arr::get($_COOKIE, $name, $default); + return (array_key_exists($name, $_COOKIE)) ? $_COOKIE[$name] : $default; } /** From 0e02a8a53df9a11d8fa88ca5ebfde8615e96605e Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 6 Jul 2011 13:34:55 -0700 Subject: [PATCH 019/206] Remove unnecessary comments from Input class. --- system/input.php | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/system/input.php b/system/input.php index 0c414af9..dee58d52 100644 --- a/system/input.php +++ b/system/input.php @@ -51,9 +51,6 @@ class Input { /** * Get input data from the previous request. * - * Since input data is flashed to the session, a session driver must be specified - * in order to use this method. - * * @param string $key * @param mixed $default * @return string @@ -71,13 +68,6 @@ class Input { /** * Get an item from the uploaded file data. * - * If a "dot" is present in the key. A specific element will be returned from - * the specified file array. - * - * Example: Input::file('picture.size'); - * - * The statement above will return the value of $_FILES['picture']['size']. - * * @param string $key * @param mixed $default * @return array @@ -97,11 +87,6 @@ class Input { /** * Hydrate the input data for the request. * - * Typically, browsers do not support PUT and DELETE methods on HTML forms. So, they are simulated - * by Laravel using a hidden POST element. If the request method is being "spoofed", the POST - * array will be moved into the PUT / DELETE array. True "PUT" or "DELETE" rqeuests will be read - * from the php://input file. - * * @return void */ public static function hydrate() @@ -118,6 +103,8 @@ class Input { case 'PUT': case 'DELETE': + // The request method can be spoofed by specifying a "REQUEST_METHOD" in the $_POST array. + // If the method is being spoofed, the $_POST array will be considered the input. if (isset($_POST['REQUEST_METHOD']) and in_array($_POST['REQUEST_METHOD'], array('PUT', 'DELETE'))) { static::$input =& $_POST; From 6fc0770bcee09045366c61afcf442337a98e44b2 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 6 Jul 2011 13:35:59 -0700 Subject: [PATCH 020/206] Continuing to clean up Input class. --- system/input.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/system/input.php b/system/input.php index dee58d52..97caf658 100644 --- a/system/input.php +++ b/system/input.php @@ -103,8 +103,10 @@ class Input { case 'PUT': case 'DELETE': + // The request method can be spoofed by specifying a "REQUEST_METHOD" in the $_POST array. // If the method is being spoofed, the $_POST array will be considered the input. + if (isset($_POST['REQUEST_METHOD']) and in_array($_POST['REQUEST_METHOD'], array('PUT', 'DELETE'))) { static::$input =& $_POST; From 39d3ccb8f01afbd7476b76ebc7d6a6c9801c7249 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 6 Jul 2011 13:37:59 -0700 Subject: [PATCH 021/206] Refactoring / comment clean-up on Auth class. --- system/auth.php | 23 ++++------------------- 1 file changed, 4 insertions(+), 19 deletions(-) diff --git a/system/auth.php b/system/auth.php index 817e0a84..9e247094 100644 --- a/system/auth.php +++ b/system/auth.php @@ -29,15 +29,12 @@ class Auth { /** * Get the current user of the application. * + * The user will be loaded using the user ID stored in the session. + * * @return object */ public static function user() { - // ----------------------------------------------------- - // Verify that sessions are enabled. Since the user ID - // is stored in the session, we can't authenticate - // without a session driver specified. - // ----------------------------------------------------- if (Config::get('session.driver') == '') { throw new \Exception("You must specify a session driver before using the Auth class."); @@ -45,9 +42,6 @@ class Auth { $model = static::model(); - // ----------------------------------------------------- - // Load the user using the ID stored in the session. - // ----------------------------------------------------- if (is_null(static::$user) and Session::has(static::$key)) { static::$user = $model::find(Session::get(static::$key)); @@ -70,11 +64,8 @@ class Auth { if ( ! is_null($user)) { - // ----------------------------------------------------- - // Hash the password. If a salt is present on the user - // record, we will recreate the hashed password using - // the salt. Otherwise, we will just use a plain hash. - // ----------------------------------------------------- + // If a salt is present on the user record, we will recreate the hashed password + // using the salt. Otherwise, we will just use a plain hash. $password = (isset($user->salt)) ? Hash::make($password, $user->salt)->value : sha1($password); if ($user->password === $password) @@ -97,13 +88,7 @@ class Auth { */ public static function logout() { - // ----------------------------------------------------- - // By removing the user ID from the session, the user - // will no longer be considered logged in on subsequent - // requests to the application. - // ----------------------------------------------------- Session::forget(static::$key); - static::$user = null; } From a3401d52474ec5fab2426310cf4a4c8b9f59b456 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 6 Jul 2011 13:40:03 -0700 Subject: [PATCH 022/206] Cleaning up Cache class. --- system/cache.php | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/system/cache.php b/system/cache.php index 6113b906..f3694b58 100644 --- a/system/cache.php +++ b/system/cache.php @@ -10,19 +10,13 @@ class Cache { private static $drivers = array(); /** - * Get the cache driver instance. + * Get a cache driver instance. Cache drivers are singletons. * * @param string $driver * @return Cache\Driver */ public static function driver($driver = null) { - // -------------------------------------------------- - // If the cache driver has already been instantiated, - // we'll just return that existing instance. - // - // Otherwise, we'll instantiate a new one. - // -------------------------------------------------- if ( ! array_key_exists($driver, static::$drivers)) { if (is_null($driver)) @@ -41,13 +35,9 @@ class Cache { */ public static function __callStatic($method, $parameters) { - // -------------------------------------------------- - // Passing method calls to the driver instance allows - // a better API for the developer. - // - // For instance, instead of saying Cache::driver()->foo(), - // we can now just say Cache::foo(). - // -------------------------------------------------- + // Passing method calls to the driver instance provides a better API for the + // developer. For instance, instead of saying Cache::driver()->foo(), we can + // now just say Cache::foo(). return call_user_func_array(array(static::driver(), $method), $parameters); } From 1e84c8901a8e29e6ec33860bc036fcff70526cf7 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 6 Jul 2011 13:40:34 -0700 Subject: [PATCH 023/206] Formatting Cache class. --- system/cache.php | 1 + 1 file changed, 1 insertion(+) diff --git a/system/cache.php b/system/cache.php index f3694b58..acf800b5 100644 --- a/system/cache.php +++ b/system/cache.php @@ -38,6 +38,7 @@ class Cache { // Passing method calls to the driver instance provides a better API for the // developer. For instance, instead of saying Cache::driver()->foo(), we can // now just say Cache::foo(). + return call_user_func_array(array(static::driver(), $method), $parameters); } From 65ffe0b6103fe93a1a19f471405d6530379e9887 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 6 Jul 2011 13:45:03 -0700 Subject: [PATCH 024/206] Added support for closures as Arr::get default parameter. --- system/arr.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/system/arr.php b/system/arr.php index 0b6e7235..24be1f2b 100644 --- a/system/arr.php +++ b/system/arr.php @@ -7,7 +7,7 @@ class Arr { * * @param array $array * @param string $key - * @param array $default + * @param mixed $default * @return mixed */ public static function get($array, $key, $default = null) @@ -17,7 +17,12 @@ class Arr { return $array; } - return (array_key_exists($key, $array)) ? $array[$key] : $default; + if (array_key_exists($key, $array)) + { + return $array[$key]; + } + + return is_callable($default) ? call_user_func($default) : $default; } } \ No newline at end of file From 3015f24e7a79fa945e0d8bfebdfd6606a0574dde Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 6 Jul 2011 13:52:11 -0700 Subject: [PATCH 025/206] Added arr.php to front controller includes. --- public/index.php | 1 + 1 file changed, 1 insertion(+) diff --git a/public/index.php b/public/index.php index 673b7b0c..2c296c50 100644 --- a/public/index.php +++ b/public/index.php @@ -32,6 +32,7 @@ define('EXT', '.php'); // Load the configuration class. // -------------------------------------------------------------- require SYS_PATH.'config'.EXT; +require SYS_PATH.'arr'.EXT; // -------------------------------------------------------------- // Register the auto-loader. From 48acf1d27363acf92eceb46850717fcc4cc9a7e0 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 6 Jul 2011 13:53:00 -0700 Subject: [PATCH 026/206] Refactoring Config class to use Arr. Removed unnecessary comments. --- system/config.php | 33 ++++++++------------------------- 1 file changed, 8 insertions(+), 25 deletions(-) diff --git a/system/config.php b/system/config.php index c726bc26..ceb4aa74 100644 --- a/system/config.php +++ b/system/config.php @@ -29,34 +29,25 @@ class Config { */ public static function get($key, $default = null) { - // ----------------------------------------------------- - // If no dot is in the key, we will just return the - // entire configuration array. - // ----------------------------------------------------- + // If no "dot" is present in the key, return the entire configuration array. if(strpos($key, '.') === false) { static::load($key); - return (array_key_exists($key, static::$items)) ? static::$items[$key] : $default; + return Arr::get(static::$items, $key, $default); } list($file, $key) = static::parse($key); static::load($file); - // ----------------------------------------------------- - // If the file doesn't exist, return the default. - // ----------------------------------------------------- + // Verify that the configuration file actually exists. if ( ! array_key_exists($file, static::$items)) { return $default; } - // ----------------------------------------------------- - // Return the configuration item. If the item doesn't - // exist, the default value will be returned. - // ----------------------------------------------------- - return (array_key_exists($key, static::$items[$file])) ? static::$items[$file][$key] : $default; + return Arr::get(static::$items[$file], $key, $default); } /** @@ -83,11 +74,9 @@ class Config { */ private static function parse($key) { - // ----------------------------------------------------- - // The left side of the dot is the file name, while - // the right side of the dot is the item within that - // file being requested. - // ----------------------------------------------------- + // The left side of the dot is the file name, while the right side of the dot + // is the item within that file being requested. + $segments = explode('.', $key); if (count($segments) < 2) @@ -99,25 +88,19 @@ class Config { } /** - * Load all of the configuration items. + * Load all of the configuration items from a file. * * @param string $file * @return void */ public static function load($file) { - // ----------------------------------------------------- // Bail out if already loaded or doesn't exist. - // ----------------------------------------------------- if (array_key_exists($file, static::$items) or ! file_exists($path = APP_PATH.'config/'.$file.EXT)) { return; } - // ----------------------------------------------------- - // Load the configuration array into the array of items. - // The items array is keyed by filename. - // ----------------------------------------------------- static::$items[$file] = require $path; } From 45333c57430faea49fd92b5b482dc446683bfb72 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 6 Jul 2011 13:53:25 -0700 Subject: [PATCH 027/206] Formatting Config class. --- system/config.php | 1 - 1 file changed, 1 deletion(-) diff --git a/system/config.php b/system/config.php index ceb4aa74..4c67e90b 100644 --- a/system/config.php +++ b/system/config.php @@ -76,7 +76,6 @@ class Config { { // The left side of the dot is the file name, while the right side of the dot // is the item within that file being requested. - $segments = explode('.', $key); if (count($segments) < 2) From d1f1c367e2763732c957181e7eed229cc1554506 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 6 Jul 2011 14:00:20 -0700 Subject: [PATCH 028/206] Formatting Cache class. --- system/cache.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/system/cache.php b/system/cache.php index acf800b5..7a6e55a1 100644 --- a/system/cache.php +++ b/system/cache.php @@ -32,13 +32,13 @@ class Cache { /** * Pass all other methods to the default driver. + * + * Passing method calls to the driver instance provides a better API for the + * developer. For instance, instead of saying Cache::driver()->foo(), we can + * now just say Cache::foo(). */ public static function __callStatic($method, $parameters) { - // Passing method calls to the driver instance provides a better API for the - // developer. For instance, instead of saying Cache::driver()->foo(), we can - // now just say Cache::foo(). - return call_user_func_array(array(static::driver(), $method), $parameters); } From 65bbea81fdc267d2e6734d74cb764054ebdec73a Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 6 Jul 2011 14:04:48 -0700 Subject: [PATCH 029/206] Put the Cookie class on a diet. --- system/cookie.php | 87 +---------------------------------------------- 1 file changed, 1 insertion(+), 86 deletions(-) diff --git a/system/cookie.php b/system/cookie.php index 90dacc4d..77d6ccc9 100644 --- a/system/cookie.php +++ b/system/cookie.php @@ -2,91 +2,6 @@ class Cookie { - /** - * The cookie name. - * - * @var string - */ - public $name; - - /** - * The cookie value. - * - * @var mixed - */ - public $value; - - /** - * The number of minutes the cookie should live. - * - * @var int - */ - public $lifetime = 0; - - /** - * The path for which the cookie is available. - * - * @var string - */ - public $path = '/'; - - /** - * The domain for which the cookie is available. - * - * @var string - */ - public $domain = null; - - /** - * Indicates if the cookie should only be sent over HTTPS. - * - * @var bool - */ - public $secure = false; - - /** - * Create a new Cookie instance. - * - * Note: Cookies can be sent using the Cookie::put method. - * However, the number of parameters that method requires - * is somewhat cumbersome. Instantiating a new Cookie class - * and setting the properties can be a little easier on the eyes. - * - * @param string $name - * @return void - */ - public function __construct($name, $value = null) - { - $this->name = $name; - $this->value = $value; - } - - /** - * Create a new Cookie instance. - * - * @param string $name - * @return Cookie - */ - public static function make($name, $value = null) - { - return new static($name, $value); - } - - /** - * Send the current cookie instance to the user's machine. - * - * @return bool - */ - public function send() - { - if (is_null($this->name)) - { - throw new \Exception("Attempting to send cookie without a name."); - } - - return static::put($this->name, $this->value, $this->lifetime, $this->path, $this->domain, $this->secure); - } - /** * Determine if a cookie exists. * @@ -107,7 +22,7 @@ class Cookie { */ public static function get($name, $default = null) { - return (array_key_exists($name, $_COOKIE)) ? $_COOKIE[$name] : $default; + return Arr::get($_COOKIE, $name, $default); } /** From e5beda1d5b276e476e20710733f2613dca1e44ee Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 6 Jul 2011 14:05:16 -0700 Subject: [PATCH 030/206] Converted Session class to use Cookie::put. --- system/session.php | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/system/session.php b/system/session.php index 1a6dc681..75c355e3 100644 --- a/system/session.php +++ b/system/session.php @@ -201,14 +201,9 @@ class Session { // --------------------------------------------------------- if ( ! headers_sent()) { - $cookie = new Cookie('laravel_session', static::$session['id']); + $minutes = (Config::get('session.expire_on_close')) ? 0 : Config::get('session.lifetime'); - $cookie->lifetime = (Config::get('session.expire_on_close')) ? 0 : Config::get('session.lifetime'); - $cookie->path = Config::get('session.path'); - $cookie->domain = Config::get('session.domain'); - $cookie->secure = Config::get('session.https'); - - $cookie->send(); + Cookie::put('laravel_session', static::$session['id'], $minutes, Config::get('session.path'), Config::get('session.domain'), Config::get('session.https')); } // --------------------------------------------------------- From 3c7dd2822b2a745725f8c2d70b07167d006498d6 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 6 Jul 2011 14:08:50 -0700 Subject: [PATCH 031/206] Trimming comment bloat in Crypt class. --- system/crypt.php | 22 +++------------------- 1 file changed, 3 insertions(+), 19 deletions(-) diff --git a/system/crypt.php b/system/crypt.php index fc338de9..4174251b 100644 --- a/system/crypt.php +++ b/system/crypt.php @@ -24,10 +24,6 @@ class Crypt { */ public static function encrypt($value) { - // ----------------------------------------------------- - // Determine the input vector source. Different servers - // and operating systems will have varying options. - // ----------------------------------------------------- if (defined('MCRYPT_DEV_URANDOM')) { $random = MCRYPT_DEV_URANDOM; @@ -41,21 +37,17 @@ class Crypt { $random = MCRYPT_RAND; } - // ----------------------------------------------------- - // The system random number generator must be seeded - // to produce adequately random results. - // ----------------------------------------------------- + // The system random number generator must be seeded to produce random results. if ($random === MCRYPT_RAND) { mt_srand(); } $iv = mcrypt_create_iv(static::iv_size(), $random); + $value = mcrypt_encrypt(static::$cipher, static::key(), $value, static::$mode, $iv); - // ----------------------------------------------------- - // We use base64 encoding to get a nice string value. - // ----------------------------------------------------- + // Use base64 encoding to get a nice string value. return base64_encode($iv.$value); } @@ -67,10 +59,6 @@ class Crypt { */ public static function decrypt($value) { - // ----------------------------------------------------- - // Since all of our encrypted values are base64 encoded, - // we will decode the value here and verify it. - // ----------------------------------------------------- $value = base64_decode($value, true); if ( ! $value) @@ -78,14 +66,10 @@ class Crypt { throw new \Exception('Decryption error. Input value is not valid base64 data.'); } - // ----------------------------------------------------- // Extract the input vector from the value. - // ----------------------------------------------------- $iv = substr($value, 0, static::iv_size()); - // ----------------------------------------------------- // Remove the input vector from the encrypted value. - // ----------------------------------------------------- $value = substr($value, static::iv_size()); return rtrim(mcrypt_decrypt(static::$cipher, static::key(), $value, static::$mode, $iv), "\0"); From 014c2ebf6691686d686a99fbee2af3adc41cdd05 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 6 Jul 2011 14:11:44 -0700 Subject: [PATCH 032/206] Removing comment bloat from DB class. --- system/db.php | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/system/db.php b/system/db.php index 0f064ef8..34d9d743 100644 --- a/system/db.php +++ b/system/db.php @@ -10,7 +10,7 @@ class DB { private static $connections = array(); /** - * Get a database connection. + * Get a database connection. Database connections are managed as singletons. * * @param string $connection * @return PDO @@ -22,12 +22,6 @@ class DB { $connection = Config::get('db.default'); } - // --------------------------------------------------- - // If we have already established this connection, - // simply return the existing connection. - // - // Don't want to establish the same connection twice! - // --------------------------------------------------- if ( ! array_key_exists($connection, static::$connections)) { $config = Config::get('db.connections'); @@ -46,6 +40,13 @@ class DB { /** * Execute a SQL query against the connection. * + * The method returns the following based on query type: + * + * SELECT -> Array of stdClasses + * UPDATE -> Number of rows affected. + * DELETE -> Number of Rows affected. + * ELSE -> Boolean true / false depending on success. + * * @param string $sql * @param array $bindings * @param string $connection @@ -57,15 +58,6 @@ class DB { $result = $query->execute($bindings); - // --------------------------------------------------- - // For SELECT statements, the results will be returned - // as an array of stdClasses. - // - // For UPDATE and DELETE statements, the number of - // rows affected by the query will be returned. - // - // For all other statements, return a boolean. - // --------------------------------------------------- if (strpos(strtoupper($sql), 'SELECT') === 0) { return $query->fetchAll(\PDO::FETCH_CLASS, 'stdClass'); From fa0e6c8db116c904233aea00b104524d85adee2f Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 6 Jul 2011 14:13:57 -0700 Subject: [PATCH 033/206] Trimming comment bloat from Error class. --- system/error.php | 24 ++---------------------- 1 file changed, 2 insertions(+), 22 deletions(-) diff --git a/system/error.php b/system/error.php index c8a7c2c1..6fdf849a 100644 --- a/system/error.php +++ b/system/error.php @@ -31,24 +31,16 @@ class Error { */ public static function handle($e) { - // ----------------------------------------------------- - // Clean the output buffer. We don't want any rendered - // views or text to be sent to the browser. - // ----------------------------------------------------- + // Clean the output buffer. We don't want any rendered views or text sent to the browser. if (ob_get_level() > 0) { ob_clean(); } - // ----------------------------------------------------- // Get the error severity in human readable format. - // ----------------------------------------------------- $severity = (array_key_exists($e->getCode(), static::$levels)) ? static::$levels[$e->getCode()] : $e->getCode(); - // ----------------------------------------------------- - // Get the error file. Views require special handling - // since view errors occur within eval'd code. - // ----------------------------------------------------- + // Get the error file. Views require special handling since view errors occur in eval'd code. if (strpos($e->getFile(), 'view.php') !== false and strpos($e->getFile(), "eval()'d code") !== false) { $file = APP_PATH.'views/'.View::$last.EXT; @@ -65,14 +57,6 @@ class Error { Log::error($message.' in '.$e->getFile().' on line '.$e->getLine()); } - // ----------------------------------------------------- - // Detailed error view contains the file name and stack - // trace of the error. It is not wise to have details - // enabled in a production environment. - // - // The generic error view (error/500) only has a simple, - // generic error message suitable for production. - // ----------------------------------------------------- if (Config::get('error.detail')) { $view = View::make('exception') @@ -109,15 +93,11 @@ class Error { array_unshift($file, ''); - // ----------------------------------------------------- // Calculate the starting position of the file context. - // ----------------------------------------------------- $start = $line - $padding; $start = ($start < 0) ? 0 : $start; - // ----------------------------------------------------- // Calculate the context length. - // ----------------------------------------------------- $length = ($line - $start) + $padding + 1; $length = (($start + $length) > count($file) - 1) ? null : $length; From 77c23c4665aa3723540e967476aac6d5b8f93d3e Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 6 Jul 2011 14:19:18 -0700 Subject: [PATCH 034/206] Trim bloat from Form class. --- system/form.php | 95 +++++++++++-------------------------------------- 1 file changed, 20 insertions(+), 75 deletions(-) diff --git a/system/form.php b/system/form.php index 5783a79b..1b6017a9 100644 --- a/system/form.php +++ b/system/form.php @@ -21,11 +21,8 @@ class Form { { $attributes['action'] = HTML::entities(URL::to((is_null($action)) ? Request::uri() : $action)); - // ------------------------------------------------------- - // If the request method is PUT or DELETE, we'll default - // the request method to POST since the reqeust method - // is being spoofed by the form. - // ------------------------------------------------------- + // If the request method is PUT or DELETE, we'll default the request method to POST + // since the request method is being spoofed by the form. $attributes['method'] = ($method == 'PUT' or $method == 'DELETE') ? 'POST' : $method; if ( ! array_key_exists('accept-charset', $attributes)) @@ -35,12 +32,8 @@ class Form { $html = ''; - // ------------------------------------------------------- - // If the method is PUT or DELETE, we'll need to spoof it - // using a hidden input field. - // - // For more information, see the Input library. - // ------------------------------------------------------- + // If the request method is PUT or DELETE, create a hidden input element with the + // request method in it since HTML forms do not support these two methods. if ($method == 'PUT' or $method == 'DELETE') { $html .= PHP_EOL.static::input('hidden', 'REQUEST_METHOD', $method); @@ -81,10 +74,6 @@ class Form { */ public static function raw_token() { - // ------------------------------------------------------- - // CSRF tokens are stored in the session, so we need to - // make sure a driver has been specified. - // ------------------------------------------------------- if (Config::get('session.driver') == '') { throw new \Exception('Sessions must be enabled to retrieve a CSRF token.'); @@ -133,6 +122,19 @@ class Form { return static::input('password', $name, null, $attributes); } + /** + * Create a HTML search input element. + * + * @param string $name + * @param string $value + * @param array $attributes + * @return string + */ + public static function search($name, $value = null, $attributes = array()) + { + return static::input('search', $name, $value, $attributes); + } + /** * Create a HTML email input element. * @@ -159,19 +161,6 @@ class Form { return static::input('tel', $name, $value, $attributes); } - /** - * Create a HTML search input element. - * - * @param string $name - * @param string $value - * @param array $attributes - * @return string - */ - public static function search($name, $value = null, $attributes = array()) - { - return static::input('search', $name, $value, $attributes); - } - /** * Create a HTML URL input element. * @@ -185,32 +174,6 @@ class Form { return static::input('url', $name, $value, $attributes); } - /** - * Create a HTML color input element. - * - * @param string $name - * @param string $value - * @param array $attributes - * @return string - */ - public static function color($name, $value = null, $attributes = array()) - { - return static::input('color', $name, $value, $attributes); - } - - /** - * Create a HTML date input element. - * - * @param string $name - * @param string $value - * @param array $attributes - * @return string - */ - public static function date($name, $value = null, $attributes = array()) - { - return static::input('date', $name, $value, $attributes); - } - /** * Create a HTML number input element. * @@ -224,19 +187,6 @@ class Form { return static::input('number', $name, $value, $attributes); } - /** - * Create a HTML range input element. - * - * @param string $name - * @param string $value - * @param array $attributes - * @return string - */ - public static function range($name, $value = null, $attributes = array()) - { - return static::input('range', $name, $value, $attributes); - } - /** * Create a HTML file input element. * @@ -396,25 +346,20 @@ class Form { /** * Determine the ID attribute for a form element. * + * An explicitly specified ID in the attributes takes first precedence, then + * the label names will be checked for a label matching the element name. + * * @param string $name * @param array $attributes * @return mixed */ private static function id($name, $attributes) { - // ------------------------------------------------------- - // If an ID attribute was already explicitly specified - // for the element in its attributes, use that ID. - // ------------------------------------------------------- if (array_key_exists('id', $attributes)) { return $attributes['id']; } - // ------------------------------------------------------- - // If a label element was created with a value matching - // the name of the form element, use the name as the ID. - // ------------------------------------------------------- if (in_array($name, static::$labels)) { return $name; From 28d11f15bb0abb50c439755f6b6265c39d7b875d Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 6 Jul 2011 14:26:04 -0700 Subject: [PATCH 035/206] Refactoring Hash class. --- system/hash.php | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/system/hash.php b/system/hash.php index 66a87e2d..6bbeda10 100644 --- a/system/hash.php +++ b/system/hash.php @@ -25,15 +25,7 @@ class Hash { */ public function __construct($value, $salt = null) { - // ------------------------------------------------------- - // If no salt is given, we'll create a random salt to - // use when hashing the password. - // - // Otherwise, we will use the given salt. - // ------------------------------------------------------- - $this->salt = (is_null($salt)) ? Str::random(16) : $salt; - - $this->value = sha1($value.$this->salt); + $this->value = sha1($value.$this->salt = (is_null($salt)) ? Str::random(16) : $salt); } /** From 2a1c01f1c90315ef0111ff2afcff7464d73a2107 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 6 Jul 2011 14:29:48 -0700 Subject: [PATCH 036/206] Cleaning up HTML class bloat. --- system/html.php | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/system/html.php b/system/html.php index 18a680bd..b568efc5 100644 --- a/system/html.php +++ b/system/html.php @@ -206,11 +206,8 @@ class HTML { foreach ($attributes as $key => $value) { - // ------------------------------------------------------- - // If the attribute key is numeric, assign the attribute - // value to the key. This allows for attributes such as - // "required", "checked", etc. - // ------------------------------------------------------- + // Assume numeric-keyed attributes to have the same key and value. + // Example: required="required", autofocus="autofocus", etc. if (is_numeric($key)) { $key = $value; @@ -235,30 +232,21 @@ class HTML { { $safe = ''; - // ------------------------------------------------------- - // Spin through the string letter by letter. - // ------------------------------------------------------- foreach (str_split($value) as $letter) { switch (rand(1, 3)) { - // ------------------------------------------------------- // Convert the letter to its entity representation. - // ------------------------------------------------------- case 1: $safe .= '&#'.ord($letter).';'; break; - // ------------------------------------------------------- // Convert the letter to a Hex character code. - // ------------------------------------------------------- case 2: $safe .= '&#x'.dechex(ord($letter)).';'; break; - // ------------------------------------------------------- // No encoding. - // ------------------------------------------------------- case 3: $safe .= $letter; } @@ -272,18 +260,14 @@ class HTML { */ public static function __callStatic($method, $parameters) { - // ------------------------------------------------------- // Handle the dynamic creation of links to secure routes. - // ------------------------------------------------------- if (strpos($method, 'link_to_secure_') === 0) { array_unshift($parameters, substr($method, 15)); return forward_static_call_array('HTML::link_to_secure_route', $parameters); } - // ------------------------------------------------------- // Handle the dynamic creation of links to routes. - // ------------------------------------------------------- if (strpos($method, 'link_to_') === 0) { array_unshift($parameters, substr($method, 8)); From 57f61dc54ced85b147cdb8c605f869f81af6eb4b Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 6 Jul 2011 14:36:10 -0700 Subject: [PATCH 037/206] Formatting Input class. --- system/input.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/system/input.php b/system/input.php index 97caf658..dee58d52 100644 --- a/system/input.php +++ b/system/input.php @@ -103,10 +103,8 @@ class Input { case 'PUT': case 'DELETE': - // The request method can be spoofed by specifying a "REQUEST_METHOD" in the $_POST array. // If the method is being spoofed, the $_POST array will be considered the input. - if (isset($_POST['REQUEST_METHOD']) and in_array($_POST['REQUEST_METHOD'], array('PUT', 'DELETE'))) { static::$input =& $_POST; From e2973e565d8b1b72ad77a0a803b629cb9ed009a7 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 6 Jul 2011 14:37:59 -0700 Subject: [PATCH 038/206] Allow closures as Config default. --- system/config.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/config.php b/system/config.php index 4c67e90b..175e370a 100644 --- a/system/config.php +++ b/system/config.php @@ -44,7 +44,7 @@ class Config { // Verify that the configuration file actually exists. if ( ! array_key_exists($file, static::$items)) { - return $default; + return is_callable($default) ? call_user_func($default) : $default; } return Arr::get(static::$items[$file], $key, $default); From 5643daa66c4d98afe1d96143ffc31d36d0bb0012 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 6 Jul 2011 14:45:08 -0700 Subject: [PATCH 039/206] Refactor Lang class. --- system/lang.php | 32 ++++++-------------------------- 1 file changed, 6 insertions(+), 26 deletions(-) diff --git a/system/lang.php b/system/lang.php index d74fd017..ad220d89 100644 --- a/system/lang.php +++ b/system/lang.php @@ -61,31 +61,16 @@ class Lang { $this->load($file, $language); - // -------------------------------------------------------------- - // If the language file did not exist, return the default value. - // -------------------------------------------------------------- if ( ! array_key_exists($language.$file, static::$lines)) { - return $default; - } - - // -------------------------------------------------------------- - // Get the language line from the appropriate file array. - // If the line doesn't exist, return the default value. - // -------------------------------------------------------------- - if (array_key_exists($line, static::$lines[$language.$file])) - { - $line = static::$lines[$language.$file][$line]; + // The language file doesn't exist, return the default value. + $line = is_callable($default) ? call_user_func($default) : $default; } else { - return $default; + $line = Arr::get(static::$lines[$language.$file], $line, $default); } - // -------------------------------------------------------------- - // Make all place-holder replacements. Place-holders are prefixed - // with a colon for convenient location. - // -------------------------------------------------------------- foreach ($this->replacements as $key => $value) { $line = str_replace(':'.$key, $value, $line); @@ -102,10 +87,8 @@ class Lang { */ private function parse($key) { - // -------------------------------------------------------------- - // The left side of the dot is the file name, while the right - // side of the dot is the item within that file being requested. - // -------------------------------------------------------------- + // The left side of the dot is the file name, while the right side of the dot + // is the item within that file being requested. $segments = explode('.', $key); if (count($segments) < 2) @@ -125,10 +108,7 @@ class Lang { */ private function load($file, $language) { - // -------------------------------------------------------------- - // If we have already loaded the language file or the file - // doesn't exist, bail out. - // -------------------------------------------------------------- + // If we have already loaded the language file or the file doesn't exist, bail out. if (array_key_exists($language.$file, static::$lines) or ! file_exists($path = APP_PATH.'lang/'.$language.'/'.$file.EXT)) { return; From 5114f2ee876ec04255b417b470c8f524f657054e Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 6 Jul 2011 14:49:18 -0700 Subject: [PATCH 040/206] Trimming comment bloat in Log class. --- system/log.php | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/system/log.php b/system/log.php index eba9d5d0..dd736e4f 100644 --- a/system/log.php +++ b/system/log.php @@ -44,20 +44,13 @@ class Log { */ public static function write($type, $message) { - // ----------------------------------------------------- // Create the yearly and monthly directories if needed. - // ----------------------------------------------------- static::make_directory($directory = APP_PATH.'storage/logs/'.date('Y')); static::make_directory($directory .= '/'.date('m')); - // ----------------------------------------------------- - // Each day has its own log file. - // ----------------------------------------------------- + // Get the daily log file filename. $file = $directory.'/'.date('d').EXT; - // ----------------------------------------------------- - // Append to the log file and set the permissions. - // ----------------------------------------------------- file_put_contents($file, date('Y-m-d H:i:s').' '.$type.' - '.$message.PHP_EOL, LOCK_EX | FILE_APPEND); chmod($file, 0666); From 31e4b7a407643d11c21657504810d966ff6c1a64 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 6 Jul 2011 14:50:05 -0700 Subject: [PATCH 041/206] Trimming comment bloat in Memcached class. --- system/memcached.php | 6 ------ 1 file changed, 6 deletions(-) diff --git a/system/memcached.php b/system/memcached.php index c07d7416..2361ec8d 100644 --- a/system/memcached.php +++ b/system/memcached.php @@ -25,17 +25,11 @@ class Memcached { $memcache = new \Memcache; - // ----------------------------------------------------- - // Configure the Memcache servers. - // ----------------------------------------------------- foreach (Config::get('cache.servers') as $server) { $memcache->addServer($server['host'], $server['port'], true, $server['weight']); } - // ----------------------------------------------------- - // Verify Memcache was configured successfully. - // ----------------------------------------------------- if ($memcache->getVersion() === false) { throw new \Exception('Memcached is configured. However, no connections could be made. Please verify your memcached configuration.'); From af7ba04628a676b0335ec99819b8af91967fbcff Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 6 Jul 2011 14:50:48 -0700 Subject: [PATCH 042/206] Trimming comment bloat in Redirect class. --- system/redirect.php | 8 -------- 1 file changed, 8 deletions(-) diff --git a/system/redirect.php b/system/redirect.php index 70d764bb..1c312b39 100644 --- a/system/redirect.php +++ b/system/redirect.php @@ -60,10 +60,6 @@ class Redirect { */ public function with($key, $value) { - // ---------------------------------------------------- - // Since this method uses sessions, make sure a driver - // has been specified in the configuration file. - // ---------------------------------------------------- if (Config::get('session.driver') != '') { Session::flash($key, $value); @@ -79,17 +75,13 @@ class Redirect { { $parameters = (isset($parameters[0])) ? $parameters[0] : array(); - // ---------------------------------------------------- // Dynamically redirect to a secure route URL. - // ---------------------------------------------------- if (strpos($method, 'to_secure_') === 0) { return static::to(URL::to_route(substr($method, 10), $parameters, true)); } - // ---------------------------------------------------- // Dynamically redirect a route URL. - // ---------------------------------------------------- if (strpos($method, 'to_') === 0) { return static::to(URL::to_route(substr($method, 3), $parameters)); From b1342a592884a4050324fa8468561909e639e51f Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 6 Jul 2011 14:57:10 -0700 Subject: [PATCH 043/206] Refactoring the Request class. --- system/request.php | 46 +++++++++++++++++++++------------------------- 1 file changed, 21 insertions(+), 25 deletions(-) diff --git a/system/request.php b/system/request.php index 0f65959a..37a5ee42 100644 --- a/system/request.php +++ b/system/request.php @@ -35,46 +35,42 @@ class Request { elseif (isset($_SERVER['REQUEST_URI'])) { $uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH); - - if ($uri === false) - { - throw new \Exception("Malformed request URI. Request terminated."); - } } else { throw new \Exception('Unable to determine the request URI.'); } - // ------------------------------------------------------- - // Remove the application URL. - // ------------------------------------------------------- - $base_url = parse_url(Config::get('application.url'), PHP_URL_PATH); - - if (strpos($uri, $base_url) === 0) + if ($uri === false) { - $uri = (string) substr($uri, strlen($base_url)); + throw new \Exception("Malformed request URI. Request terminated."); } - // ------------------------------------------------------- - // Remove the application index and any extra slashes. - // ------------------------------------------------------- - $index = Config::get('application.index'); - - if (strpos($uri, '/'.$index) === 0) - { - $uri = (string) substr($uri, strlen('/'.$index)); - } + $uri = static::remove_from_uri($uri, parse_url(Config::get('application.url'), PHP_URL_PATH)); + $uri = static::remove_from_uri($uri, '/'.Config::get('application.index')); $uri = trim($uri, '/'); - // ------------------------------------------------------- - // If the requests is to the root of the application, we - // always return a single forward slash. - // ------------------------------------------------------- return ($uri == '') ? '/' : strtolower($uri); } + /** + * Remove a string from the beginning of a URI. + * + * @param string $uri + * @param string $value + * @return string + */ + private static function remove_from_uri($uri, $value) + { + if (strpos($uri, $value) === 0) + { + $uri = (string) substr($uri, strlen($value)); + } + + return $uri; + } + /** * Get the request method. * From 37927ee44b808ab7626cf8f190adbf20692c6acf Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 6 Jul 2011 14:58:29 -0700 Subject: [PATCH 044/206] Refactoring Request::remove_from_uri method. --- system/request.php | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/system/request.php b/system/request.php index 37a5ee42..6cecf359 100644 --- a/system/request.php +++ b/system/request.php @@ -63,12 +63,7 @@ class Request { */ private static function remove_from_uri($uri, $value) { - if (strpos($uri, $value) === 0) - { - $uri = (string) substr($uri, strlen($value)); - } - - return $uri; + return (strpos($uri, $value) === 0) ? substr($uri, strlen($value)) : $uri; } /** From e334fd80b6167c48f26ec82b6abc993b0c0635dc Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 6 Jul 2011 14:59:53 -0700 Subject: [PATCH 045/206] Trim comment bloat on Response class. --- system/response.php | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/system/response.php b/system/response.php index a3d1b5cc..8c648257 100644 --- a/system/response.php +++ b/system/response.php @@ -109,10 +109,8 @@ class Response { */ public static function prepare($response) { - // -------------------------------------------------------------- - // If the response is a Redirect instance, grab the Response. - // The Redirect class manages a Response instance internally. - // -------------------------------------------------------------- + // If the response is a Redirect instance, grab the Response. The Redirect class + // manages a Response instance internally. if ($response instanceof Redirect) { $response = $response->response; From cf8e5e2f801f01e2f29947cae85e29e553302f2e Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 6 Jul 2011 15:02:01 -0700 Subject: [PATCH 046/206] Trim comment bloat from Route class. --- system/route.php | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/system/route.php b/system/route.php index 451bf718..3e497393 100644 --- a/system/route.php +++ b/system/route.php @@ -49,27 +49,17 @@ class Route { { $response = null; - // ------------------------------------------------------------ - // If the route value is just a function, all we have to do - // is execute the function! There are no filters to call. - // ------------------------------------------------------------ if (is_callable($this->callback)) { $response = call_user_func_array($this->callback, $this->parameters); } - // ------------------------------------------------------------ - // If the route value is an array, we'll need to check it for - // any filters that may be attached. - // ------------------------------------------------------------ + // If the route value is an array, we'll need to check it for any filters that may be attached. elseif (is_array($this->callback)) { $response = isset($this->callback['before']) ? Route\Filter::call($this->callback['before'], array(), true) : null; - // ------------------------------------------------------------ - // We verify that the before filters did not return a response - // Before filters can override the request cycle to make things - // like authentication convenient to implement. - // ------------------------------------------------------------ + // Verify that the before filters did not return a response. Before filters can override + // the request cycle to make things like authentication more convenient. if (is_null($response) and isset($this->callback['do'])) { $response = call_user_func_array($this->callback['do'], $this->parameters); From 881d7f78a1becf9ac68da1454ef86332ff51477c Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 6 Jul 2011 15:03:51 -0700 Subject: [PATCH 047/206] Trim comment bloat from Router class. --- system/router.php | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/system/router.php b/system/router.php index bb35cef2..33e2e7c8 100644 --- a/system/router.php +++ b/system/router.php @@ -18,9 +18,7 @@ class Router { */ public static function route($method, $uri) { - // -------------------------------------------------------------- // Prepend a forward slash since all routes begin with one. - // -------------------------------------------------------------- $uri = ($uri != '/') ? '/'.$uri : $uri; if (is_null(static::$routes)) @@ -28,28 +26,18 @@ class Router { static::$routes = Route\Loader::load($uri); } - // -------------------------------------------------------------- // Is there an exact match for the request? - // -------------------------------------------------------------- if (isset(static::$routes[$method.' '.$uri])) { return Request::$route = new Route($method.' '.$uri, static::$routes[$method.' '.$uri]); } - // -------------------------------------------------------------- - // No exact match... check each route individually. - // -------------------------------------------------------------- foreach (static::$routes as $keys => $callback) { - // -------------------------------------------------------------- - // Only check routes that have multiple URIs or wildcards. - // All other routes would have been caught by a literal match. - // -------------------------------------------------------------- + // Only check routes that have multiple URIs or wildcards. All other routes would have + // been caught by a literal match. if (strpos($keys, '(') !== false or strpos($keys, ',') !== false ) { - // -------------------------------------------------------------- - // Routes can be comma-delimited, so spin through each one. - // -------------------------------------------------------------- foreach (explode(', ', $keys) as $key) { $key = str_replace(':num', '[0-9]+', str_replace(':any', '[a-zA-Z0-9\-_]+', $key)); From 607b23b74213a63f6cc363be39794faee78c5160 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 6 Jul 2011 15:07:18 -0700 Subject: [PATCH 048/206] Refactoring Session class. Trimmed comment bloat. Added support for closures on Session::get default value. --- system/session.php | 39 ++++----------------------------------- 1 file changed, 4 insertions(+), 35 deletions(-) diff --git a/system/session.php b/system/session.php index 75c355e3..43931358 100644 --- a/system/session.php +++ b/system/session.php @@ -43,20 +43,13 @@ class Session { static::$session = static::driver()->load($id); } - // --------------------------------------------------------- // If the session is invalid or expired, start a new one. - // --------------------------------------------------------- if (is_null($id) or is_null(static::$session) or static::expired(static::$session['last_activity'])) { static::$session['id'] = Str::random(40); static::$session['data'] = array(); } - // --------------------------------------------------------- - // Create a CSRF token for the session if necessary. This - // token is used by the Form class and filters to protect - // against cross-site request forgeries. - // --------------------------------------------------------- if ( ! static::has('csrf_token')) { static::put('csrf_token', Str::random(16)); @@ -110,7 +103,7 @@ class Session { return static::$session['data'][':new:'.$key]; } - return $default; + return is_callable($default) ? call_user_func($default) : $default; } /** @@ -165,13 +158,6 @@ class Session { */ public static function regenerate() { - // --------------------------------------------------------- - // When regenerating the session ID, we go ahead and delete - // the session data from storage. Then, we assign a new ID. - // - // The session will be re-written to storage at the end - // of the request to the application. - // --------------------------------------------------------- static::driver()->delete(static::$session['id']); static::$session['id'] = Str::random(40); @@ -184,21 +170,14 @@ class Session { */ public static function close() { - // --------------------------------------------------------- - // Flash the old input data to the session. This allows - // the Input::old method to retrieve input from the - // previous request made by the user. - // --------------------------------------------------------- + // Flash the old input data to the session. This allows the Input::old method to + // retrieve the input from the previous request made by the user. static::flash('laravel_old_input', Input::get()); static::age_flash(); static::driver()->save(static::$session); - // --------------------------------------------------------- - // Send the session cookie the browser so we can remember - // who the session belongs to on subsequent requests. - // --------------------------------------------------------- if ( ! headers_sent()) { $minutes = (Config::get('session.expire_on_close')) ? 0 : Config::get('session.lifetime'); @@ -206,10 +185,7 @@ class Session { Cookie::put('laravel_session', static::$session['id'], $minutes, Config::get('session.path'), Config::get('session.domain'), Config::get('session.https')); } - // --------------------------------------------------------- - // Perform session garbage collection (2% chance). - // Session garbage collection removes all expired sessions. - // --------------------------------------------------------- + // 2% chance of performing session garbage collection... if (mt_rand(1, 100) <= 2) { static::driver()->sweep(time() - (Config::get('session.lifetime') * 60)); @@ -223,9 +199,6 @@ class Session { */ private static function age_flash() { - // ----------------------------------------------------- - // Remove all of the :old: items from the session. - // ----------------------------------------------------- foreach (static::$session['data'] as $key => $value) { if (strpos($key, ':old:') === 0) @@ -234,10 +207,6 @@ class Session { } } - // ----------------------------------------------------- - // Copy all of the :new: items to :old: items and then - // remove the :new: items from the session. - // ----------------------------------------------------- foreach (static::$session['data'] as $key => $value) { if (strpos($key, ':new:') === 0) From c50246c694a90fea441a9cda02087cb8d91fb649 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 7 Jul 2011 07:14:57 -0700 Subject: [PATCH 049/206] Refactoring Str class. --- system/str.php | 44 +++++++++++++++++++++++--------------------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/system/str.php b/system/str.php index ec446c34..7e779b81 100644 --- a/system/str.php +++ b/system/str.php @@ -70,28 +70,8 @@ class Str { { $value = ''; - // ----------------------------------------------------- - // Get the proper character pool for the type. - // ----------------------------------------------------- - switch ($type) - { - case 'alpha': - $pool = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; - break; + $pool_length = strlen($pool = static::pool($type)) - 1; - default: - $pool = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; - } - - // ----------------------------------------------------- - // Get the pool length and split the pool into an array. - // ----------------------------------------------------- - $pool_length = strlen($pool) - 1; - $pool = str_split($pool, 1); - - // ----------------------------------------------------- - // Build the random string to the specified length. - // ----------------------------------------------------- for ($i = 0; $i < $length; $i++) { $value .= $pool[mt_rand(0, $pool_length)]; @@ -100,4 +80,26 @@ class Str { return $value; } + /** + * Get a chracter pool. + * + * @param string $type + * @return string + */ + private static function pool($type = 'alnum') + { + if ($type == 'alnum') + { + return '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; + } + elseif ($type == 'alpha') + { + return 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; + } + else + { + throw new \Exception("Unrecognized random pool [$type]."); + } + } + } \ No newline at end of file From 5755fdaba0ba5206aef97cadeb80c26c99d02235 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 7 Jul 2011 07:16:20 -0700 Subject: [PATCH 050/206] Refactor Str::pool method. --- system/str.php | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/system/str.php b/system/str.php index 7e779b81..c9782279 100644 --- a/system/str.php +++ b/system/str.php @@ -88,17 +88,13 @@ class Str { */ private static function pool($type = 'alnum') { - if ($type == 'alnum') + switch ($type) { - return '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; - } - elseif ($type == 'alpha') - { - return 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; - } - else - { - throw new \Exception("Unrecognized random pool [$type]."); + case 'alnum': + return '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; + + default: + return 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; } } From a2406c017486019ab992d68b921d3202b7626284 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 7 Jul 2011 07:18:45 -0700 Subject: [PATCH 051/206] Trim comment bloat in Text class. --- system/text.php | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/system/text.php b/system/text.php index 6a06a92b..1881b1ed 100644 --- a/system/text.php +++ b/system/text.php @@ -17,15 +17,8 @@ class Text { return $value; } - // ----------------------------------------------------- - // Limit the words in the string. - // ----------------------------------------------------- preg_match('/^\s*+(?:\S++\s*+){1,'.$limit.'}/', $value, $matches); - // ----------------------------------------------------- - // If the string did not exceed the limit, we won't - // need an ending character. - // ----------------------------------------------------- if (strlen($value) == strlen($matches[0])) { $end = ''; @@ -49,9 +42,7 @@ class Text { return $value; } - // ----------------------------------------------------- // Replace new lines and whitespace in the string. - // ----------------------------------------------------- $value = preg_replace("/\s+/", ' ', str_replace(array("\r\n", "\r", "\n"), ' ', $value)); if (strlen($value) <= $limit) @@ -61,10 +52,6 @@ class Text { $out = ''; - // ----------------------------------------------------- - // The string exceeds the character limit. Add each word - // to the output individually until we reach the limit. - // ----------------------------------------------------- foreach (explode(' ', trim($value)) as $val) { $out .= $val.' '; @@ -90,14 +77,9 @@ class Text { { $value = ' '.$value.' '; - // ----------------------------------------------------- // Assume the word will be book-ended by the following. - // ----------------------------------------------------- $delim = '[-_\'\"`(){}<>\[\]|!?@#%&,.:;^~*+=\/ 0-9\n\r\t]'; - // ----------------------------------------------------- - // Replace the censored words. - // ----------------------------------------------------- foreach ($censored as $word) { if ($replacement != '') From 2f999397e3355e33337dd8dec5a3c170c42a0e65 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 7 Jul 2011 07:21:15 -0700 Subject: [PATCH 052/206] Trim comment bloat from URL class. --- system/url.php | 36 ++++++------------------------------ 1 file changed, 6 insertions(+), 30 deletions(-) diff --git a/system/url.php b/system/url.php index dfcd8208..a9f23f57 100644 --- a/system/url.php +++ b/system/url.php @@ -12,9 +12,6 @@ class URL { */ public static function to($url = '', $https = false, $asset = false) { - // ---------------------------------------------------- - // Return the URL unchanged if it is already formed. - // ---------------------------------------------------- if (strpos($url, '://') !== false) { return $url; @@ -22,19 +19,13 @@ class URL { $base = Config::get('application.url'); - // ---------------------------------------------------- - // Assets live in the public directory, so we don't - // want to append the index file to the URL if the - // URL is to an asset. - // ---------------------------------------------------- + // Assets live in the public directory, so we only want to append + // the index file if the URL is to an asset. if ( ! $asset) { $base .= '/'.Config::get('application.index'); } - // ---------------------------------------------------- - // Does the URL need an HTTPS protocol? - // ---------------------------------------------------- if (strpos($base, 'http://') === 0 and $https) { $base = 'https://'.substr($base, 7); @@ -78,18 +69,13 @@ class URL { { if ( ! is_null($route = Route\Finder::find($name))) { - // ---------------------------------------------------- // Get the first URI assigned to the route. - // ---------------------------------------------------- $uris = explode(', ', key($route)); $uri = substr($uris[0], strpos($uris[0], '/')); - // ---------------------------------------------------- - // Replace any parameters in the URI. This allows - // the dynamic creation of URLs that contain parameter - // wildcards. - // ---------------------------------------------------- + // Replace any parameters in the URI. This allows the dynamic creation of URLs + // that contain parameter wildcards. foreach ($parameters as $parameter) { $uri = preg_replace('/\(.+?\)/', $parameter, $uri, 1); @@ -122,16 +108,10 @@ class URL { */ public static function slug($title, $separator = '-') { - // ---------------------------------------------------- - // Remove all characters that are not the separator, - // letters, numbers, or whitespace. - // ---------------------------------------------------- + // Remove all characters that are not the separator, letters, numbers, or whitespace. $title = preg_replace('![^'.preg_quote($separator).'\pL\pN\s]+!u', '', Str::lower($title)); - // ---------------------------------------------------- - // Replace all separator characters and whitespace by - // a single separator - // ---------------------------------------------------- + // Replace all separator characters and whitespace by a single separator $title = preg_replace('!['.preg_quote($separator).'\s]+!u', $separator, $title); return trim($title, $separator); @@ -144,17 +124,13 @@ class URL { { $parameters = (isset($parameters[0])) ? $parameters[0] : array(); - // ---------------------------------------------------- // Dynamically create a secure route URL. - // ---------------------------------------------------- if (strpos($method, 'to_secure_') === 0) { return static::to_route(substr($method, 10), $parameters, true); } - // ---------------------------------------------------- // Dynamically create a route URL. - // ---------------------------------------------------- if (strpos($method, 'to_') === 0) { return static::to_route(substr($method, 3), $parameters); From 42cff59a7d28e1066d94ee297ea779d7fcb9a2a0 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 7 Jul 2011 07:22:50 -0700 Subject: [PATCH 053/206] Remove comment bloat from View class. --- system/view.php | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/system/view.php b/system/view.php index 990b2dcd..7ecb7054 100644 --- a/system/view.php +++ b/system/view.php @@ -57,9 +57,7 @@ class View { { static::$last = $this->view; - // ----------------------------------------------------- // Get the evaluated content of all of the sub-views. - // ----------------------------------------------------- foreach ($this->data as &$data) { if ($data instanceof View or $data instanceof Response) @@ -68,27 +66,18 @@ class View { } } - // ----------------------------------------------------- // Extract the view data into the local scope. - // ----------------------------------------------------- extract($this->data, EXTR_SKIP); - // ----------------------------------------------------- - // Start the output buffer so nothing escapes to the - // browser. The response will be sent later. - // ----------------------------------------------------- ob_start(); $path = $this->find(); - // ----------------------------------------------------- - // We include the view into the local scope within a - // try / catch block to catch any exceptions that may - // occur while the view is rendering. + // We include the view into the local scope within a try / catch to catch any + // exceptions that may occur while the view is rendering. // - // Otherwise, a white screen of death will be shown - // if an exception occurs while rendering the view. - // ----------------------------------------------------- + // Otherwise, a white screen of death will be shown if an exception occurs + // while rendering the view. try { include $path; From 4a4a79ce3434343b98093d4be448fc1e9e3f3e6f Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 7 Jul 2011 07:24:28 -0700 Subject: [PATCH 054/206] Remove comment bloat from Validator class. --- system/validator.php | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/system/validator.php b/system/validator.php index eb4532f0..b4f87ccb 100644 --- a/system/validator.php +++ b/system/validator.php @@ -38,10 +38,7 @@ class Validator { $target = Input::get(); } - // --------------------------------------------------------- - // If the source is an Eloquent model, use the model's - // attributes as the validation attributes. - // --------------------------------------------------------- + // If the source is an Eloquent model, use the model's attributes as the validation attributes. $this->attributes = ($target instanceof DB\Eloquent) ? $target->attributes : (array) $target; } @@ -67,10 +64,6 @@ class Validator { foreach ($this->rules as $rule) { - // --------------------------------------------------------- - // The error collector is passed to the rule so that the - // rule may conveniently add error messages. - // --------------------------------------------------------- $rule->validate($this->attributes, $this->errors); } @@ -82,10 +75,6 @@ class Validator { */ public function __call($method, $parameters) { - // --------------------------------------------------------- - // Check if the validation rule is defined in the rules - // directory. If it is, create a new rule and return it. - // --------------------------------------------------------- if (file_exists(SYS_PATH.'validation/rules/'.$method.EXT)) { $rule = '\\System\\Validation\\Rules\\'.$method; From ac38876e347be83f3114c69677f1ff795da10304 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 7 Jul 2011 07:28:03 -0700 Subject: [PATCH 055/206] Add support for closures in APC cache driver. --- system/cache/driver/apc.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/cache/driver/apc.php b/system/cache/driver/apc.php index 195aa0ad..93803045 100644 --- a/system/cache/driver/apc.php +++ b/system/cache/driver/apc.php @@ -38,7 +38,7 @@ class APC implements \System\Cache\Driver { if ($cache === false) { - return $default; + return is_callable($default) ? call_user_func($default) : $default; } return $this->items[$key] = $cache; From 715bed748d7a9781a735b609ce9f6ff227709220 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 7 Jul 2011 07:29:16 -0700 Subject: [PATCH 056/206] Add support for closures to File cache driver. --- system/cache/driver/file.php | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/system/cache/driver/file.php b/system/cache/driver/file.php index 0994f75d..ce5128b7 100644 --- a/system/cache/driver/file.php +++ b/system/cache/driver/file.php @@ -36,20 +36,17 @@ class File implements \System\Cache\Driver { if ( ! file_exists(APP_PATH.'storage/cache/'.$key)) { - return $default; + return is_callable($default) ? call_user_func($default) : $default; } $cache = file_get_contents(APP_PATH.'storage/cache/'.$key); - // -------------------------------------------------- - // Has the cache expired? The UNIX expiration time - // is stored at the beginning of the file. - // -------------------------------------------------- + // Has the cache expired? The UNIX expiration time is stored at the beginning of the file. if (time() >= substr($cache, 0, 10)) { $this->forget($key); - return $default; + return is_callable($default) ? call_user_func($default) : $default; } return $this->items[$key] = unserialize(substr($cache, 10)); From aaae1acb34462ad1fcbc153a24423d1f454e931b Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 7 Jul 2011 07:38:59 -0700 Subject: [PATCH 057/206] Refactor cache class. Fix driver instantiation bug. Allow closures as default parameter. --- system/cache.php | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/system/cache.php b/system/cache.php index 7a6e55a1..12d67b54 100644 --- a/system/cache.php +++ b/system/cache.php @@ -17,19 +17,39 @@ class Cache { */ public static function driver($driver = null) { + if (is_null($driver)) + { + $driver = Config::get('cache.driver'); + } + if ( ! array_key_exists($driver, static::$drivers)) { - if (is_null($driver)) - { - $driver = Config::get('cache.driver'); - } - static::$drivers[$driver] = Cache\Factory::make($driver); } return static::$drivers[$driver]; } + /** + * Get an item from the cache. + * + * @param string $key + * @param mixed $default + * @param string $driver + * @return mixed + */ + public static function get($key, $default = null, $driver = null) + { + $item = static::driver($driver)->get($key); + + if (is_null($item)) + { + return is_callable($default) ? call_user_func($default) : $default; + } + + return $item; + } + /** * Pass all other methods to the default driver. * From 3eeb69d1bf642eb35883e60362ff373d0783601a Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 7 Jul 2011 07:39:26 -0700 Subject: [PATCH 058/206] Remove default value from cache driver interface. --- system/cache/driver.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/system/cache/driver.php b/system/cache/driver.php index 0c9fb375..69a93289 100644 --- a/system/cache/driver.php +++ b/system/cache/driver.php @@ -14,10 +14,9 @@ interface Driver { * Get an item from the cache. * * @param string $key - * @param mixed $default * @return mixed */ - public function get($key, $default = null); + public function get($key); /** * Write an item to the cache. From 9927e3ed4e0a9c8602d0249fba61fd606fa792d0 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 7 Jul 2011 07:39:48 -0700 Subject: [PATCH 059/206] Tweak APC cache driver to return null for default value. --- system/cache/driver/apc.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/system/cache/driver/apc.php b/system/cache/driver/apc.php index 93803045..ab210922 100644 --- a/system/cache/driver/apc.php +++ b/system/cache/driver/apc.php @@ -24,10 +24,9 @@ class APC implements \System\Cache\Driver { * Get an item from the cache. * * @param string $key - * @param mixed $default * @return mixed */ - public function get($key, $default = null) + public function get($key) { if (array_key_exists($key, $this->items)) { @@ -38,7 +37,7 @@ class APC implements \System\Cache\Driver { if ($cache === false) { - return is_callable($default) ? call_user_func($default) : $default; + return null; } return $this->items[$key] = $cache; From 577066e07e1a2eeec5746ad1cba9521451147727 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 7 Jul 2011 07:40:42 -0700 Subject: [PATCH 060/206] Tweak file cache driver to return null as default. --- system/cache/driver/file.php | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/system/cache/driver/file.php b/system/cache/driver/file.php index ce5128b7..e68bfb50 100644 --- a/system/cache/driver/file.php +++ b/system/cache/driver/file.php @@ -27,7 +27,7 @@ class File implements \System\Cache\Driver { * @param mixed $default * @return mixed */ - public function get($key, $default = null) + public function get($key) { if (array_key_exists($key, $this->items)) { @@ -36,17 +36,14 @@ class File implements \System\Cache\Driver { if ( ! file_exists(APP_PATH.'storage/cache/'.$key)) { - return is_callable($default) ? call_user_func($default) : $default; + return null; } $cache = file_get_contents(APP_PATH.'storage/cache/'.$key); - // Has the cache expired? The UNIX expiration time is stored at the beginning of the file. if (time() >= substr($cache, 0, 10)) { - $this->forget($key); - - return is_callable($default) ? call_user_func($default) : $default; + return $this->forget($key); } return $this->items[$key] = unserialize(substr($cache, 10)); From a29e76f27bd4296d74b755928800565426856068 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 7 Jul 2011 07:41:39 -0700 Subject: [PATCH 061/206] Force File cache driver to return null when items are expired. --- system/cache/driver/file.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/system/cache/driver/file.php b/system/cache/driver/file.php index e68bfb50..f76522b5 100644 --- a/system/cache/driver/file.php +++ b/system/cache/driver/file.php @@ -43,7 +43,9 @@ class File implements \System\Cache\Driver { if (time() >= substr($cache, 0, 10)) { - return $this->forget($key); + $this->forget($key); + + return null; } return $this->items[$key] = unserialize(substr($cache, 10)); From 1d96b96828c8f5d2675eca8d1924c04240d7b246 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 7 Jul 2011 07:42:06 -0700 Subject: [PATCH 062/206] Force memcached cache driver to return null as default. --- system/cache/driver/memcached.php | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/system/cache/driver/memcached.php b/system/cache/driver/memcached.php index 22834727..0ada37db 100644 --- a/system/cache/driver/memcached.php +++ b/system/cache/driver/memcached.php @@ -24,30 +24,20 @@ class Memcached implements \System\Cache\Driver { * Get an item from the cache. * * @param string $key - * @param mixed $default * @return mixed */ - public function get($key, $default = null) + public function get($key) { - // -------------------------------------------------- - // If the item has already been loaded, return it. - // -------------------------------------------------- if (array_key_exists($key, $this->items)) { return $this->items[$key]; } - // -------------------------------------------------- - // Attempt to the get the item from cache. - // -------------------------------------------------- $cache = \System\Memcached::instance()->get(\System\Config::get('cache.key').$key); - // -------------------------------------------------- - // Verify that the item was retrieved. - // -------------------------------------------------- if ($cache === false) { - return $default; + return null; } return $this->items[$key] = $cache; From 9454927bd31a38bed6ea92f21f11d2c5b52fc297 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 7 Jul 2011 08:04:29 -0700 Subject: [PATCH 063/206] Added Cache::maybe method. --- system/cache.php | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/system/cache.php b/system/cache.php index 12d67b54..91bb181f 100644 --- a/system/cache.php +++ b/system/cache.php @@ -50,6 +50,30 @@ class Cache { return $item; } + /** + * Get an item from the cache. If it doesn't exist, store the default value + * in the cache and return it. + * + * @param string $key + * @param mixed $default + * @param int $minutes + * @param string $driver + * @return mixed + */ + public static function maybe($key, $default, $minutes, $driver = null) + { + if ( ! is_null($item = static::get($key))) + { + return $item; + } + + $default = is_callable($default) ? call_user_func($default) : $default; + + static::driver($driver)->put($key, $default, $minutes); + + return $default; + } + /** * Pass all other methods to the default driver. * From 60e3526313c5bab6dd85e19972ede9119013fac6 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 7 Jul 2011 08:15:18 -0700 Subject: [PATCH 064/206] Refactor DB\Connector class. --- system/db/connector.php | 97 ++++++++++++++++++++++------------------- 1 file changed, 52 insertions(+), 45 deletions(-) diff --git a/system/db/connector.php b/system/db/connector.php index 25a50375..d7c138da 100644 --- a/system/db/connector.php +++ b/system/db/connector.php @@ -22,59 +22,66 @@ class Connector { */ public static function connect($config) { - // ----------------------------------------------------- - // Connect to SQLite. - // ----------------------------------------------------- if ($config->driver == 'sqlite') { - // ----------------------------------------------------- - // Check the application/db directory first. - // - // If the database doesn't exist there, maybe the full - // path was specified as the database name? - // ----------------------------------------------------- - if (file_exists($path = APP_PATH.'storage/db/'.$config->database.'.sqlite')) - { - return new \PDO('sqlite:'.$path, null, null, static::$options); - } - elseif (file_exists($config->database)) - { - return new \PDO('sqlite:'.$config->database, null, null, static::$options); - } - else - { - throw new \Exception("SQLite database [".$config->database."] could not be found."); - } + return static::connect_to_sqlite($config); } - // ----------------------------------------------------- - // Connect to MySQL or Postgres. - // ----------------------------------------------------- elseif ($config->driver == 'mysql' or $config->driver == 'pgsql') { - // ----------------------------------------------------- - // Build the PDO connection DSN. - // ----------------------------------------------------- - $dsn = $config->driver.':host='.$config->host.';dbname='.$config->database; - - if (isset($config->port)) - { - $dsn .= ';port='.$config->port; - } - - $connection = new \PDO($dsn, $config->username, $config->password, static::$options); - - // ----------------------------------------------------- - // Set the appropriate character set for the datbase. - // ----------------------------------------------------- - if (isset($config->charset)) - { - $connection->prepare("SET NAMES '".$config->charset."'")->execute(); - } - - return $connection; + return static::connect_to_server($config); } throw new \Exception('Database driver '.$config->driver.' is not supported.'); } + /** + * Establish a PDO connection to a SQLite database. + * + * @param array $config + * @return PDO + */ + private static function connect_to_sqlite($config) + { + // Database paths can either be specified relative to the application/storage/db + // directory, or as an absolute path. + + if (file_exists($path = APP_PATH.'storage/db/'.$config->database.'.sqlite')) + { + return new \PDO('sqlite:'.$path, null, null, static::$options); + } + elseif (file_exists($config->database)) + { + return new \PDO('sqlite:'.$config->database, null, null, static::$options); + } + else + { + throw new \Exception("SQLite database [".$config->database."] could not be found."); + } + } + + /** + * Connect to a MySQL or PostgreSQL database server. + * + * @param array $config + * @return PDO + */ + private static function connect_to_server($config) + { + $dsn = $config->driver.':host='.$config->host.';dbname='.$config->database; + + if (isset($config->port)) + { + $dsn .= ';port='.$config->port; + } + + $connection = new \PDO($dsn, $config->username, $config->password, static::$options); + + if (isset($config->charset)) + { + $connection->prepare("SET NAMES '".$config->charset."'")->execute(); + } + + return $connection; + } + } \ No newline at end of file From 98a691fa58640df321f12b8bf67a705f2d474312 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 7 Jul 2011 08:28:42 -0700 Subject: [PATCH 065/206] Remove comment bloat from Eloquent. --- system/db/eloquent.php | 99 ++++++++++-------------------------------- 1 file changed, 24 insertions(+), 75 deletions(-) diff --git a/system/db/eloquent.php b/system/db/eloquent.php index 9263faab..28f23c86 100644 --- a/system/db/eloquent.php +++ b/system/db/eloquent.php @@ -116,11 +116,8 @@ abstract class Eloquent { { $model = new $class; - // ----------------------------------------------------- - // Since this method is only used for instantiating - // models for querying purposes, we will go ahead and - // set the Query instance on the model. - // ----------------------------------------------------- + // Since this method is only used for instantiating/ models for querying + // purposes, we will go ahead and set the Query instance on the model. $model->query = Query::table(static::table($class)); return $model; @@ -206,14 +203,9 @@ abstract class Eloquent { */ private function has_one_or_many($model, $foreign_key) { - // ----------------------------------------------------- - // The default foreign key for has one and has many - // relationships is the name of the model with an - // appended _id. - // - // For example, the foreign key for a User model would - // be user_id. Photo would be photo_id, etc. - // ----------------------------------------------------- + // The default foreign key for has one and has many relationships is the name + // of the model with an appended _id. For example, the foreign key for a + // User model would be user_id. Photo would be photo_id, etc. $this->relating_key = (is_null($foreign_key)) ? strtolower(get_class($this)).'_id' : $foreign_key; return static::make($model)->where($this->relating_key, '=', $this->id); @@ -236,13 +228,9 @@ abstract class Eloquent { } else { - // ----------------------------------------------------- - // The default foreign key for belonging relationships - // is the name of the relationship method name with _id. - // - // So, if a model has a "manager" method returning a - // belongs_to relationship, the key would be manager_id. - // ----------------------------------------------------- + // The default foreign key for belonging relationships is the name of the + // relationship method name with _id. So, if a model has a "manager" method + // returning a belongs_to relationship, the key would be manager_id. list(, $caller) = debug_backtrace(false); $this->relating_key = $caller['function'].'_id'; @@ -268,11 +256,8 @@ abstract class Eloquent { } else { - // ----------------------------------------------------- - // By default, the intermediate table name is the plural - // names of the models arranged alphabetically and - // concatenated with an underscore. - // ----------------------------------------------------- + // By default, the intermediate table name is the plural names of the models + // arranged alphabetically and concatenated with an underscore. $models = array(Inflector::plural($model), Inflector::plural(get_class($this))); sort($models); @@ -280,13 +265,8 @@ abstract class Eloquent { $this->relating_table = strtolower($models[0].'_'.$models[1]); } - // ----------------------------------------------------- - // The default foreign key for many-to-many relations - // is the name of the model with an appended _id. - // appended _id. - // + // The default foreign key for many-to-many relations is the name of the model with an appended _id. // This is the same convention as has_one and has_many. - // ----------------------------------------------------- $this->relating_key = strtolower(get_class($this)).'_id'; return static::make($model) @@ -302,10 +282,6 @@ abstract class Eloquent { */ public function save() { - // ----------------------------------------------------- - // If the model doesn't have any dirty attributes, there - // is no need to save it to the database. - // ----------------------------------------------------- if ($this->exists and count($this->dirty) == 0) { return true; @@ -313,15 +289,10 @@ abstract class Eloquent { $model = get_class($this); - // ----------------------------------------------------- - // Since the model was instantiated using "new", a query - // instance has not been set. We'll do it now. - // ----------------------------------------------------- + // Since the model was instantiated using "new", a query instance has not been set. $this->query = Query::table(static::table($model)); - // ----------------------------------------------------- // Set the creation and update timestamps. - // ----------------------------------------------------- if (property_exists($model, 'timestamps') and $model::$timestamps) { $this->updated_at = date('Y-m-d H:i:s'); @@ -332,10 +303,8 @@ abstract class Eloquent { } } - // ----------------------------------------------------- - // If the model already exists in the database, we only - // need to update it. Otherwise, we'll insert it. - // ----------------------------------------------------- + // If the model already exists in the database, we only need to update it. + // Otherwise, we'll insert the model into the database. if ($this->exists) { $result = $this->query->where('id', '=', $this->attributes['id'])->update($this->dirty) == 1; @@ -360,16 +329,12 @@ abstract class Eloquent { */ public function delete($id = null) { - // ----------------------------------------------------- - // If the method is being called from an existing model, - // only delete that model from the database. - // ----------------------------------------------------- if ($this->exists) { return Query::table(static::table(get_class($this)))->delete($this->id); } - return $this->query->delete($id); + return 0; } /** @@ -377,18 +342,15 @@ abstract class Eloquent { */ public function __get($key) { - // ----------------------------------------------------- - // Check the ignored attributes first. These attributes - // hold all of the loaded relationships. - // ----------------------------------------------------- + // Check the ignored attributes first. These attributes hold all of the + // loaded relationships for the model. if (array_key_exists($key, $this->ignore)) { return $this->ignore[$key]; } - // ----------------------------------------------------- - // Is the attribute actually a relationship method? - // ----------------------------------------------------- + // Is the attribute actually a relationship method? If it is, return the + // models for the relationship. if (method_exists($this, $key)) { $model = $this->$key(); @@ -406,10 +368,7 @@ abstract class Eloquent { */ public function __set($key, $value) { - // ----------------------------------------------------- - // If the key is a relationship, add it to the ignored. - // Otherwise, we can simply add it as an attribute. - // ----------------------------------------------------- + // If the key is a relationship, add it to the ignored attributes. if (method_exists($this, $key)) { $this->ignore[$key] = $value; @@ -454,18 +413,13 @@ abstract class Eloquent { return $this->_first(); } - // ----------------------------------------------------- - // Pass aggregate methods to the query instance. - // ----------------------------------------------------- if (in_array($method, array('count', 'sum', 'min', 'max', 'avg'))) { return call_user_func_array(array($this->query, $method), $parameters); } - // ----------------------------------------------------- - // Pass the method to the query instance. This allows - // the chaining of methods from the query builder. - // ----------------------------------------------------- + // Pass the method to the query instance. This allows the chaining of methods + // from the query builder, providing a nice, convenient API. call_user_func_array(array($this->query, $method), $parameters); return $this; @@ -488,18 +442,13 @@ abstract class Eloquent { return $model->_first(); } - // ----------------------------------------------------- - // Pass aggregate methods to the query instance. - // ----------------------------------------------------- if (in_array($method, array('count', 'sum', 'min', 'max', 'avg'))) { return call_user_func_array(array($model->query, $method), $parameters); } - // ----------------------------------------------------- - // Pass the method to the query instance. This allows - // the chaining of methods from the query builder. - // ----------------------------------------------------- + // Pass the method to the query instance. This allows the chaining of methods + // from the query builder, providing a nice, convenient API. call_user_func_array(array($model->query, $method), $parameters); return $model; From bc8c0f2cc76ac0f1c3a8ab56687fac7692bc6d5e Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 7 Jul 2011 08:45:37 -0700 Subject: [PATCH 066/206] Refactor Query class. Remove comment bloat. --- system/db/query.php | 26 ++++++-------------------- 1 file changed, 6 insertions(+), 20 deletions(-) diff --git a/system/db/query.php b/system/db/query.php index d2a7a1d9..fc214799 100644 --- a/system/db/query.php +++ b/system/db/query.php @@ -125,10 +125,8 @@ class Query { foreach (func_get_args() as $column) { - // --------------------------------------------------------- - // If the column name is being aliased, we will need to - // wrap the column name and its alias. - // --------------------------------------------------------- + // If the column name is being aliased, we will need to wrap the column + // name and its alias in keyword identifiers. if (strpos(strtolower($column), ' as ') !== false) { $segments = explode(' ', $column); @@ -433,6 +431,7 @@ class Query { private function aggregate($aggregator, $column) { $this->select = 'SELECT '.$aggregator.'('.$this->wrap($column).') AS '.$this->wrap('aggregate'); + return $this->first()->aggregate; } @@ -457,10 +456,8 @@ class Query { { $sql = Query\Compiler::insert($this, $values); - // --------------------------------------------------------- - // Use the RETURNING clause on Postgres instead of PDO. - // The Postgres PDO ID method is slightly cumbersome. - // --------------------------------------------------------- + // Use the RETURNING clause on Postgres instead of the PDO lastInsertID method. + // The PDO method is a little cumbersome using Postgres. if (DB::driver($this->connection) == 'pgsql') { $query = DB::connection($this->connection)->prepare($sql.' RETURNING '.$this->wrap('id')); @@ -470,9 +467,6 @@ class Query { return $query->fetch(\PDO::FETCH_CLASS, 'stdClass')->id; } - // --------------------------------------------------------- - // Use the PDO ID method for MySQL and SQLite. - // --------------------------------------------------------- DB::query($sql, array_values($values), $this->connection); return DB::connection($this->connection)->lastInsertId(); @@ -514,6 +508,7 @@ class Query { public function wrap($value) { $wrap = (DB::driver($this->connection) == 'mysql') ? '`' : '"'; + return implode('.', array_map(function($segment) use ($wrap) {return ($segment != '*') ? $wrap.$segment.$wrap : $segment;}, explode('.', $value))); } @@ -533,20 +528,11 @@ class Query { */ public function __call($method, $parameters) { - // --------------------------------------------------------- - // Dynamic methods allows the building of very expressive - // queries. All dynamic methods start with "where_". - // - // Ex: DB::table('users')->where_email($email)->first(); - // --------------------------------------------------------- if (strpos($method, 'where_') === 0) { return Query\Dynamic::build($method, $parameters, $this); } - // --------------------------------------------------------- - // Handle any of the aggregate functions. - // --------------------------------------------------------- if (in_array($method, array('count', 'min', 'max', 'avg', 'sum'))) { return ($method == 'count') ? $this->aggregate(strtoupper($method), '*') : $this->aggregate(strtoupper($method), $parameters[0]); From 34605ad49d9f3a7525725d5725deccf3b9bf9491 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 7 Jul 2011 08:49:59 -0700 Subject: [PATCH 067/206] Remove comment bloat from Eloquent hydrator. --- system/db/eloquent/hydrator.php | 72 ++++++--------------------------- 1 file changed, 13 insertions(+), 59 deletions(-) diff --git a/system/db/eloquent/hydrator.php b/system/db/eloquent/hydrator.php index f4ba7e02..5ebb19a3 100644 --- a/system/db/eloquent/hydrator.php +++ b/system/db/eloquent/hydrator.php @@ -12,14 +12,10 @@ class Hydrator { */ public static function hydrate($eloquent) { - // ----------------------------------------------------- // Load the base / parent models from the query results. - // ----------------------------------------------------- $results = static::base(get_class($eloquent), $eloquent->query->get()); - // ----------------------------------------------------- // Load all of the eager relationships. - // ----------------------------------------------------- if (count($results) > 0) { foreach ($eloquent->includes as $include) @@ -54,11 +50,8 @@ class Hydrator { $model->attributes = (array) $result; $model->exists = true; - // ----------------------------------------------------- - // The results are keyed by the ID on the record. This - // will allow us to conveniently match them to child - // models during eager loading. - // ----------------------------------------------------- + // The results are keyed by the ID on the record. This will allow us to conveniently + // match them to child models during eager loading. $models[$model->id] = $model; } @@ -75,39 +68,27 @@ class Hydrator { */ private static function eagerly($eloquent, &$parents, $include) { - // ----------------------------------------------------- // Get the relationship Eloquent model. // - // We temporarily spoof the belongs_to key to allow the - // query to be fetched without any problems, since the - // belongs_to method actually gets the attribute. - // ----------------------------------------------------- + // We temporarily spoof the belongs_to key to allow the query to be fetched without + // any problems, since the belongs_to method actually gets the attribute. $eloquent->attributes[$spoof = $include.'_id'] = 0; $relationship = $eloquent->$include(); unset($eloquent->attributes[$spoof]); - // ----------------------------------------------------- - // Reset the WHERE clause and bindings on the query. - // We'll add our own WHERE clause soon. - // ----------------------------------------------------- + // Reset the WHERE clause and bindings on the query. We'll add our own WHERE clause soon. $relationship->query->where = 'WHERE 1 = 1'; $relationship->query->bindings = array(); - // ----------------------------------------------------- - // Initialize the relationship attribute on the parents. - // As expected, "many" relationships are initialized to - // an array and "one" relationships to null. - // ----------------------------------------------------- + // Initialize the relationship attribute on the parents. As expected, "many" relationships + // are initialized to an array and "one" relationships are initialized to null. foreach ($parents as &$parent) { $parent->ignore[$include] = (strpos($eloquent->relating, 'has_many') === 0) ? array() : null; } - // ----------------------------------------------------- - // Eagerly load the relationships. Phew, almost there! - // ----------------------------------------------------- if ($eloquent->relating == 'has_one') { static::eagerly_load_one($relationship, $parents, $eloquent->relating_key, $include); @@ -138,14 +119,6 @@ class Hydrator { */ private static function eagerly_load_one($relationship, &$parents, $relating_key, $include) { - // ----------------------------------------------------- - // Get the all of the related models by the parent IDs. - // - // Remember, the parent results are keyed by ID. So, we - // can simply pass the keys of the array into the query. - // - // After getting the models, we'll match by ID. - // ----------------------------------------------------- foreach ($relationship->where_in($relating_key, array_keys($parents))->get() as $key => $child) { $parents[$child->$relating_key]->ignore[$include] = $child; @@ -181,11 +154,8 @@ class Hydrator { */ private static function eagerly_load_belonging($relationship, &$parents, $relating_key, $include) { - // ----------------------------------------------------- - // Gather the keys from the parent models. Since the - // foreign key is on the parent model for this type of - // relationship, we have to gather them individually. - // ----------------------------------------------------- + // Gather the keys from the parent models. Since the foreign key is on the parent model + // for this type of relationship, we have to gather them individually. $keys = array(); foreach ($parents as &$parent) @@ -193,14 +163,8 @@ class Hydrator { $keys[] = $parent->$relating_key; } - // ----------------------------------------------------- - // Get the related models. - // ----------------------------------------------------- $children = $relationship->where_in('id', array_unique($keys))->get(); - // ----------------------------------------------------- - // Match the child models with their parent by ID. - // ----------------------------------------------------- foreach ($parents as &$parent) { if (array_key_exists($parent->$relating_key, $children)) @@ -225,21 +189,16 @@ class Hydrator { { $relationship->query->select = null; - // ----------------------------------------------------- // Retrieve the raw results as stdClasses. // - // We also add the foreign key to the select which will allow us - // to match the models back to their parents. - // ----------------------------------------------------- + // We also add the foreign key to the select which will allow us to match the + // models back to their parents. $children = $relationship->query ->where_in($relating_table.'.'.$relating_key, array_keys($parents)) ->get(Eloquent::table(get_class($relationship)).'.*', $relating_table.'.'.$relating_key); $class = get_class($relationship); - // ----------------------------------------------------- - // Create the related models. - // ----------------------------------------------------- foreach ($children as $child) { $related = new $class; @@ -247,15 +206,10 @@ class Hydrator { $related->attributes = (array) $child; $related->exists = true; - // ----------------------------------------------------- - // Remove the foreign key from the attributes since it - // was added to the query to help us match the models. - // ----------------------------------------------------- + // Remove the foreign key from the attributes since it was added to the query + // to help us match the models. unset($related->attributes[$relating_key]); - // ----------------------------------------------------- - // Match the child model its parent by ID. - // ----------------------------------------------------- $parents[$child->$relating_key]->ignore[$include][$child->id] = $related; } } From a5af988d53764d39bf1f59a6fb93baee1c4d6e85 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 7 Jul 2011 08:51:52 -0700 Subject: [PATCH 068/206] Remove comment bloat from dynamic query builder. --- system/db/query/dynamic.php | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/system/db/query/dynamic.php b/system/db/query/dynamic.php index 354ac57b..8993de6b 100644 --- a/system/db/query/dynamic.php +++ b/system/db/query/dynamic.php @@ -14,32 +14,21 @@ class Dynamic { */ public static function build($method, $parameters, $query) { - // --------------------------------------------------------- // Strip the "where_" off of the method. - // --------------------------------------------------------- $finder = substr($method, 6); - // --------------------------------------------------------- // Split the column names from the connectors. - // --------------------------------------------------------- $segments = preg_split('/(_and_|_or_)/i', $finder, -1, PREG_SPLIT_DELIM_CAPTURE); - // --------------------------------------------------------- - // The connector variable will determine which connector - // will be used for the condition. We'll change it as we - // come across new connectors in the dynamic method string. + // The connector variable will determine which connector will be used for the condition. + // We'll change it as we come across new connectors in the dynamic method string. // - // The index variable helps us get the correct parameter - // value for the where condition. We increment it each time - // we add a condition. - // --------------------------------------------------------- + // The index variable helps us get the correct parameter value for the where condition. + // We increment it each time we add a condition. $connector = 'AND'; $index = 0; - // --------------------------------------------------------- - // Iterate through each segment and add the conditions. - // --------------------------------------------------------- foreach ($segments as $segment) { if ($segment != '_and_' and $segment != '_or_') From ae9824f80b5e8dc66fa991c9eb37bb95c38d69f1 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 7 Jul 2011 08:53:05 -0700 Subject: [PATCH 069/206] Remove comment bloat from Route\Filter class. --- system/route/filter.php | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/system/route/filter.php b/system/route/filter.php index 6b1c59f7..6c66e259 100644 --- a/system/route/filter.php +++ b/system/route/filter.php @@ -24,9 +24,6 @@ class Filter { static::$filters = require APP_PATH.'filters'.EXT; } - // -------------------------------------------------------------- - // Filters can be comma-delimited, so spin through each one. - // -------------------------------------------------------------- foreach (explode(', ', $filters) as $filter) { if ( ! isset(static::$filters[$filter])) @@ -36,13 +33,9 @@ class Filter { $response = call_user_func_array(static::$filters[$filter], $parameters); - // -------------------------------------------------------------- - // If overriding is set to true and the filter returned a - // response, return that response. - // - // Overriding allows for convenient halting of the request - // flow for things like authentication, CSRF protection, etc. - // -------------------------------------------------------------- + // If overriding is set to true and the filter returned a response, return that response. + // Overriding allows for convenient halting of the request flow for things like + // authentication, CSRF protection, etc. if ( ! is_null($response) and $override) { return $response; From 0d48fba1be011788be6b10835118524e37f40b00 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 7 Jul 2011 08:57:30 -0700 Subject: [PATCH 070/206] Remove comment bloat from Route\Finder class. --- system/route/finder.php | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/system/route/finder.php b/system/route/finder.php index 374da874..f38b41c6 100644 --- a/system/route/finder.php +++ b/system/route/finder.php @@ -34,10 +34,8 @@ class Finder { return static::$names[$name]; } - // --------------------------------------------------------- - // We haven't located the route before, so we'll need to - // iterate through each route to find the matching name. - // --------------------------------------------------------- + // We haven't located the route before, so we'll need to iterate through each + // route to find the matching name. $arrayIterator = new \RecursiveArrayIterator(static::$routes); $recursiveIterator = new \RecursiveIteratorIterator($arrayIterator); From c1e2f3cf909edd6070990715d4e9753acddd5e9a Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 7 Jul 2011 08:59:46 -0700 Subject: [PATCH 071/206] Remove comment bloat from Route\Loader class. --- system/route/loader.php | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/system/route/loader.php b/system/route/loader.php index d03a1d5d..3a022c36 100644 --- a/system/route/loader.php +++ b/system/route/loader.php @@ -10,9 +10,6 @@ class Loader { */ public static function load($uri) { - // -------------------------------------------------------------- - // If a single route file is being used, return it. - // -------------------------------------------------------------- if ( ! is_dir(APP_PATH.'routes')) { return require APP_PATH.'routes'.EXT; @@ -23,12 +20,6 @@ class Loader { throw new \Exception("A [home] route file is required when using a route directory."); } - // -------------------------------------------------------------- - // If the request is to the root, load the "home" routes file. - // - // Otherwise, load the route file matching the first segment of - // the URI as well as the "home" routes file. - // -------------------------------------------------------------- if ($uri == '/') { return require APP_PATH.'routes/home'.EXT; @@ -37,9 +28,6 @@ class Loader { { $segments = explode('/', trim($uri, '/')); - // -------------------------------------------------------------- - // If the file doesn't exist, we'll just return the "home" file. - // -------------------------------------------------------------- if ( ! file_exists(APP_PATH.'routes/'.$segments[0].EXT)) { return require APP_PATH.'routes/home'.EXT; From c2e1ef68ad5e890a05925334aec8dccea6424f46 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 7 Jul 2011 09:09:20 -0700 Subject: [PATCH 072/206] Refactor Router class, deprecate Route\Loader and Route\Parser. --- system/router.php | 63 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 61 insertions(+), 2 deletions(-) diff --git a/system/router.php b/system/router.php index 33e2e7c8..1adedcd6 100644 --- a/system/router.php +++ b/system/router.php @@ -23,7 +23,7 @@ class Router { if (is_null(static::$routes)) { - static::$routes = Route\Loader::load($uri); + static::$routes = static::load($uri); } // Is there an exact match for the request? @@ -44,11 +44,70 @@ class Router { if (preg_match('#^'.$key.'$#', $method.' '.$uri)) { - return Request::$route = new Route($keys, $callback, Route\Parser::parameters($uri, $key)); + return Request::$route = new Route($keys, $callback, static::parameters(explode('/', $uri), explode('/', $key))); } } } } } + /** + * Load the appropriate route file for the request URI. + * + * @param string $uri + * @return array + */ + private static function load($uri) + { + if ( ! is_dir(APP_PATH.'routes')) + { + return require APP_PATH.'routes'.EXT; + } + + if ( ! file_exists(APP_PATH.'routes/home'.EXT)) + { + throw new \Exception("A [home] route file is required when using a route directory."); + } + + if ($uri == '/') + { + return require APP_PATH.'routes/home'.EXT; + } + else + { + $segments = explode('/', trim($uri, '/')); + + if ( ! file_exists(APP_PATH.'routes/'.$segments[0].EXT)) + { + return require APP_PATH.'routes/home'.EXT; + } + + return array_merge(require APP_PATH.'routes/'.$segments[0].EXT, require APP_PATH.'routes/home'.EXT); + } + } + + /** + * Extract the parameters from a URI based on a route URI. + * + * Any route segment wrapped in parentheses is considered a parameter. + * + * @param array $uri + * @param array $route + * @return array + */ + private static function parameters($uri, $route) + { + $parameters = array(); + + for ($i = 0; $i < count($route); $i++) + { + if (strpos($route[$i], '(') === 0) + { + $parameters[] = $uri[$i]; + } + } + + return $parameters; + } + } \ No newline at end of file From 1a4a7cbbab97cea7056de8d5093c6df6cc0ead03 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 7 Jul 2011 09:12:20 -0700 Subject: [PATCH 073/206] Make the Router::load method public. --- system/router.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/router.php b/system/router.php index 1adedcd6..f3d5ed3b 100644 --- a/system/router.php +++ b/system/router.php @@ -57,7 +57,7 @@ class Router { * @param string $uri * @return array */ - private static function load($uri) + public static function load($uri) { if ( ! is_dir(APP_PATH.'routes')) { From 0767fa50276240047a1747163b2368afb0250a95 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 7 Jul 2011 09:17:31 -0700 Subject: [PATCH 074/206] Remove comment bloat from Route\Finder. --- system/route/finder.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/system/route/finder.php b/system/route/finder.php index f38b41c6..2edf5bd1 100644 --- a/system/route/finder.php +++ b/system/route/finder.php @@ -34,8 +34,6 @@ class Finder { return static::$names[$name]; } - // We haven't located the route before, so we'll need to iterate through each - // route to find the matching name. $arrayIterator = new \RecursiveArrayIterator(static::$routes); $recursiveIterator = new \RecursiveIteratorIterator($arrayIterator); From 775f11e09b192163a172aeeb4ac1b18342df1941 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 7 Jul 2011 09:24:40 -0700 Subject: [PATCH 075/206] Refactoring Route\Finder. --- system/route/finder.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/system/route/finder.php b/system/route/finder.php index 2edf5bd1..fb997e5c 100644 --- a/system/route/finder.php +++ b/system/route/finder.php @@ -34,8 +34,7 @@ class Finder { return static::$names[$name]; } - $arrayIterator = new \RecursiveArrayIterator(static::$routes); - $recursiveIterator = new \RecursiveIteratorIterator($arrayIterator); + $recursiveIterator = new \RecursiveIteratorIterator($arrayIterator = new \RecursiveArrayIterator(static::$routes)); foreach ($recursiveIterator as $iterator) { From f0b02caaec832554cef17440a33069bbdf10e2f8 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 7 Jul 2011 09:34:58 -0700 Subject: [PATCH 076/206] Refactoring Router. --- system/router.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/system/router.php b/system/router.php index f3d5ed3b..d341b4a3 100644 --- a/system/router.php +++ b/system/router.php @@ -34,8 +34,8 @@ class Router { foreach (static::$routes as $keys => $callback) { - // Only check routes that have multiple URIs or wildcards. All other routes would have - // been caught by a literal match. + // Only check routes that have multiple URIs or wildcards. All other routes would + // have been caught by a literal match. if (strpos($keys, '(') !== false or strpos($keys, ',') !== false ) { foreach (explode(', ', $keys) as $key) @@ -44,6 +44,9 @@ class Router { if (preg_match('#^'.$key.'$#', $method.' '.$uri)) { + $uri = trim($uri, '/'); + $key = trim(substr($key, strlen($method.' ')), '/'); + return Request::$route = new Route($keys, $callback, static::parameters(explode('/', $uri), explode('/', $key))); } } From 7a8e04cbdd6e4cea79beede8dc233b0f9ebf3b8e Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 7 Jul 2011 09:37:14 -0700 Subject: [PATCH 077/206] Added comment to Router class. --- system/router.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/system/router.php b/system/router.php index d341b4a3..f2de59fe 100644 --- a/system/router.php +++ b/system/router.php @@ -44,6 +44,9 @@ class Router { if (preg_match('#^'.$key.'$#', $method.' '.$uri)) { + // Remove the leading slashes from the route and request URIs. Also trim + // the request method off of the route URI. This should get the request + // and route URIs in the same format so we can extract the parameters. $uri = trim($uri, '/'); $key = trim(substr($key, strlen($method.' ')), '/'); From 654c4aa85fa074b2e5e3a70a50743156143cf119 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 7 Jul 2011 09:38:48 -0700 Subject: [PATCH 078/206] Removed comment bloat from Route\Filter. --- system/route/filter.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/system/route/filter.php b/system/route/filter.php index 6c66e259..2118595e 100644 --- a/system/route/filter.php +++ b/system/route/filter.php @@ -33,9 +33,6 @@ class Filter { $response = call_user_func_array(static::$filters[$filter], $parameters); - // If overriding is set to true and the filter returned a response, return that response. - // Overriding allows for convenient halting of the request flow for things like - // authentication, CSRF protection, etc. if ( ! is_null($response) and $override) { return $response; From b5e0eb022c6fae028332306980c2f3c143167e0d Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 7 Jul 2011 09:49:31 -0700 Subject: [PATCH 079/206] Refactoring Router. --- system/router.php | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/system/router.php b/system/router.php index f2de59fe..ace47dc3 100644 --- a/system/router.php +++ b/system/router.php @@ -23,7 +23,7 @@ class Router { if (is_null(static::$routes)) { - static::$routes = static::load($uri); + static::$routes = ( ! is_dir(APP_PATH.'routes')) ? require APP_PATH.'routes'.EXT : static::load($uri); } // Is there an exact match for the request? @@ -65,11 +65,6 @@ class Router { */ public static function load($uri) { - if ( ! is_dir(APP_PATH.'routes')) - { - return require APP_PATH.'routes'.EXT; - } - if ( ! file_exists(APP_PATH.'routes/home'.EXT)) { throw new \Exception("A [home] route file is required when using a route directory."); From 80c746edb2bc959b4a46d22bdd17a3220107eeab Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 7 Jul 2011 11:38:30 -0700 Subject: [PATCH 080/206] Refactor route parameter parsing. --- system/router.php | 24 ++++-------------------- 1 file changed, 4 insertions(+), 20 deletions(-) diff --git a/system/router.php b/system/router.php index ace47dc3..5ac5c765 100644 --- a/system/router.php +++ b/system/router.php @@ -44,13 +44,7 @@ class Router { if (preg_match('#^'.$key.'$#', $method.' '.$uri)) { - // Remove the leading slashes from the route and request URIs. Also trim - // the request method off of the route URI. This should get the request - // and route URIs in the same format so we can extract the parameters. - $uri = trim($uri, '/'); - $key = trim(substr($key, strlen($method.' ')), '/'); - - return Request::$route = new Route($keys, $callback, static::parameters(explode('/', $uri), explode('/', $key))); + return Request::$route = new Route($keys, $callback, static::parameters($uri, $key)); } } } @@ -92,23 +86,13 @@ class Router { * * Any route segment wrapped in parentheses is considered a parameter. * - * @param array $uri - * @param array $route + * @param string $uri + * @param string $route * @return array */ private static function parameters($uri, $route) { - $parameters = array(); - - for ($i = 0; $i < count($route); $i++) - { - if (strpos($route[$i], '(') === 0) - { - $parameters[] = $uri[$i]; - } - } - - return $parameters; + return array_values(array_intersect_key(explode('/', $uri), preg_grep('/\(.+\)/', explode('/', $route)))); } } \ No newline at end of file From 94ccbffb7d2997b8da15e96830fafc4b52431a86 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 7 Jul 2011 11:55:09 -0700 Subject: [PATCH 081/206] Refactoring Router class. --- system/router.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/router.php b/system/router.php index 5ac5c765..c134828b 100644 --- a/system/router.php +++ b/system/router.php @@ -23,7 +23,7 @@ class Router { if (is_null(static::$routes)) { - static::$routes = ( ! is_dir(APP_PATH.'routes')) ? require APP_PATH.'routes'.EXT : static::load($uri); + static::$routes = (is_dir(APP_PATH.'routes')) ? static::load($uri) : require APP_PATH.'routes'.EXT; } // Is there an exact match for the request? From 5a67571020d605c92213442c264c67bbd82805ba Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 7 Jul 2011 12:17:55 -0700 Subject: [PATCH 082/206] Refactoring the Router class. --- system/router.php | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/system/router.php b/system/router.php index c134828b..a2226a83 100644 --- a/system/router.php +++ b/system/router.php @@ -18,18 +18,19 @@ class Router { */ public static function route($method, $uri) { - // Prepend a forward slash since all routes begin with one. - $uri = ($uri != '/') ? '/'.$uri : $uri; - if (is_null(static::$routes)) { static::$routes = (is_dir(APP_PATH.'routes')) ? static::load($uri) : require APP_PATH.'routes'.EXT; } + // Put the request method and URI in route form. All routes begin with + // the request method and a forward slash. + $uri = $method.' /'.trim($uri, '/'); + // Is there an exact match for the request? - if (isset(static::$routes[$method.' '.$uri])) + if (isset(static::$routes[$uri])) { - return Request::$route = new Route($method.' '.$uri, static::$routes[$method.' '.$uri]); + return Request::$route = new Route($uri, static::$routes[$uri]); } foreach (static::$routes as $keys => $callback) @@ -42,7 +43,7 @@ class Router { { $key = str_replace(':num', '[0-9]+', str_replace(':any', '[a-zA-Z0-9\-_]+', $key)); - if (preg_match('#^'.$key.'$#', $method.' '.$uri)) + if (preg_match('#^'.$key.'$#', $uri)) { return Request::$route = new Route($keys, $callback, static::parameters($uri, $key)); } @@ -70,7 +71,7 @@ class Router { } else { - $segments = explode('/', trim($uri, '/')); + $segments = explode('/', $uri); if ( ! file_exists(APP_PATH.'routes/'.$segments[0].EXT)) { From 59e5cfc711c2f97df71811b483ee2f85f9a70d16 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 7 Jul 2011 12:18:53 -0700 Subject: [PATCH 083/206] Fixing comments on Router class. --- system/router.php | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/system/router.php b/system/router.php index a2226a83..2f51f775 100644 --- a/system/router.php +++ b/system/router.php @@ -23,8 +23,7 @@ class Router { static::$routes = (is_dir(APP_PATH.'routes')) ? static::load($uri) : require APP_PATH.'routes'.EXT; } - // Put the request method and URI in route form. All routes begin with - // the request method and a forward slash. + // Put the request method and URI in route form. Routes begin with the request method and a forward slash. $uri = $method.' /'.trim($uri, '/'); // Is there an exact match for the request? @@ -35,8 +34,7 @@ class Router { foreach (static::$routes as $keys => $callback) { - // Only check routes that have multiple URIs or wildcards. All other routes would - // have been caught by a literal match. + // Only check routes that have multiple URIs or wildcards. Other routes would be caught by a literal match. if (strpos($keys, '(') !== false or strpos($keys, ',') !== false ) { foreach (explode(', ', $keys) as $key) From 54750487496b121a09b8c2cff4ac5422c3967d51 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 7 Jul 2011 12:41:18 -0700 Subject: [PATCH 084/206] Add comment to Redirect class. --- system/redirect.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/system/redirect.php b/system/redirect.php index 1c312b39..ea3059bc 100644 --- a/system/redirect.php +++ b/system/redirect.php @@ -73,6 +73,13 @@ class Redirect { */ public static function __callStatic($method, $parameters) { + // Get the parameters for the method. Dynamic routes can be generated using an + // array of parameters for routes that contain wildcards, such as /user/(:num). + // + // Example: Redirect::to_profile(array(1)); + // + // Here we'll check to see if a parameter was passed. If it wasn't, we'll just + // pass an empty array into the URL generator. $parameters = (isset($parameters[0])) ? $parameters[0] : array(); // Dynamically redirect to a secure route URL. From ad824341a6ff882b1d85eba849894428d1bdb54d Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 7 Jul 2011 22:30:15 -0500 Subject: [PATCH 085/206] fixed bug in input class causing entire array to not be returned. --- system/input.php | 6 +++--- system/router.php | 9 +++++++-- system/session.php | 4 ++-- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/system/input.php b/system/input.php index dee58d52..f5d59a05 100644 --- a/system/input.php +++ b/system/input.php @@ -34,7 +34,7 @@ class Input { static::hydrate(); } - return (array_key_exists($key, static::$input)) ? static::$input[$key] : $default; + return Arr::get(static::$input, $key, $default); } /** @@ -62,7 +62,7 @@ class Input { throw new \Exception("Sessions must be enabled to retrieve old input data."); } - return (array_key_exists($key, $old = Session::get('laravel_old_input', array()))) ? $old[$key] : $default; + return Arr::get(Session::get('laravel_old_input', array()), $key, $default); } /** @@ -81,7 +81,7 @@ class Input { return (isset($_FILES[$file][$key])) ? $_FILES[$file][$key] : $default; } - return (array_key_exists($key, $_FILES)) ? $_FILES[$key] : $default; + return Arr::get($_FILES, $key, $default); } /** diff --git a/system/router.php b/system/router.php index 2f51f775..1a898bfd 100644 --- a/system/router.php +++ b/system/router.php @@ -20,7 +20,7 @@ class Router { { if (is_null(static::$routes)) { - static::$routes = (is_dir(APP_PATH.'routes')) ? static::load($uri) : require APP_PATH.'routes'.EXT; + static::$routes = static::load($uri); } // Put the request method and URI in route form. Routes begin with the request method and a forward slash. @@ -58,6 +58,11 @@ class Router { */ public static function load($uri) { + if ( ! is_dir(APP_PATH.'routes')) + { + return require APP_PATH.'routes'.EXT; + } + if ( ! file_exists(APP_PATH.'routes/home'.EXT)) { throw new \Exception("A [home] route file is required when using a route directory."); @@ -89,7 +94,7 @@ class Router { * @param string $route * @return array */ - private static function parameters($uri, $route) + public static function parameters($uri, $route) { return array_values(array_intersect_key(explode('/', $uri), preg_grep('/\(.+\)/', explode('/', $route)))); } diff --git a/system/session.php b/system/session.php index 43931358..644345d6 100644 --- a/system/session.php +++ b/system/session.php @@ -7,14 +7,14 @@ class Session { * * @var Session\Driver */ - private static $driver; + public static $driver; /** * The session. * * @var array */ - private static $session = array(); + public static $session = array(); /** * Get the session driver. From 06cb63f502d6243c60cfc4a585986da6852c45ee Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 7 Jul 2011 22:55:27 -0500 Subject: [PATCH 086/206] removed route\loader and route\parser classes. --- system/route/loader.php | 40 ---------------------------------------- system/route/parser.php | 33 --------------------------------- 2 files changed, 73 deletions(-) delete mode 100644 system/route/loader.php delete mode 100644 system/route/parser.php diff --git a/system/route/loader.php b/system/route/loader.php deleted file mode 100644 index 3a022c36..00000000 --- a/system/route/loader.php +++ /dev/null @@ -1,40 +0,0 @@ - Date: Thu, 7 Jul 2011 23:00:40 -0500 Subject: [PATCH 087/206] refactoring auth class. --- system/arr.php | 2 ++ system/auth.php | 7 ++----- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/system/arr.php b/system/arr.php index 24be1f2b..ba3875e5 100644 --- a/system/arr.php +++ b/system/arr.php @@ -5,6 +5,8 @@ class Arr { /** * Get an item from an array. * + * If the specified key is null, the entire array will be returned. + * * @param array $array * @param string $key * @param mixed $default diff --git a/system/auth.php b/system/auth.php index 9e247094..4fd8fcc8 100644 --- a/system/auth.php +++ b/system/auth.php @@ -64,11 +64,7 @@ class Auth { if ( ! is_null($user)) { - // If a salt is present on the user record, we will recreate the hashed password - // using the salt. Otherwise, we will just use a plain hash. - $password = (isset($user->salt)) ? Hash::make($password, $user->salt)->value : sha1($password); - - if ($user->password === $password) + if ($user->password === Hash::make($password, $user->salt)->value) { static::$user = $user; @@ -89,6 +85,7 @@ class Auth { public static function logout() { Session::forget(static::$key); + static::$user = null; } From 22c1353ceaa2dd3af2b6c1dae282e7c5bc44c392 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 7 Jul 2011 23:01:44 -0500 Subject: [PATCH 088/206] added comment to auth class. --- system/auth.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/system/auth.php b/system/auth.php index 4fd8fcc8..b06dcc7f 100644 --- a/system/auth.php +++ b/system/auth.php @@ -53,6 +53,9 @@ class Auth { /** * Attempt to login a user. * + * If the user credentials are valid. The user ID will be stored in the session + * and will be considered "logged in" on subsequent requests to the application. + * * @param string $username * @param string $password */ @@ -78,7 +81,7 @@ class Auth { } /** - * Logout the current user of the application. + * Logout the user of the application. * * @return void */ From 8203bd9af53ee7c6627b94b17f23ef16eb36773c Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 7 Jul 2011 23:07:44 -0500 Subject: [PATCH 089/206] refactoring cache class. --- system/cache.php | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/system/cache.php b/system/cache.php index 91bb181f..3bff3599 100644 --- a/system/cache.php +++ b/system/cache.php @@ -10,7 +10,10 @@ class Cache { private static $drivers = array(); /** - * Get a cache driver instance. Cache drivers are singletons. + * Get a cache driver instance. If no driver name is specified, the default + * cache driver will be returned as defined in the cache configuration file. + * + * Note: Cache drivers are managed as singleton instances. * * @param string $driver * @return Cache\Driver @@ -22,12 +25,9 @@ class Cache { $driver = Config::get('cache.driver'); } - if ( ! array_key_exists($driver, static::$drivers)) - { - static::$drivers[$driver] = Cache\Factory::make($driver); - } - - return static::$drivers[$driver]; + return (array_key_exists($driver, static::$drivers)) + ? static::$drivers[$driver] + : static::$drivers[$driver] = Cache\Factory::make($driver); } /** @@ -40,9 +40,7 @@ class Cache { */ public static function get($key, $default = null, $driver = null) { - $item = static::driver($driver)->get($key); - - if (is_null($item)) + if (is_null($item = static::driver($driver)->get($key))) { return is_callable($default) ? call_user_func($default) : $default; } @@ -51,8 +49,8 @@ class Cache { } /** - * Get an item from the cache. If it doesn't exist, store the default value - * in the cache and return it. + * Get an item from the cache. If the item doesn't exist in the cache, store + * the default value in the cache and return it. * * @param string $key * @param mixed $default @@ -60,7 +58,7 @@ class Cache { * @param string $driver * @return mixed */ - public static function maybe($key, $default, $minutes, $driver = null) + public static function remember($key, $default, $minutes, $driver = null) { if ( ! is_null($item = static::get($key))) { From 6e93237ec946279900b587a803fbb2857b3ac2b5 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 7 Jul 2011 23:16:52 -0500 Subject: [PATCH 090/206] refactoring config class. --- system/config.php | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/system/config.php b/system/config.php index 175e370a..af8360c3 100644 --- a/system/config.php +++ b/system/config.php @@ -10,7 +10,7 @@ class Config { private static $items = array(); /** - * Determine if a configuration item exists. + * Determine if a configuration item or file exists. * * @param string $key * @return bool @@ -23,14 +23,20 @@ class Config { /** * Get a configuration item. * + * Configuration items are retrieved using "dot" notation. So, asking for the + * "application.timezone" configuration item would return the "timezone" option + * from the "application" configuration file. + * + * If the name of a configuration file is passed without specifying an item, the + * entire configuration array will be returned. + * * @param string $key * @param string $default - * @return mixed + * @return array */ public static function get($key, $default = null) { - // If no "dot" is present in the key, return the entire configuration array. - if(strpos($key, '.') === false) + if (strpos($key, '.') === false) { static::load($key); @@ -41,7 +47,6 @@ class Config { static::load($file); - // Verify that the configuration file actually exists. if ( ! array_key_exists($file, static::$items)) { return is_callable($default) ? call_user_func($default) : $default; @@ -69,13 +74,14 @@ class Config { /** * Parse a configuration key. * + * The value on the left side of the dot is the configuration file + * name, while the right side of the dot is the item within that file. + * * @param string $key * @return array */ private static function parse($key) { - // The left side of the dot is the file name, while the right side of the dot - // is the item within that file being requested. $segments = explode('.', $key); if (count($segments) < 2) @@ -94,13 +100,10 @@ class Config { */ public static function load($file) { - // Bail out if already loaded or doesn't exist. - if (array_key_exists($file, static::$items) or ! file_exists($path = APP_PATH.'config/'.$file.EXT)) + if ( ! array_key_exists($file, static::$items) and file_exists($path = APP_PATH.'config/'.$file.EXT)) { - return; + static::$items[$file] = require $path; } - - static::$items[$file] = require $path; } } \ No newline at end of file From 77d65b89cb0400ef1f20a21689669016361f0d1c Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 7 Jul 2011 23:24:43 -0500 Subject: [PATCH 091/206] refactoring crypt class. --- system/crypt.php | 41 ++++++++++++++++++++++++----------------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/system/crypt.php b/system/crypt.php index 4174251b..a7cd77c2 100644 --- a/system/crypt.php +++ b/system/crypt.php @@ -24,21 +24,8 @@ class Crypt { */ public static function encrypt($value) { - if (defined('MCRYPT_DEV_URANDOM')) - { - $random = MCRYPT_DEV_URANDOM; - } - elseif (defined('MCRYPT_DEV_RANDOM')) - { - $random = MCRYPT_DEV_RANDOM; - } - else - { - $random = MCRYPT_RAND; - } - - // The system random number generator must be seeded to produce random results. - if ($random === MCRYPT_RAND) + // Seed the system random number generator if it is being used. + if (($random = static::randomizer()) === MCRYPT_RAND) { mt_srand(); } @@ -47,7 +34,6 @@ class Crypt { $value = mcrypt_encrypt(static::$cipher, static::key(), $value, static::$mode, $iv); - // Use base64 encoding to get a nice string value. return base64_encode($iv.$value); } @@ -76,7 +62,28 @@ class Crypt { } /** - * Get the application key. + * Get the random number source that should be used for the OS. + * + * @return int + */ + private static function randomizer() + { + if (defined('MCRYPT_DEV_URANDOM')) + { + return MCRYPT_DEV_URANDOM; + } + elseif (defined('MCRYPT_DEV_RANDOM')) + { + return MCRYPT_DEV_RANDOM; + } + else + { + return MCRYPT_RAND; + } + } + + /** + * Get the application key from the application configuration file. * * @return string */ From 4b063ac042464840c040550627f62a247e22564f Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 7 Jul 2011 23:35:23 -0500 Subject: [PATCH 092/206] refactoring database and connector classes. --- system/db.php | 23 ++++++++--------------- system/db/connector.php | 32 +++++++++++++++++++++++++++----- 2 files changed, 35 insertions(+), 20 deletions(-) diff --git a/system/db.php b/system/db.php index 34d9d743..1b03d851 100644 --- a/system/db.php +++ b/system/db.php @@ -7,10 +7,13 @@ class DB { * * @var array */ - private static $connections = array(); + public static $connections = array(); /** - * Get a database connection. Database connections are managed as singletons. + * Get a database connection. If no database name is specified, the default + * connection will be returned as defined in the db configuration file. + * + * Note: Database connections are managed as singletons. * * @param string $connection * @return PDO @@ -22,19 +25,9 @@ class DB { $connection = Config::get('db.default'); } - if ( ! array_key_exists($connection, static::$connections)) - { - $config = Config::get('db.connections'); - - if ( ! array_key_exists($connection, $config)) - { - throw new \Exception("Database connection [$connection] is not defined."); - } - - static::$connections[$connection] = DB\Connector::connect((object) $config[$connection]); - } - - return static::$connections[$connection]; + return array_key_exists($connection, static::$connections) + ? static::$connections[$connection] + : static::$connections[$connection] = DB\Connector::connect($connection); } /** diff --git a/system/db/connector.php b/system/db/connector.php index d7c138da..bbc831d9 100644 --- a/system/db/connector.php +++ b/system/db/connector.php @@ -1,5 +1,7 @@ driver == 'sqlite') { return static::connect_to_sqlite($config); @@ -37,14 +41,14 @@ class Connector { /** * Establish a PDO connection to a SQLite database. * + * SQLite database paths can be specified either relative to the application/db + * directory, or as an absolute path to any location on the file system. + * * @param array $config * @return PDO */ private static function connect_to_sqlite($config) { - // Database paths can either be specified relative to the application/storage/db - // directory, or as an absolute path. - if (file_exists($path = APP_PATH.'storage/db/'.$config->database.'.sqlite')) { return new \PDO('sqlite:'.$path, null, null, static::$options); @@ -84,4 +88,22 @@ class Connector { return $connection; } + /** + * Get the configuration options for a database connection. + * + * @param string $connection + * @return object + */ + private static function configuration($connection) + { + $config = Config::get('db.connections'); + + if ( ! array_key_exists($connection, $config)) + { + throw new \Exception("Database connection [$connection] is not defined."); + } + + return (object) $config[$connection]; + } + } \ No newline at end of file From 6a77a98f9561052dcad42eaf5b854da22eef0f57 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 7 Jul 2011 23:40:29 -0500 Subject: [PATCH 093/206] refactored error and file classes. --- system/error.php | 17 +++++++++-------- system/file.php | 7 +------ 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/system/error.php b/system/error.php index 6fdf849a..6e98cde3 100644 --- a/system/error.php +++ b/system/error.php @@ -31,7 +31,7 @@ class Error { */ public static function handle($e) { - // Clean the output buffer. We don't want any rendered views or text sent to the browser. + // Clean the output buffer so no previously rendered views or text is sent to the browser. if (ob_get_level() > 0) { ob_clean(); @@ -40,7 +40,8 @@ class Error { // Get the error severity in human readable format. $severity = (array_key_exists($e->getCode(), static::$levels)) ? static::$levels[$e->getCode()] : $e->getCode(); - // Get the error file. Views require special handling since view errors occur in eval'd code. + // Get the file in which the error occured. + // Views require special handling since view errors occur in eval'd code. if (strpos($e->getFile(), 'view.php') !== false and strpos($e->getFile(), "eval()'d code") !== false) { $file = APP_PATH.'views/'.View::$last.EXT; @@ -60,12 +61,12 @@ class Error { if (Config::get('error.detail')) { $view = View::make('exception') - ->bind('severity', $severity) - ->bind('message', $message) - ->bind('file', $file) - ->bind('line', $e->getLine()) - ->bind('trace', $e->getTraceAsString()) - ->bind('contexts', static::context($file, $e->getLine())); + ->bind('severity', $severity) + ->bind('message', $message) + ->bind('file', $file) + ->bind('line', $e->getLine()) + ->bind('trace', $e->getTraceAsString()) + ->bind('contexts', static::context($file, $e->getLine())); Response::make($view, 500)->send(); } diff --git a/system/file.php b/system/file.php index 7156243b..8575b0c3 100644 --- a/system/file.php +++ b/system/file.php @@ -125,11 +125,6 @@ class File { */ public static function upload($key, $path) { - if ( ! array_key_exists($key, $_FILES)) - { - return false; - } - - return move_uploaded_file($_FILES[$key]['tmp_name'], $path); + return array_key_exists($key, $_FILES) ? move_uploaded_file($_FILES[$key]['tmp_name'], $path) : false; } } \ No newline at end of file From 6d296f2a1dd0402f85e8eb6bbcef9e2755c8b791 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 7 Jul 2011 23:42:11 -0500 Subject: [PATCH 094/206] added comment to file class. --- system/form.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/system/form.php b/system/form.php index 1b6017a9..e7b99a27 100644 --- a/system/form.php +++ b/system/form.php @@ -34,6 +34,9 @@ class Form { // If the request method is PUT or DELETE, create a hidden input element with the // request method in it since HTML forms do not support these two methods. + // + // The Request class will check for this hidden POST element when determining the + // request method. If it is present, it will override the real request method. if ($method == 'PUT' or $method == 'DELETE') { $html .= PHP_EOL.static::input('hidden', 'REQUEST_METHOD', $method); From c5ddb67d6bad95d0f75add2bcfe25122d8113cd2 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 7 Jul 2011 23:43:54 -0500 Subject: [PATCH 095/206] added comment to hash class. --- system/hash.php | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/system/hash.php b/system/hash.php index 6bbeda10..501b34eb 100644 --- a/system/hash.php +++ b/system/hash.php @@ -17,7 +17,11 @@ class Hash { public $salt; /** - * Create a new hash instance. + * Create a new salted hash instance. + * + * If no salt is provided, a random, 16 character salt will be generated + * to created the salted, hashed value. If a salt is provided, that salt + * will be used when hashing the value. * * @param string $value * @param string $salt @@ -25,7 +29,9 @@ class Hash { */ public function __construct($value, $salt = null) { - $this->value = sha1($value.$this->salt = (is_null($salt)) ? Str::random(16) : $salt); + $this->salt = (is_null($salt)) ? Str::random(16) : $salt; + + $this->value = sha1($value.$this->salt); } /** From ea6b58b28cb76bc7d06dac16ede0c3ab4916683c Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 7 Jul 2011 23:48:05 -0500 Subject: [PATCH 096/206] refactoring lang class. --- system/lang.php | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/system/lang.php b/system/lang.php index ad220d89..40dd37e9 100644 --- a/system/lang.php +++ b/system/lang.php @@ -28,6 +28,10 @@ class Lang { /** * Create a new Lang instance. * + * Language lines are retrieved using "dot" notation. So, asking for the + * "messages.required" language line would return the "required" line + * from the "messages" language file. + * * @param string $line * @return void */ @@ -63,7 +67,6 @@ class Lang { if ( ! array_key_exists($language.$file, static::$lines)) { - // The language file doesn't exist, return the default value. $line = is_callable($default) ? call_user_func($default) : $default; } else @@ -82,13 +85,14 @@ class Lang { /** * Parse a language key. * + * The value on the left side of the dot is the language file name, + * while the right side of the dot is the item within that file. + * * @param string $key * @return array */ private function parse($key) { - // The left side of the dot is the file name, while the right side of the dot - // is the item within that file being requested. $segments = explode('.', $key); if (count($segments) < 2) @@ -108,13 +112,10 @@ class Lang { */ private function load($file, $language) { - // If we have already loaded the language file or the file doesn't exist, bail out. - if (array_key_exists($language.$file, static::$lines) or ! file_exists($path = APP_PATH.'lang/'.$language.'/'.$file.EXT)) + if ( ! array_key_exists($language.$file, static::$lines) and file_exists($path = APP_PATH.'lang/'.$language.'/'.$file.EXT)) { - return; + static::$lines[$language.$file] = require $path; } - - static::$lines[$language.$file] = require $path; } /** From a86d4477ca01ba45cc704213bced78b9b7ddb796 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 7 Jul 2011 23:55:27 -0500 Subject: [PATCH 097/206] refactoring redirect class. --- system/redirect.php | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/system/redirect.php b/system/redirect.php index ea3059bc..9c5ada19 100644 --- a/system/redirect.php +++ b/system/redirect.php @@ -34,8 +34,8 @@ class Redirect { $url = URL::to($url, $https); return ($method == 'refresh') - ? new static(Response::make('', $status)->header('Refresh', '0;url='.$url)) - : new static(Response::make('', $status)->header('Location', $url)); + ? new static(Response::make('', $status)->header('Refresh', '0;url='.$url)) + : new static(Response::make('', $status)->header('Location', $url)); } /** @@ -60,11 +60,13 @@ class Redirect { */ public function with($key, $value) { - if (Config::get('session.driver') != '') + if (Config::get('session.driver') == '') { - Session::flash($key, $value); + throw new \Exception("Attempting to flash data to the session, but no session driver has been specified."); } + Session::flash($key, $value); + return $this; } From 60c9c9151668da33789dfe1ca38e2483f15ed58d Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 8 Jul 2011 06:28:41 -0700 Subject: [PATCH 098/206] Trimming comment bloat from Route class. --- system/route.php | 1 - 1 file changed, 1 deletion(-) diff --git a/system/route.php b/system/route.php index 3e497393..df4a24a4 100644 --- a/system/route.php +++ b/system/route.php @@ -53,7 +53,6 @@ class Route { { $response = call_user_func_array($this->callback, $this->parameters); } - // If the route value is an array, we'll need to check it for any filters that may be attached. elseif (is_array($this->callback)) { $response = isset($this->callback['before']) ? Route\Filter::call($this->callback['before'], array(), true) : null; From 767a49f54b5efbf208ddfa2fc1cb10b27f4b4f22 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 8 Jul 2011 06:35:42 -0700 Subject: [PATCH 099/206] Tweaking comments in URL class. --- system/url.php | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/system/url.php b/system/url.php index a9f23f57..1105f6d8 100644 --- a/system/url.php +++ b/system/url.php @@ -19,8 +19,8 @@ class URL { $base = Config::get('application.url'); - // Assets live in the public directory, so we only want to append - // the index file if the URL is to an asset. + // Assets live in the public directory, and should not have + // the index file appended to their URLs. if ( ! $asset) { $base .= '/'.Config::get('application.index'); @@ -60,6 +60,10 @@ class URL { /** * Generate a URL from a route name. * + * For routes that have wildcard parameters, an array may be passed as the + * second parameter to the method. The values of this array will be used + * to fill the wildcard segments of the route URI. + * * @param string $name * @param array $parameters * @param bool $https @@ -69,13 +73,10 @@ class URL { { if ( ! is_null($route = Route\Finder::find($name))) { - // Get the first URI assigned to the route. $uris = explode(', ', key($route)); $uri = substr($uris[0], strpos($uris[0], '/')); - // Replace any parameters in the URI. This allows the dynamic creation of URLs - // that contain parameter wildcards. foreach ($parameters as $parameter) { $uri = preg_replace('/\(.+?\)/', $parameter, $uri, 1); From bbc8b8f954eb04c531476ee79197d0a8c2258443 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 8 Jul 2011 06:37:50 -0700 Subject: [PATCH 100/206] Refactoring View class. --- system/view.php | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/system/view.php b/system/view.php index 7ecb7054..c1016f9d 100644 --- a/system/view.php +++ b/system/view.php @@ -78,14 +78,7 @@ class View { // // Otherwise, a white screen of death will be shown if an exception occurs // while rendering the view. - try - { - include $path; - } - catch (\Exception $e) - { - Error::handle($e); - } + try { include $path; } catch (\Exception $e) { Error::handle($e); } return ob_get_clean(); } From ec43d5dd03bbfe9a5cfc39d0ff3ada8ba6843fe9 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 8 Jul 2011 06:39:58 -0700 Subject: [PATCH 101/206] Make item array public on APC cache driver. --- system/cache/driver/apc.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/cache/driver/apc.php b/system/cache/driver/apc.php index ab210922..c8dba83e 100644 --- a/system/cache/driver/apc.php +++ b/system/cache/driver/apc.php @@ -7,7 +7,7 @@ class APC implements \System\Cache\Driver { * * @var array */ - private $items = array(); + public $items = array(); /** * Determine if an item exists in the cache. From 967428c4410b65320bcd21914d9237344fea99bb Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 8 Jul 2011 06:40:20 -0700 Subject: [PATCH 102/206] Make items array public on File cache driver. --- system/cache/driver/file.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/cache/driver/file.php b/system/cache/driver/file.php index f76522b5..27c6a5f1 100644 --- a/system/cache/driver/file.php +++ b/system/cache/driver/file.php @@ -7,7 +7,7 @@ class File implements \System\Cache\Driver { * * @var array */ - private $items = array(); + public $items = array(); /** * Determine if an item exists in the cache. From 82d5760a32a2628f9f1c7122576fc45216157ce5 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 8 Jul 2011 06:40:49 -0700 Subject: [PATCH 103/206] Make items array public on Memcached cache driver. --- system/cache/driver/memcached.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/cache/driver/memcached.php b/system/cache/driver/memcached.php index 0ada37db..cf25d20b 100644 --- a/system/cache/driver/memcached.php +++ b/system/cache/driver/memcached.php @@ -7,7 +7,7 @@ class Memcached implements \System\Cache\Driver { * * @var array */ - private $items = array(); + public $items = array(); /** * Determine if an item exists in the cache. From 44a48256c7048eb6241bd7f83fce2ca5e6654aa8 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 8 Jul 2011 06:43:02 -0700 Subject: [PATCH 104/206] Allow cache class to look for existing items in driver's items array. --- system/cache.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/system/cache.php b/system/cache.php index 3bff3599..afb641a5 100644 --- a/system/cache.php +++ b/system/cache.php @@ -40,6 +40,11 @@ class Cache { */ public static function get($key, $default = null, $driver = null) { + if (array_key_exists($key, static::driver($driver)->items)) + { + return static::driver($driver)->items[$key]; + } + if (is_null($item = static::driver($driver)->get($key))) { return is_callable($default) ? call_user_func($default) : $default; From 24fefe9eb26b202e3cd56c0132e086343d40bd16 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 8 Jul 2011 06:44:54 -0700 Subject: [PATCH 105/206] Remove check for items in APC driver. --- system/cache/driver/apc.php | 5 ----- 1 file changed, 5 deletions(-) diff --git a/system/cache/driver/apc.php b/system/cache/driver/apc.php index c8dba83e..978d995c 100644 --- a/system/cache/driver/apc.php +++ b/system/cache/driver/apc.php @@ -28,11 +28,6 @@ class APC implements \System\Cache\Driver { */ public function get($key) { - if (array_key_exists($key, $this->items)) - { - return $this->items[$key]; - } - $cache = apc_fetch(\System\Config::get('cache.key').$key); if ($cache === false) From de19b8db40190804e0703c3c8254cd39c9961fd1 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 8 Jul 2011 06:45:15 -0700 Subject: [PATCH 106/206] Remove check for items in File cache driver. --- system/cache/driver/file.php | 5 ----- 1 file changed, 5 deletions(-) diff --git a/system/cache/driver/file.php b/system/cache/driver/file.php index 27c6a5f1..3de5ba4f 100644 --- a/system/cache/driver/file.php +++ b/system/cache/driver/file.php @@ -29,11 +29,6 @@ class File implements \System\Cache\Driver { */ public function get($key) { - if (array_key_exists($key, $this->items)) - { - return $this->items[$key]; - } - if ( ! file_exists(APP_PATH.'storage/cache/'.$key)) { return null; From 6c51536d7d8ae5ebf753738c8cc26d6397b8a743 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 8 Jul 2011 06:45:38 -0700 Subject: [PATCH 107/206] Remove check for items in Memcached cache driver. --- system/cache/driver/memcached.php | 5 ----- 1 file changed, 5 deletions(-) diff --git a/system/cache/driver/memcached.php b/system/cache/driver/memcached.php index cf25d20b..2710ea78 100644 --- a/system/cache/driver/memcached.php +++ b/system/cache/driver/memcached.php @@ -28,11 +28,6 @@ class Memcached implements \System\Cache\Driver { */ public function get($key) { - if (array_key_exists($key, $this->items)) - { - return $this->items[$key]; - } - $cache = \System\Memcached::instance()->get(\System\Config::get('cache.key').$key); if ($cache === false) From e6e7586dbccf0354d675983117d75c6a20152105 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 8 Jul 2011 06:48:31 -0700 Subject: [PATCH 108/206] Refactoring DB\Connector. --- system/db/connector.php | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/system/db/connector.php b/system/db/connector.php index bbc831d9..ee4f275f 100644 --- a/system/db/connector.php +++ b/system/db/connector.php @@ -26,13 +26,14 @@ class Connector { { $config = static::configuration($connection); - if ($config->driver == 'sqlite') + switch ($config->driver) { - return static::connect_to_sqlite($config); - } - elseif ($config->driver == 'mysql' or $config->driver == 'pgsql') - { - return static::connect_to_server($config); + case 'sqlite': + return static::connect_to_sqlite($config); + + case 'mysql': + case 'pgsql': + return static::connect_to_server($config); } throw new \Exception('Database driver '.$config->driver.' is not supported.'); From 743cf6f44a95fd3c850e362659897e63b930ba63 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 8 Jul 2011 06:56:29 -0700 Subject: [PATCH 109/206] Refactoring DB\Eloquent. --- system/db/eloquent.php | 37 +++++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/system/db/eloquent.php b/system/db/eloquent.php index 28f23c86..3e7b0127 100644 --- a/system/db/eloquent.php +++ b/system/db/eloquent.php @@ -116,7 +116,7 @@ abstract class Eloquent { { $model = new $class; - // Since this method is only used for instantiating/ models for querying + // Since this method is only used for instantiating models for querying // purposes, we will go ahead and set the Query instance on the model. $model->query = Query::table(static::table($class)); @@ -132,6 +132,7 @@ abstract class Eloquent { public static function with() { $model = static::make(get_called_class()); + $model->includes = func_get_args(); return $model; @@ -197,15 +198,16 @@ abstract class Eloquent { /** * Retrieve the query for a 1:1 or 1:* relationship. * + * The default foreign key for has one and has many relationships is the name + * of the model with an appended _id. For example, the foreign key for a + * User model would be user_id. Photo would be photo_id, etc. + * * @param string $model * @param string $foreign_key * @return mixed */ private function has_one_or_many($model, $foreign_key) { - // The default foreign key for has one and has many relationships is the name - // of the model with an appended _id. For example, the foreign key for a - // User model would be user_id. Photo would be photo_id, etc. $this->relating_key = (is_null($foreign_key)) ? strtolower(get_class($this)).'_id' : $foreign_key; return static::make($model)->where($this->relating_key, '=', $this->id); @@ -214,6 +216,10 @@ abstract class Eloquent { /** * Retrieve the query for a 1:1 belonging relationship. * + * The default foreign key for belonging relationships is the name of the + * relationship method name with _id. So, if a model has a "manager" method + * returning a belongs_to relationship, the key would be manager_id. + * * @param string $model * @param string $foreign_key * @return mixed @@ -228,9 +234,6 @@ abstract class Eloquent { } else { - // The default foreign key for belonging relationships is the name of the - // relationship method name with _id. So, if a model has a "manager" method - // returning a belongs_to relationship, the key would be manager_id. list(, $caller) = debug_backtrace(false); $this->relating_key = $caller['function'].'_id'; @@ -242,6 +245,12 @@ abstract class Eloquent { /** * Retrieve the query for a *:* relationship. * + * By default, the intermediate table name is the plural names of the models + * arranged alphabetically and concatenated with an underscore. + * + * The default foreign key for many-to-many relations is the name of the model + * with an appended _id. This is the same convention as has_one and has_many. + * * @param string $model * @param string $table * @return mixed @@ -250,23 +259,19 @@ abstract class Eloquent { { $this->relating = __FUNCTION__; - if ( ! is_null($table)) + if (is_null($table)) { - $this->relating_table = $table; - } - else - { - // By default, the intermediate table name is the plural names of the models - // arranged alphabetically and concatenated with an underscore. $models = array(Inflector::plural($model), Inflector::plural(get_class($this))); sort($models); $this->relating_table = strtolower($models[0].'_'.$models[1]); } + else + { + $this->relating_table = $table; + } - // The default foreign key for many-to-many relations is the name of the model with an appended _id. - // This is the same convention as has_one and has_many. $this->relating_key = strtolower(get_class($this)).'_id'; return static::make($model) From 666ebf21195a171702631674aae19ef1244d9c81 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 8 Jul 2011 07:09:43 -0700 Subject: [PATCH 110/206] Refactoring Eloquent class. --- system/db/eloquent.php | 65 +++++++++++++++++++++++++++--------------- 1 file changed, 42 insertions(+), 23 deletions(-) diff --git a/system/db/eloquent.php b/system/db/eloquent.php index 3e7b0127..fce85ea8 100644 --- a/system/db/eloquent.php +++ b/system/db/eloquent.php @@ -295,37 +295,43 @@ abstract class Eloquent { $model = get_class($this); // Since the model was instantiated using "new", a query instance has not been set. + // Only models being used for querying have their query instances set by default. $this->query = Query::table(static::table($model)); - // Set the creation and update timestamps. if (property_exists($model, 'timestamps') and $model::$timestamps) { - $this->updated_at = date('Y-m-d H:i:s'); - - if ( ! $this->exists) - { - $this->created_at = $this->updated_at; - } + $this->timestamp(); } - // If the model already exists in the database, we only need to update it. - // Otherwise, we'll insert the model into the database. - if ($this->exists) - { - $result = $this->query->where('id', '=', $this->attributes['id'])->update($this->dirty) == 1; - } - else - { - $this->attributes['id'] = $this->query->insert_get_id($this->attributes); - - $result = $this->exists = is_numeric($this->id); - } + $result = ($this->exists) ? $this->update() : $this->insert(); $this->dirty = array(); return $result; } + /** + * Update an existing model in the database. + * + * @return bool + */ + private function update() + { + return $this->query->where('id', '=', $this->attributes['id'])->update($this->dirty) == 1; + } + + /** + * Insert a new model into the database. + * + * @return bool + */ + private function insert() + { + $this->attributes['id'] = $this->query->insert_get_id($this->attributes); + + return $this->exists = is_numeric($this->id); + } + /** * Delete a model from the database. * @@ -342,20 +348,33 @@ abstract class Eloquent { return 0; } + /** + * Set the creation and update timestamps on the model. + * + * @return void + */ + private function timestamp() + { + $this->updated_at = date('Y-m-d H:i:s'); + + if ( ! $this->exists) + { + $this->created_at = $this->updated_at; + } + } + /** * Magic method for retrieving model attributes. */ public function __get($key) { - // Check the ignored attributes first. These attributes hold all of the - // loaded relationships for the model. + // The ignored attributes hold all of the loaded relationships for the model. if (array_key_exists($key, $this->ignore)) { return $this->ignore[$key]; } - // Is the attribute actually a relationship method? If it is, return the - // models for the relationship. + // If the attribute is a relationship method, return the related models. if (method_exists($this, $key)) { $model = $this->$key(); From 21efdbf69b031935f812076f106d7aff5c3b7aaf Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 8 Jul 2011 07:13:30 -0700 Subject: [PATCH 111/206] Remove insert and update methods from Eloquent. --- system/db/eloquent.php | 35 +++++++++++------------------------ 1 file changed, 11 insertions(+), 24 deletions(-) diff --git a/system/db/eloquent.php b/system/db/eloquent.php index fce85ea8..64ab84e3 100644 --- a/system/db/eloquent.php +++ b/system/db/eloquent.php @@ -303,35 +303,22 @@ abstract class Eloquent { $this->timestamp(); } - $result = ($this->exists) ? $this->update() : $this->insert(); + if ($this->exists) + { + $result = $this->query->where('id', '=', $this->attributes['id'])->update($this->dirty) == 1; + } + else + { + $this->attributes['id'] = $this->query->insert_get_id($this->attributes); + + $result = $this->exists = is_numeric($this->id); + } $this->dirty = array(); return $result; } - /** - * Update an existing model in the database. - * - * @return bool - */ - private function update() - { - return $this->query->where('id', '=', $this->attributes['id'])->update($this->dirty) == 1; - } - - /** - * Insert a new model into the database. - * - * @return bool - */ - private function insert() - { - $this->attributes['id'] = $this->query->insert_get_id($this->attributes); - - return $this->exists = is_numeric($this->id); - } - /** * Delete a model from the database. * @@ -345,7 +332,7 @@ abstract class Eloquent { return Query::table(static::table(get_class($this)))->delete($this->id); } - return 0; + return $this->query->delete(); } /** From 787be6bc99fda3a9f8fdb9c19a6135b6efe099bf Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 8 Jul 2011 07:28:38 -0700 Subject: [PATCH 112/206] Changed Query::get and Query::first to accept arrays of columns instead of a dynamic parameter list. --- system/db/query.php | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/system/db/query.php b/system/db/query.php index fc214799..cc1e5bb2 100644 --- a/system/db/query.php +++ b/system/db/query.php @@ -115,15 +115,16 @@ class Query { /** * Add columns to the SELECT clause. * + * @param array $columns * @return Query */ - public function select() + public function select($columns = array('*')) { $this->select = ($this->distinct) ? 'SELECT DISTINCT ' : 'SELECT '; - $columns = array(); + $wrapped = array(); - foreach (func_get_args() as $column) + foreach ($columns as $column) { // If the column name is being aliased, we will need to wrap the column // name and its alias in keyword identifiers. @@ -131,15 +132,15 @@ class Query { { $segments = explode(' ', $column); - $columns[] = $this->wrap($segments[0]).' AS '.$this->wrap($segments[2]); + $wrapped[] = $this->wrap($segments[0]).' AS '.$this->wrap($segments[2]); } else { - $columns[] = $this->wrap($column); + $wrapped[] = $this->wrap($column); } } - $this->select .= implode(', ', $columns); + $this->select .= implode(', ', $wrapped); return $this; } @@ -388,34 +389,38 @@ class Query { /** * Find a record by the primary key. * - * @param int $id + * @param int $id + * @param array $columns * @return object */ - public function find($id) + public function find($id, $columns = array('*')) { - return $this->where('id', '=', $id)->first(); + return $this->where('id', '=', $id)->first($columns); } /** * Execute the query as a SELECT statement and return the first result. * + * @param array $columns * @return object */ - public function first() + public function first($columns = array('*')) { - return (count($results = call_user_func_array(array($this->take(1), 'get'), func_get_args())) > 0) ? $results[0] : null; + + return (count($results = $this->take(1)->get($columns)) > 0) ? $results[0] : null; } /** * Execute the query as a SELECT statement. * + * @param array $columns * @return array */ - public function get() + public function get($columns = array('*')) { if (is_null($this->select)) { - call_user_func_array(array($this, 'select'), (count(func_get_args()) > 0) ? func_get_args() : array('*')); + $this->select($columns); } return DB::query(Query\Compiler::select($this), $this->bindings, $this->connection); From 16265a2cf6bcc954250e1d453e733aea83593111 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 8 Jul 2011 07:31:15 -0700 Subject: [PATCH 113/206] Adjust Eloquent to use new Query API. --- system/db/eloquent.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/db/eloquent.php b/system/db/eloquent.php index 64ab84e3..d5258ca2 100644 --- a/system/db/eloquent.php +++ b/system/db/eloquent.php @@ -275,7 +275,7 @@ abstract class Eloquent { $this->relating_key = strtolower(get_class($this)).'_id'; return static::make($model) - ->select(static::table($model).'.*') + ->select(array(static::table($model).'.*')) ->join($this->relating_table, static::table($model).'.id', '=', $this->relating_table.'.'.strtolower($model).'_id') ->where($this->relating_table.'.'.$this->relating_key, '=', $this->id); } From 00b064c7d52d8f43af819a5d62b1c35f01856748 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 8 Jul 2011 07:32:25 -0700 Subject: [PATCH 114/206] Revert previous changes to Eloquent. --- system/db/eloquent.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/db/eloquent.php b/system/db/eloquent.php index d5258ca2..64ab84e3 100644 --- a/system/db/eloquent.php +++ b/system/db/eloquent.php @@ -275,7 +275,7 @@ abstract class Eloquent { $this->relating_key = strtolower(get_class($this)).'_id'; return static::make($model) - ->select(array(static::table($model).'.*')) + ->select(static::table($model).'.*') ->join($this->relating_table, static::table($model).'.id', '=', $this->relating_table.'.'.strtolower($model).'_id') ->where($this->relating_table.'.'.$this->relating_key, '=', $this->id); } From a0195e938658dc1f5a5bab70da17fa7216b79641 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 8 Jul 2011 07:36:20 -0700 Subject: [PATCH 115/206] Adjust Eloquent to pass array of columns when selecting. --- system/db/eloquent.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/db/eloquent.php b/system/db/eloquent.php index 64ab84e3..d5258ca2 100644 --- a/system/db/eloquent.php +++ b/system/db/eloquent.php @@ -275,7 +275,7 @@ abstract class Eloquent { $this->relating_key = strtolower(get_class($this)).'_id'; return static::make($model) - ->select(static::table($model).'.*') + ->select(array(static::table($model).'.*')) ->join($this->relating_table, static::table($model).'.id', '=', $this->relating_table.'.'.strtolower($model).'_id') ->where($this->relating_table.'.'.$this->relating_key, '=', $this->id); } From 745ac394bbb6f66ba98643cab4f20d74f04d8b78 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 8 Jul 2011 07:37:50 -0700 Subject: [PATCH 116/206] Adjust Eloquent\Hydrator to pass array of columns into Query->get method. --- system/db/eloquent/hydrator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/db/eloquent/hydrator.php b/system/db/eloquent/hydrator.php index 5ebb19a3..b196053f 100644 --- a/system/db/eloquent/hydrator.php +++ b/system/db/eloquent/hydrator.php @@ -195,7 +195,7 @@ class Hydrator { // models back to their parents. $children = $relationship->query ->where_in($relating_table.'.'.$relating_key, array_keys($parents)) - ->get(Eloquent::table(get_class($relationship)).'.*', $relating_table.'.'.$relating_key); + ->get(array(Eloquent::table(get_class($relationship)).'.*', $relating_table.'.'.$relating_key)); $class = get_class($relationship); From 8b1fe5920b5d184be33b7a237a0e0e7fa91954b6 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 8 Jul 2011 07:54:38 -0700 Subject: [PATCH 117/206] Refactoring Eloquent\Hydrator. --- system/db/eloquent/hydrator.php | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/system/db/eloquent/hydrator.php b/system/db/eloquent/hydrator.php index b196053f..b2aa942d 100644 --- a/system/db/eloquent/hydrator.php +++ b/system/db/eloquent/hydrator.php @@ -5,17 +5,15 @@ use System\DB\Eloquent; class Hydrator { /** - * Load the array of hydrated models. + * Load the array of hydrated models and their eager relationships. * * @param object $eloquent * @return array */ public static function hydrate($eloquent) { - // Load the base / parent models from the query results. $results = static::base(get_class($eloquent), $eloquent->query->get()); - // Load all of the eager relationships. if (count($results) > 0) { foreach ($eloquent->includes as $include) @@ -35,6 +33,9 @@ class Hydrator { /** * Hydrate the base models for a query. * + * The resulting model array is keyed by the primary keys of the models. + * This allows the models to easily be matched to their children. + * * @param string $class * @param array $results * @return array @@ -48,10 +49,9 @@ class Hydrator { $model = new $class; $model->attributes = (array) $result; + $model->exists = true; - // The results are keyed by the ID on the record. This will allow us to conveniently - // match them to child models during eager loading. $models[$model->id] = $model; } @@ -80,6 +80,7 @@ class Hydrator { // Reset the WHERE clause and bindings on the query. We'll add our own WHERE clause soon. $relationship->query->where = 'WHERE 1 = 1'; + $relationship->query->bindings = array(); // Initialize the relationship attribute on the parents. As expected, "many" relationships @@ -154,8 +155,6 @@ class Hydrator { */ private static function eagerly_load_belonging($relationship, &$parents, $relating_key, $include) { - // Gather the keys from the parent models. Since the foreign key is on the parent model - // for this type of relationship, we have to gather them individually. $keys = array(); foreach ($parents as &$parent) @@ -189,13 +188,10 @@ class Hydrator { { $relationship->query->select = null; - // Retrieve the raw results as stdClasses. - // - // We also add the foreign key to the select which will allow us to match the - // models back to their parents. - $children = $relationship->query - ->where_in($relating_table.'.'.$relating_key, array_keys($parents)) - ->get(array(Eloquent::table(get_class($relationship)).'.*', $relating_table.'.'.$relating_key)); + $relationship->query->where_in($relating_table.'.'.$relating_key, array_keys($parents)); + + // The foreign key is added to the select to allow us to easily match the models back to their parents. + $children = $relationship->query->get(array(Eloquent::table(get_class($relationship)).'.*', $relating_table.'.'.$relating_key)); $class = get_class($relationship); @@ -204,10 +200,10 @@ class Hydrator { $related = new $class; $related->attributes = (array) $child; + $related->exists = true; - // Remove the foreign key from the attributes since it was added to the query - // to help us match the models. + // Remove the foreign key since it was added to the query to help match to the children. unset($related->attributes[$relating_key]); $parents[$child->$relating_key]->ignore[$include][$child->id] = $related; From c4eb994fbc431454e536a34380977ee6c0765bb7 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 8 Jul 2011 07:58:28 -0700 Subject: [PATCH 118/206] Refactoring Eloquent. --- system/db/eloquent.php | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/system/db/eloquent.php b/system/db/eloquent.php index d5258ca2..8b67cfea 100644 --- a/system/db/eloquent.php +++ b/system/db/eloquent.php @@ -274,10 +274,13 @@ abstract class Eloquent { $this->relating_key = strtolower(get_class($this)).'_id'; - return static::make($model) - ->select(array(static::table($model).'.*')) - ->join($this->relating_table, static::table($model).'.id', '=', $this->relating_table.'.'.strtolower($model).'_id') - ->where($this->relating_table.'.'.$this->relating_key, '=', $this->id); + $relationship = static::make($model); + + $relationship->select(array(static::table($model).'.*')); + $relationship->join($this->relating_table, static::table($model).'.id', '=', $this->relating_table.'.'.strtolower($model).'_id'); + $relationship->where($this->relating_table.'.'.$this->relating_key, '=', $this->id); + + return $relationship; } /** @@ -366,9 +369,12 @@ abstract class Eloquent { { $model = $this->$key(); - return ($this->relating == 'has_one' or $this->relating == 'belongs_to') - ? $this->ignore[$key] = $model->first() - : $this->ignore[$key] = $model->get(); + if (in_array($this->relating, array('has_one', 'belongs_to'))) + { + return $this->ignore[$key] = $model->first(); + } + + return $this->ignore[$key] = $model->get(); } return (array_key_exists($key, $this->attributes)) ? $this->attributes[$key] : null; From 7af9e08a60f51769b10a3af30bf94f098af84ed7 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 8 Jul 2011 08:00:04 -0700 Subject: [PATCH 119/206] Refactoring Eloquent. --- system/db/eloquent.php | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/system/db/eloquent.php b/system/db/eloquent.php index 8b67cfea..a705cc5b 100644 --- a/system/db/eloquent.php +++ b/system/db/eloquent.php @@ -369,12 +369,7 @@ abstract class Eloquent { { $model = $this->$key(); - if (in_array($this->relating, array('has_one', 'belongs_to'))) - { - return $this->ignore[$key] = $model->first(); - } - - return $this->ignore[$key] = $model->get(); + return $this->ignore[$key] = (in_array($this->relating, array('has_one', 'belongs_to'))) ? $model->first() : $model->get(); } return (array_key_exists($key, $this->attributes)) ? $this->attributes[$key] : null; From c2b7e60bddbe98504618bcfc6cb6ef08a7628adb Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 8 Jul 2011 08:19:40 -0700 Subject: [PATCH 120/206] Cleaning up DB session driver. --- system/session/driver/db.php | 1 + 1 file changed, 1 insertion(+) diff --git a/system/session/driver/db.php b/system/session/driver/db.php index f16e3c85..969c642c 100644 --- a/system/session/driver/db.php +++ b/system/session/driver/db.php @@ -27,6 +27,7 @@ class DB implements \System\Session\Driver { public function save($session) { $this->delete($session['id']); + $this->table()->insert(array('id' => $session['id'], 'last_activity' => $session['last_activity'], 'data' => serialize($session['data']))); } From c5a2752e8a3e2480eab4a7adaed9189225476b4a Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 8 Jul 2011 08:24:57 -0700 Subject: [PATCH 121/206] Refactoring Input class. --- system/input.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/input.php b/system/input.php index f5d59a05..0cf4a223 100644 --- a/system/input.php +++ b/system/input.php @@ -78,7 +78,7 @@ class Input { { list($file, $key) = explode('.', $key); - return (isset($_FILES[$file][$key])) ? $_FILES[$file][$key] : $default; + return Arr::get($_FILES[$file], $key, $default); } return Arr::get($_FILES, $key, $default); From b1b2b932cc1e4c681a4511a4fed05d1ddd9c5052 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 8 Jul 2011 09:43:04 -0700 Subject: [PATCH 122/206] Moved Cache\Factory functionality into Cache class. --- system/cache.php | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/system/cache.php b/system/cache.php index afb641a5..321f942c 100644 --- a/system/cache.php +++ b/system/cache.php @@ -25,9 +25,28 @@ class Cache { $driver = Config::get('cache.driver'); } - return (array_key_exists($driver, static::$drivers)) - ? static::$drivers[$driver] - : static::$drivers[$driver] = Cache\Factory::make($driver); + if ( ! array_key_exists($driver, static::$drivers)) + { + switch ($driver) + { + case 'file': + static::$drivers[$driver] = new Cache\Driver\File; + break; + + case 'memcached': + static::$drivers[$driver] = new Cache\Driver\Memcached; + break; + + case 'apc': + static::$drivers[$driver] = new Cache\Driver\APC; + break; + + default: + throw new \Exception("Cache driver [$driver] is not supported."); + } + } + + return static::$drivers[$driver]; } /** From 8056e0d1a70787e294a67f327d2c581d169fecdb Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 8 Jul 2011 09:48:01 -0700 Subject: [PATCH 123/206] Moved Session\Factory functionality into the Session class. --- system/session.php | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/system/session.php b/system/session.php index 644345d6..48ff3664 100644 --- a/system/session.php +++ b/system/session.php @@ -25,7 +25,27 @@ class Session { { if (is_null(static::$driver)) { - static::$driver = Session\Factory::make(Config::get('session.driver')); + switch (Config::get('session.driver')) + { + case 'file': + static::$driver = new Session\Driver\File; + break; + + case 'db': + static::$driver = new Session\Driver\DB; + break; + + case 'memcached': + static::$driver = new Session\Driver\Memcached; + break; + + case 'apc': + static::$driver = new Session\Driver\APC; + break; + + default: + throw new \Exception("Session driver [$driver] is not supported."); + } } return static::$driver; From d0fafd1301aaca86cc6f9f97f1ec6275673e5674 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 8 Jul 2011 12:16:30 -0700 Subject: [PATCH 124/206] Formatting DB class. --- system/db.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/system/db.php b/system/db.php index 1b03d851..4769fc74 100644 --- a/system/db.php +++ b/system/db.php @@ -25,9 +25,12 @@ class DB { $connection = Config::get('db.default'); } - return array_key_exists($connection, static::$connections) - ? static::$connections[$connection] - : static::$connections[$connection] = DB\Connector::connect($connection); + if ( ! array_key_exists($connection, static::$connections)) + { + static::$connections[$connection] = DB\Connector::connect($connection); + } + + return static::$connections[$connection]; } /** From 5bb5adcf9c769612eb61717cdaf97079d47df927 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 8 Jul 2011 12:38:54 -0700 Subject: [PATCH 125/206] Added Request::spoofed method. --- system/request.php | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/system/request.php b/system/request.php index 6cecf359..64b8b83c 100644 --- a/system/request.php +++ b/system/request.php @@ -69,14 +69,24 @@ class Request { /** * Get the request method. * - * The request method may be spoofed if a hidden "REQUEST_METHOD" POST element - * is present, allowing HTML forms to simulate PUT and DELETE requests. - * * @return string */ public static function method() { - return (array_key_exists('REQUEST_METHOD', $_POST)) ? $_POST['REQUEST_METHOD'] : $_SERVER['REQUEST_METHOD']; + return (static::spoofed()) ? $_POST['REQUEST_METHOD'] : $_SERVER['REQUEST_METHOD']; + } + + /** + * Determine if the request method is being spoofed by a hidden Form element. + * + * Hidden form elements are used to spoof PUT and DELETE requests since + * they are not supported by HTML forms. + * + * @return bool + */ + public static function spoofed() + { + return array_key_exists('REQUEST_METHOD', $_POST); } /** From 5bea9121afdbfd69c5d5a164229c43f6fe7b9220 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 8 Jul 2011 12:41:03 -0700 Subject: [PATCH 126/206] Refactoring input class to use Request::spoofed when hydrating input. --- system/input.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/system/input.php b/system/input.php index 0cf4a223..fac8aa54 100644 --- a/system/input.php +++ b/system/input.php @@ -103,9 +103,7 @@ class Input { case 'PUT': case 'DELETE': - // The request method can be spoofed by specifying a "REQUEST_METHOD" in the $_POST array. - // If the method is being spoofed, the $_POST array will be considered the input. - if (isset($_POST['REQUEST_METHOD']) and in_array($_POST['REQUEST_METHOD'], array('PUT', 'DELETE'))) + if (Request::spoofed()) { static::$input =& $_POST; } From 9fc8c36b9413e0a12f77308c820d6d446b4fc6c5 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 8 Jul 2011 12:44:25 -0700 Subject: [PATCH 127/206] Removing comment bloat from Redirect class. --- system/redirect.php | 7 ------- 1 file changed, 7 deletions(-) diff --git a/system/redirect.php b/system/redirect.php index 9c5ada19..df3eb094 100644 --- a/system/redirect.php +++ b/system/redirect.php @@ -75,13 +75,6 @@ class Redirect { */ public static function __callStatic($method, $parameters) { - // Get the parameters for the method. Dynamic routes can be generated using an - // array of parameters for routes that contain wildcards, such as /user/(:num). - // - // Example: Redirect::to_profile(array(1)); - // - // Here we'll check to see if a parameter was passed. If it wasn't, we'll just - // pass an empty array into the URL generator. $parameters = (isset($parameters[0])) ? $parameters[0] : array(); // Dynamically redirect to a secure route URL. From 0dd06ad31408b3a7d7cfa487cf4f48d66f1a4a32 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 8 Jul 2011 12:45:06 -0700 Subject: [PATCH 128/206] Remove comment bloat from Response class. --- system/response.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/system/response.php b/system/response.php index 8c648257..2f90bdf0 100644 --- a/system/response.php +++ b/system/response.php @@ -109,8 +109,6 @@ class Response { */ public static function prepare($response) { - // If the response is a Redirect instance, grab the Response. The Redirect class - // manages a Response instance internally. if ($response instanceof Redirect) { $response = $response->response; From 5ea2974bd634bbf7990ff7e94710b12a23d801bb Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 8 Jul 2011 12:46:21 -0700 Subject: [PATCH 129/206] Trim comment bloat from Route class. --- system/route.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/system/route.php b/system/route.php index df4a24a4..62ce1cce 100644 --- a/system/route.php +++ b/system/route.php @@ -43,7 +43,7 @@ class Route { * * @param mixed $route * @param array $parameters - * @return mixed + * @return Response */ public function call() { @@ -57,8 +57,6 @@ class Route { { $response = isset($this->callback['before']) ? Route\Filter::call($this->callback['before'], array(), true) : null; - // Verify that the before filters did not return a response. Before filters can override - // the request cycle to make things like authentication more convenient. if (is_null($response) and isset($this->callback['do'])) { $response = call_user_func_array($this->callback['do'], $this->parameters); From e48d6111499dd926b69d1d4e94bc8cb00a5b571d Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 8 Jul 2011 12:48:44 -0700 Subject: [PATCH 130/206] Remove comment bloat from URL class. --- system/url.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/system/url.php b/system/url.php index 1105f6d8..3e1de338 100644 --- a/system/url.php +++ b/system/url.php @@ -19,8 +19,6 @@ class URL { $base = Config::get('application.url'); - // Assets live in the public directory, and should not have - // the index file appended to their URLs. if ( ! $asset) { $base .= '/'.Config::get('application.index'); From 924ecf879cf00f8e062d7f8537e48c1a038271ad Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 8 Jul 2011 12:57:11 -0700 Subject: [PATCH 131/206] Added __toString method to Response. --- system/response.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/system/response.php b/system/response.php index 2f90bdf0..c1734d2b 100644 --- a/system/response.php +++ b/system/response.php @@ -177,4 +177,12 @@ class Response { return $this->status == 301 or $this->status == 302; } + /** + * Get the parsed content of the Response. + */ + public function __toString() + { + return (string) $this->content; + } + } \ No newline at end of file From 9608a0dee727fb8b9221c8bfbeddab14d90f8a5c Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 8 Jul 2011 12:57:32 -0700 Subject: [PATCH 132/206] Removed comment bloat from View class. --- system/view.php | 6 ------ 1 file changed, 6 deletions(-) diff --git a/system/view.php b/system/view.php index c1016f9d..46159018 100644 --- a/system/view.php +++ b/system/view.php @@ -66,18 +66,12 @@ class View { } } - // Extract the view data into the local scope. extract($this->data, EXTR_SKIP); ob_start(); $path = $this->find(); - // We include the view into the local scope within a try / catch to catch any - // exceptions that may occur while the view is rendering. - // - // Otherwise, a white screen of death will be shown if an exception occurs - // while rendering the view. try { include $path; } catch (\Exception $e) { Error::handle($e); } return ob_get_clean(); From ceb0e1a8074372d4391f0fdcf1fc4714a639c5a1 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Sat, 9 Jul 2011 17:15:23 -0500 Subject: [PATCH 133/206] added check for array in Request::spoofed. --- system/request.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/request.php b/system/request.php index 64b8b83c..a65a96d1 100644 --- a/system/request.php +++ b/system/request.php @@ -86,7 +86,7 @@ class Request { */ public static function spoofed() { - return array_key_exists('REQUEST_METHOD', $_POST); + return is_array($_POST) and array_key_exists('REQUEST_METHOD', $_POST); } /** From ca58f80b227fc5adc9595bd78dce2a4e35b99217 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Sat, 9 Jul 2011 22:15:42 -0500 Subject: [PATCH 134/206] removed cache and session factories. moved cache item management into cache class and out of drivers. tweaked auth class. --- system/auth.php | 8 ++------ system/cache.php | 15 +++++++++++---- system/cache/driver/apc.php | 22 +++++---------------- system/cache/driver/file.php | 9 +-------- system/cache/driver/memcached.php | 22 +++++---------------- system/cache/factory.php | 29 ---------------------------- system/session/factory.php | 32 ------------------------------- 7 files changed, 24 insertions(+), 113 deletions(-) delete mode 100644 system/cache/factory.php delete mode 100644 system/session/factory.php diff --git a/system/auth.php b/system/auth.php index b06dcc7f..408930fd 100644 --- a/system/auth.php +++ b/system/auth.php @@ -40,11 +40,9 @@ class Auth { throw new \Exception("You must specify a session driver before using the Auth class."); } - $model = static::model(); - if (is_null(static::$user) and Session::has(static::$key)) { - static::$user = $model::find(Session::get(static::$key)); + static::$user = forward_static_call(array(static::model(), 'find'), Session::get(static::$key)); } return static::$user; @@ -61,9 +59,7 @@ class Auth { */ public static function login($username, $password) { - $model = static::model(); - - $user = $model::where(Config::get('auth.username'), '=', $username)->first(); + $model = forward_static_call(array(static::model(), 'where'), Config::get('auth.username'), '=', $username)->first(); if ( ! is_null($user)) { diff --git a/system/cache.php b/system/cache.php index 321f942c..fe2d2dc1 100644 --- a/system/cache.php +++ b/system/cache.php @@ -7,7 +7,14 @@ class Cache { * * @var Cache\Driver */ - private static $drivers = array(); + public static $drivers = array(); + + /** + * All of the items retrieved by the cache drivers. + * + * @var array + */ + public static $items = array(); /** * Get a cache driver instance. If no driver name is specified, the default @@ -59,9 +66,9 @@ class Cache { */ public static function get($key, $default = null, $driver = null) { - if (array_key_exists($key, static::driver($driver)->items)) + if (isset(static::$items[$driver][$key])) { - return static::driver($driver)->items[$key]; + return static::$items[$driver][$key]; } if (is_null($item = static::driver($driver)->get($key))) @@ -69,7 +76,7 @@ class Cache { return is_callable($default) ? call_user_func($default) : $default; } - return $item; + return static::$items[$driver][$key] = $item; } /** diff --git a/system/cache/driver/apc.php b/system/cache/driver/apc.php index 978d995c..ba178634 100644 --- a/system/cache/driver/apc.php +++ b/system/cache/driver/apc.php @@ -1,13 +1,8 @@ items[$key] = $cache; + return ( ! is_null($cache = apc_fetch(Config::get('cache.key').$key))) ? $cache : null; } /** @@ -48,7 +36,7 @@ class APC implements \System\Cache\Driver { */ public function put($key, $value, $minutes) { - apc_store(\System\Config::get('cache.key').$key, $value, $minutes * 60); + apc_store(Config::get('cache.key').$key, $value, $minutes * 60); } /** @@ -59,7 +47,7 @@ class APC implements \System\Cache\Driver { */ public function forget($key) { - apc_delete(\System\Config::get('cache.key').$key); + apc_delete(Config::get('cache.key').$key); } } \ No newline at end of file diff --git a/system/cache/driver/file.php b/system/cache/driver/file.php index 3de5ba4f..442f1cfd 100644 --- a/system/cache/driver/file.php +++ b/system/cache/driver/file.php @@ -2,13 +2,6 @@ class File implements \System\Cache\Driver { - /** - * All of the loaded cache items. - * - * @var array - */ - public $items = array(); - /** * Determine if an item exists in the cache. * @@ -43,7 +36,7 @@ class File implements \System\Cache\Driver { return null; } - return $this->items[$key] = unserialize(substr($cache, 10)); + return unserialize(substr($cache, 10)); } /** diff --git a/system/cache/driver/memcached.php b/system/cache/driver/memcached.php index 2710ea78..c2f4fe06 100644 --- a/system/cache/driver/memcached.php +++ b/system/cache/driver/memcached.php @@ -1,13 +1,8 @@ get(\System\Config::get('cache.key').$key); - - if ($cache === false) - { - return null; - } - - return $this->items[$key] = $cache; + return (($cache = \System\Memcached::instance()->get(Config::get('cache.key').$key)) !== false) ? $cache : null; } /** @@ -48,7 +36,7 @@ class Memcached implements \System\Cache\Driver { */ public function put($key, $value, $minutes) { - \System\Memcached::instance()->set(\System\Config::get('cache.key').$key, $value, 0, $minutes * 60); + \System\Memcached::instance()->set(Config::get('cache.key').$key, $value, 0, $minutes * 60); } /** @@ -59,7 +47,7 @@ class Memcached implements \System\Cache\Driver { */ public function forget($key) { - \System\Memcached::instance()->delete(\System\Config::get('cache.key').$key); + \System\Memcached::instance()->delete(Config::get('cache.key').$key); } } \ No newline at end of file diff --git a/system/cache/factory.php b/system/cache/factory.php deleted file mode 100644 index 245f4c85..00000000 --- a/system/cache/factory.php +++ /dev/null @@ -1,29 +0,0 @@ - Date: Sat, 9 Jul 2011 22:31:06 -0500 Subject: [PATCH 135/206] cleaning up Request::uri method. --- system/request.php | 45 +++++++++++++++++---------------------------- 1 file changed, 17 insertions(+), 28 deletions(-) diff --git a/system/request.php b/system/request.php index a65a96d1..6381dd43 100644 --- a/system/request.php +++ b/system/request.php @@ -2,13 +2,6 @@ class Request { - /** - * The request URI. - * - * @var string - */ - public static $uri; - /** * The route handling the current request. * @@ -19,15 +12,15 @@ class Request { /** * Get the request URI. * + * The server PATH_INFO will be used if available. Otherwise, the REQUEST_URI will be used. + * The application URL and index will be removed from the URI. + * + * If the request is to the root of application, a single forward slash will be returned. + * * @return string */ public static function uri() { - if ( ! is_null(static::$uri)) - { - return static::$uri; - } - if (isset($_SERVER['PATH_INFO'])) { $uri = $_SERVER['PATH_INFO']; @@ -46,24 +39,19 @@ class Request { throw new \Exception("Malformed request URI. Request terminated."); } - $uri = static::remove_from_uri($uri, parse_url(Config::get('application.url'), PHP_URL_PATH)); - $uri = static::remove_from_uri($uri, '/'.Config::get('application.index')); + // Remove the application URL from the URI. + if (strpos($uri, $base = parse_url(Config::get('application.url'), PHP_URL_PATH)) === 0) + { + $uri = substr($uri, strlen($base)); + } - $uri = trim($uri, '/'); + // Remove the application index page from the URI. + if (strpos($uri, $index = '/'.Config::get('application.index')) === 0) + { + $uri = substr($uri, strlen($index)); + } - return ($uri == '') ? '/' : strtolower($uri); - } - - /** - * Remove a string from the beginning of a URI. - * - * @param string $uri - * @param string $value - * @return string - */ - private static function remove_from_uri($uri, $value) - { - return (strpos($uri, $value) === 0) ? substr($uri, strlen($value)) : $uri; + return (($uri = trim($uri, '/')) == '') ? '/' : strtolower($uri); } /** @@ -156,6 +144,7 @@ class Request { */ public static function __callStatic($method, $parameters) { + // Dynamically determine if a given route is handling the request. if (strpos($method, 'route_is_') === 0) { return static::route_is(substr($method, 9)); From 8277694693928d019784f959fad3a1fcee7f324f Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Sat, 9 Jul 2011 23:41:27 -0500 Subject: [PATCH 136/206] refactoring router. --- system/router.php | 46 +++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/system/router.php b/system/router.php index 1a898bfd..3aa7e5d9 100644 --- a/system/router.php +++ b/system/router.php @@ -23,7 +23,8 @@ class Router { static::$routes = static::load($uri); } - // Put the request method and URI in route form. Routes begin with the request method and a forward slash. + // Put the request method and URI in route form. + // Routes begin with the request method and a forward slash. $uri = $method.' /'.trim($uri, '/'); // Is there an exact match for the request? @@ -34,7 +35,8 @@ class Router { foreach (static::$routes as $keys => $callback) { - // Only check routes that have multiple URIs or wildcards. Other routes would be caught by a literal match. + // Only check routes that have multiple URIs or wildcards. + // Other routes would have been caught by the check for literal matches. if (strpos($keys, '(') !== false or strpos($keys, ',') !== false ) { foreach (explode(', ', $keys) as $key) @@ -58,31 +60,29 @@ class Router { */ public static function load($uri) { - if ( ! is_dir(APP_PATH.'routes')) + return (is_dir(APP_PATH.'routes')) ? static::load_from_directory($uri) : require APP_PATH.'routes'.EXT; + } + + /** + * Load the appropriate route file from the routes directory. + * + * @param string $uri + * @return array + */ + private static function load_from_directory($uri) + { + // If it exists, The "home" routes file is loaded for every request. This allows + // for "catch-all" routes such as http://example.com/username... + $home = (file_exists($path = APP_PATH.'routes/home'.EXT)) ? require $path : array(); + + if ($uri == '') { - return require APP_PATH.'routes'.EXT; + return $home; } - if ( ! file_exists(APP_PATH.'routes/home'.EXT)) - { - throw new \Exception("A [home] route file is required when using a route directory."); - } + $segments = explode('/', $uri); - if ($uri == '/') - { - return require APP_PATH.'routes/home'.EXT; - } - else - { - $segments = explode('/', $uri); - - if ( ! file_exists(APP_PATH.'routes/'.$segments[0].EXT)) - { - return require APP_PATH.'routes/home'.EXT; - } - - return array_merge(require APP_PATH.'routes/'.$segments[0].EXT, require APP_PATH.'routes/home'.EXT); - } + return (file_exists($path = APP_PATH.'routes/'.$segments[0].EXT)) ? array_merge(require $path, $home) : $home; } /** From d0ba4719d800c790a9fc0948bfb13d7c1797258b Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Sat, 9 Jul 2011 23:54:07 -0500 Subject: [PATCH 137/206] extracted the route wildcard translation to a separate method. --- system/router.php | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/system/router.php b/system/router.php index 3aa7e5d9..97dc0830 100644 --- a/system/router.php +++ b/system/router.php @@ -41,9 +41,7 @@ class Router { { foreach (explode(', ', $keys) as $key) { - $key = str_replace(':num', '[0-9]+', str_replace(':any', '[a-zA-Z0-9\-_]+', $key)); - - if (preg_match('#^'.$key.'$#', $uri)) + if (preg_match('#^'.$key = static::translate_wildcards($key).'$#', $uri)) { return Request::$route = new Route($keys, $callback, static::parameters($uri, $key)); } @@ -85,6 +83,17 @@ class Router { return (file_exists($path = APP_PATH.'routes/'.$segments[0].EXT)) ? array_merge(require $path, $home) : $home; } + /** + * Translate route URI wildcards to regular expressions. + * + * @param string $key + * @return string + */ + private static function translate_wildcards($key) + { + return str_replace(':num', '[0-9]+', str_replace(':any', '[a-zA-Z0-9\-_]+', $key)); + } + /** * Extract the parameters from a URI based on a route URI. * From da8c412dbc001b01f2c19ef9e258f4b003df35dc Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Sun, 10 Jul 2011 22:38:55 -0500 Subject: [PATCH 138/206] deprecated text class. --- system/text.php | 98 ------------------------------------------------- 1 file changed, 98 deletions(-) delete mode 100644 system/text.php diff --git a/system/text.php b/system/text.php deleted file mode 100644 index 1881b1ed..00000000 --- a/system/text.php +++ /dev/null @@ -1,98 +0,0 @@ -= $limit) - { - $out = trim($out); - - return (strlen($out) == strlen($value)) ? $out : $out.$end; - } - } - } - - /** - * Censor a string. - * - * @param string $value - * @param array $censored - * @param string $replacement - * @return string - */ - public static function censor($value, $censored, $replacement = '####') - { - $value = ' '.$value.' '; - - // Assume the word will be book-ended by the following. - $delim = '[-_\'\"`(){}<>\[\]|!?@#%&,.:;^~*+=\/ 0-9\n\r\t]'; - - foreach ($censored as $word) - { - if ($replacement != '') - { - $value = preg_replace("/({$delim})(".str_replace('\*', '\w*?', preg_quote($word, '/')).")({$delim})/i", "\\1{$replacement}\\3", $value); - } - else - { - $value = preg_replace("/({$delim})(".str_replace('\*', '\w*?', preg_quote($word, '/')).")({$delim})/ie", "'\\1'.str_repeat('#', strlen('\\2')).'\\3'", $value); - } - } - - return trim($value); - } - -} \ No newline at end of file From 46b0a7d8adfbe96180a20f91c73fe65107c9f499 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Sun, 10 Jul 2011 22:59:05 -0500 Subject: [PATCH 139/206] removed some comment bloat from the Form class. --- system/form.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/system/form.php b/system/form.php index e7b99a27..1b6017a9 100644 --- a/system/form.php +++ b/system/form.php @@ -34,9 +34,6 @@ class Form { // If the request method is PUT or DELETE, create a hidden input element with the // request method in it since HTML forms do not support these two methods. - // - // The Request class will check for this hidden POST element when determining the - // request method. If it is present, it will override the real request method. if ($method == 'PUT' or $method == 'DELETE') { $html .= PHP_EOL.static::input('hidden', 'REQUEST_METHOD', $method); From 063cde50c0a0f37962e3b9367b2a0789b8b4bbf7 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Sun, 10 Jul 2011 23:08:14 -0500 Subject: [PATCH 140/206] removed file context from error messages. --- system/error.php | 32 +------------------------------- system/views/exception.php | 14 -------------- 2 files changed, 1 insertion(+), 45 deletions(-) diff --git a/system/error.php b/system/error.php index 6e98cde3..7af64e51 100644 --- a/system/error.php +++ b/system/error.php @@ -51,6 +51,7 @@ class Error { $file = $e->getFile(); } + // Trim the period off the error message since we will be formatting it oursevles. $message = rtrim($e->getMessage(), '.'); if (Config::get('error.log')) @@ -66,7 +67,6 @@ class Error { ->bind('file', $file) ->bind('line', $e->getLine()) ->bind('trace', $e->getTraceAsString()) - ->bind('contexts', static::context($file, $e->getLine())); Response::make($view, 500)->send(); } @@ -78,34 +78,4 @@ class Error { exit(1); } - /** - * Get the file context of an exception. - * - * @param string $path - * @param int $line - * @param int $padding - * @return array - */ - private static function context($path, $line, $padding = 5) - { - if (file_exists($path)) - { - $file = file($path, FILE_IGNORE_NEW_LINES); - - array_unshift($file, ''); - - // Calculate the starting position of the file context. - $start = $line - $padding; - $start = ($start < 0) ? 0 : $start; - - // Calculate the context length. - $length = ($line - $start) + $padding + 1; - $length = (($start + $length) > count($file) - 1) ? null : $length; - - return array_slice($file, $start, $length, true); - } - - return array(); - } - } \ No newline at end of file diff --git a/system/views/exception.php b/system/views/exception.php index 996eecf9..f2606d86 100644 --- a/system/views/exception.php +++ b/system/views/exception.php @@ -60,20 +60,6 @@
- -
-

Context:

- - 0) { ?> - - $context) { ?> -
- - - - Context unavailable. - -
\ No newline at end of file From 95528adab5e4f409d20f81c935e8ffdfee263f8d Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Sun, 10 Jul 2011 23:16:11 -0500 Subject: [PATCH 141/206] fix syntax error in error class. --- application/routes.php | 1 + system/error.php | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/application/routes.php b/application/routes.php index 229a673a..6f5afff2 100644 --- a/application/routes.php +++ b/application/routes.php @@ -17,6 +17,7 @@ return array( 'GET /' => function() { + $this-> return View::make('home/index'); }, diff --git a/system/error.php b/system/error.php index 7af64e51..a4070fab 100644 --- a/system/error.php +++ b/system/error.php @@ -66,7 +66,7 @@ class Error { ->bind('message', $message) ->bind('file', $file) ->bind('line', $e->getLine()) - ->bind('trace', $e->getTraceAsString()) + ->bind('trace', $e->getTraceAsString()); Response::make($view, 500)->send(); } From 48419ad321356c3be5200d6d449fc43e530eaf29 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Sun, 10 Jul 2011 23:21:33 -0500 Subject: [PATCH 142/206] tweaking routes. --- application/routes.php | 1 - 1 file changed, 1 deletion(-) diff --git a/application/routes.php b/application/routes.php index 6f5afff2..229a673a 100644 --- a/application/routes.php +++ b/application/routes.php @@ -17,7 +17,6 @@ return array( 'GET /' => function() { - $this-> return View::make('home/index'); }, From c30185eb27c81e01f2087bcdbe566d2f6e968c01 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Sun, 10 Jul 2011 23:31:37 -0500 Subject: [PATCH 143/206] refactoring eloquent. --- system/db/eloquent.php | 49 +++++++++++++++--------------------------- 1 file changed, 17 insertions(+), 32 deletions(-) diff --git a/system/db/eloquent.php b/system/db/eloquent.php index a705cc5b..f63d265c 100644 --- a/system/db/eloquent.php +++ b/system/db/eloquent.php @@ -274,19 +274,16 @@ abstract class Eloquent { $this->relating_key = strtolower(get_class($this)).'_id'; - $relationship = static::make($model); - - $relationship->select(array(static::table($model).'.*')); - $relationship->join($this->relating_table, static::table($model).'.id', '=', $this->relating_table.'.'.strtolower($model).'_id'); - $relationship->where($this->relating_table.'.'.$this->relating_key, '=', $this->id); - - return $relationship; + return static::make($model) + ->select(array(static::table($model).'.*')) + ->join($this->relating_table, static::table($model).'.id', '=', $this->relating_table.'.'.strtolower($model).'_id') + ->where($this->relating_table.'.'.$this->relating_key, '=', $this->id); } /** * Save the model to the database. * - * @return bool + * @return void */ public function save() { @@ -303,23 +300,28 @@ abstract class Eloquent { if (property_exists($model, 'timestamps') and $model::$timestamps) { - $this->timestamp(); + $this->updated_at = date('Y-m-d H:i:s'); + + if ( ! $this->exists) + { + $this->created_at = $this->updated_at; + } } + // If the model already exists in the database, we will just update it. + // Otherwise, we will insert the model and set the ID attribute. if ($this->exists) { - $result = $this->query->where('id', '=', $this->attributes['id'])->update($this->dirty) == 1; + $this->query->where('id', '=', $this->attributes['id'])->update($this->dirty); } else { $this->attributes['id'] = $this->query->insert_get_id($this->attributes); - - $result = $this->exists = is_numeric($this->id); } - $this->dirty = array(); + $this->exists = true; - return $result; + $this->dirty = array(); } /** @@ -338,21 +340,6 @@ abstract class Eloquent { return $this->query->delete(); } - /** - * Set the creation and update timestamps on the model. - * - * @return void - */ - private function timestamp() - { - $this->updated_at = date('Y-m-d H:i:s'); - - if ( ! $this->exists) - { - $this->created_at = $this->updated_at; - } - } - /** * Magic method for retrieving model attributes. */ @@ -405,9 +392,7 @@ abstract class Eloquent { */ public function __unset($key) { - unset($this->attributes[$key]); - unset($this->ignore[$key]); - unset($this->dirty[$key]); + unset($this->attributes[$key], $this->ignore[$key], $this->dirty[$key]); } /** From c75f298c34a281e0a30959ceafcf515271ce945d Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Sun, 10 Jul 2011 23:36:49 -0500 Subject: [PATCH 144/206] fix typo in error class. --- system/error.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/error.php b/system/error.php index a4070fab..c1e64a7a 100644 --- a/system/error.php +++ b/system/error.php @@ -51,7 +51,7 @@ class Error { $file = $e->getFile(); } - // Trim the period off the error message since we will be formatting it oursevles. + // Trim the period off the error message since we will be formatting it ourselves. $message = rtrim($e->getMessage(), '.'); if (Config::get('error.log')) From 9624d275a48f85e08969e86b130012ba68b89637 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Sun, 10 Jul 2011 23:40:05 -0500 Subject: [PATCH 145/206] removed comment bloat from index.php. --- public/index.php | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/public/index.php b/public/index.php index 2c296c50..3fdfb6b4 100644 --- a/public/index.php +++ b/public/index.php @@ -9,11 +9,6 @@ * @link http://laravel.com */ -// -------------------------------------------------------------- -// Set the framework starting time. -// -------------------------------------------------------------- -define('LARAVEL_START', microtime(true)); - // -------------------------------------------------------------- // Define the framework paths. // -------------------------------------------------------------- @@ -86,20 +81,13 @@ if (System\Config::get('session.driver') != '') // -------------------------------------------------------------- $response = System\Route\Filter::call('before', array(), true); -// -------------------------------------------------------------- -// Only execute the route function if the "before" filter did -// not override by sending a response. -// -------------------------------------------------------------- +// ---------------------------------------------------------- +// Execute the route function. +// ---------------------------------------------------------- if (is_null($response)) { - // ---------------------------------------------------------- - // Route the request to the proper route. - // ---------------------------------------------------------- $route = System\Router::route(Request::method(), Request::uri()); - // ---------------------------------------------------------- - // Execute the route function. - // ---------------------------------------------------------- if ( ! is_null($route)) { $response = $route->call(); From 15328afa4d671e293c3b76207d3a039a168b954e Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Sun, 10 Jul 2011 23:45:38 -0500 Subject: [PATCH 146/206] refactoring auto-loader. --- system/loader.php | 21 +++------------------ 1 file changed, 3 insertions(+), 18 deletions(-) diff --git a/system/loader.php b/system/loader.php index 86960f89..adf3a126 100644 --- a/system/loader.php +++ b/system/loader.php @@ -2,46 +2,31 @@ /** * This function is registered on the auto-loader stack by the front controller. + * + * All namespace slashes will be replaced with directory slashes since all Laravel + * system classes are organized using a namespace to directory convention. */ return function($class) { - // ---------------------------------------------------------- - // Replace namespace slashes with directory slashes. - // ---------------------------------------------------------- $file = strtolower(str_replace('\\', '/', $class)); - // ---------------------------------------------------------- - // Should the class be aliased? - // ---------------------------------------------------------- if (array_key_exists($class, $aliases = System\Config::get('aliases'))) { return class_alias($aliases[$class], $class); } - // ---------------------------------------------------------- - // Is the class a Laravel framework class? - // ---------------------------------------------------------- if (file_exists($path = BASE_PATH.$file.EXT)) { require $path; } - // ---------------------------------------------------------- - // Is the class in the application/models directory? - // ---------------------------------------------------------- elseif (file_exists($path = APP_PATH.'models/'.$file.EXT)) { require $path; } - // ---------------------------------------------------------- - // Is the class in the application/libraries directory? - // ---------------------------------------------------------- elseif (file_exists($path = APP_PATH.'libraries/'.$file.EXT)) { require $path; } - // ---------------------------------------------------------- - // Is the class anywhere in the application directory? - // ---------------------------------------------------------- elseif (file_exists($path = APP_PATH.$file.EXT)) { require $path; From 5eb379d6cc95717ffc1f90b2943b17280283dd21 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Mon, 11 Jul 2011 07:05:46 -0700 Subject: [PATCH 147/206] Refactoring the session class. --- system/session.php | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/system/session.php b/system/session.php index 48ff3664..cde095ac 100644 --- a/system/session.php +++ b/system/session.php @@ -52,22 +52,18 @@ class Session { } /** - * Load the session for the user. + * Load a user session by ID. * + * @param string $id * @return void */ - public static function load() + public static function load($id) { - if ( ! is_null($id = Cookie::get('laravel_session'))) - { - static::$session = static::driver()->load($id); - } + static::$session = ( ! is_null($id)) ? static::driver()->load($id) : null; - // If the session is invalid or expired, start a new one. - if (is_null($id) or is_null(static::$session) or static::expired(static::$session['last_activity'])) + if (is_null(static::$session) or static::expired(static::$session['last_activity'])) { - static::$session['id'] = Str::random(40); - static::$session['data'] = array(); + static::$session = array('id' => Str::random(40), 'data' => array()); } if ( ! static::has('csrf_token')) From bb2a23174970186ad02c0d6ca10502ffbc0b91c6 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Mon, 11 Jul 2011 07:06:20 -0700 Subject: [PATCH 148/206] Pass session ID into session::load. --- public/index.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/index.php b/public/index.php index 3fdfb6b4..a3b21fd6 100644 --- a/public/index.php +++ b/public/index.php @@ -73,7 +73,7 @@ date_default_timezone_set(System\Config::get('application.timezone')); // -------------------------------------------------------------- if (System\Config::get('session.driver') != '') { - System\Session::load(); + System\Session::load(System\Cookie::get('laravel_session')); } // -------------------------------------------------------------- From 69d1377fd485023086ead755267f1269984f4499 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Mon, 11 Jul 2011 07:47:23 -0700 Subject: [PATCH 149/206] Refactoring the error class. --- system/error.php | 48 +++++++++++++++++++++++++++++++++--------------- 1 file changed, 33 insertions(+), 15 deletions(-) diff --git a/system/error.php b/system/error.php index c1e64a7a..b7f69917 100644 --- a/system/error.php +++ b/system/error.php @@ -31,27 +31,15 @@ class Error { */ public static function handle($e) { - // Clean the output buffer so no previously rendered views or text is sent to the browser. if (ob_get_level() > 0) { ob_clean(); } - // Get the error severity in human readable format. $severity = (array_key_exists($e->getCode(), static::$levels)) ? static::$levels[$e->getCode()] : $e->getCode(); - // Get the file in which the error occured. - // Views require special handling since view errors occur in eval'd code. - if (strpos($e->getFile(), 'view.php') !== false and strpos($e->getFile(), "eval()'d code") !== false) - { - $file = APP_PATH.'views/'.View::$last.EXT; - } - else - { - $file = $e->getFile(); - } + $file = static::file($e); - // Trim the period off the error message since we will be formatting it ourselves. $message = rtrim($e->getMessage(), '.'); if (Config::get('error.log')) @@ -59,6 +47,38 @@ class Error { Log::error($message.' in '.$e->getFile().' on line '.$e->getLine()); } + static::show($e, $severity, $message, $file); + + exit(1); + } + + /** + * Get the path to the file in which an exception occured. + * + * @param Exception $e + * @return string + */ + private static function file($e) + { + if (strpos($e->getFile(), 'view.php') !== false and strpos($e->getFile(), "eval()'d code") !== false) + { + return APP_PATH.'views/'.View::$last.EXT; + } + + return $e->getFile(); + } + + /** + * Show the error view. + * + * @param Exception $e + * @param string $severity + * @param string $message + * @param string $file + * @return void + */ + private static function show($e, $severity, $message, $file) + { if (Config::get('error.detail')) { $view = View::make('exception') @@ -74,8 +94,6 @@ class Error { { Response::make(View::make('error/500'), 500)->send(); } - - exit(1); } } \ No newline at end of file From f6ff5370ef0b46d45fc5b6484d988ae4c9952ed4 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Mon, 11 Jul 2011 07:47:54 -0700 Subject: [PATCH 150/206] Tweaking error handlers in the front controller. --- public/index.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/public/index.php b/public/index.php index a3b21fd6..eb184739 100644 --- a/public/index.php +++ b/public/index.php @@ -45,13 +45,15 @@ error_reporting((System\Config::get('error.detail')) ? E_ALL | E_STRICT : 0); set_exception_handler(function($e) { require_once SYS_PATH.'error'.EXT; + System\Error::handle($e); }); set_error_handler(function($number, $error, $file, $line) { require_once SYS_PATH.'error'.EXT; - System\Error::handle(new ErrorException($error, 0, $number, $file, $line)); + + System\Error::handle(new ErrorException($error, $number, 0, $file, $line)); }); register_shutdown_function(function() @@ -59,7 +61,8 @@ register_shutdown_function(function() if ( ! is_null($error = error_get_last())) { require_once SYS_PATH.'error'.EXT; - System\Error::handle(new ErrorException($error['message'], 0, $error['type'], $error['file'], $error['line'])); + + System\Error::handle(new ErrorException($error['message'], $error['type'], 0, $error['file'], $error['line'])); } }); From 54311a41cee2be30955bcc47d18796c95c7a392c Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Mon, 11 Jul 2011 08:38:49 -0700 Subject: [PATCH 151/206] Added file context to error class. --- system/error.php | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/system/error.php b/system/error.php index b7f69917..091d5be0 100644 --- a/system/error.php +++ b/system/error.php @@ -86,7 +86,8 @@ class Error { ->bind('message', $message) ->bind('file', $file) ->bind('line', $e->getLine()) - ->bind('trace', $e->getTraceAsString()); + ->bind('trace', $e->getTraceAsString()) + ->bind('contexts', static::context($file, $e->getLine())); Response::make($view, 500)->send(); } @@ -96,4 +97,36 @@ class Error { } } + /** + * Get the code surrounding a given line in a file. + * + * @param string $path + * @param int $line + * @param int $padding + * @return string + */ + private static function context($path, $line, $padding = 5) + { + if (file_exists($path)) + { + $file = file($path, FILE_IGNORE_NEW_LINES); + + array_unshift($file, ''); + + if (($start = $line - $padding) < 0) + { + $start = 0; + } + + if (($length = ($line - $start) + $padding + 1) < 0) + { + $length = 0; + } + + return array_slice($file, $start, $length, true); + } + + return array(); + } + } \ No newline at end of file From 4fc4d04028b1e4e753296629eea93fb13d2bb58d Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Mon, 11 Jul 2011 08:39:20 -0700 Subject: [PATCH 152/206] Improved error exception view. --- system/views/exception.php | 113 ++++++++++++++++++++++++------------- 1 file changed, 75 insertions(+), 38 deletions(-) diff --git a/system/views/exception.php b/system/views/exception.php index f2606d86..825c13b4 100644 --- a/system/views/exception.php +++ b/system/views/exception.php @@ -2,64 +2,101 @@ - Laravel - Error - - + Laravel - <?php echo $severity; ?> + + + + + +
-

- -
-

Message:

- in on line . -
+

Message:

-
-

Stack Trace:

+

in on line .

-
-
+

Stack Trace:

+ +
+ +

Snapshot:

+ +

+ 0): ?> + + $context): ?> +

+ + + + Snapshot Unavailable. + +

\ No newline at end of file From 4b25d9e4d24d68ccbaf5ba00b346828af941a130 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Mon, 11 Jul 2011 08:40:11 -0700 Subject: [PATCH 153/206] Refactor exception view. --- system/views/exception.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/system/views/exception.php b/system/views/exception.php index 825c13b4..6c4aa8e3 100644 --- a/system/views/exception.php +++ b/system/views/exception.php @@ -45,7 +45,7 @@ margin: 0; padding: 0; } - .strong { + pre.highlight { font-weight: bold; color: #990000; } @@ -90,7 +90,7 @@ 0): ?> $context): ?> -
+
From 3adfe6407a93c8bd78400d45c0371f93d6fb9414 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Mon, 11 Jul 2011 08:40:53 -0700 Subject: [PATCH 154/206] Turned off display_errors runtime directive. --- public/index.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/public/index.php b/public/index.php index eb184739..6d4a6264 100644 --- a/public/index.php +++ b/public/index.php @@ -35,9 +35,11 @@ require SYS_PATH.'arr'.EXT; spl_autoload_register(require SYS_PATH.'loader'.EXT); // -------------------------------------------------------------- -// Set the error reporting level. +// Set the error reporting and display levels. // -------------------------------------------------------------- -error_reporting((System\Config::get('error.detail')) ? E_ALL | E_STRICT : 0); +error_reporting(E_ALL | E_STRICT); + +ini_set('display_errors', 'Off'); // -------------------------------------------------------------- // Register the error handlers. From d60772e534793d22c6a1caed493e944140c6d011 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Mon, 11 Jul 2011 09:08:04 -0700 Subject: [PATCH 155/206] Improved the default 404 page and made it consistent with other system views. --- application/views/error/404.php | 81 +++++++++++++++++++++++---------- 1 file changed, 56 insertions(+), 25 deletions(-) diff --git a/application/views/error/404.php b/application/views/error/404.php index beca7493..9fe78810 100644 --- a/application/views/error/404.php +++ b/application/views/error/404.php @@ -4,53 +4,84 @@ 404 - Not Found - + + + -
+ -
- The resource you requested was not found. -

- Would you like go to our home page instead? -
+
+ + +

+ +

We couldn't find the resource you requested. Would you like go to our home page instead?

\ No newline at end of file From 9249ab7d40e8098f98a5a64195b733000e330225 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Mon, 11 Jul 2011 09:08:46 -0700 Subject: [PATCH 156/206] Improved the generic error view and made it consistent with other system views. --- application/views/error/500.php | 79 +++++++++++++++++++++++---------- 1 file changed, 55 insertions(+), 24 deletions(-) diff --git a/application/views/error/500.php b/application/views/error/500.php index 213a9ea3..5167e59c 100644 --- a/application/views/error/500.php +++ b/application/views/error/500.php @@ -4,53 +4,84 @@ 500 - Internal Server Error - + + + -
+ -
- An error occured while we were processing your request. -

- Would you like go to our home page instead? -
+
+ + +

+ +

Something failed while we were handling your request. Would you like go to our home page instead?

\ No newline at end of file From 44d14df331417551ecc2d2eacaeaf2b098f5e5ae Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Mon, 11 Jul 2011 09:17:30 -0700 Subject: [PATCH 157/206] Added note to Route\Finder. --- system/route/finder.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/system/route/finder.php b/system/route/finder.php index fb997e5c..f04c1d0c 100644 --- a/system/route/finder.php +++ b/system/route/finder.php @@ -24,6 +24,9 @@ class Finder { */ public static function find($name) { + // This class maintains its own list of routes because the router only loads routes that + // are applicable to the current request URI. But, this class obviously needs access + // to all of the routes, not just the ones applicable to the request URI. if (is_null(static::$routes)) { static::$routes = (is_dir(APP_PATH.'routes')) ? static::load() : require APP_PATH.'routes'.EXT; From 3f20c10d022213478ccd771fb3d0684614ce511a Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Mon, 11 Jul 2011 09:18:59 -0700 Subject: [PATCH 158/206] Order the exception CSS. --- system/views/exception.php | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/system/views/exception.php b/system/views/exception.php index 6c4aa8e3..5eba8540 100644 --- a/system/views/exception.php +++ b/system/views/exception.php @@ -50,6 +50,13 @@ color: #990000; } + #header { + margin: 0 auto; + margin-bottom: 15px; + margin-top: 20px; + width: 80%; + } + #wrapper { background-color: #fff; border-radius: 10px; @@ -61,13 +68,6 @@ #wrapper h2:first-of-type { margin-top: 0; } - - #header { - margin: 0 auto; - margin-bottom: 15px; - margin-top: 20px; - width: 80%; - } From f54ef2b4c926733638c10834d9a4aac932e9c3fd Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Mon, 11 Jul 2011 09:56:47 -0700 Subject: [PATCH 159/206] Added "logger" option to error configuration. --- application/config/error.php | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/application/config/error.php b/application/config/error.php index dd021e46..360c963e 100644 --- a/application/config/error.php +++ b/application/config/error.php @@ -14,7 +14,7 @@ return array( | */ - 'detail' => true, + 'detail' => false, /* |-------------------------------------------------------------------------- @@ -24,11 +24,32 @@ return array( | Would you like errors to be logged? Error logging can be extremely | helpful when debugging a production application. | - | Note: When error logging is enabled, errors will be logged even when - | error detail is disabled. + */ + + 'log' => true, + + /* + |-------------------------------------------------------------------------- + | Error Logger + |-------------------------------------------------------------------------- + | + | Because of the sundry ways of managing error logging, you get complete + | flexibility to manage error logging as you see fit. + | + | This function will be called when an error occurs in your application. + | You can log the error however you like. + | + | The error "severity" passed to the method is a human-readable severity + | level such as "Parsing Error", "Fatal Error", etc. + | + | A simple logging system has been setup for you. By default, all errors + | will be logged to the application/log.txt file. | */ - 'log' => false, + 'logger' => function($severity, $message) + { + System\File::append(APP_PATH.'storage/log.txt', date('Y-m-d H:i:s').' '.$severity.' - '.$message.PHP_EOL); + }, ); \ No newline at end of file From 8053d8e954032e40d939a5c883fd441a703e2454 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Mon, 11 Jul 2011 09:57:20 -0700 Subject: [PATCH 160/206] Call the configuration "logger" when writing logs in the error class. --- system/error.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/error.php b/system/error.php index 091d5be0..79069c99 100644 --- a/system/error.php +++ b/system/error.php @@ -44,7 +44,7 @@ class Error { if (Config::get('error.log')) { - Log::error($message.' in '.$e->getFile().' on line '.$e->getLine()); + call_user_func(Config::get('error.logger'), $severity, $message.' in '.$e->getFile().' on line '.$e->getLine()); } static::show($e, $severity, $message, $file); From a6d1449b40b31318cd2a47b275b4e3797c6e618b Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Mon, 11 Jul 2011 12:33:22 -0500 Subject: [PATCH 161/206] deprecated log class. removed from aliases. added storage/log.txt. --- application/config/aliases.php | 2 - .../storage/{logs/.gitignore => log.txt} | 0 system/log.php | 76 ------------------- 3 files changed, 78 deletions(-) rename application/storage/{logs/.gitignore => log.txt} (100%) delete mode 100644 system/log.php diff --git a/application/config/aliases.php b/application/config/aliases.php index d7d3f4b6..7bf753df 100644 --- a/application/config/aliases.php +++ b/application/config/aliases.php @@ -30,14 +30,12 @@ return array( 'Inflector' => 'System\\Inflector', 'Input' => 'System\\Input', 'Lang' => 'System\\Lang', - 'Log' => 'System\\Log', 'URL' => 'System\\URL', 'Redirect' => 'System\\Redirect', 'Request' => 'System\\Request', 'Response' => 'System\\Response', 'Session' => 'System\\Session', 'Str' => 'System\\Str', - 'Text' => 'System\\Text', 'Validator' => 'System\\Validator', 'View' => 'System\\View', diff --git a/application/storage/logs/.gitignore b/application/storage/log.txt similarity index 100% rename from application/storage/logs/.gitignore rename to application/storage/log.txt diff --git a/system/log.php b/system/log.php deleted file mode 100644 index dd736e4f..00000000 --- a/system/log.php +++ /dev/null @@ -1,76 +0,0 @@ - Date: Mon, 11 Jul 2011 12:14:06 -0700 Subject: [PATCH 162/206] Fix default error config values. --- application/config/error.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/application/config/error.php b/application/config/error.php index 360c963e..d0029390 100644 --- a/application/config/error.php +++ b/application/config/error.php @@ -14,7 +14,7 @@ return array( | */ - 'detail' => false, + 'detail' => true, /* |-------------------------------------------------------------------------- @@ -26,7 +26,7 @@ return array( | */ - 'log' => true, + 'log' => false, /* |-------------------------------------------------------------------------- From 427fe625682a9db7e16fdac1a377eabc4f0f9301 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Mon, 11 Jul 2011 12:15:06 -0700 Subject: [PATCH 163/206] Refactor hash class to use PHPass. --- system/hash.php | 66 ++++++++++++++++++++++--------------------------- 1 file changed, 30 insertions(+), 36 deletions(-) diff --git a/system/hash.php b/system/hash.php index 501b34eb..de53321f 100644 --- a/system/hash.php +++ b/system/hash.php @@ -2,49 +2,43 @@ class Hash { - /** - * The salty, hashed value. - * - * @var string - */ - public $value; - /** - * The salt used during hashing. + * Hash a string using PHPass. * - * @var string - */ - public $salt; - - /** - * Create a new salted hash instance. - * - * If no salt is provided, a random, 16 character salt will be generated - * to created the salted, hashed value. If a salt is provided, that salt - * will be used when hashing the value. - * - * @param string $value - * @param string $salt - * @return void - */ - public function __construct($value, $salt = null) - { - $this->salt = (is_null($salt)) ? Str::random(16) : $salt; - - $this->value = sha1($value.$this->salt); - } - - /** - * Factory for creating hash instances. + * PHPass provides reliable bcrypt hashing, and is used by many popular PHP + * applications such as Wordpress and Joomla. * * @access public * @param string $value - * @param string $salt - * @return Hash + * @return string */ - public static function make($value, $salt = null) + public static function make($value) { - return new self($value, $salt); + return static::hasher()->HashPassword($value); + } + + /** + * Determine if an unhashed value matches a given hash. + * + * @param string $value + * @param string $hash + * @return bool + */ + public static function check($value, $hash) + { + return static::hasher()->CheckPassword($value, $hash); + } + + /** + * Create a new PHPass instance. + * + * @return PasswordHash + */ + private static function hasher() + { + require_once SYS_PATH.'vendor/phpass'.EXT; + + return new \PasswordHash(10, false); } } \ No newline at end of file From 757e1280a88b84c2a5cf86fdcf9b2b4f202937ba Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Mon, 11 Jul 2011 12:25:12 -0700 Subject: [PATCH 164/206] Refactor Auth class to use new bcrypt hashing. --- system/auth.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/system/auth.php b/system/auth.php index 408930fd..c5ced635 100644 --- a/system/auth.php +++ b/system/auth.php @@ -59,11 +59,11 @@ class Auth { */ public static function login($username, $password) { - $model = forward_static_call(array(static::model(), 'where'), Config::get('auth.username'), '=', $username)->first(); + $user = forward_static_call(array(static::model(), 'where'), Config::get('auth.username'), '=', $username)->first(); if ( ! is_null($user)) { - if ($user->password === Hash::make($password, $user->salt)->value) + if (Hash::check($password, $user->password)) { static::$user = $user; From 9ec8f4a5b62407f2a6d0a88370a83a6a3d85b934 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Mon, 11 Jul 2011 12:41:50 -0700 Subject: [PATCH 165/206] Refactor Auth config to use closures. --- application/config/auth.php | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/application/config/auth.php b/application/config/auth.php index 1f994252..008b0eb7 100644 --- a/application/config/auth.php +++ b/application/config/auth.php @@ -4,29 +4,37 @@ return array( /* |-------------------------------------------------------------------------- - | Authentication Model + | Retrieve Users By ID |-------------------------------------------------------------------------- | - | This model will be used by the Auth class when retrieving the users of - | your application. Feel free to change it to the name of your user model. + | This method is called by the Auth::user() method when attempting to load + | a user by their user ID. | - | Note: The authentication model must be an Eloquent model. + | You are free to change this method for your application however you wish. | */ - 'model' => 'User', + 'by_id' => function($id) + { + return User::find($id); + }, /* |-------------------------------------------------------------------------- - | Authentication Username + | Retrieve Users By Username |-------------------------------------------------------------------------- | - | The authentication username is the column on your users table that - | is considered the username of the user. Typically, this is either "email" - | or "username". However, you are free to make it whatever you wish. + | This method is called by the Auth::check() method when attempting to load + | a user by their username. + | + | You are free to change this method for your application however you wish, + | as long as you return an object that has "id" and "password" properties. | */ - 'username' => 'email', + 'by_username' => function($username) + { + return User::where('email', '=', $username)->first(); + }, ); \ No newline at end of file From 0d2cfd24177c17f6fc91db24388cfee5bc375a15 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Mon, 11 Jul 2011 12:42:19 -0700 Subject: [PATCH 166/206] Refactor Auth class to use Auth config closures. --- system/auth.php | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/system/auth.php b/system/auth.php index c5ced635..92be397a 100644 --- a/system/auth.php +++ b/system/auth.php @@ -42,7 +42,7 @@ class Auth { if (is_null(static::$user) and Session::has(static::$key)) { - static::$user = forward_static_call(array(static::model(), 'find'), Session::get(static::$key)); + static::$user = call_user_func(Config::get('auth.by_id'), Session::get(static::$key)); } return static::$user; @@ -59,9 +59,7 @@ class Auth { */ public static function login($username, $password) { - $user = forward_static_call(array(static::model(), 'where'), Config::get('auth.username'), '=', $username)->first(); - - if ( ! is_null($user)) + if ( ! is_null($user = call_user_func(Config::get('auth.by_username'), $username))) { if (Hash::check($password, $user->password)) { @@ -88,14 +86,4 @@ class Auth { static::$user = null; } - /** - * Get the authentication model. - * - * @return string - */ - private static function model() - { - return '\\'.Config::get('auth.model'); - } - } \ No newline at end of file From a9c14b3db5861fd293e6fd46a528b9d0b35b7849 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Mon, 11 Jul 2011 12:45:04 -0700 Subject: [PATCH 167/206] Tweaking Auth config comments. --- application/config/auth.php | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/application/config/auth.php b/application/config/auth.php index 008b0eb7..979231dd 100644 --- a/application/config/auth.php +++ b/application/config/auth.php @@ -7,8 +7,8 @@ return array( | Retrieve Users By ID |-------------------------------------------------------------------------- | - | This method is called by the Auth::user() method when attempting to load - | a user by their user ID. + | This method is called by the Auth::user() method when attempting to + | retrieve a user by their user ID. | | You are free to change this method for your application however you wish. | @@ -24,11 +24,13 @@ return array( | Retrieve Users By Username |-------------------------------------------------------------------------- | - | This method is called by the Auth::check() method when attempting to load - | a user by their username. + | This method is called by the Auth::check() method when attempting to + | retrieve a user by their username. | - | You are free to change this method for your application however you wish, - | as long as you return an object that has "id" and "password" properties. + | You are free to change this method for your application however you wish. + | + | Note: This method must return an object that has an "id" and a "password" + | property. The type of object returned doesn't matter. | */ From 218e64368c7352bb7f157b147d27082a11301d13 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Mon, 11 Jul 2011 13:02:07 -0700 Subject: [PATCH 168/206] Tweak routes comment. --- application/routes.php | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/application/routes.php b/application/routes.php index 229a673a..4accc4c6 100644 --- a/application/routes.php +++ b/application/routes.php @@ -7,11 +7,10 @@ return array( | Application Routes |-------------------------------------------------------------------------- | - | Here is the "definition", or the public API, of your application. + | Here is the public API of your application. To add functionality to your + | application, you add to the array located in this file. | - | To add functionality to your application, you add to the array located - | in this file. It's a breeze. Just tell Laravel the request method and - | URI a function should respond to. + | It's a breeze. Just tell Laravel the request URIs it should respond to. | */ From 9224e62cbef6bdc603914dfe06d7994df8519383 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Mon, 11 Jul 2011 13:03:09 -0700 Subject: [PATCH 169/206] More tweaking to routes.php comment. --- application/routes.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/application/routes.php b/application/routes.php index 4accc4c6..2f7242be 100644 --- a/application/routes.php +++ b/application/routes.php @@ -8,9 +8,9 @@ return array( |-------------------------------------------------------------------------- | | Here is the public API of your application. To add functionality to your - | application, you add to the array located in this file. + | application, you just add to the array located in this file. | - | It's a breeze. Just tell Laravel the request URIs it should respond to. + | It's a breeze. Simply tell Laravel the request URIs it should respond to. | */ From c95e7fdbbe0ab69b1dd74b3fbd8b43ba58c0fba1 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Mon, 11 Jul 2011 13:38:07 -0700 Subject: [PATCH 170/206] Refactor router to use routes.php as the default routes for all requests, even when using a routes directory. --- system/router.php | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/system/router.php b/system/router.php index 97dc0830..86715ece 100644 --- a/system/router.php +++ b/system/router.php @@ -58,7 +58,9 @@ class Router { */ public static function load($uri) { - return (is_dir(APP_PATH.'routes')) ? static::load_from_directory($uri) : require APP_PATH.'routes'.EXT; + $base = require APP_PATH.'routes'.EXT; + + return (is_dir(APP_PATH.'routes') and $uri !== '') ? array_merge(static::load_from_directory($uri), $base) : $base; } /** @@ -69,18 +71,9 @@ class Router { */ private static function load_from_directory($uri) { - // If it exists, The "home" routes file is loaded for every request. This allows - // for "catch-all" routes such as http://example.com/username... - $home = (file_exists($path = APP_PATH.'routes/home'.EXT)) ? require $path : array(); - - if ($uri == '') - { - return $home; - } - $segments = explode('/', $uri); - return (file_exists($path = APP_PATH.'routes/'.$segments[0].EXT)) ? array_merge(require $path, $home) : $home; + return (file_exists($path = APP_PATH.'routes/'.$segments[0].EXT)) ? require $path : array(); } /** From bbc45f92b7ccf39162a6d8559e076b66c102b074 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Mon, 11 Jul 2011 21:58:51 -0500 Subject: [PATCH 171/206] added PHPass to system/vendor. --- system/vendor/phpass.php | 253 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 253 insertions(+) create mode 100644 system/vendor/phpass.php diff --git a/system/vendor/phpass.php b/system/vendor/phpass.php new file mode 100644 index 00000000..12958c7f --- /dev/null +++ b/system/vendor/phpass.php @@ -0,0 +1,253 @@ + in 2004-2006 and placed in +# the public domain. Revised in subsequent years, still public domain. +# +# There's absolutely no warranty. +# +# The homepage URL for this framework is: +# +# http://www.openwall.com/phpass/ +# +# Please be sure to update the Version line if you edit this file in any way. +# It is suggested that you leave the main version number intact, but indicate +# your project name (after the slash) and add your own revision information. +# +# Please do not change the "private" password hashing method implemented in +# here, thereby making your hashes incompatible. However, if you must, please +# change the hash type identifier (the "$P$") to something different. +# +# Obviously, since this code is in the public domain, the above are not +# requirements (there can be none), but merely suggestions. +# +class PasswordHash { + var $itoa64; + var $iteration_count_log2; + var $portable_hashes; + var $random_state; + + function PasswordHash($iteration_count_log2, $portable_hashes) + { + $this->itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'; + + if ($iteration_count_log2 < 4 || $iteration_count_log2 > 31) + $iteration_count_log2 = 8; + $this->iteration_count_log2 = $iteration_count_log2; + + $this->portable_hashes = $portable_hashes; + + $this->random_state = microtime(); + if (function_exists('getmypid')) + $this->random_state .= getmypid(); + } + + function get_random_bytes($count) + { + $output = ''; + if (is_readable('/dev/urandom') && + ($fh = @fopen('/dev/urandom', 'rb'))) { + $output = fread($fh, $count); + fclose($fh); + } + + if (strlen($output) < $count) { + $output = ''; + for ($i = 0; $i < $count; $i += 16) { + $this->random_state = + md5(microtime() . $this->random_state); + $output .= + pack('H*', md5($this->random_state)); + } + $output = substr($output, 0, $count); + } + + return $output; + } + + function encode64($input, $count) + { + $output = ''; + $i = 0; + do { + $value = ord($input[$i++]); + $output .= $this->itoa64[$value & 0x3f]; + if ($i < $count) + $value |= ord($input[$i]) << 8; + $output .= $this->itoa64[($value >> 6) & 0x3f]; + if ($i++ >= $count) + break; + if ($i < $count) + $value |= ord($input[$i]) << 16; + $output .= $this->itoa64[($value >> 12) & 0x3f]; + if ($i++ >= $count) + break; + $output .= $this->itoa64[($value >> 18) & 0x3f]; + } while ($i < $count); + + return $output; + } + + function gensalt_private($input) + { + $output = '$P$'; + $output .= $this->itoa64[min($this->iteration_count_log2 + + ((PHP_VERSION >= '5') ? 5 : 3), 30)]; + $output .= $this->encode64($input, 6); + + return $output; + } + + function crypt_private($password, $setting) + { + $output = '*0'; + if (substr($setting, 0, 2) == $output) + $output = '*1'; + + $id = substr($setting, 0, 3); + # We use "$P$", phpBB3 uses "$H$" for the same thing + if ($id != '$P$' && $id != '$H$') + return $output; + + $count_log2 = strpos($this->itoa64, $setting[3]); + if ($count_log2 < 7 || $count_log2 > 30) + return $output; + + $count = 1 << $count_log2; + + $salt = substr($setting, 4, 8); + if (strlen($salt) != 8) + return $output; + + # We're kind of forced to use MD5 here since it's the only + # cryptographic primitive available in all versions of PHP + # currently in use. To implement our own low-level crypto + # in PHP would result in much worse performance and + # consequently in lower iteration counts and hashes that are + # quicker to crack (by non-PHP code). + if (PHP_VERSION >= '5') { + $hash = md5($salt . $password, TRUE); + do { + $hash = md5($hash . $password, TRUE); + } while (--$count); + } else { + $hash = pack('H*', md5($salt . $password)); + do { + $hash = pack('H*', md5($hash . $password)); + } while (--$count); + } + + $output = substr($setting, 0, 12); + $output .= $this->encode64($hash, 16); + + return $output; + } + + function gensalt_extended($input) + { + $count_log2 = min($this->iteration_count_log2 + 8, 24); + # This should be odd to not reveal weak DES keys, and the + # maximum valid value is (2**24 - 1) which is odd anyway. + $count = (1 << $count_log2) - 1; + + $output = '_'; + $output .= $this->itoa64[$count & 0x3f]; + $output .= $this->itoa64[($count >> 6) & 0x3f]; + $output .= $this->itoa64[($count >> 12) & 0x3f]; + $output .= $this->itoa64[($count >> 18) & 0x3f]; + + $output .= $this->encode64($input, 3); + + return $output; + } + + function gensalt_blowfish($input) + { + # This one needs to use a different order of characters and a + # different encoding scheme from the one in encode64() above. + # We care because the last character in our encoded string will + # only represent 2 bits. While two known implementations of + # bcrypt will happily accept and correct a salt string which + # has the 4 unused bits set to non-zero, we do not want to take + # chances and we also do not want to waste an additional byte + # of entropy. + $itoa64 = './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + + $output = '$2a$'; + $output .= chr(ord('0') + $this->iteration_count_log2 / 10); + $output .= chr(ord('0') + $this->iteration_count_log2 % 10); + $output .= '$'; + + $i = 0; + do { + $c1 = ord($input[$i++]); + $output .= $itoa64[$c1 >> 2]; + $c1 = ($c1 & 0x03) << 4; + if ($i >= 16) { + $output .= $itoa64[$c1]; + break; + } + + $c2 = ord($input[$i++]); + $c1 |= $c2 >> 4; + $output .= $itoa64[$c1]; + $c1 = ($c2 & 0x0f) << 2; + + $c2 = ord($input[$i++]); + $c1 |= $c2 >> 6; + $output .= $itoa64[$c1]; + $output .= $itoa64[$c2 & 0x3f]; + } while (1); + + return $output; + } + + function HashPassword($password) + { + $random = ''; + + if (CRYPT_BLOWFISH == 1 && !$this->portable_hashes) { + $random = $this->get_random_bytes(16); + $hash = + crypt($password, $this->gensalt_blowfish($random)); + if (strlen($hash) == 60) + return $hash; + } + + if (CRYPT_EXT_DES == 1 && !$this->portable_hashes) { + if (strlen($random) < 3) + $random = $this->get_random_bytes(3); + $hash = + crypt($password, $this->gensalt_extended($random)); + if (strlen($hash) == 20) + return $hash; + } + + if (strlen($random) < 6) + $random = $this->get_random_bytes(6); + $hash = + $this->crypt_private($password, + $this->gensalt_private($random)); + if (strlen($hash) == 34) + return $hash; + + # Returning '*' on error is safe here, but would _not_ be safe + # in a crypt(3)-like function used _both_ for generating new + # hashes and for validating passwords against existing hashes. + return '*'; + } + + function CheckPassword($password, $stored_hash) + { + $hash = $this->crypt_private($password, $stored_hash); + if ($hash[0] == '*') + $hash = crypt($password, $stored_hash); + + return $hash == $stored_hash; + } +} + +?> From c3b8524e1b97bbe14e3b5130d3c9b5d75eea75d7 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 12 Jul 2011 20:04:14 -0500 Subject: [PATCH 172/206] rewrote validation library. --- application/lang/en/validation.php | 65 +-- system/db/eloquent.php | 11 + system/input.php | 12 + system/lang.php | 18 +- system/str.php | 8 +- .../{error_collector.php => errors.php} | 14 +- system/validation/message.php | 155 ------ system/validation/nullable_rule.php | 94 ---- system/validation/rangable_rule.php | 145 ------ system/validation/rule.php | 72 --- system/validation/rules/acceptance_of.php | 39 -- system/validation/rules/confirmation_of.php | 25 - system/validation/rules/exclusion_of.php | 43 -- system/validation/rules/format_of.php | 43 -- system/validation/rules/inclusion_of.php | 43 -- system/validation/rules/length_of.php | 49 -- system/validation/rules/numericality_of.php | 116 ----- system/validation/rules/presence_of.php | 29 -- system/validation/rules/uniqueness_of.php | 62 --- system/validation/rules/upload_of.php | 160 ------ system/validation/rules/with_callback.php | 48 -- system/validator.php | 493 ++++++++++++++++-- 22 files changed, 508 insertions(+), 1236 deletions(-) rename system/validation/{error_collector.php => errors.php} (78%) delete mode 100644 system/validation/message.php delete mode 100644 system/validation/nullable_rule.php delete mode 100644 system/validation/rangable_rule.php delete mode 100644 system/validation/rule.php delete mode 100644 system/validation/rules/acceptance_of.php delete mode 100644 system/validation/rules/confirmation_of.php delete mode 100644 system/validation/rules/exclusion_of.php delete mode 100644 system/validation/rules/format_of.php delete mode 100644 system/validation/rules/inclusion_of.php delete mode 100644 system/validation/rules/length_of.php delete mode 100644 system/validation/rules/numericality_of.php delete mode 100644 system/validation/rules/presence_of.php delete mode 100644 system/validation/rules/uniqueness_of.php delete mode 100644 system/validation/rules/upload_of.php delete mode 100644 system/validation/rules/with_callback.php diff --git a/application/lang/en/validation.php b/application/lang/en/validation.php index fa09a0be..46a72f4d 100644 --- a/application/lang/en/validation.php +++ b/application/lang/en/validation.php @@ -2,50 +2,25 @@ return array( - /* - |-------------------------------------------------------------------------- - | General Validation Messages - |-------------------------------------------------------------------------- - */ - - "acceptance_of" => "The :attribute must be accepted.", - "confirmation_of" => "The :attribute confirmation does not match.", - "exclusion_of" => "The :attribute value is invalid.", - "format_of" => "The :attribute format is invalid.", - "inclusion_of" => "The :attribute value is invalid.", - "presence_of" => "The :attribute can't be empty.", - "uniqueness_of" => "The :attribute has already been taken.", - "with_callback" => "The :attribute is invalid.", - - /* - |-------------------------------------------------------------------------- - | Numericality_Of Validation Messages - |-------------------------------------------------------------------------- - */ - - "number_not_valid" => "The :attribute must be a number.", - "number_not_integer" => "The :attribute must be an integer.", - "number_wrong_size" => "The :attribute must be :size.", - "number_too_big" => "The :attribute must be no more than :max.", - "number_too_small" => "The :attribute must be at least :min.", - - /* - |-------------------------------------------------------------------------- - | Length_Of Validation Messages - |-------------------------------------------------------------------------- - */ - - "string_wrong_size" => "The :attribute must be :size characters.", - "string_too_big" => "The :attribute must be no more than :max characters.", - "string_too_small" => "The :attribute must be at least :min characters.", - - /* - |-------------------------------------------------------------------------- - | Upload_Of Validation Messages - |-------------------------------------------------------------------------- - */ - - "file_wrong_type" => "The :attribute must be a file of type: :types.", - "file_too_big" => "The :attribute exceeds size limit of :maxkb.", + "accepted" => "The :attribute must be accepted.", + "active_url" => "The :attribute does not exist.", + "alpha" => "The :attribute may only contain letters.", + "alpha_dash" => "The :attribute may only contain letters, numbers, dashes, and underscores.", + "alpha_num" => "The :attribute may only contain letters and numbers.", + "between" => "The :attribute must be between :min - :max.", + "confirmed" => "The :attribute confirmation does not match.", + "email" => "The :attribute format is invalid.", + "image" => "The :attribute must be an image.", + "in" => "The selected :attribute is invalid.", + "integer" => "The :attribute must be an integer.", + "max" => "The :attribute must be less than :max.", + "mimes" => "The :attribute must be a file of type: :values.", + "min" => "The :attribute must be at least :min.", + "not_in" => "The selected :attribute is invalid.", + "numeric" => "The :attribute must be a number.", + "required" => "The :attribute field is required.", + "size" => "The :attribute must be :size.", + "unique" => "The :attribute has already been taken.", + "url" => "The :attribute format is invalid.", ); \ No newline at end of file diff --git a/system/db/eloquent.php b/system/db/eloquent.php index f63d265c..625d7559 100644 --- a/system/db/eloquent.php +++ b/system/db/eloquent.php @@ -83,6 +83,17 @@ abstract class Eloquent { * @return void */ public function __construct($attributes = array()) + { + $this->fill($attributes); + } + + /** + * Set the attributes of the model using an array. + * + * @param array $attributes + * @return void + */ + public function fill($attributes) { foreach ($attributes as $key => $value) { diff --git a/system/input.php b/system/input.php index fac8aa54..cabe1f96 100644 --- a/system/input.php +++ b/system/input.php @@ -9,6 +9,18 @@ class Input { */ public static $input; + /** + * Get all of the input data for the request. + * + * This method returns a merged array containing Input::get and Input::file. + * + * @return array + */ + public static function all() + { + return array_merge(static::get(), static::file()); + } + /** * Determine if the input data contains an item. * diff --git a/system/lang.php b/system/lang.php index 40dd37e9..1611bd56 100644 --- a/system/lang.php +++ b/system/lang.php @@ -54,26 +54,28 @@ class Lang { /** * Get the language line. * + * @param string $language * @param mixed $default * @return string */ - public function get($default = null) + public function get($language = null, $default = null) { - $language = Config::get('application.language'); + if (is_null($language)) + { + $language = Config::get('application.language'); + } list($file, $line) = $this->parse($this->key); $this->load($file, $language); - if ( ! array_key_exists($language.$file, static::$lines)) + if ( ! isset(static::$lines[$language.$file][$line])) { - $line = is_callable($default) ? call_user_func($default) : $default; - } - else - { - $line = Arr::get(static::$lines[$language.$file], $line, $default); + return is_callable($default) ? call_user_func($default) : $default; } + $line = static::$lines[$language.$file][$line]; + foreach ($this->replacements as $key => $value) { $line = str_replace(':'.$key, $value, $line); diff --git a/system/str.php b/system/str.php index c9782279..1c1d05eb 100644 --- a/system/str.php +++ b/system/str.php @@ -60,13 +60,13 @@ class Str { /** * Generate a random alpha or alpha-numeric string. * - * Supported types: 'alnum' and 'alpha'. + * Supported types: 'alpha_num' and 'alpha'. * * @param int $length * @param string $type * @return string */ - public static function random($length = 16, $type = 'alnum') + public static function random($length = 16, $type = 'alpha_num') { $value = ''; @@ -86,11 +86,11 @@ class Str { * @param string $type * @return string */ - private static function pool($type = 'alnum') + private static function pool($type = 'alpha_num') { switch ($type) { - case 'alnum': + case 'alpha_num': return '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; default: diff --git a/system/validation/error_collector.php b/system/validation/errors.php similarity index 78% rename from system/validation/error_collector.php rename to system/validation/errors.php index b0bbee9b..722eaa84 100644 --- a/system/validation/error_collector.php +++ b/system/validation/errors.php @@ -1,6 +1,6 @@ messages) or ! is_array($this->messages[$attribute]) or ! in_array($message, $this->messages[$attribute])) { $this->messages[$attribute][] = $message; @@ -94,10 +88,6 @@ class Error_Collector { { $all = array(); - // --------------------------------------------------------- - // Add each error message to the array of messages. Each - // messages will have the specified format applied to it. - // --------------------------------------------------------- foreach ($this->messages as $messages) { $all = array_merge($all, $this->format($messages, $format)); diff --git a/system/validation/message.php b/system/validation/message.php deleted file mode 100644 index 34fc95a1..00000000 --- a/system/validation/message.php +++ /dev/null @@ -1,155 +0,0 @@ -error)) - { - $class = explode('\\', get_class($rule)); - - $rule->error = strtolower(end($class)); - } - - return (is_null($rule->message)) ? Lang::line('validation.'.$rule->error)->get() : $rule->message; - } - - /** - * Get the error message for a Rangable rule. - * - * @param Rule $rule - * @return string - */ - private static function get_rangable_message($rule) - { - // --------------------------------------------------------- - // Rangable rules sometimes set a "presence_of" error. - // - // This occurs when an attribute is null and the option to - // allow null values has not been set. - // --------------------------------------------------------- - if ($rule->error == 'presence_of') - { - return static::get_message($rule); - } - - // --------------------------------------------------------- - // Slice "number_" or "string_" off of the error type. - // --------------------------------------------------------- - $error_type = substr($rule->error, 7); - - return (is_null($rule->$error_type)) ? Lang::line('validation.'.$rule->error)->get() : $rule->$error_type; - } - - /** - * Get the error message for an Upload_Of rule. - * - * @param Rule $rule - * @return string - */ - private static function get_upload_of_message($rule) - { - // --------------------------------------------------------- - // Upload_Of rules sometimes set a "presence_of" error. - // - // This occurs when the uploaded file didn't exist and the - // "not_required" method was not called. - // --------------------------------------------------------- - if ($rule->error == 'presence_of') - { - return static::get_message($rule); - } - - // --------------------------------------------------------- - // Slice "file_" off of the error type. - // --------------------------------------------------------- - $error_type = substr($rule->error, 5); - - return (is_null($rule->$error_type)) ? Lang::line('validation.'.$rule->error)->get() : $rule->$error_type; - } - - /** - * Prepare an error message for display. All place-holders will be replaced - * with their actual values. - * - * @param Rule $rule - * @param string $attribute - * @param string $message - * @return string - */ - private static function prepare($rule, $attribute, $message) - { - // --------------------------------------------------------- - // The rangable rule messages have three place-holders that - // must be replaced. - // - // :max = The maximum size of the attribute. - // :min = The minimum size of the attribute. - // :size = The exact size the attribute must be. - // --------------------------------------------------------- - if ($rule instanceof Rangable_Rule) - { - $message = str_replace(':max', $rule->maximum, $message); - $message = str_replace(':min', $rule->minimum, $message); - $message = str_replace(':size', $rule->size, $message); - } - // --------------------------------------------------------- - // The Upload_Of rule message have two place-holders taht - // must be replaced. - // - // :max = The maximum file size of the upload (kilobytes). - // :types = The allowed file types for the upload. - // --------------------------------------------------------- - elseif ($rule instanceof Rules\Upload_Of) - { - $message = str_replace(':max', $rule->maximum, $message); - - if (is_array($rule->types)) - { - $message = str_replace(':types', implode(', ', $rule->types), $message); - } - } - - return str_replace(':attribute', Lang::line('attributes.'.$attribute)->get(str_replace('_', ' ', $attribute)), $message); - } - -} \ No newline at end of file diff --git a/system/validation/nullable_rule.php b/system/validation/nullable_rule.php deleted file mode 100644 index 40f43f6e..00000000 --- a/system/validation/nullable_rule.php +++ /dev/null @@ -1,94 +0,0 @@ -allow_null) - { - $this->error = 'presence_of'; - } - - return is_null($this->error); - } - - // ------------------------------------------------------------- - // Make sure the attribute is not an empty string. An error - // will be raised if the attribute is empty and empty strings - // are not allowed, halting the child's validation. - // ------------------------------------------------------------- - elseif (Str::length((string) $attributes[$attribute]) == 0 and ! $this->allow_empty) - { - $this->error = 'presence_of'; - - return false; - } - } - - /** - * Allow a empty and null to be considered valid. - * - * @return Nullable_Rule - */ - public function not_required() - { - return $this->allow_empty()->allow_null(); - } - - /** - * Allow empty to be considered valid. - * - * @return Nullable_Rule - */ - public function allow_empty() - { - $this->allow_empty = true; - return $this; - } - - /** - * Allow null to be considered valid. - * - * @return Nullable_Rule - */ - public function allow_null() - { - $this->allow_null = true; - return $this; - } - -} \ No newline at end of file diff --git a/system/validation/rangable_rule.php b/system/validation/rangable_rule.php deleted file mode 100644 index 1845ca29..00000000 --- a/system/validation/rangable_rule.php +++ /dev/null @@ -1,145 +0,0 @@ -size = $size; - return $this; - } - - /** - * Set the minimum and maximum size of the attribute. - * - * @param int $minimum - * @param int $maximum - * @return Rangable_Rule - */ - public function between($minimum, $maximum) - { - $this->minimum = $minimum; - $this->maximum = $maximum; - - return $this; - } - - /** - * Set the minimum size the attribute. - * - * @param int $minimum - * @return Rangable_Rule - */ - public function minimum($minimum) - { - $this->minimum = $minimum; - return $this; - } - - /** - * Set the maximum size the attribute. - * - * @param int $maximum - * @return Rangable_Rule - */ - public function maximum($maximum) - { - $this->maximum = $maximum; - return $this; - } - - /** - * Set the validation error message. - * - * @param string $message - * @return Rangable_Rule - */ - public function message($message) - { - return $this->wrong_size($message)->too_big($message)->too_small($message); - } - - /** - * Set the "wrong size" error message. - * - * @param string $message - * @return Rangable_Rule - */ - public function wrong_size($message) - { - $this->wrong_size = $message; - return $this; - } - - /** - * Set the "too big" error message. - * - * @param string $message - * @return Rangable_Rule - */ - public function too_big($message) - { - $this->too_big = $message; - return $this; - } - - /** - * Set the "too small" error message. - * - * @param string $message - * @return Rangable_Rule - */ - public function too_small($message) - { - $this->too_small = $message; - return $this; - } - -} \ No newline at end of file diff --git a/system/validation/rule.php b/system/validation/rule.php deleted file mode 100644 index fa0c6692..00000000 --- a/system/validation/rule.php +++ /dev/null @@ -1,72 +0,0 @@ -attributes = $attributes; - } - - /** - * Run the validation rule. - * - * @param array $attributes - * @param Error_Collector $errors - * @return void - */ - public function validate($attributes, $errors) - { - foreach ($this->attributes as $attribute) - { - $this->error = null; - - if ( ! $this->check($attribute, $attributes)) - { - $errors->add($attribute, Message::get($this, $attribute)); - } - } - } - - /** - * Set the validation error message. - * - * @param string $message - * @return Rule - */ - public function message($message) - { - $this->message = $message; - return $this; - } - -} \ No newline at end of file diff --git a/system/validation/rules/acceptance_of.php b/system/validation/rules/acceptance_of.php deleted file mode 100644 index 02b505a9..00000000 --- a/system/validation/rules/acceptance_of.php +++ /dev/null @@ -1,39 +0,0 @@ -accepts; - } - - /** - * Set the accepted value. - * - * @param string $value - * @return Acceptance_Of - */ - public function accepts($value) - { - $this->accepts = $value; - return $this; - } - -} \ No newline at end of file diff --git a/system/validation/rules/confirmation_of.php b/system/validation/rules/confirmation_of.php deleted file mode 100644 index a6d7de0e..00000000 --- a/system/validation/rules/confirmation_of.php +++ /dev/null @@ -1,25 +0,0 @@ -reserved); - } - - /** - * Set the reserved values for the attribute - * - * @param array $reserved - * @return Exclusion_Of - */ - public function from($reserved) - { - $this->reserved = $reserved; - return $this; - } - -} \ No newline at end of file diff --git a/system/validation/rules/format_of.php b/system/validation/rules/format_of.php deleted file mode 100644 index dfcebb16..00000000 --- a/system/validation/rules/format_of.php +++ /dev/null @@ -1,43 +0,0 @@ -expression, $attributes[$attribute]); - } - - /** - * Set the regular expression. - * - * @param string $expression - * @return Format_Of - */ - public function using($expression) - { - $this->expression = $expression; - return $this; - } - -} \ No newline at end of file diff --git a/system/validation/rules/inclusion_of.php b/system/validation/rules/inclusion_of.php deleted file mode 100644 index f92ace6f..00000000 --- a/system/validation/rules/inclusion_of.php +++ /dev/null @@ -1,43 +0,0 @@ -accepted); - } - - /** - * Set the accepted values for the attribute. - * - * @param array $accepted - * @return Inclusion_Of - */ - public function in($accepted) - { - $this->accepted = $accepted; - return $this; - } - -} \ No newline at end of file diff --git a/system/validation/rules/length_of.php b/system/validation/rules/length_of.php deleted file mode 100644 index f53ff5aa..00000000 --- a/system/validation/rules/length_of.php +++ /dev/null @@ -1,49 +0,0 @@ -size) and Str::length($value) !== $this->size) - { - $this->error = 'string_wrong_size'; - } - // --------------------------------------------------------- - // Validate the maximum length of the attribute. - // --------------------------------------------------------- - elseif ( ! is_null($this->maximum) and Str::length($value) > $this->maximum) - { - $this->error = 'string_too_big'; - } - // --------------------------------------------------------- - // Validate the minimum length of the attribute. - // --------------------------------------------------------- - elseif ( ! is_null($this->minimum) and Str::length($value) < $this->minimum) - { - $this->error = 'string_too_small'; - } - - return is_null($this->error); - } - -} \ No newline at end of file diff --git a/system/validation/rules/numericality_of.php b/system/validation/rules/numericality_of.php deleted file mode 100644 index 43813ba7..00000000 --- a/system/validation/rules/numericality_of.php +++ /dev/null @@ -1,116 +0,0 @@ -error = 'number_not_valid'; - } - // --------------------------------------------------------- - // Validate the attribute is an integer. - // --------------------------------------------------------- - elseif ($this->only_integer and filter_var($attributes[$attribute], FILTER_VALIDATE_INT) === false) - { - $this->error = 'number_not_integer'; - } - // --------------------------------------------------------- - // Validate the exact size of the attribute. - // --------------------------------------------------------- - elseif ( ! is_null($this->size) and $attributes[$attribute] != $this->size) - { - $this->error = 'number_wrong_size'; - } - // --------------------------------------------------------- - // Validate the maximum size of the attribute. - // --------------------------------------------------------- - elseif ( ! is_null($this->maximum) and $attributes[$attribute] > $this->maximum) - { - $this->error = 'number_too_big'; - } - // --------------------------------------------------------- - // Validate the minimum size of the attribute. - // --------------------------------------------------------- - elseif ( ! is_null($this->minimum) and $attributes[$attribute] < $this->minimum) - { - $this->error = 'number_too_small'; - } - - return is_null($this->error); - } - - /** - * Specify that the attribute must be an integer. - * - * @return Numericality_Of - */ - public function only_integer() - { - $this->only_integer = true; - return $this; - } - - /** - * Set the "not valid" error message. - * - * @param string $message - * @return Numericality_Of - */ - public function not_valid($message) - { - $this->not_valid = $message; - return $this; - } - - /** - * Set the "not integer" error message. - * - * @param string $message - * @return Numericality_Of - */ - public function not_integer($message) - { - $this->not_integer = $message; - return $this; - } - -} \ No newline at end of file diff --git a/system/validation/rules/presence_of.php b/system/validation/rules/presence_of.php deleted file mode 100644 index 8926e967..00000000 --- a/system/validation/rules/presence_of.php +++ /dev/null @@ -1,29 +0,0 @@ -column)) - { - $this->column = $attribute; - } - - return DB::table($this->table)->where($this->column, '=', $attributes[$attribute])->count() == 0; - } - - /** - * Set the database table and column. - * - * The attribute name will be used as the column name if no other - * column name is specified. - * - * @param string $table - * @param string $column - * @return Uniqueness_Of - */ - public function on($table, $column = null) - { - $this->table = $table; - $this->column = $column; - - return $this; - } - -} \ No newline at end of file diff --git a/system/validation/rules/upload_of.php b/system/validation/rules/upload_of.php deleted file mode 100644 index 4dfba8ca..00000000 --- a/system/validation/rules/upload_of.php +++ /dev/null @@ -1,160 +0,0 @@ -allow_null) - { - $this->error = 'presence_of'; - } - - return is_null($this->error); - } - - // ----------------------------------------------------- - // Uploaded files are stored in the $_FILES array, so - // we use that array instead of the $attributes. - // ----------------------------------------------------- - $file = Input::file($attribute); - - if ( ! is_null($this->maximum) and $file['size'] > $this->maximum * 1000) - { - $this->error = 'file_too_big'; - } - - // ----------------------------------------------------- - // The File::is method uses the Fileinfo PHP extension - // to determine the MIME type of the file. - // ----------------------------------------------------- - foreach ($this->types as $type) - { - if (File::is($type, $file['tmp_name'])) - { - break; - } - - $this->error = 'file_wrong_type'; - } - - return is_null($this->error); - } - - /** - * Set the acceptable file types. - * - * @return Upload_Of - */ - public function is() - { - $this->types = func_get_args(); - return $this; - } - - /** - * Require that the uploaded file is an image type. - * - * @return Upload_Of - */ - public function is_image() - { - $this->types = array_merge($this->types, array('jpg', 'gif', 'png', 'bmp')); - return $this; - } - - /** - * Set the maximum file size in kilobytes. - * - * @param int $maximum - * @return Upload_Of - */ - public function maximum($maximum) - { - $this->maximum = $maximum; - return $this; - } - - /** - * Set the validation error message. - * - * @param string $message - * @return Upload_Of - */ - public function message($message) - { - return $this->wrong_type($message)->too_big($message); - } - - /** - * Set the "wrong type" error message. - * - * @param string $message - * @return Upload_Of - */ - public function wrong_type($message) - { - $this->wrong_type = $message; - return $this; - } - - /** - * Set the "too big" error message. - * - * @param string $message - * @return Upload_Of - */ - public function too_big($message) - { - $this->too_big = $message; - return $this; - } - -} \ No newline at end of file diff --git a/system/validation/rules/with_callback.php b/system/validation/rules/with_callback.php deleted file mode 100644 index ed1d2ff3..00000000 --- a/system/validation/rules/with_callback.php +++ /dev/null @@ -1,48 +0,0 @@ -callback)) - { - throw new \Exception("The validation callback for the [$attribute] attribute is not callable."); - } - - if ( ! is_null($nullable = parent::check($attribute, $attributes))) - { - return $nullable; - } - - return call_user_func($this->callback, $attributes[$attribute]); - } - - /** - * Set the validation callback. - * - * @param function $callback - * @return With_Callback - */ - public function using($callback) - { - $this->callback = $callback; - return $this; - } - -} \ No newline at end of file diff --git a/system/validator.php b/system/validator.php index b4f87ccb..ff3e1209 100644 --- a/system/validator.php +++ b/system/validator.php @@ -3,86 +3,491 @@ class Validator { /** - * The attributes being validated. + * The array being validated. * * @var array */ public $attributes; - /** - * The validation error collector. - * - * @var Error_Collector - */ - public $errors; - /** * The validation rules. * * @var array */ - public $rules = array(); + public $rules; /** - * Create a new Validator instance. + * The validation messages. * - * @param mixed $target + * @var array + */ + public $messages; + + /** + * The post-validation error messages. + * + * @var array + */ + public $errors; + + /** + * The "size" related validation rules. + * + * @var array + */ + protected $size_rules = array('size', 'between', 'min', 'max'); + + /** + * Create a new validator instance. + * + * @param array $attributes + * @param array $rules + * @param array $messages * @return void */ - public function __construct($target = null) + public function __construct($attributes, $rules, $messages = array()) { - $this->errors = new Validation\Error_Collector; - - if (is_null($target)) - { - $target = Input::get(); - } - - // If the source is an Eloquent model, use the model's attributes as the validation attributes. - $this->attributes = ($target instanceof DB\Eloquent) ? $target->attributes : (array) $target; + $this->attributes = $attributes; + $this->rules = $rules; + $this->messages = $messages; } /** - * Create a new Validator instance. - * - * @param mixed $target - * @return Validator - */ - public static function make($target = null) - { - return new static($target); - } - - /** - * Determine if the attributes pass all of the validation rules. + * Validate the target array using the specified validation rules. * * @return bool */ - public function is_valid() + public function invalid() { - $this->errors->messages = array(); + return ! $this->valid(); + } - foreach ($this->rules as $rule) + /** + * Validate the target array using the specified validation rules. + * + * @return bool + */ + public function valid() + { + $this->errors = new Validation\Errors; + + foreach ($this->rules as $attribute => $rules) { - $rule->validate($this->attributes, $this->errors); + if (is_string($rules)) + { + $rules = explode('|', $rules); + } + + foreach ($rules as $rule) + { + $this->check($attribute, $rule); + } } return count($this->errors->messages) == 0; } /** - * Magic Method for dynamically creating validation rules. + * Evaluate an attribute against a validation rule. + * + * @param string $attribute + * @param string $rule + * @return void */ - public function __call($method, $parameters) + protected function check($attribute, $rule) { - if (file_exists(SYS_PATH.'validation/rules/'.$method.EXT)) - { - $rule = '\\System\\Validation\\Rules\\'.$method; + list($rule, $parameters) = $this->parse($rule); - return $this->rules[] = new $rule($parameters); + if ( ! method_exists($this, $validator = 'validate_'.$rule)) + { + throw new \Exception("Validation rule [$rule] doesn't exist."); } - throw new \Exception("Method [$method] does not exist on Validator class."); + // No validation will be run for attributes that do not exist unless the rule being validated + // is "required" or "accepted". No other rules have implicit "required" checks. + if ( ! array_key_exists($attribute, $this->attributes) and ! in_array($rule, array('required', 'accepted'))) + { + continue; + } + + if ( ! $this->$validator($attribute, $parameters)) + { + $this->errors->add($attribute, $this->format_message($this->get_message($attribute, $rule), $attribute, $rule, $parameters)); + } + } + + /** + * Validate that a required attribute exists in the attributes array. + * + * @param string $attribute + * @return bool + */ + protected function validate_required($attribute) + { + return array_key_exists($attribute, $this->attributes) and trim($this->attributes[$attribute]) !== ''; + } + + /** + * Validate that an attribute has a matching confirmation attribute. + * + * @param string $attribute + * @return bool + */ + protected function validate_confirmed($attribute) + { + return array_key_exists($attribute.'_confirmation', $this->attributes) and $this->attributes[$attribute] == $this->attributes[$attribute.'_confirmation']; + } + + /** + * Validate that an attribute was "accepted". + * + * This validation rule implies the attribute is "required". + * + * @param string $attribute + * @return bool + */ + protected function validate_accepted($attribute) + { + return static::validate_required($attribute) and ($this->attributes[$attribute] == 'yes' or $this->attributes[$attribute] == '1'); + } + + /** + * Validate that an attribute is numeric. + * + * @param string $attribute + * @return bool + */ + protected function validate_numeric($attribute) + { + return is_numeric($this->attributes[$attribute]); + } + + /** + * Validate that an attribute is an integer. + * + * @param string $attribute + * @return bool + */ + protected function validate_integer($attribute) + { + return filter_var($this->attributes[$attribute], FILTER_VALIDATE_INT) !== false; + } + + /** + * Validate the size of an attribute. + * + * @param string $attribute + * @param array $parameters + * @return bool + */ + protected function validate_size($attribute, $parameters) + { + return $this->get_size($attribute) == $parameters[0]; + } + + /** + * Validate the size of an attribute is between a set of values. + * + * @param string $attribute + * @param array $parameters + * @return bool + */ + protected function validate_between($attribute, $parameters) + { + return $this->get_size($attribute) >= $parameters[0] and $this->get_size($attribute) <= $parameters[1]; + } + + /** + * Validate the size of an attribute is greater than a minimum value. + * + * @param string $attribute + * @param array $parameters + * @return bool + */ + protected function validate_min($attribute, $parameters) + { + return $this->get_size($attribute) >= $parameters[0]; + } + + /** + * Validate the size of an attribute is less than a maximum value. + * + * @param string $attribute + * @param array $parameters + * @return bool + */ + protected function validate_max($attribute, $parameters) + { + return $this->get_size($attribute) <= $parameters[0]; + } + + /** + * Get the size of an attribute. + * + * @param string $attribute + * @return mixed + */ + protected function get_size($attribute) + { + if ($this->has_numeric_rule($attribute)) + { + return $this->attributes[$attribute]; + } + + return (array_key_exists($attribute, $_FILES)) ? $this->attributes[$attribute]['size'] / 1000 : Str::length(trim($this->attributes[$attribute])); + } + + /** + * Validate an attribute is contained within a list of values. + * + * @param string $attribute + * @param array $parameters + * @return bool + */ + protected function validate_in($attribute, $parameters) + { + return in_array($this->attributes[$attribute], $parameters); + } + + /** + * Validate an attribute is not contained within a list of values. + * + * @param string $attribute + * @param array $parameters + * @return bool + */ + protected function validate_not_in($attribute, $parameters) + { + return ! in_array($this->attributes[$attribute], $parameters); + } + + /** + * Validate the uniqueness of an attribute value on a given database table. + * + * @param string $attribute + * @param array $parameters + * @return bool + */ + protected function validate_unique($attribute, $parameters) + { + if ( ! isset($parameters[1])) + { + $parameters[1] = $attribute; + } + + return DB::table($parameters[0])->where($parameters[1], '=', $this->attributes[$attribute])->count() == 0; + } + + /** + * Validate than an attribute is a valid e-mail address. + * + * @param string $attribute + * @return bool + */ + protected function validate_email($attribute) + { + return filter_var($this->attributes[$attribute], FILTER_VALIDATE_EMAIL) !== false; + } + + /** + * Validate than an attribute is a valid URL. + * + * @param string $attribute + * @return bool + */ + protected function validate_url($attribute) + { + return filter_var($this->attributes[$attribute], FILTER_VALIDATE_URL) !== false; + } + + /** + * Validate that an attribute is an active URL. + * + * @param string $attribute + * @return bool + */ + protected function validate_active_url($attribute) + { + $url = str_replace(array('http://', 'https://', 'ftp://'), '', Str::lower($this->attributes[$attribute])); + + return checkdnsrr($url); + } + + /** + * Validate the MIME type of a file is an image MIME type. + * + * @param string $attribute + * @return bool + */ + protected function validate_image($attribute) + { + return static::validate_mime($attribute, array('jpg', 'png', 'gif', 'bmp')); + } + + /** + * Validate than an attribute contains only alphabetic characters. + * + * @param string $attribute + * @return bool + */ + protected function validate_alpha($attribute) + { + return preg_match('/^([a-z])+$/i', $this->attributes[$attribute]); + } + + /** + * Validate than an attribute contains only alpha-numeric characters. + * + * @param string $attribute + * @return bool + */ + protected function validate_alpha_num($attribute) + { + return preg_match('/^([a-z0-9])+$/i', $this->attributes[$attribute]); + } + + /** + * Validate than an attribute contains only alpha-numeric characters, dashes, and underscores. + * + * @param string $attribute + * @return bool + */ + protected function validate_alpha_dash($attribute) + { + return preg_match('/^([-a-z0-9_-])+$/i', $this->attributes[$attribute]); + } + + /** + * Validate the MIME type of a file upload attribute is in a set of MIME types. + * + * @param string $attribute + * @param array $parameters + * @return bool + */ + protected function validate_mimes($attribute, $parameters) + { + foreach ($parameters as $extension) + { + if (File::is($extension, $this->attributes[$attribute]['tmp_name'])) + { + return true; + } + } + + return false; + } + + /** + * Get the proper error message for an attribute and rule. + * + * Developer specified attribute specific rules take first priority. + * Developer specified error rules take second priority. + * + * If the message has not been specified by the developer, the default will be used + * from the validation language file. + * + * @param string $attribute + * @param string $rule + * @return string + */ + protected function get_message($attribute, $rule) + { + if (array_key_exists($attribute.'_'.$rule, $this->messages)) + { + return $this->messages[$attribute.'_'.$rule]; + } + elseif (array_key_exists($rule, $this->messages)) + { + return $this->messages[$rule]; + } + else + { + $message = Lang::line('validation.'.$rule)->get(); + + // For "size" rules that are validating strings or files, we need to adjust + // the default error message appropriately. + if (in_array($rule, $this->size_rules) and ! $this->has_numeric_rule($attribute)) + { + return (array_key_exists($attribute, $_FILES)) ? rtrim($message, '.').' kilobytes.' : rtrim($message, '.').' characters.'; + } + + return $message; + } + } + + /** + * Replace all error message place-holders with actual values. + * + * @param string $message + * @param string $attribute + * @param string $rule + * @param array $parameters + * @return string + */ + protected function format_message($message, $attribute, $rule, $parameters) + { + $display = Lang::line('attributes.'.$attribute)->get(null, function() use ($attribute) { return str_replace('_', ' ', $attribute); }); + + $message = str_replace(':attribute', $display, $message); + + if (in_array($rule, $this->size_rules)) + { + $max = ($rule == 'between') ? $parameters[1] : $parameters[0]; + + $message = str_replace(':size', $parameters[0], str_replace(':min', $parameters[0], str_replace(':max', $max, $message))); + } + elseif (in_array($rule, array('in', 'not_in', 'mimes'))) + { + $message = str_replace(':values', implode(', ', $parameters), $message); + } + + return $message; + } + + /** + * Determine if an attribute has either a "numeric" or "integer" rule. + * + * @param string $attribute + * @return bool + */ + protected function has_numeric_rule($attribute) + { + return $this->has_rule($attribute, array('numeric', 'integer')); + } + + /** + * Determine if an attribute has a rule assigned to it. + * + * @param string $attribute + * @param array $rules + * @return bool + */ + protected function has_rule($attribute, $rules) + { + foreach ($this->rules[$attribute] as $rule) + { + list($rule, $parameters) = $this->parse($rule); + + if (in_array($rule, $rules)) + { + return true; + } + } + + return false; + } + + /** + * Extrac the rule name and parameters from a rule. + * + * @param string $rule + * @return array + */ + protected function parse($rule) + { + $parameters = (($colon = strpos($rule, ':')) !== false) ? explode(',', substr($rule, $colon + 1)) : array(); + + return array(is_numeric($colon) ? substr($rule, 0, $colon) : $rule, $parameters); } } \ No newline at end of file From 6c6f92a7c98fcaa05ed7cef4bdd5600c4bfc3613 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 13 Jul 2011 11:03:22 -0700 Subject: [PATCH 173/206] Added default value to URL::to_asset method. --- system/url.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/url.php b/system/url.php index 3e1de338..76a615d3 100644 --- a/system/url.php +++ b/system/url.php @@ -50,7 +50,7 @@ class URL { * @param string $url * @return string */ - public static function to_asset($url) + public static function to_asset($url = '') { return static::to($url, false, true); } From 03044c7d4c61161d8e22d82ec4be84fb57a2a549 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 13 Jul 2011 13:13:18 -0700 Subject: [PATCH 174/206] Refactoring router. --- system/router.php | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/system/router.php b/system/router.php index 86715ece..baaf5ee2 100644 --- a/system/router.php +++ b/system/router.php @@ -41,7 +41,9 @@ class Router { { foreach (explode(', ', $keys) as $key) { - if (preg_match('#^'.$key = static::translate_wildcards($key).'$#', $uri)) + $key = str_replace(':num', '[0-9]+', str_replace(':any', '[a-zA-Z0-9\-_]+', $key)); + + if (preg_match('#^'.$key.'$#', $uri)) { return Request::$route = new Route($keys, $callback, static::parameters($uri, $key)); } @@ -76,17 +78,6 @@ class Router { return (file_exists($path = APP_PATH.'routes/'.$segments[0].EXT)) ? require $path : array(); } - /** - * Translate route URI wildcards to regular expressions. - * - * @param string $key - * @return string - */ - private static function translate_wildcards($key) - { - return str_replace(':num', '[0-9]+', str_replace(':any', '[a-zA-Z0-9\-_]+', $key)); - } - /** * Extract the parameters from a URI based on a route URI. * From 398a5bb41a78087c7740efcecaf282346960a281 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 13 Jul 2011 17:28:42 -0500 Subject: [PATCH 175/206] added support for optional route parameters. --- system/router.php | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/system/router.php b/system/router.php index 86715ece..dc9a870c 100644 --- a/system/router.php +++ b/system/router.php @@ -41,7 +41,7 @@ class Router { { foreach (explode(', ', $keys) as $key) { - if (preg_match('#^'.$key = static::translate_wildcards($key).'$#', $uri)) + if (preg_match('#^'.static::translate_wildcards($key).'$#', $uri)) { return Request::$route = new Route($keys, $callback, static::parameters($uri, $key)); } @@ -77,14 +77,25 @@ class Router { } /** - * Translate route URI wildcards to regular expressions. + * Translate route URI wildcards into actual regular expressions. * * @param string $key * @return string */ private static function translate_wildcards($key) { - return str_replace(':num', '[0-9]+', str_replace(':any', '[a-zA-Z0-9\-_]+', $key)); + $replacements = 0; + + // For optional parameters, first translate the wildcards to their regex equivalent, sans the ")?" ending. + $key = str_replace(array('/(:num?)', '/(:any?)'), array('(?:/([0-9]+)', '(?:/([a-zA-Z0-9\-_]+)'), $key, $replacements); + + // Now, to properly close the regular expression, we need to append a ")?" for each optional segment in the route. + if ($replacements > 0) + { + $key .= implode('', array_fill(0, $replacements, ')?')); + } + + return str_replace(array(':num', ':any'), array('[0-9]+', '[a-zA-Z0-9\-_]+'), $key); } /** From 36fe006b089e4384a28aaf392b8d71a83e140c5c Mon Sep 17 00:00:00 2001 From: Michael Hasselbring Date: Wed, 13 Jul 2011 19:40:55 -0500 Subject: [PATCH 176/206] Created Validator::make() --- system/validator.php | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/system/validator.php b/system/validator.php index ff3e1209..d49ebce1 100644 --- a/system/validator.php +++ b/system/validator.php @@ -52,6 +52,19 @@ class Validator { $this->messages = $messages; } + /** + * Factory for creating new validator instances. + * + * @param array $attributes + * @param array $rules + * @param array $messages + * @return Validator + */ + public static function make($attributes, $rules, $messages = array()) + { + return new static($attributes, $rules, $messages); + } + /** * Validate the target array using the specified validation rules. * From cffbcd010d8f95ef65212e4325fc4dab52ed0688 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 13 Jul 2011 20:59:51 -0500 Subject: [PATCH 177/206] changed continue to return in validation library. --- system/validator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/validator.php b/system/validator.php index d49ebce1..937f0b57 100644 --- a/system/validator.php +++ b/system/validator.php @@ -120,7 +120,7 @@ class Validator { // is "required" or "accepted". No other rules have implicit "required" checks. if ( ! array_key_exists($attribute, $this->attributes) and ! in_array($rule, array('required', 'accepted'))) { - continue; + return; } if ( ! $this->$validator($attribute, $parameters)) From d3398db56f6c5bb4597fd33346307cc78224c991 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 13 Jul 2011 22:27:14 -0500 Subject: [PATCH 178/206] added tests to main repository. --- public/index.php | 2 +- tests/bootstrap.php | 26 ++++++ tests/phpunit.xml | 7 ++ tests/suite/InputTest.php | 151 ++++++++++++++++++++++++++++++++ tests/suite/RequestTest.php | 135 ++++++++++++++++++++++++++++ tests/suite/RouteFilterTest.php | 45 ++++++++++ tests/suite/RouteIsTest.php | 41 +++++++++ tests/suite/RouteLoaderTest.php | 63 +++++++++++++ tests/suite/RouteParserTest.php | 19 ++++ tests/suite/RouteTest.php | 55 ++++++++++++ tests/suite/RouterTest.php | 44 ++++++++++ 11 files changed, 587 insertions(+), 1 deletion(-) create mode 100644 tests/bootstrap.php create mode 100644 tests/phpunit.xml create mode 100644 tests/suite/InputTest.php create mode 100644 tests/suite/RequestTest.php create mode 100644 tests/suite/RouteFilterTest.php create mode 100644 tests/suite/RouteIsTest.php create mode 100644 tests/suite/RouteLoaderTest.php create mode 100644 tests/suite/RouteParserTest.php create mode 100644 tests/suite/RouteTest.php create mode 100644 tests/suite/RouterTest.php diff --git a/public/index.php b/public/index.php index 6d4a6264..ba63074c 100644 --- a/public/index.php +++ b/public/index.php @@ -24,7 +24,7 @@ define('PACKAGE_PATH', APP_PATH.'packages/'); define('EXT', '.php'); // -------------------------------------------------------------- -// Load the configuration class. +// Load the classes used by the auto-loader. // -------------------------------------------------------------- require SYS_PATH.'config'.EXT; require SYS_PATH.'arr'.EXT; diff --git a/tests/bootstrap.php b/tests/bootstrap.php new file mode 100644 index 00000000..b7d95cff --- /dev/null +++ b/tests/bootstrap.php @@ -0,0 +1,26 @@ + + + + suite + + + \ No newline at end of file diff --git a/tests/suite/InputTest.php b/tests/suite/InputTest.php new file mode 100644 index 00000000..1008500c --- /dev/null +++ b/tests/suite/InputTest.php @@ -0,0 +1,151 @@ +assertEquals(System\Input::get(), $data); + } + + public function inputByMethodProvider() + { + return array( + array('GET', array('method' => 'GET')), + array('POST', array('method' => 'POST')), + ); + } + + /** + * @dataProvider inputBySpoofedMethodProvider + */ + public function testInputShouldHydrateBasedOnSpoofedRequestMethod($method, $data) + { + $_SERVER['REQUEST_METHOD'] = 'POST'; + $_POST = $data; + + $this->assertEquals(System\Input::get(), $data); + } + + public function inputBySpoofedMethodProvider() + { + return array( + array('PUT', array('request_method' => 'PUT', 'method' => 'PUT')), + array('DELETE', array('request_method' => 'DELETE', 'method' => 'DELETE')), + ); + } + + public function testHasMethodReturnsTrueIfItemIsPresentInInputData() + { + System\Input::$input = array('name' => 'taylor'); + $this->assertTrue(System\Input::has('name')); + } + + public function testHasMethodReturnsFalseIfItemIsNotPresentInInputData() + { + System\Input::$input = array(); + $this->assertFalse(System\Input::has('name')); + } + + public function testGetMethodReturnsItemByInputKey() + { + System\Input::$input = array('name' => 'taylor'); + $this->assertEquals(System\Input::get('name'), 'taylor'); + } + + public function testGetMethodReturnsDefaultValueWhenItemDoesntExist() + { + System\Input::$input = array(); + + $this->assertNull(System\Input::get('name')); + $this->assertEquals(System\Input::get('name', 'test'), 'test'); + $this->assertTrue(is_array(System\Input::get()) and count(System\Input::get()) == 0); + } + + public function testGetMethodReturnsEntireInputArrayWhenNoKeyGiven() + { + System\Input::$input = array('name' => 'taylor', 'age' => 25); + $this->assertEquals(System\Input::get(), System\Input::$input); + } + + public function testFileMethodReturnsItemFromGlobalFilesArray() + { + $_FILES['test'] = array('name' => 'taylor'); + $this->assertEquals(System\Input::file('test'), $_FILES['test']); + } + + public function testFileMethodReturnsSpecificItemFromFileArrayWhenSpecified() + { + $_FILES['test'] = array('size' => 500); + $this->assertEquals(System\Input::file('test.size'), 500); + } + + /** + * @expectedException Exception + */ + public function testOldMethodShouldThrowExceptionWhenSessionsArentEnabled() + { + System\Input::old(); + } + + /** + * @expectedException Exception + */ + public function testHadMethodShouldThrowExceptionWhenSessionsArentEnabled() + { + System\Input::has(); + } + + public function testOldMethodShouldReturnOldInputDataFromSession() + { + System\Config::set('session.driver', 'test'); + System\Session::$session['data']['laravel_old_input'] = array('name' => 'taylor'); + + $this->assertEquals(System\Input::old('name'), 'taylor'); + } + + public function testHadMethodReturnsTrueIfItemIsPresentInOldInputData() + { + System\Config::set('session.driver', 'test'); + System\Session::$session['data']['laravel_old_input'] = array('name' => 'taylor'); + + $this->assertTrue(System\Input::had('name')); + } + + public function testHadMethodReturnsFalseIfItemIsNotPresentInOldInputData() + { + System\Config::set('session.driver', 'test'); + System\Session::$session['data']['laravel_old_input'] = array(); + + $this->assertFalse(System\Input::had('name')); + } + +} \ No newline at end of file diff --git a/tests/suite/RequestTest.php b/tests/suite/RequestTest.php new file mode 100644 index 00000000..33c75f41 --- /dev/null +++ b/tests/suite/RequestTest.php @@ -0,0 +1,135 @@ +assertEquals(System\Request::uri(), 'test'); + } + + /** + * @dataProvider rootUriProvider1 + */ + public function testUriMethodReturnsSingleSlashOnRequestForRoot($uri) + { + Config::set('application.url', 'http://example.com'); + Config::set('appliation.index', ''); + + $_SERVER['REQUEST_URI'] = $uri; + $this->assertEquals(System\Request::uri(), '/'); + } + + public function rootUriProvider1() + { + return array( + array(''), + array('/'), + array('/index.php'), + array('/index.php/'), + array('/index.php///'), + array('http://example.com'), + array('http://example.com/'), + ); + } + + /** + * @dataProvider rootUriProvider2 + */ + public function testUriMethodReturnsSingleSlashOnRequestForFolderNestedRoot($uri) + { + Config::set('application.url', 'http://example.com/laravel/public'); + Config::set('appliation.index', 'index.php'); + + $_SERVER['REQUEST_URI'] = $uri; + $this->assertEquals(System\Request::uri(), '/'); + } + + public function rootUriProvider2() + { + return array( + array('http://example.com/laravel/public'), + array('http://example.com/laravel/public/index.php'), + array('http://example.com/laravel/public/index.php/'), + array('http://example.com/laravel/public/index.php///'), + array(''), + array('/'), + array('/index.php'), + array('/index.php/'), + array('/index.php///'), + array('http://example.com'), + array('http://example.com/'), + ); + } + + /** + * @dataProvider segmentedUriProvider1 + */ + public function testUriMethodReturnsSegmentForSingleSegmentUri($uri) + { + Config::set('application.url', 'http://example.com'); + Config::set('appliation.index', ''); + + $_SERVER['REQUEST_URI'] = $uri; + $this->assertEquals(System\Request::uri(), 'user'); + } + + public function segmentedUriProvider1() + { + return array( + array('http://example.com/user'), + array('http://example.com/user/'), + array('http://example.com/user//'), + ); + } + + /** + * @dataProvider segmentedUriProvider2 + */ + public function testUriMethodReturnsSegmentsForMultiSegmentUri($uri) + { + Config::set('application.url', 'http://example.com'); + Config::set('appliation.index', ''); + + $_SERVER['REQUEST_URI'] = $uri; + $this->assertEquals(System\Request::uri(), 'user/something'); + } + + public function segmentedUriProvider2() + { + return array( + array('http://example.com/user/something'), + array('http://example.com/user/something/'), + array('http://example.com/user/something//'), + ); + } + + public function testMethodForNonSpoofedRequests() + { + $_SERVER['REQUEST_METHOD'] = 'GET'; + $this->assertEquals(System\Request::method(), 'GET'); + } + + public function testMethodForSpoofedRequests() + { + $_SERVER['REQUEST_METHOD'] = 'GET'; + $_POST['REQUEST_METHOD'] = 'PUT'; + $this->assertEquals(System\Request::method(), 'PUT'); + } + +} \ No newline at end of file diff --git a/tests/suite/RouteFilterTest.php b/tests/suite/RouteFilterTest.php new file mode 100644 index 00000000..bdadb563 --- /dev/null +++ b/tests/suite/RouteFilterTest.php @@ -0,0 +1,45 @@ + function() {return 'test';}, + 'vars' => function($var) {return $var;}, + 'vars2' => function($var1, $var2) {return $var1.$var2;}, + ); + + System\Route\Filter::$filters = $filters; + } + + /** + * @expectedException Exception + */ + public function testCallingUndefinedFilterThrowsException() + { + System\Route\Filter::call('not-found'); + } + + public function testCallingFilterWithoutOverrideReturnsNull() + { + $this->assertNull(System\Route\Filter::call('test')); + } + + public function testCallingFilterWithOverrideReturnsResult() + { + $this->assertEquals(System\Route\Filter::call('test', array(), true), 'test'); + } + + public function testCallingFilterWithParametersPassesParametersToFilter() + { + $this->assertEquals(System\Route\Filter::call('vars', array('test'), true), 'test'); + $this->assertEquals(System\Route\Filter::call('vars2', array('test1', 'test2'), true), 'test1test2'); + } + + public static function tearDownAfterClass() + { + System\Route\Filter::$filters = require APP_PATH.'filters'.EXT; + } + +} \ No newline at end of file diff --git a/tests/suite/RouteIsTest.php b/tests/suite/RouteIsTest.php new file mode 100644 index 00000000..7056bcbd --- /dev/null +++ b/tests/suite/RouteIsTest.php @@ -0,0 +1,41 @@ +callback = array('name' => 'test', 'do' => function() {}); + + System\Request::$route = $route; + } + + public function tearDown() + { + System\Request::$route = null; + } + + public function testRouteIsReturnsFalseWhenNoName() + { + $route = new System\Route(null, null); + $route->callback = function() {}; + + System\Request::$route = $route; + + $this->assertFalse(System\Request::route_is('test')); + $this->assertFalse(System\Request::route_is_test()); + } + + public function testRouteIsReturnsFalseWhenWrongName() + { + $this->assertFalse(System\Request::route_is('something')); + $this->assertFalse(System\Request::route_is_something()); + } + + public function testRouteIsReturnsTrueWhenMatch() + { + $this->assertTrue(System\Request::route_is('test')); + $this->assertTrue(System\Request::route_is_test()); + } + +} \ No newline at end of file diff --git a/tests/suite/RouteLoaderTest.php b/tests/suite/RouteLoaderTest.php new file mode 100644 index 00000000..bd11a316 --- /dev/null +++ b/tests/suite/RouteLoaderTest.php @@ -0,0 +1,63 @@ +rrmdir(APP_PATH.'routes'); + } + + public function testRouteArrayShouldBeReturnedWhenUsingSingleRoutesFile() + { + $routes = System\Router::load('test'); + + $this->assertEquals(count($routes), 1); + $this->assertArrayHasKey('GET /', $routes); + $this->assertTrue(is_callable($routes['GET /'])); + } + + public function testRouteLoaderReturnsHomeRoutesWhenItIsOnlyFileInRoutesDirectory() + { + mkdir(APP_PATH.'routes', 0777); + file_put_contents(APP_PATH.'routes/home.php', " function() {return '/';}); ?>", LOCK_EX); + + $this->assertEquals(count(System\Router::load('')), 1); + } + + public function testRouteLoaderWithRoutesDirectory() + { + mkdir(APP_PATH.'routes', 0777); + file_put_contents(APP_PATH.'routes/user.php', " function() {return '/user';}); ?>", LOCK_EX); + + $routes = System\Router::load('user/home'); + + $this->assertEquals(count($routes), 2); + $this->assertArrayHasKey('GET /', $routes); + $this->assertArrayHasKey('GET /user', $routes); + $this->assertTrue(is_callable($routes['GET /'])); + $this->assertTrue(is_callable($routes['GET /user'])); + } + + /** + * Recursively Remove A Directory. + */ + public function rrmdir($dir) + { + if (is_dir($dir)) + { + $objects = scandir($dir); + + foreach ($objects as $object) + { + if ($object != "." && $object != "..") + { + if (filetype($dir."/".$object) == "dir") rrmdir($dir."/".$object); else unlink($dir."/".$object); + } + } + + reset($objects); + rmdir($dir); + } + } + +} \ No newline at end of file diff --git a/tests/suite/RouteParserTest.php b/tests/suite/RouteParserTest.php new file mode 100644 index 00000000..db615d81 --- /dev/null +++ b/tests/suite/RouteParserTest.php @@ -0,0 +1,19 @@ +assertEmpty(System\Router::parameters('/test/route', '/test/route')); + $this->assertEmpty(System\Router::parameters('/', '/')); + } + + public function testParserReturnsParametersWhenTheyArePresent() + { + $this->assertEquals(System\Router::parameters('/user/1', '/user/(:num)'), array(1)); + $this->assertEquals(System\Router::parameters('/user/1/2', '/user/(:num)/(:num)'), array(1, 2)); + $this->assertEquals(System\Router::parameters('/user/1/test', '/user/(:num)/(:any)'), array(1, 'test')); + $this->assertEquals(System\Router::parameters('/user/1/test/again', '/user/(:num)/test/(:any)'), array(1, 'again')); + } + +} \ No newline at end of file diff --git a/tests/suite/RouteTest.php b/tests/suite/RouteTest.php new file mode 100644 index 00000000..d75516ff --- /dev/null +++ b/tests/suite/RouteTest.php @@ -0,0 +1,55 @@ +assertInstanceOf('System\\Response', $route->call()); + $this->assertEquals($route->call()->content, 'test'); + } + + public function testRouteCallPassesParametersToCallback() + { + $route = new System\Route('GET /', function($parameter) {return $parameter;}, array('test')); + $this->assertEquals($route->call()->content, 'test'); + + $route = new System\Route('GET /', function($parameter1, $parameter2) {return $parameter1.$parameter2;}, array('test1', 'test2')); + $this->assertEquals($route->call()->content, 'test1test2'); + } + + public function testRouteCallWithNullBeforeFilterReturnsRouteResponse() + { + $route = new System\Route('GET /', array('before' => 'test', 'do' => function() {return 'route';})); + System\Route\Filter::$filters = array('test' => function() {return null;}); + + $this->assertEquals($route->call()->content, 'route'); + } + + public function testRouteCallWithOverridingBeforeFilterReturnsFilterResponse() + { + $route = new System\Route('GET /', array('before' => 'test', 'do' => function() {return 'route';})); + System\Route\Filter::$filters = array('test' => function() {return 'filter';}); + + $this->assertEquals($route->call()->content, 'filter'); + } + + public function testRouteAfterFilterIsCalled() + { + $route = new System\Route('GET /', array('after' => 'test', 'do' => function() {return 'route';})); + System\Route\Filter::$filters = array('test' => function() {define('LARAVEL_TEST_AFTER_FILTER', 'ran');}); + $route->call(); + + $this->assertTrue(defined('LARAVEL_TEST_AFTER_FILTER')); + } + + public function testRouteAfterFilterDoesNotAffectResponse() + { + $route = new System\Route('GET /', array('after' => 'test', 'do' => function() {return 'route';})); + System\Route\Filter::$filters = array('test' => function() {return 'filter';}); + + $this->assertEquals($route->call()->content, 'route'); + } + +} \ No newline at end of file diff --git a/tests/suite/RouterTest.php b/tests/suite/RouterTest.php new file mode 100644 index 00000000..7ec11468 --- /dev/null +++ b/tests/suite/RouterTest.php @@ -0,0 +1,44 @@ + 'home', 'do' => function() {}); + $routes['POST /home'] = array('name' => 'post-home', 'do' => function() {}); + $routes['GET /user/(:num)'] = array('name' => 'user', 'do' => function() {}); + $routes['GET /user/(:any)/(:num)/edit'] = array('name' => 'edit', 'do' => function() {}); + + System\Router::$routes = $routes; + } + + public function testRouterReturnsNullWhenNotFound() + { + $this->assertNull(System\Router::route('GET', 'not-found')); + } + + public function testRouterRoutesToProperRouteWhenSegmentsArePresent() + { + $this->assertEquals(System\Router::route('GET', 'home')->callback['name'], 'home'); + $this->assertEquals(System\Router::route('POST', 'home')->callback['name'], 'post-home'); + $this->assertEquals(System\Router::route('GET', 'user/1')->callback['name'], 'user'); + $this->assertEquals(System\Router::route('GET', 'user/taylor/25/edit')->callback['name'], 'edit'); + } + + public function testRouterReturnsNullWhenRouteNotFound() + { + $this->assertNull(System\Router::route('POST', 'user/taylor/25/edit')); + $this->assertNull(System\Router::route('GET', 'user/taylor/taylor/edit')); + $this->assertNull(System\Router::route('GET', 'user/taylor')); + $this->assertNull(System\Router::route('GET', 'user/12-3')); + } + + public static function tearDownAfterClass() + { + System\Router::$routes = null; + } + +} \ No newline at end of file From 537139a2ca67f2beaf6e9cdcf7556ea041a4ce0f Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 13 Jul 2011 23:14:51 -0500 Subject: [PATCH 179/206] refactoring test structure. --- tests/bootstrap.php | 5 ++ tests/suite/InputTest.php | 27 ++++++++ tests/suite/RequestTest.php | 37 +++++++++++ tests/suite/RouteFilterTest.php | 10 +-- tests/suite/RouteIsTest.php | 41 ------------ tests/suite/RouteLoaderTest.php | 63 ------------------ tests/suite/RouteParserTest.php | 19 ------ tests/suite/RouteTest.php | 1 + tests/suite/RouterTest.php | 114 ++++++++++++++++++++++++++------ tests/utils.php | 30 +++++++++ 10 files changed, 197 insertions(+), 150 deletions(-) delete mode 100644 tests/suite/RouteIsTest.php delete mode 100644 tests/suite/RouteLoaderTest.php delete mode 100644 tests/suite/RouteParserTest.php create mode 100644 tests/utils.php diff --git a/tests/bootstrap.php b/tests/bootstrap.php index b7d95cff..5d978d58 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -20,6 +20,11 @@ define('EXT', '.php'); require SYS_PATH.'config'.EXT; require SYS_PATH.'arr'.EXT; +// -------------------------------------------------------------- +// Load the test utilities. +// -------------------------------------------------------------- +require 'utils'.EXT; + // -------------------------------------------------------------- // Register the auto-loader. // -------------------------------------------------------------- diff --git a/tests/suite/InputTest.php b/tests/suite/InputTest.php index 1008500c..b4fdd458 100644 --- a/tests/suite/InputTest.php +++ b/tests/suite/InputTest.php @@ -75,6 +75,12 @@ class InputTest extends PHPUnit_Framework_TestCase { $this->assertFalse(System\Input::has('name')); } + public function testHasMethodReturnsFalseIfItemIsInInputButIsEmptyString() + { + System\Input::$input = array('name' => ''); + $this->assertFalse(System\Input::has('name')); + } + public function testGetMethodReturnsItemByInputKey() { System\Input::$input = array('name' => 'taylor'); @@ -87,6 +93,7 @@ class InputTest extends PHPUnit_Framework_TestCase { $this->assertNull(System\Input::get('name')); $this->assertEquals(System\Input::get('name', 'test'), 'test'); + $this->assertEquals(System\Input::get('name', function() {return 'test';}), 'test'); $this->assertTrue(is_array(System\Input::get()) and count(System\Input::get()) == 0); } @@ -108,6 +115,15 @@ class InputTest extends PHPUnit_Framework_TestCase { $this->assertEquals(System\Input::file('test.size'), 500); } + public function testAllMethodReturnsBothGetAndFileArrays() + { + $_GET['name'] = 'test'; + $_FILES['picture'] = array(); + + $this->assertArrayHasKey('name', System\Input::all()); + $this->assertArrayHasKey('picture', System\Input::all()); + } + /** * @expectedException Exception */ @@ -132,6 +148,17 @@ class InputTest extends PHPUnit_Framework_TestCase { $this->assertEquals(System\Input::old('name'), 'taylor'); } + public function testOldMethodReturnsDefaultValueWhenItemDoesntExist() + { + System\Config::set('session.driver', 'test'); + System\Session::$session['data']['laravel_old_input'] = array(); + + $this->assertNull(System\Input::old('name')); + $this->assertEquals(System\Input::old('name', 'test'), 'test'); + $this->assertEquals(System\Input::old('name', function() {return 'test';}), 'test'); + $this->assertTrue(is_array(System\Input::old()) and count(System\Input::old()) == 0); + } + public function testHadMethodReturnsTrueIfItemIsPresentInOldInputData() { System\Config::set('session.driver', 'test'); diff --git a/tests/suite/RequestTest.php b/tests/suite/RequestTest.php index 33c75f41..b6cf25c4 100644 --- a/tests/suite/RequestTest.php +++ b/tests/suite/RequestTest.php @@ -5,6 +5,16 @@ class RequestTest extends PHPUnit_Framework_TestCase { public function setUp() { unset($_SERVER['PATH_INFO'], $_SERVER['REQUEST_METHOD']); + + $route = new System\Route(null, null); + $route->callback = array('name' => 'test', 'do' => function() {}); + + System\Request::$route = $route; + } + + public function tearDown() + { + System\Request::$route = null; } /** @@ -128,8 +138,35 @@ class RequestTest extends PHPUnit_Framework_TestCase { public function testMethodForSpoofedRequests() { $_SERVER['REQUEST_METHOD'] = 'GET'; + $_POST['REQUEST_METHOD'] = 'PUT'; $this->assertEquals(System\Request::method(), 'PUT'); + + $_POST['REQUEST_METHOD'] = 'DELETE'; + $this->assertEquals(System\Request::method(), 'DELETE'); + } + + public function testRouteIsReturnsFalseWhenNoSuchNamedRouteExists() + { + $route = new System\Route(null, null); + $route->callback = function() {}; + + System\Request::$route = $route; + + $this->assertFalse(System\Request::route_is('test')); + $this->assertFalse(System\Request::route_is_test()); + } + + public function testRouteIsReturnsFalseWhenWrongRouteNameIsGiven() + { + $this->assertFalse(System\Request::route_is('something')); + $this->assertFalse(System\Request::route_is_something()); + } + + public function testRouteIsReturnsTrueWhenNamedRouteExists() + { + $this->assertTrue(System\Request::route_is('test')); + $this->assertTrue(System\Request::route_is_test()); } } \ No newline at end of file diff --git a/tests/suite/RouteFilterTest.php b/tests/suite/RouteFilterTest.php index bdadb563..f0a984e1 100644 --- a/tests/suite/RouteFilterTest.php +++ b/tests/suite/RouteFilterTest.php @@ -13,6 +13,11 @@ class RouteFilerTest extends PHPUnit_Framework_TestCase { System\Route\Filter::$filters = $filters; } + public static function tearDownAfterClass() + { + System\Route\Filter::$filters = require APP_PATH.'filters'.EXT; + } + /** * @expectedException Exception */ @@ -37,9 +42,4 @@ class RouteFilerTest extends PHPUnit_Framework_TestCase { $this->assertEquals(System\Route\Filter::call('vars2', array('test1', 'test2'), true), 'test1test2'); } - public static function tearDownAfterClass() - { - System\Route\Filter::$filters = require APP_PATH.'filters'.EXT; - } - } \ No newline at end of file diff --git a/tests/suite/RouteIsTest.php b/tests/suite/RouteIsTest.php deleted file mode 100644 index 7056bcbd..00000000 --- a/tests/suite/RouteIsTest.php +++ /dev/null @@ -1,41 +0,0 @@ -callback = array('name' => 'test', 'do' => function() {}); - - System\Request::$route = $route; - } - - public function tearDown() - { - System\Request::$route = null; - } - - public function testRouteIsReturnsFalseWhenNoName() - { - $route = new System\Route(null, null); - $route->callback = function() {}; - - System\Request::$route = $route; - - $this->assertFalse(System\Request::route_is('test')); - $this->assertFalse(System\Request::route_is_test()); - } - - public function testRouteIsReturnsFalseWhenWrongName() - { - $this->assertFalse(System\Request::route_is('something')); - $this->assertFalse(System\Request::route_is_something()); - } - - public function testRouteIsReturnsTrueWhenMatch() - { - $this->assertTrue(System\Request::route_is('test')); - $this->assertTrue(System\Request::route_is_test()); - } - -} \ No newline at end of file diff --git a/tests/suite/RouteLoaderTest.php b/tests/suite/RouteLoaderTest.php deleted file mode 100644 index bd11a316..00000000 --- a/tests/suite/RouteLoaderTest.php +++ /dev/null @@ -1,63 +0,0 @@ -rrmdir(APP_PATH.'routes'); - } - - public function testRouteArrayShouldBeReturnedWhenUsingSingleRoutesFile() - { - $routes = System\Router::load('test'); - - $this->assertEquals(count($routes), 1); - $this->assertArrayHasKey('GET /', $routes); - $this->assertTrue(is_callable($routes['GET /'])); - } - - public function testRouteLoaderReturnsHomeRoutesWhenItIsOnlyFileInRoutesDirectory() - { - mkdir(APP_PATH.'routes', 0777); - file_put_contents(APP_PATH.'routes/home.php', " function() {return '/';}); ?>", LOCK_EX); - - $this->assertEquals(count(System\Router::load('')), 1); - } - - public function testRouteLoaderWithRoutesDirectory() - { - mkdir(APP_PATH.'routes', 0777); - file_put_contents(APP_PATH.'routes/user.php', " function() {return '/user';}); ?>", LOCK_EX); - - $routes = System\Router::load('user/home'); - - $this->assertEquals(count($routes), 2); - $this->assertArrayHasKey('GET /', $routes); - $this->assertArrayHasKey('GET /user', $routes); - $this->assertTrue(is_callable($routes['GET /'])); - $this->assertTrue(is_callable($routes['GET /user'])); - } - - /** - * Recursively Remove A Directory. - */ - public function rrmdir($dir) - { - if (is_dir($dir)) - { - $objects = scandir($dir); - - foreach ($objects as $object) - { - if ($object != "." && $object != "..") - { - if (filetype($dir."/".$object) == "dir") rrmdir($dir."/".$object); else unlink($dir."/".$object); - } - } - - reset($objects); - rmdir($dir); - } - } - -} \ No newline at end of file diff --git a/tests/suite/RouteParserTest.php b/tests/suite/RouteParserTest.php deleted file mode 100644 index db615d81..00000000 --- a/tests/suite/RouteParserTest.php +++ /dev/null @@ -1,19 +0,0 @@ -assertEmpty(System\Router::parameters('/test/route', '/test/route')); - $this->assertEmpty(System\Router::parameters('/', '/')); - } - - public function testParserReturnsParametersWhenTheyArePresent() - { - $this->assertEquals(System\Router::parameters('/user/1', '/user/(:num)'), array(1)); - $this->assertEquals(System\Router::parameters('/user/1/2', '/user/(:num)/(:num)'), array(1, 2)); - $this->assertEquals(System\Router::parameters('/user/1/test', '/user/(:num)/(:any)'), array(1, 'test')); - $this->assertEquals(System\Router::parameters('/user/1/test/again', '/user/(:num)/test/(:any)'), array(1, 'again')); - } - -} \ No newline at end of file diff --git a/tests/suite/RouteTest.php b/tests/suite/RouteTest.php index d75516ff..70f0df35 100644 --- a/tests/suite/RouteTest.php +++ b/tests/suite/RouteTest.php @@ -39,6 +39,7 @@ class RouteTest extends PHPUnit_Framework_TestCase { { $route = new System\Route('GET /', array('after' => 'test', 'do' => function() {return 'route';})); System\Route\Filter::$filters = array('test' => function() {define('LARAVEL_TEST_AFTER_FILTER', 'ran');}); + $route->call(); $this->assertTrue(defined('LARAVEL_TEST_AFTER_FILTER')); diff --git a/tests/suite/RouterTest.php b/tests/suite/RouterTest.php index 7ec11468..680bab3b 100644 --- a/tests/suite/RouterTest.php +++ b/tests/suite/RouterTest.php @@ -6,39 +6,109 @@ class RoutingTest extends PHPUnit_Framework_TestCase { { $routes = array(); - $routes['GET /'] = function() {return 'root';}; + $routes['GET /'] = array('name' => 'root', 'do' => function() {}); $routes['GET /home'] = array('name' => 'home', 'do' => function() {}); $routes['POST /home'] = array('name' => 'post-home', 'do' => function() {}); $routes['GET /user/(:num)'] = array('name' => 'user', 'do' => function() {}); $routes['GET /user/(:any)/(:num)/edit'] = array('name' => 'edit', 'do' => function() {}); + $routes['GET /cart/(:num?)'] = array('name' => 'cart', 'do' => function() {}); + $routes['GET /download/(:num?)/(:any?)'] = array('name' => 'download', 'do' => function() {}); System\Router::$routes = $routes; } - public function testRouterReturnsNullWhenNotFound() - { - $this->assertNull(System\Router::route('GET', 'not-found')); - } - - public function testRouterRoutesToProperRouteWhenSegmentsArePresent() - { - $this->assertEquals(System\Router::route('GET', 'home')->callback['name'], 'home'); - $this->assertEquals(System\Router::route('POST', 'home')->callback['name'], 'post-home'); - $this->assertEquals(System\Router::route('GET', 'user/1')->callback['name'], 'user'); - $this->assertEquals(System\Router::route('GET', 'user/taylor/25/edit')->callback['name'], 'edit'); - } - - public function testRouterReturnsNullWhenRouteNotFound() - { - $this->assertNull(System\Router::route('POST', 'user/taylor/25/edit')); - $this->assertNull(System\Router::route('GET', 'user/taylor/taylor/edit')); - $this->assertNull(System\Router::route('GET', 'user/taylor')); - $this->assertNull(System\Router::route('GET', 'user/12-3')); - } - public static function tearDownAfterClass() { System\Router::$routes = null; } + public function tearDown() + { + Utils::rrmdir(APP_PATH.'routes'); + } + + public function testRouterReturnsNullWhenNotFound() + { + $this->assertNull(System\Router::route('GET', 'doesnt-exist')); + } + + public function testRouterRoutesToRootWhenItIsRequest() + { + $this->assertEquals(System\Router::route('GET', '/')->callback['name'], 'root'); + } + + public function testRouterRoutesToProperRouteWhenSegmentsArePresent() + { + $this->assertEquals(System\Router::route('GET', 'home')->callback['name'], 'home'); + $this->assertEquals(System\Router::route('GET', 'user/1')->callback['name'], 'user'); + $this->assertEquals(System\Router::route('GET', 'user/taylor/25/edit')->callback['name'], 'edit'); + $this->assertEquals(System\Router::route('POST', 'home')->callback['name'], 'post-home'); + } + + public function testRouterRoutesToProperRouteWhenUsingOptionalSegments() + { + $this->assertEquals(System\Router::route('GET', 'cart')->callback['name'], 'cart'); + $this->assertEquals(System\Router::route('GET', 'cart/1')->callback['name'], 'cart'); + $this->assertEquals(System\Router::route('GET', 'download')->callback['name'], 'download'); + $this->assertEquals(System\Router::route('GET', 'download/1')->callback['name'], 'download'); + $this->assertEquals(System\Router::route('GET', 'download/1/a')->callback['name'], 'download'); + } + + public function testRouterReturnsNullWhenRouteNotFound() + { + $this->assertNull(System\Router::route('GET', 'user/taylor/taylor/edit')); + $this->assertNull(System\Router::route('GET', 'user/taylor')); + $this->assertNull(System\Router::route('GET', 'user/12-3')); + $this->assertNull(System\Router::route('GET', 'cart/a')); + $this->assertNull(System\Router::route('GET', 'cart/12-3')); + $this->assertNull(System\Router::route('GET', 'download/a')); + $this->assertNull(System\Router::route('GET', 'download/1a')); + $this->assertNull(System\Router::route('POST', 'user/taylor/25/edit')); + } + + public function testRouteArrayShouldBeReturnedWhenUsingSingleRoutesFile() + { + $routes = System\Router::load('test'); + + // Only the Laravel default route should be returned. + $this->assertArrayHasKey('GET /', $routes); + } + + public function testRouteLoaderLoadsRouteFilesInRouteDirectoryByURI() + { + $this->setupRoutesDirectory(); + + $this->assertArrayHasKey('GET /user', System\Router::load('user')); + $this->assertArrayHasKey('GET /cart/edit', System\Router::load('cart')); + $this->assertArrayHasKey('GET /cart/edit', System\Router::load('cart/edit')); + } + + public function testRouteLoaderLoadsBaseRoutesFileForEveryRequest() + { + $this->setupRoutesDirectory(); + $this->assertArrayHasKey('GET /', System\Router::load('user')); + } + + private function setupRoutesDirectory() + { + mkdir(APP_PATH.'routes', 0777); + + file_put_contents(APP_PATH.'routes/user.php', " function() {return '/user';}); ?>", LOCK_EX); + file_put_contents(APP_PATH.'routes/cart.php', " function() {return '/cart/edit';}); ?>", LOCK_EX); + } + + public function testParameterMethodReturnsNoParametersWhenNoneArePresent() + { + $this->assertEmpty(System\Router::parameters('GET /test/route', 'GET /test/route')); + $this->assertEmpty(System\Router::parameters('GET /', 'GET /')); + } + + public function testParameterMethodReturnsParametersWhenTheyArePresent() + { + $this->assertEquals(System\Router::parameters('GET /user/1', 'GET /user/(:num)'), array(1)); + $this->assertEquals(System\Router::parameters('GET /user/1/2', 'GET /user/(:num)/(:num)'), array(1, 2)); + $this->assertEquals(System\Router::parameters('GET /user/1/test', 'GET /user/(:num)/(:any)'), array(1, 'test')); + $this->assertEquals(System\Router::parameters('GET /user/1/test/again', 'GET /user/(:num)/test/(:any)'), array(1, 'again')); + } + } \ No newline at end of file diff --git a/tests/utils.php b/tests/utils.php new file mode 100644 index 00000000..14399f36 --- /dev/null +++ b/tests/utils.php @@ -0,0 +1,30 @@ + Date: Wed, 13 Jul 2011 23:24:22 -0500 Subject: [PATCH 180/206] refactored route parameter parsing tests. --- system/router.php | 2 +- tests/suite/RouterTest.php | 39 ++++++++++++++++++++++++-------------- 2 files changed, 26 insertions(+), 15 deletions(-) diff --git a/system/router.php b/system/router.php index dc9a870c..858753dd 100644 --- a/system/router.php +++ b/system/router.php @@ -107,7 +107,7 @@ class Router { * @param string $route * @return array */ - public static function parameters($uri, $route) + private static function parameters($uri, $route) { return array_values(array_intersect_key(explode('/', $uri), preg_grep('/\(.+\)/', explode('/', $route)))); } diff --git a/tests/suite/RouterTest.php b/tests/suite/RouterTest.php index 680bab3b..cff55cbc 100644 --- a/tests/suite/RouterTest.php +++ b/tests/suite/RouterTest.php @@ -45,6 +45,16 @@ class RoutingTest extends PHPUnit_Framework_TestCase { $this->assertEquals(System\Router::route('POST', 'home')->callback['name'], 'post-home'); } + public function testRouterGivesRouteProperSegmentsWhenTheyArePresent() + { + $this->assertEquals(System\Router::route('GET', 'user/1')->parameters[0], 1); + $this->assertEquals(count(System\Router::route('GET', 'user/1')->parameters), 1); + + $this->assertEquals(System\Router::route('GET', 'user/taylor/25/edit')->parameters[0], 'taylor'); + $this->assertEquals(System\Router::route('GET', 'user/taylor/25/edit')->parameters[1], 25); + $this->assertEquals(count(System\Router::route('GET', 'user/taylor/25/edit')->parameters), 2); + } + public function testRouterRoutesToProperRouteWhenUsingOptionalSegments() { $this->assertEquals(System\Router::route('GET', 'cart')->callback['name'], 'cart'); @@ -54,6 +64,21 @@ class RoutingTest extends PHPUnit_Framework_TestCase { $this->assertEquals(System\Router::route('GET', 'download/1/a')->callback['name'], 'download'); } + public function testRouterGivesRouteProperOptionalSegmentsWhenTheyArePresent() + { + $this->assertTrue(is_array(System\Router::route('GET', 'cart')->parameters)); + $this->assertEquals(count(System\Router::route('GET', 'cart')->parameters), 0); + $this->assertEquals(System\Router::route('GET', 'cart/1')->parameters[0], 1); + + $this->assertEquals(count(System\Router::route('GET', 'download')->parameters), 0); + $this->assertEquals(System\Router::route('GET', 'download/1')->parameters[0], 1); + $this->assertEquals(count(System\Router::route('GET', 'download/1')->parameters), 1); + + $this->assertEquals(System\Router::route('GET', 'download/1/a')->parameters[0], 1); + $this->assertEquals(System\Router::route('GET', 'download/1/a')->parameters[1], 'a'); + $this->assertEquals(count(System\Router::route('GET', 'download/1/a')->parameters), 2); + } + public function testRouterReturnsNullWhenRouteNotFound() { $this->assertNull(System\Router::route('GET', 'user/taylor/taylor/edit')); @@ -97,18 +122,4 @@ class RoutingTest extends PHPUnit_Framework_TestCase { file_put_contents(APP_PATH.'routes/cart.php', " function() {return '/cart/edit';}); ?>", LOCK_EX); } - public function testParameterMethodReturnsNoParametersWhenNoneArePresent() - { - $this->assertEmpty(System\Router::parameters('GET /test/route', 'GET /test/route')); - $this->assertEmpty(System\Router::parameters('GET /', 'GET /')); - } - - public function testParameterMethodReturnsParametersWhenTheyArePresent() - { - $this->assertEquals(System\Router::parameters('GET /user/1', 'GET /user/(:num)'), array(1)); - $this->assertEquals(System\Router::parameters('GET /user/1/2', 'GET /user/(:num)/(:num)'), array(1, 2)); - $this->assertEquals(System\Router::parameters('GET /user/1/test', 'GET /user/(:num)/(:any)'), array(1, 'test')); - $this->assertEquals(System\Router::parameters('GET /user/1/test/again', 'GET /user/(:num)/test/(:any)'), array(1, 'again')); - } - } \ No newline at end of file From 0086fff896aedd51695edd1edc724f4f075038f5 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 13 Jul 2011 23:25:10 -0500 Subject: [PATCH 181/206] renamed test. --- tests/suite/RouterTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/suite/RouterTest.php b/tests/suite/RouterTest.php index cff55cbc..a6c10a28 100644 --- a/tests/suite/RouterTest.php +++ b/tests/suite/RouterTest.php @@ -91,7 +91,7 @@ class RoutingTest extends PHPUnit_Framework_TestCase { $this->assertNull(System\Router::route('POST', 'user/taylor/25/edit')); } - public function testRouteArrayShouldBeReturnedWhenUsingSingleRoutesFile() + public function testRouteLoaderShouldReturnSingleRoutesFileWhenNoFolderIsPresent() { $routes = System\Router::load('test'); From 23f167acc7c955cc7e0ef49fbc09806f68e375ec Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 13 Jul 2011 23:38:18 -0500 Subject: [PATCH 182/206] added route finder tests. --- tests/suite/RouteFinderTest.php | 42 +++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 tests/suite/RouteFinderTest.php diff --git a/tests/suite/RouteFinderTest.php b/tests/suite/RouteFinderTest.php new file mode 100644 index 00000000..988fad6b --- /dev/null +++ b/tests/suite/RouteFinderTest.php @@ -0,0 +1,42 @@ + array('name' => 'home', 'do' => function() {})); + $routes['GET /user'] = array('GET /user' => array('name' => 'user', 'do' => function() {})); + + System\Route\Finder::$routes = $routes; + } + + public function testRouteFinderReturnsNullWhenRouteIsNotFound() + { + $this->assertNull(System\Route\Finder::find('doesnt-exist')); + } + + public function testRouteFinderReturnsRouteWhenFoundInSingleRoutesFile() + { + $this->assertArrayHasKey('GET /home', System\Route\Finder::find('home')); + $this->assertArrayHasKey('GET /user', System\Route\Finder::find('user')); + } + + public function testRouteFinderLoadsRoutesFromRouteDirectoryToFindRoutes() + { + System\Route\Finder::$routes = null; + $this->setupRoutesDirectory(); + + $this->assertArrayHasKey('GET /user', System\Route\Finder::find('user')); + + Utils::rrmdir(APP_PATH.'routes'); + } + + private function setupRoutesDirectory() + { + mkdir(APP_PATH.'routes', 0777); + file_put_contents(APP_PATH.'routes/user.php', " array('name' => 'user', 'do' => function() {return '/user';})); ?>", LOCK_EX); + } + +} \ No newline at end of file From ca3c6623a2e12011c93da97252d8a8fb543bca28 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 13 Jul 2011 23:43:32 -0500 Subject: [PATCH 183/206] added arr tests. --- tests/suite/ArrTest.php | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 tests/suite/ArrTest.php diff --git a/tests/suite/ArrTest.php b/tests/suite/ArrTest.php new file mode 100644 index 00000000..81d201bd --- /dev/null +++ b/tests/suite/ArrTest.php @@ -0,0 +1,17 @@ +assertNull(System\Arr::get(array(), 'name')); + $this->assertEquals(System\Arr::get(array(), 'name', 'test'), 'test'); + $this->assertEquals(System\Arr::get(array(), 'name', function() {return 'test';}), 'test'); + } + + public function testReturnsItemWhenPresentInArray() + { + $this->assertEquals(System\Arr::get(array('name' => 'test'), 'name'), 'test'); + } + +} \ No newline at end of file From f266a1c6ca4b3bf8378dccda5cf4ed041e88157e Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 13 Jul 2011 23:53:14 -0500 Subject: [PATCH 184/206] added view tests. --- tests/suite/ViewTest.php | 43 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 tests/suite/ViewTest.php diff --git a/tests/suite/ViewTest.php b/tests/suite/ViewTest.php new file mode 100644 index 00000000..174c58a1 --- /dev/null +++ b/tests/suite/ViewTest.php @@ -0,0 +1,43 @@ + 'test')); + + $this->assertEquals($view->view, 'view'); + $this->assertEquals($view->data, array('name' => 'test')); + + $view = new System\View('view'); + $this->assertEquals($view->data, array()); + } + + public function testMakeMethodReturnsNewViewInstance() + { + $this->assertInstanceOf('System\\View', System\View::make('test')); + } + + public function testBindMethodAddsItemToViewData() + { + $view = System\View::make('test')->bind('name', 'test'); + $this->assertEquals($view->data, array('name' => 'test')); + } + + public function testBoundViewDataCanBeRetrievedThroughMagicMethods() + { + $view = System\View::make('test')->bind('name', 'test'); + + $this->assertTrue(isset($view->name)); + $this->assertEquals($view->name, 'test'); + + unset($view->name); + $this->assertFalse(isset($view->name)); + } + + public function testGetMethodReturnsStringContentOfView() + { + $this->assertTrue(is_string(System\View::make('home/index')->get())); + } + +} \ No newline at end of file From 794e0e6d17a423f605b76d0b8a5b26d1d9793986 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 13 Jul 2011 23:56:44 -0500 Subject: [PATCH 185/206] added exception test to view tests. --- tests/suite/ViewTest.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/suite/ViewTest.php b/tests/suite/ViewTest.php index 174c58a1..080fb3fe 100644 --- a/tests/suite/ViewTest.php +++ b/tests/suite/ViewTest.php @@ -40,4 +40,12 @@ class ViewTest extends PHPUnit_Framework_TestCase { $this->assertTrue(is_string(System\View::make('home/index')->get())); } + /** + * @expectedException Exception + */ + public function testExceptionIsThrownWhenViewDoesntExist() + { + System\View::make('doesnt-exist')->get(); + } + } \ No newline at end of file From d95ead812aee4549fd702b0c59ce56d79b5f3eda Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 14 Jul 2011 00:05:13 -0500 Subject: [PATCH 186/206] removed bloated if statement from front controller. --- public/index.php | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/public/index.php b/public/index.php index ba63074c..fbb7901f 100644 --- a/public/index.php +++ b/public/index.php @@ -93,14 +93,7 @@ if (is_null($response)) { $route = System\Router::route(Request::method(), Request::uri()); - if ( ! is_null($route)) - { - $response = $route->call(); - } - else - { - $response = System\Response::make(View::make('error/404'), 404); - } + $response = (is_null($route)) ? System\Response::make(View::make('error/404'), 404) : $route->call(); } else { @@ -128,4 +121,4 @@ if (System\Config::get('session.driver') != '') // -------------------------------------------------------------- // Send the response to the browser. // -------------------------------------------------------------- -$response->send(); +$response->send(); \ No newline at end of file From 514c128957a670efa1ca9584df4f64083342df51 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 14 Jul 2011 06:30:07 -0700 Subject: [PATCH 187/206] Fixed bug in bindings that was causing null to be saved as 0 in MySQL. --- system/db.php | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/system/db.php b/system/db.php index 4769fc74..c4460513 100644 --- a/system/db.php +++ b/system/db.php @@ -52,7 +52,21 @@ class DB { { $query = static::connection($connection)->prepare($sql); - $result = $query->execute($bindings); + $bindings = array_values($bindings); + + foreach ($bindings as $key => &$binding) + { + if (is_null($binding)) + { + $query->bindValue($key + 1, null, \PDO::PARAM_INT); + } + else + { + $query->bindParam($key + 1, $binding); + } + } + + $result = $query->execute(); if (strpos(strtoupper($sql), 'SELECT') === 0) { From 549fbcc289a256914cbedd0f1ae0635e6be32129 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 14 Jul 2011 06:54:33 -0700 Subject: [PATCH 188/206] Edited readme.md via GitHub --- readme.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/readme.md b/readme.md index 30adf4bc..2e166e88 100644 --- a/readme.md +++ b/readme.md @@ -1,5 +1,9 @@ -# Laravel - A Clean & Classy PHP Framework +# Laravel Documentation -### For more information, visit [http://laravel.com](http://laravel.com) +## Introduction -### For complete documentation, visit [http://docs.laravel.com](http://docs.laravel.com) \ No newline at end of file +Laravel is a clean and classy framework for PHP web development. Freeing you from spaghetti code, Laravel helps you create wonderful applications using simple, expressive syntax. Development should be a creative experience that you enjoy, not something that is painful. Enjoy the fresh air. + +## Table Of Contents + +- Installation \ No newline at end of file From 4e11d4291babaca16b6e5bedc0f80e74fb6c35f9 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 14 Jul 2011 06:55:14 -0700 Subject: [PATCH 189/206] Edited readme.md via GitHub --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 2e166e88..d6f94d6e 100644 --- a/readme.md +++ b/readme.md @@ -1,4 +1,4 @@ -# Laravel Documentation +# Laravel - A Clean & Classy PHP Framework ## Introduction From b2b40a24a50dc7f7a79cce4d5c6f30503600f594 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 14 Jul 2011 08:55:49 -0700 Subject: [PATCH 190/206] Edited readme.md via GitHub --- readme.md | 44 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/readme.md b/readme.md index d6f94d6e..de99d64b 100644 --- a/readme.md +++ b/readme.md @@ -6,4 +6,46 @@ Laravel is a clean and classy framework for PHP web development. Freeing you fro ## Table Of Contents -- Installation \ No newline at end of file +### Getting Started + +- Requirements & Installation +- Basic Configuration +- Routes +- Views & Responses +- Generating URLs +- Generating HTML +- Errors & Logs + +### Input + +- Retrieving Input +- Cookies +- Validation +- Building Forms + +### Database + +- Configuration +- Usage +- Fluent Query Builder +- Eloquent ORM + +### Caching + +- Configuration +- Usage + +### Sessions + +- Configuration +- Usage + +### Authentication + +- Configuration +- Usage + +### Other Topics + +- Localization +- Encryption \ No newline at end of file From 2b0969e106a5c73548d70d017d5d4f601ceb7f65 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 14 Jul 2011 09:52:10 -0700 Subject: [PATCH 191/206] Edited readme.md via GitHub --- readme.md | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/readme.md b/readme.md index de99d64b..ee1fb121 100644 --- a/readme.md +++ b/readme.md @@ -48,4 +48,33 @@ Laravel is a clean and classy framework for PHP web development. Freeing you fro ### Other Topics - Localization -- Encryption \ No newline at end of file +- Encryption + + +## Requirements & Installation + +### Requirements + +- Apache, nginx, or another compatible web server. +- PHP 5.3+ (which supports namespaces, closures, etc.) + +### Installation + +1. Download Laravel +2. Extract the Laravel archive and upload the contents to your web server. +3. Set the URL of your application in the **application/config/application.php** file. +4. Navigate to your application in a web browser. + +If all is well, you should see a pretty Laravel splash page. Get ready, there is lots more to learn! + +### Extras + +Installaing the following goodies will help you take full advantage of Laravel, but they are not required: + +- SQLite, MySQL, or PostgreSQL PDO drivers. +- Memcached or APC. + +### Problems? + +- Make sure the **public** directory is the document root of your web server. +- If you are using mod\_rewrite, set the **index** option in **application/config/application.php** to an empty string. \ No newline at end of file From a8a28d8355931e607b62298b503882538ff5d5e7 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 14 Jul 2011 09:56:46 -0700 Subject: [PATCH 192/206] Edited readme.md via GitHub --- readme.md | 43 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/readme.md b/readme.md index ee1fb121..3d96e847 100644 --- a/readme.md +++ b/readme.md @@ -77,4 +77,45 @@ Installaing the following goodies will help you take full advantage of Laravel, ### Problems? - Make sure the **public** directory is the document root of your web server. -- If you are using mod\_rewrite, set the **index** option in **application/config/application.php** to an empty string. \ No newline at end of file +- If you are using mod\_rewrite, set the **index** option in **application/config/application.php** to an empty string. + + +## Basic Configuration + +### Quick Start + +When starting a new project, you shouldn't be bombarded with loads of confusing configuration decisions. For that reason, Laravel is intelligently configured out of the box. The **application/config/application.php** file contains the basic configuration options for your application. + +There is only one option that **must** be set when starting a new application. Laravel needs to know the URL you will use to access your application. Simply set the url in the **application/config/application.php** file: + + 'url' => 'http://localhost'; + +> **Note:** If you are using mod_rewrite for [cleaner URLs](/start/config#clean), you should set the index option to an empty string. + + +### Cleaner URLs + +Most likely, you do not want your application URLs to contain "index.php". You can remove it using HTTP rewrite rules. If you are using Apache to serve your application, make sure to enable mod_rewrite and create a **.htaccess** file like this one in your **public** directory: + + + RewriteEngine on + + RewriteCond %{REQUEST_FILENAME} !-f + RewriteCond %{REQUEST_FILENAME} !-d + + RewriteRule ^(.*)$ index.php/$1 [L] + + +Is the .htaccess file above not working for you? Try this one: + + Options +FollowSymLinks + RewriteEngine on + + RewriteCond %{REQUEST_FILENAME} !-f + RewriteCond %{REQUEST_FILENAME} !-d + + RewriteRule . index.php [L] + +After setting up HTTP rewriting, you should set the **index** configuration option in **application/config/application.php** to an empty string. + +> **Note:** Each web server has a different method of doing HTTP rewrites, and may require a slightly different .htaccess file. \ No newline at end of file From a571b51d4f8434f25068cc2c815db5bfc17ad40f Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 14 Jul 2011 09:57:26 -0700 Subject: [PATCH 193/206] Edited readme.md via GitHub --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 3d96e847..16948abd 100644 --- a/readme.md +++ b/readme.md @@ -90,7 +90,7 @@ There is only one option that **must** be set when starting a new application. L 'url' => 'http://localhost'; -> **Note:** If you are using mod_rewrite for [cleaner URLs](/start/config#clean), you should set the index option to an empty string. +> **Note:** If you are using mod_rewrite for cleaner URLs, you should set the index option to an empty string. ### Cleaner URLs From eec19f700e8b894c553fa4904db6dfc36f06751d Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 14 Jul 2011 09:58:29 -0700 Subject: [PATCH 194/206] Edited readme.md via GitHub --- readme.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/readme.md b/readme.md index 16948abd..8698c3f9 100644 --- a/readme.md +++ b/readme.md @@ -4,6 +4,7 @@ Laravel is a clean and classy framework for PHP web development. Freeing you from spaghetti code, Laravel helps you create wonderful applications using simple, expressive syntax. Development should be a creative experience that you enjoy, not something that is painful. Enjoy the fresh air. + ## Table Of Contents ### Getting Started @@ -79,6 +80,8 @@ Installaing the following goodies will help you take full advantage of Laravel, - Make sure the **public** directory is the document root of your web server. - If you are using mod\_rewrite, set the **index** option in **application/config/application.php** to an empty string. +[Back To Top](#top) + ## Basic Configuration From 6fbd868efdfb68d45fb6c0d35557ba346b21f086 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 14 Jul 2011 10:03:27 -0700 Subject: [PATCH 195/206] Edited readme.md via GitHub --- readme.md | 41 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 8698c3f9..a9bdef26 100644 --- a/readme.md +++ b/readme.md @@ -121,4 +121,43 @@ Is the .htaccess file above not working for you? Try this one: After setting up HTTP rewriting, you should set the **index** configuration option in **application/config/application.php** to an empty string. -> **Note:** Each web server has a different method of doing HTTP rewrites, and may require a slightly different .htaccess file. \ No newline at end of file +> **Note:** Each web server has a different method of doing HTTP rewrites, and may require a slightly different .htaccess file. + +[Back To Top](#top) + + +## Defining Routes + +- [The Basics](#routes-basics) +- [Route Wildcards & Parameters](#routes-parameters) +- [Route Filters](#route-filters) +- [Named Routes](#routes-named) +- [Organizing Routes](#routes-folder) + + +### The Basics + +Unlike other PHP frameworks, Laravel places routes and their corresponding functions in one file: **application/routes.php**. This file contains the "definition", or public API, of your application. To add functionality to your application, you add to the array located in this file. + +All you need to do is tell Laravel the request methods and URIs it should respond to. You define the behavior of the route using an anonymous method: + + 'GET /home' => function() + { + // Handles GET requests to http://example.com/index.php/home + }, + + 'PUT /user/update' => function() + { + // Handles PUT requests to http://example.com/index.php/user/update + } + +You can easily define a route to handle requests to more than one URI. Just use commas: + + 'POST /, POST /home' => function() + { + // Handles POST requests to http://example.com and http://example.com/index.php/home + } + +> **Note:** The routes.php file replaces the "controllers" found in most frameworks. Have a fat model and keep this file light and clean. Thank us later. + +[Back To Top](#top) \ No newline at end of file From a911d54166ec7ade8af421355f26c4c94aabb1c9 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 14 Jul 2011 10:04:12 -0700 Subject: [PATCH 196/206] Edited readme.md via GitHub --- readme.md | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/readme.md b/readme.md index a9bdef26..2f08d5ea 100644 --- a/readme.md +++ b/readme.md @@ -141,15 +141,19 @@ Unlike other PHP frameworks, Laravel places routes and their corresponding funct All you need to do is tell Laravel the request methods and URIs it should respond to. You define the behavior of the route using an anonymous method: - 'GET /home' => function() - { - // Handles GET requests to http://example.com/index.php/home - }, + function() - { - // Handles PUT requests to http://example.com/index.php/user/update - } + return array( + + 'GET /home' => function() + { + // Handles GET requests to http://example.com/index.php/home + }, + + 'PUT /user/update' => function() + { + // Handles PUT requests to http://example.com/index.php/user/update + } You can easily define a route to handle requests to more than one URI. Just use commas: From 48849f63d56e2733759a7bf431939ccc38b410f2 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 14 Jul 2011 10:05:07 -0700 Subject: [PATCH 197/206] Edited readme.md via GitHub --- readme.md | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/readme.md b/readme.md index 2f08d5ea..f1d99dce 100644 --- a/readme.md +++ b/readme.md @@ -141,19 +141,21 @@ Unlike other PHP frameworks, Laravel places routes and their corresponding funct All you need to do is tell Laravel the request methods and URIs it should respond to. You define the behavior of the route using an anonymous method: - function() - { - // Handles GET requests to http://example.com/index.php/home - }, + 'GET /home' => function() + { + // Handles GET requests to http://example.com/index.php/home + }, - 'PUT /user/update' => function() - { - // Handles PUT requests to http://example.com/index.php/user/update - } + 'PUT /user/update' => function() + { + // Handles PUT requests to http://example.com/index.php/user/update + } +``` You can easily define a route to handle requests to more than one URI. Just use commas: From 20a92ee123021d5fb2f0921d3c626454911debe8 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 14 Jul 2011 10:05:35 -0700 Subject: [PATCH 198/206] Edited readme.md via GitHub --- readme.md | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/readme.md b/readme.md index f1d99dce..c67cf13e 100644 --- a/readme.md +++ b/readme.md @@ -142,19 +142,15 @@ Unlike other PHP frameworks, Laravel places routes and their corresponding funct All you need to do is tell Laravel the request methods and URIs it should respond to. You define the behavior of the route using an anonymous method: ```php - function() +{ + // Handles GET requests to http://example.com/index.php/home +}, -return array( - - 'GET /home' => function() - { - // Handles GET requests to http://example.com/index.php/home - }, - - 'PUT /user/update' => function() - { - // Handles PUT requests to http://example.com/index.php/user/update - } +'PUT /user/update' => function() +{ + // Handles PUT requests to http://example.com/index.php/user/update +} ``` You can easily define a route to handle requests to more than one URI. Just use commas: From b80b3d36d465df4161482b4587090d44fa9acb20 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 14 Jul 2011 10:06:27 -0700 Subject: [PATCH 199/206] Edited readme.md via GitHub --- readme.md | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/readme.md b/readme.md index c67cf13e..a9bdef26 100644 --- a/readme.md +++ b/readme.md @@ -141,17 +141,15 @@ Unlike other PHP frameworks, Laravel places routes and their corresponding funct All you need to do is tell Laravel the request methods and URIs it should respond to. You define the behavior of the route using an anonymous method: -```php -'GET /home' => function() -{ - // Handles GET requests to http://example.com/index.php/home -}, + 'GET /home' => function() + { + // Handles GET requests to http://example.com/index.php/home + }, -'PUT /user/update' => function() -{ - // Handles PUT requests to http://example.com/index.php/user/update -} -``` + 'PUT /user/update' => function() + { + // Handles PUT requests to http://example.com/index.php/user/update + } You can easily define a route to handle requests to more than one URI. Just use commas: From 335d0c00eda647f0ac5778a2d07441cd61cb5a00 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 14 Jul 2011 11:04:45 -0700 Subject: [PATCH 200/206] Edited readme.md via GitHub --- readme.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/readme.md b/readme.md index a9bdef26..a93a799a 100644 --- a/readme.md +++ b/readme.md @@ -146,6 +146,8 @@ All you need to do is tell Laravel the request methods and URIs it should respon // Handles GET requests to http://example.com/index.php/home }, + + 'PUT /user/update' => function() { // Handles PUT requests to http://example.com/index.php/user/update From aca2458574188dc9f67686895e3a9fd0d66de80f Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 14 Jul 2011 11:05:05 -0700 Subject: [PATCH 201/206] Edited readme.md via GitHub --- readme.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/readme.md b/readme.md index a93a799a..a9bdef26 100644 --- a/readme.md +++ b/readme.md @@ -146,8 +146,6 @@ All you need to do is tell Laravel the request methods and URIs it should respon // Handles GET requests to http://example.com/index.php/home }, - - 'PUT /user/update' => function() { // Handles PUT requests to http://example.com/index.php/user/update From a9f7aacea9ed9dae3d6673d61dff187fdf83a436 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 14 Jul 2011 11:08:46 -0700 Subject: [PATCH 202/206] Edited readme.md via GitHub --- readme.md | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/readme.md b/readme.md index a9bdef26..4876ec22 100644 --- a/readme.md +++ b/readme.md @@ -129,7 +129,7 @@ After setting up HTTP rewriting, you should set the **index** configuration opti ## Defining Routes - [The Basics](#routes-basics) -- [Route Wildcards & Parameters](#routes-parameters) +- [Route URI Parameters](#routes-parameters) - [Route Filters](#route-filters) - [Named Routes](#routes-named) - [Organizing Routes](#routes-folder) @@ -160,4 +160,31 @@ You can easily define a route to handle requests to more than one URI. Just use > **Note:** The routes.php file replaces the "controllers" found in most frameworks. Have a fat model and keep this file light and clean. Thank us later. +[Back To Top](#top) + + +### Route URI Parameters + +Laravel makes passing URI parameters to your route functions a breeze. Check out this route: + + 'PUT /user/(:num)' => function($id) {} + +Notice the **(:num)** parameter in the URI? This tells Laravel to allow any numeric value in the second segment of the URI, as well as to pass the segment into the method. + +We can also use the **(:any)** parameter to match the segment to any value: + + 'DELETE /product/(:any)' => function($name) {} + +Of course, you are not limited to one parameter: + + 'GET /post/(:num)/(:num)' => function($month, $day) {} + +Sometimes you may wish to make a parameter optional. You can do so by placing a **?** in parameter: + + 'GET /branch/(:any?)' => function($branch = 'master') {} + +If you need more power and precision (or just want to be extra nerdy), you can even use regular expressions: + + 'GET /product/([0-9]+)' => function($id) {} + [Back To Top](#top) \ No newline at end of file From ecdd559ace17bc4637f8ffdf14fbdb4583ae2d37 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 14 Jul 2011 11:11:57 -0700 Subject: [PATCH 203/206] Edited readme.md via GitHub --- readme.md | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/readme.md b/readme.md index 4876ec22..889af30c 100644 --- a/readme.md +++ b/readme.md @@ -165,19 +165,11 @@ You can easily define a route to handle requests to more than one URI. Just use ### Route URI Parameters -Laravel makes passing URI parameters to your route functions a breeze. Check out this route: +Laravel makes passing URI parameters to your route functions a breeze using the **(:num)** and **(:any)** place-holders: 'PUT /user/(:num)' => function($id) {} -Notice the **(:num)** parameter in the URI? This tells Laravel to allow any numeric value in the second segment of the URI, as well as to pass the segment into the method. - -We can also use the **(:any)** parameter to match the segment to any value: - - 'DELETE /product/(:any)' => function($name) {} - -Of course, you are not limited to one parameter: - - 'GET /post/(:num)/(:num)' => function($month, $day) {} + 'GET /user/(:any)/edit' => function($username) {} Sometimes you may wish to make a parameter optional. You can do so by placing a **?** in parameter: From cec583aafee611aa07cc3a0756289e976a336ec8 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 14 Jul 2011 11:19:14 -0700 Subject: [PATCH 204/206] Edited readme.md via GitHub --- readme.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/readme.md b/readme.md index 889af30c..83f8f632 100644 --- a/readme.md +++ b/readme.md @@ -129,7 +129,7 @@ After setting up HTTP rewriting, you should set the **index** configuration opti ## Defining Routes - [The Basics](#routes-basics) -- [Route URI Parameters](#routes-parameters) +- [Route URI Wildcards](#routes-wildcards) - [Route Filters](#route-filters) - [Named Routes](#routes-named) - [Organizing Routes](#routes-folder) @@ -162,20 +162,20 @@ You can easily define a route to handle requests to more than one URI. Just use [Back To Top](#top) - -### Route URI Parameters + +### Route URI Wildcards -Laravel makes passing URI parameters to your route functions a breeze using the **(:num)** and **(:any)** place-holders: +You can pass URI segments to your route functions using the **(:num)** and **(:any)** wildcards: 'PUT /user/(:num)' => function($id) {} 'GET /user/(:any)/edit' => function($username) {} -Sometimes you may wish to make a parameter optional. You can do so by placing a **?** in parameter: +You may make segments optional by placing a **?** in the wildcard: 'GET /branch/(:any?)' => function($branch = 'master') {} -If you need more power and precision (or just want to be extra nerdy), you can even use regular expressions: +If you need more power and precision, you can even use regular expressions: 'GET /product/([0-9]+)' => function($id) {} From bfaf6950c691dc3b56cabac85d1b9cebe0483162 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 15 Jul 2011 19:25:29 -0500 Subject: [PATCH 205/206] added documentation to main repository. --- documentation/auth/config.md | 30 +++ documentation/auth/usage.md | 77 ++++++ documentation/cache/config.md | 38 +++ documentation/cache/usage.md | 53 ++++ documentation/contents.md | 53 ++++ documentation/database/config.md | 52 ++++ documentation/database/eloquent.md | 306 +++++++++++++++++++++++ documentation/database/query.md | 216 ++++++++++++++++ documentation/database/usage.md | 33 +++ documentation/other/benchmark.md | 30 +++ documentation/other/crypt.md | 36 +++ documentation/other/file.md | 69 ++++++ documentation/other/lang.md | 62 +++++ documentation/session/config.md | 70 ++++++ documentation/session/usage.md | 59 +++++ documentation/start/config.md | 78 ++++++ documentation/start/install.md | 27 ++ documentation/start/interaction.md | 232 ++++++++++++++++++ documentation/start/routes.md | 124 ++++++++++ documentation/start/validation.md | 368 ++++++++++++++++++++++++++++ documentation/start/views.md | 379 +++++++++++++++++++++++++++++ 21 files changed, 2392 insertions(+) create mode 100644 documentation/auth/config.md create mode 100644 documentation/auth/usage.md create mode 100644 documentation/cache/config.md create mode 100644 documentation/cache/usage.md create mode 100644 documentation/contents.md create mode 100644 documentation/database/config.md create mode 100644 documentation/database/eloquent.md create mode 100644 documentation/database/query.md create mode 100644 documentation/database/usage.md create mode 100644 documentation/other/benchmark.md create mode 100644 documentation/other/crypt.md create mode 100644 documentation/other/file.md create mode 100644 documentation/other/lang.md create mode 100644 documentation/session/config.md create mode 100644 documentation/session/usage.md create mode 100644 documentation/start/config.md create mode 100644 documentation/start/install.md create mode 100644 documentation/start/interaction.md create mode 100644 documentation/start/routes.md create mode 100644 documentation/start/validation.md create mode 100644 documentation/start/views.md diff --git a/documentation/auth/config.md b/documentation/auth/config.md new file mode 100644 index 00000000..a9be4d69 --- /dev/null +++ b/documentation/auth/config.md @@ -0,0 +1,30 @@ +## Authentication Configuration + +Most interactive applications have the ability for users to login and logout. Obvious, right? Laravel provides a simple class to help you validate user credentials and retrieve information about the current user of your application. + +The quickest way to get started is to create an [Eloquent User model](/docs/database/eloquent) in your **application/models** directory: + + class User extends Eloquent {} + +Next, you will need to define **email** and **password** columns on your user database table. The password column should hold 60 alpha-numeric characters. The Auth class **requires** that all passwords be hashed and salted. + +> **Note:** The password column on your user table must really be named "password". + +Great job! You're ready to start using the Auth class. However, there are more advanced configuration options available if you wish to use them. + +Let's dig into the **application/config/auth.php** file. In this file you will find two closures: **by\_id** and **by\_username**: + + 'by_id' => function($id) + { + return User::find($id); + } + +The **by_id** function is called when the Auth class needs to retrieve a user by their primary key. As you can see, the default implementation of this function uses an "User" Eloquent model to retrieve the user by ID. However, if you are not using Eloquent, you are free to modify this function to meet the needs of your application. + + 'by_username' => function($username) + { + return User::where('email', '=', $username)->first(); + } + + +The **by_username** function is called when the Auth class needs to retrieve a user by their username, such as when using the **login** method. The default implementation of this function uses an "User" Eloquent model to retrieve the user by e-mail address. However, if you are not using Eloquent or do not wish to use e-mail addresses as usernames, you are free to modify this function as you wish as long as you return an object with **password** and **id** properties. \ No newline at end of file diff --git a/documentation/auth/usage.md b/documentation/auth/usage.md new file mode 100644 index 00000000..988ac27b --- /dev/null +++ b/documentation/auth/usage.md @@ -0,0 +1,77 @@ +## Authentication Usage + +- [Salting & Hashing](#hash) +- [Logging In](#login) +- [Protecting Routes](#filter) +- [Retrieving The Logged In User](#user) +- [Logging Out](#logout) + +> **Note:** Before using the Auth class, you must [specify a session driver](/docs/session/config). + + +### Salting & Hashing + +If you are using the Auth class, Laravel requires all passwords to be hashed and salted. Web development must be done responsibly. Salted, hashed passwords make a rainbow table attack against your user's passwords impractical. + +Don't worry, salting and hashing passwords is easy using the **Hash** class. The Hash class provides a simple way to hash passwords using the **bcrypt** hashing algorithm. Check out this example: + + $password = Hash::make('secret'); + +The **make** method of the Hash class will return a 60 character hashed string. + +You can compare an unhashed value against a hashed one using the **check** method on the **Hash** class: + + if (Hash::check('secret', $hashed_value)) + { + return 'The password is valid!'; + } + +> **Note:** Before using the Auth class, be sure to [create the "password" column](/docs/auth/config) on your user table. + + +### Logging In + +Logging a user into your application is simple using the **login** method on the Auth class. Simply pass the username and password of the user to the method. The login method will return **true** if the credentials are valid. Otherwise, **false** will be returned: + + if (Auth::login('example@gmail.com', 'password')) + { + return Redirect::to('user/profile'); + } + +If the user's credentials are valid, the user ID will be stored in the session and the user will be considered "logged in" on subsequent requests to your application. + +To determine if the user of your application is logged in, call the **check** method: + + if (Auth::check()) + { + return "You're logged in!"; + } + + +### Protecting Routes + +It is common to limit access to certain routes only to logged in users. It's a breeze in Laravel using the built-in [auth filter](/docs/start/routes#filters). If the user is logged in, the request will proceed as normal; however, if the user is not logged in, they will be redirected to the "login" [named route](/docs/start/routes#named). + +To protect a route, simply attach the **auth** filter: + + 'GET /admin' => array('before' => 'auth', 'do' => function() {}) + +> **Note:** You are free to edit the **auth** filter however you like. A default implementation is located in **application/filters.php**. + + +### Retrieving The Logged In User + +Once a user has logged in to your application, you may easily access the user model via the **user** method on the Auth class: + + return Auth::user()->email; + +> **Note:** If the user is not logged in, the **user** method will return NULL. + + +### Logging Out + +Ready to log the user out of your application? It's simple: + + Auth::logout(); + +This method will remove the user ID from the session, and the user will no longer be considered logged in on subsequent requests to your application. \ No newline at end of file diff --git a/documentation/cache/config.md b/documentation/cache/config.md new file mode 100644 index 00000000..00457368 --- /dev/null +++ b/documentation/cache/config.md @@ -0,0 +1,38 @@ +## Cache Configuration + +- [Memcached](#memcached) +- [Cache Keys](#keys) + +Imagine your application displays the ten most popular songs as voted on by your users. Do you really need to look up these ten songs every time someone visits your site? What if you could store them for 10 minutes, or even an hour, allowing you to dramatically speed up your application? Caching makes it simple. + +Laravel provides three wonderful cache drivers out of the box: + +- File System +- Memcached +- APC + +By default, Laravel is configured to use the **file** system cache driver. It's ready to go. The file system driver stores cached items as files in the **application/storage/cache** directory. If you're satisfied with this driver, no other configuration is required. You're ready to start using it. + +> **Note:** Before using the file system cache driver, make sure your **application/storage/cache** directory is writeable. + + +### Memcached + +[Memcached](http://memcached.org) is an ultra-fast, open-source distributed memory object caching system used by sites such as Wikipedia and Facebook. Before using Laravel's Memcached driver, you will need to install and configure Memcached and the PHP Memcache extension on your server. + +Once Memcached is installed on your server, configuring the Laravel driver is a breeze. First, set the **driver** in the **application/config/cache.php** file: + + 'driver' => 'memcached' + +Next, add your Memcached servers to the **servers** array: + + 'servers' => array( + array('host' => '127.0.0.1', 'port' => 11211, 'weight' => 100), + ) + + +### Cache Keys + +To avoid naming collisions with other applications using APC or a Memcached server, Laravel prepends a **key** to each item stored in the cache using these drivers. Feel free to change this value: + + 'key' => 'laravel' \ No newline at end of file diff --git a/documentation/cache/usage.md b/documentation/cache/usage.md new file mode 100644 index 00000000..4464e95a --- /dev/null +++ b/documentation/cache/usage.md @@ -0,0 +1,53 @@ +## Cache Usage + +- [Storing Items](#put) +- [Retrieving Items](#get) +- [Removing Items](#forget) + + +### Storing Items + +Storing items in the cache is simple. Simply call the **put** method on the Cache class: + + Cache::put('name', 'Taylor', 10); + +The first parameter is the **key** to the cache item. You will use this key to retrieve the item from the cache. The second parameter is the **value** of the item. The third parameter is the number of **minutes** you want the item to be cached. + +> **Note:** It is not necessary to serialize objects when storing them in the cache. + + +### Retrieving Items + +Retrieving items from the cache is even more simple than storing them. It is done using the **get** method. Just mention the key of the item you wish to retrieve: + + $name = Cache::get('name'); + +By default, NULL will be returned if the cached item has expired or does not exist. However, you may pass a different default value as a second parameter to the method: + + $name = Cache::get('name', 'Fred'); + +Now, "Fred" will be returned if the "name" cache item has expired or does not exist. + +What if you need a value from your database if a cache item doesn't exist? The solution is simple. You can pass a closure into the **get** method as a default value. The closure will only be executed if the cached item doesn't exist: + + $users = Cache::get('count', function() {return DB::table('users')->count();}); + +Let's take this example a step further. Imagine you want to retrieve the number of registered users for your application; however, if the value is not cached, you want to store the default value in the cache. It's a breeze using the **remember** method: + + $users = Cache::remember('count', function() {return DB::table('users')->count();}, 5); + +Let's talk through that example. If the **count** item exists in the cache, it will be returned. If it doesn't exist, the result of the closure will be stored in the cache for five minutes **and** be returned by the method. Slick, huh? + +Laravel even gives you a simple way to determine if a cached item exists using the **has** method: + + if (Cache::has('name')) + { + $name = Cache::get('name'); + } + + +### Removing Items + +Need to get rid of a cached item? No problem. Just mention the name of the item to the **forget** method: + + Cache::forget('name'); \ No newline at end of file diff --git a/documentation/contents.md b/documentation/contents.md new file mode 100644 index 00000000..e548e29b --- /dev/null +++ b/documentation/contents.md @@ -0,0 +1,53 @@ +## Getting Started + +- [Requirements & Installation](/docs/start/install) +- [Basic Configuration](/docs/start/config) +- [Routes](/docs/start/routes) + - [Defining Routes](/docs/start/routes#define) + - [Wildcard URI Segments](/docs/start/routes#segments) + - [Named Routes](/docs/start/routes#named) + - [Route Filters](/docs/start/routes#filters) + - [Organizing Routes](/docs/start/routes#organize) +- [Views & Responses](/docs/start/views) + - [Creating Views](/docs/start/views#create) + - [Binding Data To Views](/docs/start/views#bind) + - [Nesting Views Within Views](/docs/start/views#nest) + - [Redirects](/docs/start/views#redirect) + - [Downloads](/docs/start/views#downloads) + - [Building URLs](/docs/start/views#urls) + - [Building HTML](/docs/start/views#html) +- [Interaction](/docs/start/interaction) + - [Input](/docs/start/interaction#basics) + - [Old Input](/docs/start/interaction#old) + - [Cookies](/docs/start/interaction#cookies) + - [Building Forms](/docs/start/interaction#forms) +- [Data Validation](/docs/start/validation) + +## Database + +- [Configuration](/docs/database/config) +- [Usage](/docs/database/usage) +- [Fluent Query Builder](/docs/database/query) +- [Eloquent ORM](/docs/database/eloquent) + +## Caching + +- [Configuration](/docs/cache/config) +- [Usage](/docs/cache/usage) + +## Sessions + +- [Configuration](/docs/session/config) +- [Usage](/docs/session/usage) + +## Authentication + +- [Configuration](/docs/auth/config) +- [Usage](/docs/auth/usage) + +## Other Topics + +- [Working With Files](/docs/other/file) +- [Localization](/docs/other/lang) +- [Encryption](/docs/other/crypt) +- [Benchmarking Code](/docs/other/benchmark) \ No newline at end of file diff --git a/documentation/database/config.md b/documentation/database/config.md new file mode 100644 index 00000000..f0a24e54 --- /dev/null +++ b/documentation/database/config.md @@ -0,0 +1,52 @@ +## Database Configuration + +- [Quick Start Using SQLite](#quick) +- [Configuring MySQL or PostgreSQL](#server) +- [Setting The Default Connection Name](#default) + +Database configuration in Laravel is easy. The hardest part is deciding which database to use. Three popular open-source databases are supported out of the box: + +- MySQL +- PostgreSQL +- SQLite + +All of the database configuration options live in the **application/config/db.php** file. Let's get started. + + +### Quick Start Using SQLite + +[SQLite](http://sqlite.org) is an awesome, zero-configuration database system. By default, Laravel is configured to use a SQLite database. Really, you don't have to change anything. Just drop a SQLite database named **application.sqlite** into the **application/storage/db directory**. You're done. + +Of course, if you want to name your database something besides "application", you can modify the database option in the SQLite section of the **application/config/db.php** file: + + 'sqlite' => array( + 'driver' => 'sqlite', + 'database' => 'your_database_name', + ) + +If your application receives less than 100,000 hits per day, SQLite should be suitable for production use in your application. Otherwise, consider using MySQL or PostgreSQL. + +> **Note:** Need a good SQLite manager? Check out this [Firefox extension](https://addons.mozilla.org/en-US/firefox/addon/sqlite-manager/). + + +### Configuring MySQL or PostgreSQL + +If you are using MySQL or PostgreSQL, you will need to edit the configuration options in **application/config/db.php**. Don't worry. In the configuration file, sample configurations exist for both systems. All you need to do is change the options as necessary for your server and set the default connection name. + + 'mysql' => array( + 'driver' => 'mysql', + 'host' => 'localhost', + 'database' => 'database', + 'username' => 'root', + 'password' => 'password', + 'charset' => 'utf8', + ), + + +### Setting The Default Connection Name + +As you have probably noticed, each database connection defined in the **application/config/db.php** file has a name. By default, there are three connections defined: **sqlite**, **mysql**, and **pgsql**. You are free to change these connection names. The default connection can be specified via the **default** option: + + 'default' => 'sqlite'; + +The default connection will always be used by the [fluent query builder](/docs/database/query) and [Eloquent ORM](/docs/database/eloquent). If you need to change the default connection during a request, use the **Config::set** method. \ No newline at end of file diff --git a/documentation/database/eloquent.md b/documentation/database/eloquent.md new file mode 100644 index 00000000..aedca705 --- /dev/null +++ b/documentation/database/eloquent.md @@ -0,0 +1,306 @@ +## Eloquent ORM + +- [Conventions](#conventions) +- [Retrieving Models](#get) +- [Aggregates](#aggregates) +- [Inserting & Updating Models](#save) +- [Relationships](#relationships) +- [Eager Loading](#eager) + +An ORM is an [object-relational mapper](http://en.wikipedia.org/wiki/Object-relational_mapping), and Laravel has one that you will absolutely love to use. It is named "Eloquent" because it allows you to work with your database objects and relationships using an eloquent and expressive syntax. In general, you will define one Eloquent model for each table in your database. To get started, let's define a simple model: + + class User extends Eloquent {} + +Nice! Notice that our model extends the **Eloquent** class. This class will provide all of the functionality you need to start working eloquently with your database. + +> **Note:** Typically, Eloquent models live in the **application/models** directory. + + +### Conventions + +Eloquent makes a few basic assumptions about your database structure: + +- Each table should have a primary key named **id**. +- Each table name should be the plural form of its corresponding model name. + +Sometimes you may wish to use a table name other than the plural form of your model. No problem. Just add a static **table** property your model: + + class User extends Eloquent { + + public static $table = 'my_users'; + + } + + +### Retrieving Models + +Retrieving models using Eloquent is refreshingly simple. The most basic way to retrieve an Eloquent model is the static **find** method. This method will return a single model by primary key with properties corresponding to each column on the table: + + $user = User::find(1); + + echo $user->email; + +The find method will execute a query that looks something like this: + + SELECT * FROM "users" WHERE "id" = 1 + +Need to retrieve an entire table? Just use the static **all** method: + + $users = User::all(); + + foreach ($users as $user) + { + echo $user->email; + } + +Of course, retrieving an entire table isn't very helpful. Thankfully, **every method that is available through the fluent query builder is available in Eloquent**. Just begin querying your model with a static call to one of the [query builder](/docs/database/query) methods, and execute the query using the **get** or **first** method. The get method will return an array of models, while the first method will return a single model: + + $user = User::where('email', '=', $email)->first(); + + $user = User::where_email($email)->first(); + + $users = User::where_in('id', array(1, 2, 3))->or_where('email', '=', $email)->get(); + + $users = User::order_by('votes', 'desc')->take(10)->get(); + +> **Note:** If no results are found, the **first** method will return NULL. The **all** and **get** methods return an empty array. + + +### Aggregates + +Need to get a **MIN**, **MAX**, **AVG**, **SUM**, or **COUNT** value? Just pass the column to the appropriate method: + + $min = User::min('id'); + + $max = User::max('id'); + + $avg = User::avg('id'); + + $sum = User::sum('id'); + + $count = User::count(); + +Of course, you may wish to limit the query using a WHERE clause first: + + $count = User::where('id', '>', 10)->count(); + + +### Inserting & Updating Models + +Inserting Eloquent models into your tables couldn't be easier. First, instantiate a new model. Second, set its properties. Third, call the **save** method: + + $user = new User; + + $user->email = 'example@gmail.com'; + $user->password = 'secret'; + + $user->save(); + +Updating models is just as simple. Instead of instantiating a new model, retrieve one from your database. Then, set its properties and save: + + $user = User::find(1); + + $user->email = 'new_email@gmail.com'; + $user->password = 'new_secret'; + + $user->save(); + +Need to maintain creation and update timestamps on your database records? With Eloquent, you don't have to worry about it. Just add a static **timestamps** property to your model: + + class User extends Eloquent { + + public static $timestamps = true; + + } + +Next, add **created_at** and **updated_at** date columns to your table. Now, whenever you save the model, the creation and update timestamps will be set automatically. You're welcome. + +> **Note:** You can change the default timezone of your application in the **application/config/application.php** file. + + +### Relationships + +Unless you're doing it wrong, your database tables are probably related to one another. For instance, an order may belong to a user. Or, a post may have many comments. Eloquent makes defining relationships and retrieving related models simple and intuitive. Laravel supports three types of relationships: + +- [One-To-One](#one-to-one) +- [One-To-Many](#one-to-many) +- [Many-To-Many](#many-to-many) + +To define a relationship on an Eloquent model, you simply create a method that returns the result of either the **has\_one**, **has\_many**, **belongs\_to**, or **has\_and\_belongs\_to\_many** method. Let's examine each one in detail. + + +#### One-To-One + +A one-to-one relationship is the most basic form of relationship. For example, let's pretend a user has one phone. Simply describe this relationship to Eloquent: + + class User extends Eloquent { + + public function phone() + { + return $this->has_one('Phone'); + } + + } + +Notice that the name of the related model is passed to the **has_one** method. You can now retrieve the phone of a user through the **phone** method: + + $phone = User::find(1)->phone()->first(); + +Let's examine the SQL performed by this statement. Two queries will be performed: one to retrieve the user and one to retrieve the user's phone: + + SELECT * FROM "users" WHERE "id" = 1 + + SELECT * FROM "phones" WHERE "user_id" = 1 + +Note that Eloquent assumes the foreign key of the relationship will be **user\_id**. Most foreign keys will follow this **model\_id** convention; however, if you want to use a different column name as the foreign key, just pass it in the second parameter to the method: + + return $this->has_one('Phone', 'my_foreign_key'); + +Want to just retrieve the user's phone without calling the first method? No problem. Just use the **dynamic phone property**. Eloquent will automatically load the relationship for you, and is even smart enough to know whether to call the get (for one-to-many relationships) or first (for one-to-one relationships) method: + + $phone = User::find(1)->phone; + +What if you need to retrieve a phone's user? Since the foreign key (**user\_id**) is on the phones table, we should describe this relationship using the **belongs\_to** method. It makes sense, right? Phones belong to users. When using the **belongs\_to** method, the name of the relationship method should correspond to the foreign key (sans the **\_id**). Since the foreign key is **user\_id**, your relationship method should be named **user**: + + class Phone extends Eloquent { + + public function user() + { + return $this->belongs_to('User'); + } + + } + +Great! You can now access a User model through a Phone model using either your relationship method or dynamic property: + + echo Phone::find(1)->user()->first()->email; + + echo Phone::find(1)->user->email; + + +#### One-To-Many + +Assume a blog post has many comments. It's easy to define this relationship using the **has_many** method: + + class Post extends Eloquent { + + public function comments() + { + return $this->has_many('Comment'); + } + + } + +Now, simply access the post comments through the relationship method or dynamic property: + + $comments = Post::find(1)->comments()->get(); + + $comments = Post::find(1)->comments; + +Both of these statements will execute the following SQL: + + SELECT * FROM "posts" WHERE "id" = 1 + + SELECT * FROM "comments" WHERE "post_id" = 1 + +Want to join on a different foreign key? No problem. Just pass it in the second parameter to the method: + + return $this->has_many('Comment', 'my_foreign_key'); + +You may be wondering: _If the dynamic properties return the relationship and require less keystokes, why would I ever use the relationship methods?_ Actually, relationship methods are very powerful. They allow you to continue to chain query methods before retrieving the relationship. Check this out: + + echo Post::find(1)->comments()->order_by('votes', 'desc')->take(10)->get(); + + +#### Many-To-Many + +Many-to-many relationships are the most complicated of the three relationships. But don't worry, you can do this. For example, assume a User has many Roles, but a Role can also belong to many Users. Three database tables must be created to accomplish this relationship: a **users** table, a **roles** table, and a **roles_users** table. The structure for each table looks like this: + +**Users:** + + id - INTEGER + email - VARCHAR + +**Roles:** + + id - INTEGER + name - VARCHAR + +**Roles_Users:** + + user_id - INTEGER + role_id - INTEGER + +Now you're ready to define the relationship on your models using the **has\_and\_belongs\_to\_many** method: + + class User extends Eloquent { + + public function roles() + { + return $this->has_and_belongs_to_many('Role'); + } + + } + +Great! Now it's time to retrieve a user's roles: + + $roles = User::find(1)->roles()->get(); + +Or, as usual, you may retrieve the relationship through the dynamic roles property: + + $roles = User::find(1)->roles; + +As you may have noticed, the default name of the intermediate table is the plural names of the two related models arranged alphabetically and concatenated by an underscore. However, you are free to specify your own table name. Simply pass the table name in the second parameter to the **has\_and\_belongs\_to\_many** method: + + class User extends Eloquent { + + public function roles() + { + return $this->has_and_belongs_to_many('Role', 'user_roles'); + } + + } + + +### Eager Loading + +Eager loading exists to alleviate the N + 1 query problem. Exactly what is this problem? Well, pretend each Book belongs to an Author. We would describe this relationship like so: + + class Book extends Eloquent { + + public function author() + { + return $this->belongs_to('Author'); + } + + } + +Now, examine the following code: + + foreach (Book::all() as $book) + { + echo $book->author->name; + } + +How many queries will be executed? Well, one query will be executed to retrieve all of the books from the table. However, another query will be required for each book to retrieve the author. To display the author name for 25 books would require **26 queries**. See how the queries can add up fast? + +Thankfully, you can eager load the author models using the **with** method. Simply mention the **function name** of the relationship you wish to eager load: + + foreach (Book::with('author')->get() as $book) + { + echo $book->author->name; + } + +In this example, **only two queries will be executed**! + + SELECT * FROM "books" + + SELECT * FROM "authors" WHERE "id" IN (1, 2, 3, 4, 5, ...) + +Obviously, wise use of eager loading can dramatically increase the performance of your application. In the example above, eager loading cut the execution time in half. + +Need to eager load more than one relationship? It's easy: + + $books = Book::with('author', 'publisher')->get(); + +> **Note:** When eager loading, the call to the static **with** method must always be at the beginning of the query. \ No newline at end of file diff --git a/documentation/database/query.md b/documentation/database/query.md new file mode 100644 index 00000000..70dda78f --- /dev/null +++ b/documentation/database/query.md @@ -0,0 +1,216 @@ +## Fluent Query Builder + +- [Retrieving Records](#get) +- [Building Where Clauses](#where) +- [Dynamic Where Clauses](#dynamic) +- [Table Joins](#joins) +- [Ordering Results](#ordering) +- [Skip & Take](#limit) +- [Aggregates](#aggregates) +- [Inserting Records](#insert) +- [Updating Records](#update) +- [Deleting Records](#delete) + +Laravel provides an awesome, easy-to-use fluent interface for building SQL queries and working with your database. All queries use prepared statements and are protected against SQL injection. Working with your database doesn't have to be a headache. + +You can begin a fluent query using the **table** method on the DB class. Just mention the table you wish to query: + + $query = DB::table('users'); + +You now have a fluent query builder for the "users" table. Using this query builder, you can retrieve, insert, update, or delete records from the table. + + +### Retrieving Records + +There are two methods available for retrieving records using a fluent query: **get** and **first**. The **get** method will return an array of records from your database. Each record will be an object with properties corresponding to the columns of the table: + + $users = DB::table('users')->get(); + + foreach ($users as $user) + { + echo $user->email; + } + +Instead of returning an array, the **first** method will return a single object: + + $user = DB::table('users')->first(); + + echo $user->email; + +It's easy to limit the columns returned by your query. Simply pass an array of columns you want into the **get** or **first** method: + + $user = DB::table('users')->get(array('id', 'email as user_email')); + +Need to get distinct records from the database? It's easy. Call the **distinct** method before retrieving your records: + + $user = DB::table('users')->distinct()->get(); + +> **Note:** If no results are found, the **first** method will return NULL. The **get** method will return an empty array. + + +### Building Where Clauses + +#### where and or\_where + +Building WHERE clauses in Laravel is painless. There are a variety of methods to assist you. The most basic of these methods are the **where** and **or_where** methods. Here is how to use them: + + return DB::table('users') + ->where('id', '=', 1) + ->or_where('email', '=', 'example@gmail.com') + ->first(); + +Of course, you are not limited to simply checking equality. You may also use **greater-than**, **less-than**, **not-equal**, and **like**: + + return DB::table('users') + ->where('id', '>', 1) + ->or_where('name', 'LIKE', '%Taylor%') + ->first(); + +You may have assumed that the **where** method will add to the query using an AND condition, while the **or_where** method will use an OR condition. You assumed correctly. + +#### where\_in, where\_not\_in, or\_where\_in, and or\_where\_not\_in + +The suite of **where_in** methods allows you to easily construct queries that search an array of values: + + DB::table('users')->where_in('id', array(1, 2, 3))->get(); + + DB::table('users')->where_not_in('id', array(1, 2, 3))->get(); + + DB::table('users') + ->where('email', '=', 'example@gmail.com') + ->or_where_in('id', array(1, 2, 3)) + ->get(); + + DB::table('users') + ->where('email', '=', 'example@gmail.com') + ->or_where_not_in('id', array(1, 2, 3)) + ->get(); + +#### where\_null, where\_not\_null, or\_where\_null, and or\_where\_not\_null + +The suite of **where_null** methods makes checking for NULL values a piece of cake: + + return DB::table('users')->where_null('updated_at')->get(); + + return DB::table('users')->where_not_null('updated_at')->get(); + + return DB::table('users') + ->where('email', '=', 'example@gmail.com') + ->or_where_null('updated_at') + ->get(); + + return DB::table('users') + ->where('email', '=', 'example@gmail.com') + ->or_where_not_null('updated_at') + ->get(); + + +### Dynamic Where Clauses + +Ready for some really beautiful syntax? Check out **dynamic where methods**: + + $user = DB::table('users')->where_email('example@gmail.com')->first(); + + $user = DB::table('users')->where_email_and_password('example@gmail.com', 'secret'); + + $user = DB::table('users')->where_id_or_name(1, 'Fred'); + +Aren't they a breathe of fresh air? + + +### Table Joins + +Need to join to another table? Try the **join** and **left\_join** methods: + + DB::table('users') + ->join('phone', 'users.id', '=', 'phone.user_id') + ->get(array('users.email', 'phone.number')); + +The **table** you wish to join is passed as the first parameter. The remaining three parameters are used to construct the **ON** clause of the join. + +Once you know how to use the join method, you know how to **left_join**. The method signatures are the same: + + DB::table('users') + ->left_join('phone', 'users.id', '=', 'phone.user_id') + ->get(array('users.email', 'phone.number')); + + +### Ordering Results + +You can easily order the results of your query using the **order_by** method. Simply mention the column and direction (desc or asc) of the sort: + + return DB::table('users')->order_by('email', 'desc')->get(); + +Of course, you may sort on as many columns as you wish: + + return DB::table('users') + ->order_by('email', 'desc') + ->order_by('name', 'asc') + ->get(); + + +### Skip & Take + +If you would like to **LIMIT** the number of results returned by your query, you can use the **take** method: + + return DB::table('users')->take(10)->get(); + +To set the **OFFSET** of your query, use the **skip** method: + + return DB::table('users')->skip(10)->get(); + + +### Aggregates + +Need to get a **MIN**, **MAX**, **AVG**, **SUM**, or **COUNT** value? Just pass the column to the query: + + $min = DB::table('users')->min('age'); + + $max = DB::table('users')->max('weight'); + + $avg = DB::table('users')->avg('salary'); + + $sum = DB::table('users')->sum('votes'); + + $count = DB::table('users')->count(); + +Of course, you may wish to limit the query using a WHERE clause first: + + $count = DB::table('users')->where('id', '>', 10)->count(); + + +### Inserting Records + +Inserting records is amazingly easy using the **insert** method. The method only expects an array of values to insert. It couldn't be simpler. The insert method will simply return true or false, indicating whether the query was successful: + + DB::table('users')->insert(array('email' => 'example@gmail.com')); + +Inserting a record that has an auto-incrementing ID? You can use the **insert\_get\_id** method to insert a record and retrieve the ID: + + $id = DB::table('users')->insert_get_id(array('email' => 'example@gmail.com')); + +> **Note:** The **insert\_get\_id** method expects the name of the auto-incrementing column to be "id". + + +### Updating Records + +Updating records is just as simple as inserting them. Simply pass an array of values to the **update** method: + + $affected = DB::table('users')->update(array('email' => 'new_email@gmail.com')); + +Of course, when you only want to update a few records, you should add a WHERE clause before calling the update method: + + $affected = DB::table('users') + ->where('id', '=', 1) + ->update(array('email' => 'new_email@gmail.com')); + + +### Deleting Records + +When you want to delete records from your database, simply call the **delete** method: + + $affected = DB::table('users')->where('id', '=', 1)->delete(); + +Want to quickly delete a record by its ID? No problem. Just pass the ID into the delete method: + + $affected = DB::table('users')->delete(1); \ No newline at end of file diff --git a/documentation/database/usage.md b/documentation/database/usage.md new file mode 100644 index 00000000..1308153e --- /dev/null +++ b/documentation/database/usage.md @@ -0,0 +1,33 @@ +## Database Usage + +### Queries + +Running queries against a database connection is a breeze using the **query** method on the DB class: + + $users = DB::query('select * from users'); + +The **query** method also allows you to specify bindings for your query in the second parameter to the method: + + $users = DB::query('select * from users where name = ?', array('test')); + +The return value of the query method depends on the type of query that is executed: + +- **SELECT** statements will return an array of stdClass objects with properties corresponding to each column on the table. +- **INSERT** statements will return **true** or **false**, depending on the success of the query. +- **UPDATE** and **DELETE** statements will return the number of rows affected by the query. + +### Connections + +Need to get the raw PDO object for a connection? It's easy. Just mention the connection name to the **connection** method on the DB class: + + $pdo = DB::connection('sqlite'); + +> **Note:** If no connection name is specified, the **default** connection will be returned. + +### Driver + +Want to know which PDO driver is being used for a connection? Check out the **driver** method: + + $driver = DB::driver('connection_name'); + +> **Note:** If no connection name is specified, the **default** connection driver will be returned. \ No newline at end of file diff --git a/documentation/other/benchmark.md b/documentation/other/benchmark.md new file mode 100644 index 00000000..72a83433 --- /dev/null +++ b/documentation/other/benchmark.md @@ -0,0 +1,30 @@ +## Benchmarking Code + +- [The Basics](#basics) +- [Using Timers](#timers) +- [Checking Memory Usage](#memory) + + +### The Basics + +When making changes to your code, it's helpful to know the performance impact of your changes. Laravel provides a simple class to help you time code execution and check memory consumption. It's called the **Benchmark** class and it's a breeze to use. + + +### Using Timers + +To start a timer, simply call the **start** method on the Benchmark class and give your timer a name: + + Benchmark::start('foo'); + +Pretty easy, right? + +You can easily check how much time has elapsed (in milliseconds) using the **check** method. Again, just mention the name of the timer to the method: + + echo Benchmark::check('foo'); + + +### Checking Memory Usage + +Need to know how much memory is being used by your application? No problem. Just call the **memory** method to get your current memory usage in megabytes: + + echo Benchmark::memory(); \ No newline at end of file diff --git a/documentation/other/crypt.md b/documentation/other/crypt.md new file mode 100644 index 00000000..9c1e6471 --- /dev/null +++ b/documentation/other/crypt.md @@ -0,0 +1,36 @@ +## Encryption + +- [The Basics](#basics) +- [Encrypting A String](#encrypt) +- [Decrypting A String](#decrypt) + + +### The Basics + +Need to do secure, two-way encryption? Laravel has you covered with the **Crypt** class. The Crypt class provides strong AES-256 encryption and decryption out of the box via the Mcrypt PHP extension. + +To get started, you must set your **application key** in the **application/config/application.php** file. This key should be very random and very secret, as it will be used during the encryption and decryption process. It is best to use a random, 32 character alpha-numeric string: + + 'key' => 'xXSAVghP7myRo5xqJAnMvQwBc7j8qBZI'; + +Wonderful. You're ready to start encrypting. + +> **Note:** Don't forget to install the Mcrypt PHP extension on your server. + + +### Encrypting A String + +Encrypting a string is a breeze. Just pass it to the **encrypt** method on the Crypt class: + + Crypt::encrypt($value); + +Do you feel like James Bond yet? + + +### Decrypting A String + +So you're ready to decrypt a string? It's simple. Just use the **decrypt** method on the Crypt class: + + Crypt::decrypt($encrypted_value); + +> **Note:** The decrypt method will only decrypt strings that were encrypted using **your** application key. \ No newline at end of file diff --git a/documentation/other/file.md b/documentation/other/file.md new file mode 100644 index 00000000..ee0e0ee6 --- /dev/null +++ b/documentation/other/file.md @@ -0,0 +1,69 @@ +## Working With Files + +- [Reading Files](#get) +- [Writing Files](#put) +- [File Uploads](#upload) +- [File Extensions](#ext) +- [Checking File Types](#is) +- [Getting MIME Types](#mime) + + +### Reading Files + +It's a breeze to get the contents of a file using the **get** method on the **File** class: + + $contents = File::get('path/to/file'); + + +### Writing Files + +Need to write to a file? Check out the **put** method: + + File::put('path/to/file', 'file contents'); + +Want to append to the file instead of overwriting the existing contents? No problem. Use the **append** method: + + File::append('path/to/file', 'appended file content'); + + +### File Uploads + +After a file has been uploaded to your application, you will want to move it from its temporary location to a permanent directory. You can do so using the **upload** method. Simply mention the **name** of the uploaded file and the path where you wish to store it: + + File::upload('picture', 'path/to/pictures'); + +> **Note:** You can easily validate file uploads using the [Validator class](/docs/start/validation). + + +### File Extensions + +Need to get the extension of a file? Just pass the filename to the **extension** method: + + File::extension('picture.png'); + + +### Checking File Types + +Often, it is important to know the type of a file. For instance, if a file is uploaded to your application, you may wish to verify that it is an image. It's easy using the **is** method on the **File** class. Simply pass the extension of the file type you are expecting. Here's how to verify that a file is a JPG image: + + if (File::is('jpg', 'path/to/file.jpg')) + { + // + } + +The **is** method does not simply check the file extension. The Fileinfo PHP extension will be used to read the content of the file and determine the actual MIME type. Pretty cool, huh? + +> **Note:** You may pass any of the extensions defined in the **application/config/mimes.php** file to the **is** method. + + +### Getting MIME Types + +Need to know the MIME type associated with a file extension? Check out the **mime** method: + + echo File::mime('gif'); + +The statement above returns the following string: + + image/gif + +> **Note:** This method simply returns the MIME type defined for the extension in the **application/config/mimes.php** file. \ No newline at end of file diff --git a/documentation/other/lang.md b/documentation/other/lang.md new file mode 100644 index 00000000..9fc5c066 --- /dev/null +++ b/documentation/other/lang.md @@ -0,0 +1,62 @@ +## Localization + +- [The Basics](#basics) +- [Retrieving A Language Line](#get) +- [Place Holders & Replacements](#replace) + + +### The Basics + +Localization is the process of translating your application into different languages. The **Lang** class provides a simple mechanism to help you organize and retrieve the text of your multilingual application. + +All of the language files for your application live under the **application/lang** directory. Within the **application/lang** directory, you should create a directory for each language your application speaks. So, for example, if your application speaks English and Spanish, you might create **en** and **sp** directories under the **lang** directory. + +Each language directory may contain many different language files. Each language file is simply an array of string values in that language. In fact, language files are structured identically to configuration files. For example, within the **application/lang/en** directory, you could create a **marketing.php** file that looks like this: + + return array( + + 'welcome' => 'Welcome to our website!', + + ); + +Next, you should create a corresponding **marketing.php** file within the **application/lang/sp** directory. The file would look something like this: + + return array( + + 'welcome' => 'Bienvenido a nuestro sitio web!', + + ); + +Nice! Now you know how to get started setting up your language files and directories. Let's keep localizing! + + +### Retrieving A Language Line + +To retrieve a language line, first create a Lang instance using the **line** method, then call the **get** method on the instance: + + echo Lang::line('marketing.welcome')->get(); + +Notice how a dot was used to separate "marketing" and "welcome"? The text before the dot corresponds to the language file, while the text after the dot corresponds to a specific string within that file. + +But, how did the method know which language directory to retrieve the message from? By default, the **get** method will use the language specified in your **application/config/application.php** configuration file. In this file you may set the default language of your application using the **language** option: + + 'language' => 'en' + +Need to retrieve the line in a language other than your default? Not a problem. Just mention the language to the **get** method: + + echo Lang::line('marketing.welcome')->get('sp'); + + +### Place Holders & Replacements + +Now, let's work on our welcome message. "Welcome to our website!" is a pretty generic message. It would be helpful to be able to specify the name of the person we are welcoming. But, creating a language line for each user of our application would be time-consuming and ridiculous. Thankfully, you don't have to. You can specify "place-holders" within your language lines. Place-holders are preceeded by a colon: + + 'welcome' => 'Welcome to our website, :name!' + +Then, simply pass an array of place-holder replacements to the **replace** method on a Lang instance: + + echo Lang::line('marketing.welcome')->replace(array('name' => 'Taylor'))->get(); + +This statement will return a nice, heart-warming welcome message: + + Welcome to our website, Taylor! \ No newline at end of file diff --git a/documentation/session/config.md b/documentation/session/config.md new file mode 100644 index 00000000..1904d40a --- /dev/null +++ b/documentation/session/config.md @@ -0,0 +1,70 @@ + +## Session Configuration + +- [File System Sessions](#file) +- [Database Sessions](#database) +- [Memcached Sessions](#memcached) + +The web is a stateless environment. This means that each request to your application is considered unrelated to any previous request. However, **sessions** allow you to store arbitrary data for each visitor to your application. The session data for each visitor is stored on your web server, while a cookie containing a **session ID** is stored on the visitor's machine. This cookie allows your application to "remember" the session for that user and retrieve their session data on subsequent requests to your application. + +Sound complicated? If so, don't worry about it. Just tell Laravel where to store the sessions and it will take care of the rest. + +Three great session drivers are available out of the box: + +- File System +- Database +- Memcached + + +### File System Sessions + +Most likely, your application will work great using file system sessions. However, if your application receives heavy traffic or runs on a server farm, use database or Memcached sessions. + +To get started using file system sessions, just set the driver option in the **application/config/session.php** file: + + 'driver' => 'file' + +That's it. You're ready to go! + +> **Note:** File system sessions are stored in the **application/storage/sessions** directory, so make sure it's writeable. + + +### Database Sessions + +To start using database sessions, you will first need to [configure your database connection](/docs/database/config). + +Already setup your database? Nice! Next, you will need to create a session table. Here are some SQL statements to help you get started: + +#### SQLite + + CREATE TABLE "sessions" ( + "id" VARCHAR PRIMARY KEY NOT NULL UNIQUE, + "last_activity" INTEGER NOT NULL, + "data" TEXT NOT NULL + ); + +#### MySQL + + CREATE TABLE `sessions` ( + `id` VARCHAR(40) NOT NULL, + `last_activity` INT(10) NOT NULL, + `data` TEXT NOT NULL, + PRIMARY KEY (`id`) + ); + +If you would like to use a different table name, simply change the **table** option in the **application/config/session.php** file: + + 'table' => 'sessions' + +Great! All you need to do now is set the driver in the **application/config/session.php** file: + + 'driver' => 'db' + + +### Memcached Sessions + +Before using Memcached sessions, you must [configure your Memcached servers](/docs/cache/config#memcached). + +All done? Great! Just set the driver in the **application/config/session.php** file: + + 'driver' => 'memcached' \ No newline at end of file diff --git a/documentation/session/usage.md b/documentation/session/usage.md new file mode 100644 index 00000000..5e76a7dd --- /dev/null +++ b/documentation/session/usage.md @@ -0,0 +1,59 @@ +## Session Usage + +- [Storing Items](#put) +- [Retrieving Items](#get) +- [Removing Items](#forget) +- [Regeneration](#regeneration) + + +### Storing Items + +Storing items in the session is a breeze. Simply call the put method on the Session class: + + Session::put('name', 'Taylor'); + +The first parameter is the **key** to the session item. You will use this key to retrieve the item from the session. The second parameter is the **value** of the item. + +Need to store an item in the session that should expire after the next request? Check out the **flash** method. It provides an easy way to store temporary data like status or error messages: + + Session::flash('status', 'Welcome Back!'); + + +### Retrieving Items + +Retrieving items from the session is no problem. You can use the **get** method on the Session class to retrieve any item in the session, including flash data. Just pass the key of the item you wish to retrieve: + + $name = Session::get('name'); + +By default, NULL will be returned if the session item does not exist. However, you may pass a default value as a second parameter to the get method: + + $name = Session::get('name', 'Fred'); + + $name = Session::get('name', function() {return 'Fred';}); + +Now, "Fred" will be returned if the "name" item does not exist in the session. + +Laravel even provides a simple way to determine if a session item exists using the **has** method: + + if (Session::has('name')) + { + $name = Session::get('name'); + } + + +### Removing Items + +Need to get rid of a session item? No problem. Just mention the name of the item to the **forget** method on the Session class: + + Session::forget('name'); + +You can even remove all of the items from the session using the **flush** method: + + Session::flush(); + + +### Regeneration + +Sometimes you may want to "regenerate" the session ID. This simply means that a new, random session ID will be assigned to the session. Here's how to do it: + + Session::regenerate(); \ No newline at end of file diff --git a/documentation/start/config.md b/documentation/start/config.md new file mode 100644 index 00000000..0051cfa8 --- /dev/null +++ b/documentation/start/config.md @@ -0,0 +1,78 @@ +## Basic Configuration + +- [Quick Start](#quick) +- [Cleaner URLs](#clean) +- [Errors & Logging](#errors) + + +### Quick Start + +When starting a new project, you shouldn't be bombarded with loads of confusing configuration decisions. For that reason, Laravel is intelligently configured out of the box. The **application/config/application.php** file contains the basic configuration options for your application. + +There is only one option that **must** be set when starting a new application. Laravel needs to know the URL you will use to access your application. Simply set the url in the **application/config/application.php** file: + + 'url' => 'http://localhost'; + +> **Note:** If you are using mod_rewrite, you should set the index option to an empty string. + + +### Cleaner URLs + +Most likely, you do not want your application URLs to contain "index.php". You can remove it using HTTP rewrite rules. If you are using Apache to serve your application, make sure to enable mod_rewrite and create a **.htaccess** file like this one in your **public** directory: + + + RewriteEngine on + + RewriteCond %{REQUEST_FILENAME} !-f + RewriteCond %{REQUEST_FILENAME} !-d + + RewriteRule ^(.*)$ index.php/$1 [L] + + +Is the .htaccess file above not working for you? Try this one: + + Options +FollowSymLinks + RewriteEngine on + + RewriteCond %{REQUEST_FILENAME} !-f + RewriteCond %{REQUEST_FILENAME} !-d + + RewriteRule . index.php [L] + +After setting up HTTP rewriting, you should set the **index** configuration option in **application/config/application.php** to an empty string. + +> **Note:** Each web server has a different method of doing HTTP rewrites, and may require a slightly different .htaccess file. + + +### Errors & Logging + +- [404 Errors](#error-404) +- [Error Detail](#error-detail) +- [Logging](#error-logging) + + +#### 404 Errors + +When a request is made to your application that cannot be matched to a route, the 404 error view will be sent to the browser. This view lives in **application/views/error/404.php** and you are free to modify it however you wish. + + +#### Error Detail + +You can easily control the level of error detail via the **detail** option in the **application/config/errors.php** file. + + 'detail' => true; + +When set to **true**, error messages will be detailed with a stack trace and snippet of the relevant file. When set to **false**, the generic error page (**application/views/error/500.php**) will be displayed. Feel free to modify this view. + +> **Note:** In a production environment, it is strongly suggested that you turn off error details. + + +#### Logging + +You may wish to log any errors that occur in your application. Laravel makes it a breeze. You can turn on logging by setting the log option to **true** in the **application/config/errors.php** file: + + 'log' => true; + +You have total control over how your errors are logged via the **logger** function defined in **application/config/error.php**. This function is called every time there is an unhandled error or exception in your application. + +As you can see, the default logger implementation writes to the **application/storage/log.txt** file; however, you are free to modify this function however you wish. \ No newline at end of file diff --git a/documentation/start/install.md b/documentation/start/install.md new file mode 100644 index 00000000..30fb6fd4 --- /dev/null +++ b/documentation/start/install.md @@ -0,0 +1,27 @@ +## Requirements & Installation + +### Requirements + +- Apache, nginx, or another compatible web server. +- PHP 5.3+. + +### Installation + +1. [Download Laravel](https://github.com/taylorotwell/laravel/zipball/master) +2. Extract the Laravel archive and upload the contents to your web server. +4. Set the URL of your application in the **application/config/application.php** file. +5. Navigate to your application in a web browser. + +If all is well, you should see a pretty Laravel splash page. Get ready, there is lots more to learn! + +### Extras + +Installing the following goodies will help you take full advantage of Laravel, but they are not required: + +- SQLite, MySQL, or PostgreSQL PDO drivers. +- [Memcached](http://memcached.org) or APC. + +### Problems? + +- Make sure the **public** directory is the document root of your web server. +- If you are using mod_rewrite, set the **index** option in **application/config/application.php** to an empty string. \ No newline at end of file diff --git a/documentation/start/interaction.md b/documentation/start/interaction.md new file mode 100644 index 00000000..14312823 --- /dev/null +++ b/documentation/start/interaction.md @@ -0,0 +1,232 @@ +## Interaction + +- [Input](/docs/start/interaction#basics) +- [Old Input](/docs/start/interaction#old) +- [Cookies](/docs/start/interaction#cookies) +- [Building Forms](/docs/start/interaction#forms) + +All web applications receive input via HTTP requests. The input can be sent to your application via any of the four HTTP verbs: **GET**, **POST**, **PUT**, or **DELETE**. Input can also be sent to your application via cookies, which can store small amounts of information and are stored on the user's computer. + +Let's dig into the classes Laravel provides for working with user input! + +> **Note:** Laravel doesn't mess with your query strings. Feel free to use them. + + +## Input + +The **Input** class handles input that comes into your application via GET, POST, PUT, or DELETE requests. Retrieving input using the Input class is effortless. Just use the **get** method: + + $email = Input::get('email'); + +> **Note:** The get method is used for all request types, not just GET requests. You may use it on POST, PUT, and DELETE requests as well. + +By default, NULL will be returned if the input item does not exist. However, you may pass a different default value as a second parameter to the method: + + $name = Input::get('name', 'Fred'); + +Now, "Fred" will be returned if the "name" input item does not exist. You may even pass a closure as a default value: + + $name = Input::get('name', function() {return 'Fred';}); + +Need to determine if an input item exists? Use the **has** method: + + if (Input::has('name')) + { + $name = Input::get('name'); + } + +> **Note:** The **has** method will return **false** if the input item exists but is an empty string. + +Need to access the **$_FILES** array? It's easy using the **file** method: + + $picture = Input::file('picture'); + + $size = Input::file('picture.size'); + +Sometimes you may need to merge the input and $_FILES array. Check out the **all** method: + + $input = Input::all(); + + +## Old Input + +Have you ever tried to re-populate an input form after an invalid form submission? It can get pretty clunky. Not in Laravel. You can easily retrieve the input from the previous request using the **old** method on the Input class: + + $name = Input::old('name'); + +> **Note:** You must specifiy a session driver before using the **old** Input method. + +As you would expect, you may pass a default value in the second parameter to the method: + + $name = Input::old('name', 'Fred'); + +Once again, there is a simple way to determine if an old input item exists using the **had** method: + + if (Input::had('name')) + { + $name = Input::old('name'); + } + + +## Cookies + +The **Cookie** class provides simple functions for retrieving, setting, and deleting cookies. + +To retrieve a cookie value, simply mention its name to the **get** method: + + $name = Cookie::get('name'); + +Of course, just like the Input class, you may pass a default value in the second parameter to the **get** method: + + $name = Cookie::get('name', 'Fred'); + +Also just like the Input class, the Cookie class has a simple method to determine if a cookie exists: + + if (Cookie::has('name')) + { + $name = Cookie::get('name'); + } + +Need to create a cookie? No problem. Check out the **put** method: + + Cookie::put('name', 'Fred', 60); + +The put method accepts almost the exact same parameters as the PHP setcookie method. However, just pass the number of **minutes** you want the cookie to live as the third parameter. You don't have to worry about any clunky expiration date calculations. + +If you need to create a "permanent" cookie, try the **forever** method. It creates a cookie that lives for five years: + + Cookie::forever('name', 'Fred'); + +To delete a cookie, use the **forget** method: + + Cookie::forget('name'); + + +## Building Forms + +- [Opening A Form](#form-open) +- [CSRF Protection](#form-csrf) +- [Labels](#form-labels) +- [Text, Text Area, Password & Hidden Fields](#form-text) +- [Checkboxes & Radio Buttons](#form-check) +- [Drop-Down Lists](#form-lists) +- [Buttons](#form-buttons) + +Almost every web application receives input through HTML forms. As you have probably already learned, Laravel is here to make your life easier. That's why generating forms using the **Form** class is a breeze. + +> **Note:** All input data displayed in elements generated by the **Form** class is filtered through the HTML::entities method. + + +### Opening A Form + +Opening a form is simple. Just call the **open** method on the Form class: + + echo Form::open(); + +When called without any parameters, the open method will create a form that will POST to the current URL. However, you'll probably want to point your forms to other URLs too. No problem. Just mention the URL to the method. You can even specify the request method (GET, POST, PUT, or DELETE) in the second parameter to the method: + + echo Form::open('user/profile', 'PUT'); + +Need to apply a class or other attribute to the form tag generated by the open method? Simply pass an array of attributes as a third parameter: + + echo Form::open('user/profile', 'PUT', array('class' => 'awesome')); + +> **Note:** The open method automatically prepares your form to receive UTF-8 input. + +Need a form that can handle file uploads? Use the **open_for_files** method: + + echo Form::open_for_files('user/profile'); + + +### CSRF Protection + +Laravel provides an easy method of protecting your application from [cross-site request forgeries](http://en.wikipedia.org/wiki/Cross-site_request_forgery). First, a random token is placed in your user's session. Don't sweat it, this is done automatically. Next, use the **token** method to generate a hidden form input field containing the random token on your form: + + echo Form::token(); + +Now, simply [attach the built-in CSRF filter](/docs/start/routes#filters) to the route the form is posting to. If the token submitted by the form does not match the token in the user's session, the **application/views/error/500.php** view will be displayed. + +Want to just get the CSRF token without generating a hidden input field? Use the **raw_token** method: + + echo Form::raw_token(); + +> **Note:** Don't forget to [specify a session driver](/docs/session/config) before using these methods. + + +### Labels + +Need to generate a label for a form element? It's simple using the **label** method. Just pass the label name and display value to the method: + + echo Form::label('email', 'E-Mail Address'); + +Of course, you may pass any attributes you wish in the third parameter to the method: + + echo Form::label('email', 'E-Mail Address', array('class' => 'awesome')); + +> **Note:** After creating a label, any form element you create with a name matching the label name will automatically receive an ID matching the label name as well. + + +### Text, Text Area, Password & Hidden Fields + +Generating text boxes couldn't be easier. Just call the **text** method on the Form class and mention the name of the field: + + echo Form::text('username'); + +Already have a value you want to put in the text box? Throw it in as a second parameter: + + echo Form::text('email', 'example@gmail.com'); + +Again, any other attributes you wish to apply to the text box may be passed in an array as the third parameter: + + echo Form::text('email', 'example@gmail.com', array('class' => 'awesome')); + +> **Note:** The **password**, **hidden**, and **textarea** methods have the same signature as the text method. You just learned four methods for the price of one! + + +### Checkboxes & Radio Buttons + +What website doesn't have a checkbox? Actually, this one doesn't! But, thankfully, generating them is simple using the **checkbox** method on the Form class. Just give the checkbox a name and a value: + + echo Form::checkbox('remember', 'yes'); + +Of course, the example above will generate the following HTML: + + + +Need to generate a "checked" checkbox? No problem. Simply pass **true** as the third parameter to the method: + + echo Form::checkbox('remember', 'yes', true); + +As always, you may specify any extra attributes that should be applied to the checkbox. Pass them as the fourth parameter to the method: + + echo Form::checkbox('remember', 'yes', true, array('class' => 'awesome')); + +> **Note:** The **radio** method has the same signature as the checkbox method. Two for one! + + +### Drop-Down Lists + +Generating drop-down lists can be a headache. Thankfully, Laravel makes it refreshingly simple using the **select** method on the Form class. All you need to do is give your list a name and an array of options: + + echo Form::select('size', array('L' => 'Large', 'S' => 'Small')); + +If you wish to set the selected item, just pass the value as the third parameter to the method: + + echo Form::select('size', array('L' => 'Large', 'S' => 'Small'), 'S'); + +You may specify any other attributes that should be applied to the list in the fourth parameter to the method: + + echo Form::select('size', array('L' => 'Large', 'S' => 'Small'), 'S', array('class' => 'awesome')); + + +### Buttons + +Creating a submit button is a cinch. Use the **submit** method on the Form class: + + echo Form::submit('Click Me!'); + +Again, any other attributes that should be applied to the button may be passed to the method: + + echo Form::submit('Click Me!', array('class' => 'awesome')); + +> **Note:** Need to create a button element? Try the **button** method. It has the same signature as submit. \ No newline at end of file diff --git a/documentation/start/routes.md b/documentation/start/routes.md new file mode 100644 index 00000000..6bcdcea7 --- /dev/null +++ b/documentation/start/routes.md @@ -0,0 +1,124 @@ +## Routes + +- [Defining Routes](/docs/start/routes#define) +- [Wildcard URI Segments](/docs/start/routes#segments) +- [Named Routes](/docs/start/routes#named) +- [Route Filters](/docs/start/routes#filters) +- [Organizing Routes](/docs/start/routes#organize) + +Unlike other PHP frameworks, Laravel places routes and their corresponding functions in one file: **application/routes.php**. This file contains the "definition", or public API, of your application. To add functionality to your application, you add to the array located in this file. It's a breeze. + + +## Defining Routes + +All you need to do is tell Laravel the request methods and URIs it should respond to. You define the behavior of the route using an anonymous method: + + 'GET /home' => function() + { + // Handles GET requests to http://example.com/index.php/home + }, + + +You can easily define a route to handle requests to more than one URI. Just use commas: + + 'POST /, POST /home' => function() + { + // Handles POST requests to http://example.com and http://example.com/home + } + +> **Note:** The routes.php file replaces the "controllers" found in most frameworks. Have a fat model and keep this file light and clean. Thank us later. + + +## Wildcard URI Segments + +Laravel makes matching wildcard URI segments a breeze using the **(:num)** and **(:any)** place-holders. Check out these routes: + + 'PUT /user/(:num)' => function($id) {} + + 'DELETE /user/(:any)' => function($username) {} + +Laravel will automatically pass the value of the wildcard segment into your route function. + +> **Note:** The **(:any)** place-holder matches letters, number, dashes, and underscores. + +Want to make an URI segment optional? No problem. Just put a **?** in the place-holder: + + 'GET /download/(:any?)' => function($branch = 'master') {} + +If you need more power and precision (or just want to be extra nerdy), you can even use regular expressions: + + 'GET /product/([0-9]+)' => function($id) {} + + +## Named Routes + +Once you start using named routes, you won't be able to live without them. They are that great. Here's how to do it: + + 'GET /user/login' => array('name' => 'login', 'do' => function() {}) + +Notice the route now has an array value with two keys: **name** and **do**. As you learned while studying filters, the **do** value is the method that will be executed by the route. As you have probably guessed, the **name** value is the name of the route. + +Now that you have named the route, you can [generate URLs](/docs/start/views#urls) and [perform redirects](/docs/start/views#redirect) using the route name instead of the route URI. This means that you can change the route URI as much as you want and the links to that route on your views will always be correct. It's beautiful, isn't it? + + +## Route Filters + +Filters are methods that run before and after a request to your application. "Before" filters can even halt the request cycle by returning a response, providing an amazingly simple way to implement common tasks like redirecting a user to a login view. Let's dig in. + +All filters are defined in the **application/filters.php** file. Intuitive, right? If you open the file, you will see that four filters have already been defined for you: **before**, **after**, **auth**, and **csrf**. The **before** and **after** filters are the two "global" filters. They are always executed on every request, regardless of the request method or URI. + +All other filters must be attached to individual routes. Don't worry, you'll learn how to do this soon. The built-in **auth** and **csrf** filters handle two scenarios that are common to almost every web application: redirecting users to a login page and protecting against cross-site request forgeries. + +### Defining Filters + +To define your own filter, simply add it to the array in the **application/filters.php** file: + + 'my_filter' => function() + { + return 'Filtered!'; + } + +### Attaching Filters To Routes + +Alright, ready to attach the filter to a route? Do it like this: + + 'GET /user' => array('before' => 'my_filter', 'do' => function() + { + // + }) + +Notice the route now has an array value with two keys: **before** and **do**. The **do** value is the method that will be executed by the route, while the **before** value contains the names of any filters that should be run before the method is executed. + +Why stop with one filter? You can define multiple filters for a single route by separating the filter names with commas: + + 'POST /user' => array('before' => 'auth, csrf', 'do' => function() {}) + +Remember, if a "before" filter returns a value, that value will be considered the output of the request. For example, the built-in **auth** filter checks if the user has logged in to your application. If they haven't, a [Redirect](/docs/start/views#redirect) to the login page is sent to the browser. Isn't the simplicity refreshing? + +Of course, adding filters to run after the request is just as easy: + + 'my_filter' => function($response) {} + + 'GET /user' => array('after' => 'my_filter', 'do' => function() {}) + +> **Note:** "After" filters receive the response returned by the route function that handled the request. + + +## Organizing Routes + +So, you're building the next monolithic web application and your **application/routes.php** file is getting a little cramped? Don't worry, we have you covered. + +Here's what to do. First, create an **application/routes** directory. Great! You're almost there. Now, just add route files to **application/routes** corresponding to the base URIs of your application. So, a **photo.php** file within **application/routes** would handle all requests to URIs beginning with **/photo**. Similarly, a **user.php** file handles all requests to URIs beginning with **/user**. For example, check out this **user.php** file: + + function($id) + { + return View::make('user/profile'); + } + + ); + +The **application/routes.php** file will continue to be loaded on every request, so any "catch-all" routes can still be placed in that file. The **application/routes.php** file should also still contain the route for the root of your application. \ No newline at end of file diff --git a/documentation/start/validation.md b/documentation/start/validation.md new file mode 100644 index 00000000..17efcd10 --- /dev/null +++ b/documentation/start/validation.md @@ -0,0 +1,368 @@ +## Validation + +- [The Basics](#basics) +- [Validation Rules](#rules) +- [Retrieving Error Messages](#errors) +- [Specifying Custom Error Messages](#messages) +- [Creating Custom Validation Rules](#custom) + + +### The Basics + +Almost every interactive web application needs to validate data. For instance, a registration form probably requires the password to be confirmed. Maybe the e-mail address must be unique. Validating data can be a cumbersome process. Thankfully, it isn't in Laravel. The **Validator** class provides as awesome array of validation helpers to make validating your data a breeze. + +To get started, let's imagine we have the following array: + + $array = array('name' => 'Taylor', 'email' => 'example@gmail.com'); + +Next, we're ready to define [validation rules](#rules) for our array: + + $rules = array( + 'name' => array('required', 'max:50'), + 'email' => array('required', 'email', 'unique:users'), + ); + +If you don't like using arrays, you may also delimit rules using a pipe character: + + $rules = array( + 'name' => 'required|max:50', + 'email' => 'required|email|unique:users', + ); + +Great! Now we're ready to make a **Validator** instance and validate our array: + + $validator = Validator::make($array, $rules); + + if ( ! $validator->valid()) + { + return $validator->errors; + } + +Via the **errors** property, you can access a simple error collector class that makes working with your error messages a breeze. Of course, default error messages have been setup for all validation rules. The default messages live in the **application/lang/en/validation.php** file. + +Now you are familiar with the basic usage of the Validator class. You're ready to dig in and learn about the rules you can use to validate your data! + + +### Validation Rules + +- [Required](#rule-required) +- [Alpha, Alpha Numeric, & Alpha Dash](#rule-alphas) +- [Size](#rule-size) +- [Numericality](#rule-numeric) +- [Inclusion & Exclusion](#rule-inclusion) +- [Confirmation](#rule-confirmed) +- [Acceptance](#rule-accepted) +- [Uniqueness](#rule-unique) +- [E-Mail Addresses](#rule-email) +- [URLs](#rule-urls) +- [Uploads](#rule-uploads) + + +#### Required + +The **required** rule validates that an attribute is present in the array and is not an empty string: + + $rules = array( + 'name' => 'required', + ); + + +#### Alpha, Alpha Numeric, & Alpha Dash + +The **alpha** rule validates that an attribute consists solely of letters: + + $rules = array( + 'name' => 'alpha', + ); + +The **alpha_num** rule validates that an attribute consists solely of letters and numbers: + + $rules = array( + 'username' => 'alpha_num', + ); + +The **alpha_dash** rule validates that an attribute consists solely of letters, numbers, dashes, and underscores: + + $rules = array( + 'username' => 'alpha_dash', + ); + + +#### Size + +The **size** rule validates that an attribute is of a given length, or, if the attribute is numeric, is a given value: + + $rules = array( + 'name' => 'size:10', + ); + +The **between** rule validates that an attribute is between a given minimum and maximum: + + $rules = array( + 'payment' => 'between:10,50', + ); + +> **Note:** All minimum and maximum checks are inclusive. + +The **min** rule validates that an attribute is greater than or equal to a given value: + + $rules = array( + 'payment' => 'min:10', + ); + +The **max** rule validates that an attribute is less than or equal to a given value: + + $rules = array( + 'payment' => 'max:50', + ); + + +#### Numericality + +The **numeric** rule validates that an attribute is (surprise!) numeric: + + $rules = array( + 'payment' => 'numeric', + ); + +The **integer** rule validates that an attribute is an integer: + + $rules = array( + 'payment' => 'integer', + ); + + +#### Inclusion & Exclusion + +The **in** rule validates that an attribute is contained in a list of values: + + $rules = array( + 'size' => 'in:small,medium,large', + ); + +The **not_in** rule validates that an attribute is not contained in a list of values: + + $rules = array( + 'language' => 'not_in:cobol,assembler', + ); + + +#### Confirmation + +The **confirmed** rule validates that, for a given attribute, a matching **attribute_confirmation** attribute exists. For example, given the following rule: + + $rules = array( + 'password' => 'confirmed', + ); + +The Validator will make sure that the **password** attribute matches the **password_confirmation** attribute in the array being validated. + + +#### Acceptance + +The **accepted** rule validates that an attribute is equal to **yes** or **1**. This rule is helpful for validating checkbox form fields such as "terms of service". + + $rules = array( + 'terms' => 'accepted', + ); + + +#### Uniqueness + +The **unique** rule validates the uniqueness of an attribute on a given database table: + + $rules = array( + 'email' => 'unique:users', + ); + +In the example above, the **email** attribute will be checked for uniqueness on the **users** table. Need to verify uniqueness on a column name other than the attribute name? No problem: + + $rules = array( + 'email' => 'unique:users,email_address', + ); + + +#### E-Mail Addresses + +The **email** rule validates that an attribute contains a correctly formatted e-mail address: + + $rules = array( + 'email' => 'email', + ); + + +#### URLs + +The **url** rule validates that an attribute contains a correctly formatted URL: + + $rules = array( + 'link' => 'url', + ); + +The **active_url** rule uses the PHP **checkdnsrr** function to verify that a URL is active: + + $rules = array( + 'link' => 'active_url', + ); + + +#### Uploads + +The **mimes** rule validates that an uploaded file has a given MIME type. This rule uses the PHP Fileinfo extension to read the contents of the file and determine the actual MIME type. Any extension defined in the **application/config/mimes.php** file may be passed to this rule as a parameter: + + $rules = array( + 'picture' => 'mimes:jpg,gif', + ); + + $validator = Validator::make(Input::file(), $rules); + +Need to validate form data and upload data at the same time? Use the **all** method on the **Input** class to get form and upload data in one array: + + $validator = Validator::make(Input::all(), $rules); + +The **image** rule validates that an uploaded file has a **jpg**, **gif**, **bmp**, or **png** MIME type: + + $rules = array( + 'picture' => 'image', + ); + +You may also validate the size of an upload using the **max** rule. Simply specify the maximum number of **kilobytes** the upload may be: + + $rules = array( + 'picture' => 'image|max:100', + ); + + +### Retrieving Error Messages + +Laravel makes working with your error messages a cinch using a simple error collector class. After calling the **valid** or **invalid** method on a **Validator** instance, you may access the errors via the **errors** property: + + if ( ! $validator->valid()) + { + return $validator->errors; + } + +The error collector has the following simple functions for retrieving your error messages: **has**, **first**, **get**, and **all**. + +The **has** method will check if an error message exists for a given attribute: + + if ($validator->errors->has('email')) + { + // The e-mail attribute has errors... + } + +The **first** method will return the first error message for a given attribute: + + echo $validator->errors->first('email'); + +Sometimes you may need to format the error message by wrapping it in HTML. No problem. Along with the **:message** place-holder, pass the format as the second parameter to the method: + + echo $validator->errors->first('email', '

:message

'); + +The **get** method returns an array containing all of the error messages for a given attribute: + + return $validator->errors->get('email'); + + return $validator->errors->get('email', '

:message

'); + +The **all** method returns an array containing all error messages for all attributes: + + return $validator->errors->all(); + + return $validator->errors->all('

:message

'); + + +### Specifying Custom Error Messages + +Want to use an error message other than the default? Maybe you even want to use a custom error message for a given attribute and rule. Either way, the **Validator** class makes it easy. + +Simply create an array of custom messages to pass to the Validator instance: + + $messages = array( + 'required' => 'The :attribute field is required.', + ); + + $validator = Validator::make(Input::get(), $rules, $messages); + +Great! Now our custom message will be used anytime a **required** validation check fails. But, what is this **:attribute** stuff in our message? To make your life easier, the Validator class will replace the **:attribute** place-holder with the actual name of the attribute! It will even remove underscores from the attribute name. + +You may also use the **:size**, **:min**, **:max**, and **:values** place-holders when constructing your error messages: + + $messages = array( + 'size' => 'The :attribute must be exactly :size.', + 'between' => 'The :attribute must be between :min - :max.', + 'in' => 'The :attribute must be one of the following types: :values', + ); + +So, what if you need to specify a custom **required** message, but only for the **email** attribute? No problem. Just specify the message using an **attribute_rule** naming convention: + + $messages = array( + 'email_required' => 'We need to know your e-mail address!', + ); + +In the example above, the custom required message will be used for the **email** attribute, while the default message will be used for all other attributes. + + +### Creating Custom Validation Rules + +Need to create your own validation rules? You will love how easy it is! First, create a class that extends **System\Validator** and place it in your **application/libraries** directory: + + 'required|awesome', + ); + +Of course, you will need to define an error message for your new rule. You can do this either in an ad-hoc messages array: + + $messages = array( + 'awesome' => 'The attribute value must be awesome!', + ); + + $validator = Validator::make(Input::get(), $rules, $messages); + +Or by adding an entry for your rule in the **application/lang/en/validation.php** file: + + 'awesome' => 'The attribute value must be awesome!', + +As mentioned above, you may even specify and receive a list of parameters in your custom validator: + + // When building your rules array... + + $rules = array( + 'username' => 'required|awesome:yes', + ); + + // In your custom validator... + + class Validator extends System\Validator { + + public function validate_awesome($attribute, $parameters) + { + return $attribute = $parameters[0]; + } + + } + +In this case, the **parameters** argument of your validation rule would receive an array containing one element: "yes". \ No newline at end of file diff --git a/documentation/start/views.md b/documentation/start/views.md new file mode 100644 index 00000000..398423db --- /dev/null +++ b/documentation/start/views.md @@ -0,0 +1,379 @@ +## Views & Responses + +- [Creating Views](/docs/start/views#create) +- [Binding Data To Views](/docs/start/views#bind) +- [Nesting Views Within Views](/docs/start/views#nest) +- [Redirects](/docs/start/views#redirect) +- [Downloads](/docs/start/views#downloads) +- [Building URLs](/docs/start/views#urls) +- [Building HTML](/docs/start/views#html) + + +## Creating Views + +Typically, a route function returns a **View**. Views consist of plain ole' HTML and are stored in your **application/views** directory. + +A very simple view file could look like this: + + + Welcome to my website! + + +Assuming this view lives in **application/views/simple.php**, we could return it from a route like so: + + 'GET /home' => function() + { + return View::make('simple'); + } + +When building a large application, you may want to organize your views into sub-directories. That's a great idea! Assuming a view lives in **application/views/user/login.php**, we could return it like so: + + 'GET /home' => function() + { + return View::make('user/login'); + } + +It's that simple. Of course, you are not required to return a View. Strings are also welcome: + + 'GET /home' => function() + { + return json_encode('This is my response!'); + } + + +## Binding Data To Views + +You can pass data to a view by "binding" the data to a variable. This is done using the **bind** method on the View: + + 'GET /home' => function() + { + return View::make('simple')->bind('email', 'example@gmail.com'); + } + +In the example above, the first parameter is the **name** of the view variable. The second parameter is the **value** that will be assigned to the variable. + +Now, in our view, we can access the e-mail address like so: + + + + + +Of course, we can bind as many variables as we wish: + + 'GET /home' => function() + { + return View::make('simple') + ->bind('name', 'Taylor') + ->bind('email', 'example@gmail.com'); + } + +You may also bind view data by simply setting properties on a view instance: + + 'GET /home' => function() + { + $view = View::make('user/login'); + + $view->name = 'Taylor'; + $view->email = 'example@gmail.com'; + + return $view; + } + + +## Nesting Views Within Views + +Want to nest views inside of views? There are two ways to do it, and they are both easy. First, you can bind the view to a variable: + + 'GET /home' => function() + { + $view = View::make('user/login'); + + $view->content = View::make('partials/content'); + $view->footer = View::make('partials/footer'); + + return $view; + } + +Or, you can get the string content of a view using the **get** method: + + + get(); ?> + get(); ?> + + + +## Redirects + +### Redirect Using URIs + +You will often need to redirect the browser to another location within your application. The **Redirect** class makes this a piece of cake. Here's how to do it: + + 'GET /redirect' => function() + { + return Redirect::to('user/profile'); + } + +Of course, you can also redirect to the root of your application: + + return Redirect::to('/'); + +Need to redirect to a URI that should be accessed over HTTPS? Check out the **to_secure** method: + + return Redirect::to_secure('user/profile'); + +You can even redirect the browser to a location outside of your application: + + return Redirect::to('http://www.google.com/'); + + +### Redirect Using Named Routes + +So far we've created redirects by specifying a URI to redirect to. But, what if the URI to the user's profile changes? It would be better to create a redirect based on the [route name](/docs/start/routes#named). Let's learn how to do it. Assuming the user profile route is named **profile**, you can redirect to the route using a dynamic, static method call like this: + + return Redirect::to_profile(); + +Need to redirect to a route that should be accessed over HTTPS? No problem: + + return Redirect::to_secure_profile(); + +That's great! But, what if the route URI looks like this: + + 'GET /user/profile/(:any)/(:any)' => array('name' => 'profile', 'do' => function() + { + // + }) + +You need to redirect to the named route, but you also need to replace the **(:any)** place-holders in the URI with actual values. It sounds complicated, but Laravel makes it a breeze. Just pass the parameters to the method in an array: + + return Redirect::to_profile(array('taylor', 'otwell')); + +The statement above will redirect the user to the following URL: + + http://example.com/index.php/user/profile/taylor/otwell + + +### Redirect With Flash Data + +After a user creates an account or signs into your application, it is common to display a welcome or status message. But, how can you set the status message so it is available for the next request? The **Response** and **Redirect** classes make it simple using the **with** method. This method will add a value to the Session flash data, which will be available for the next request: + + return Redirect::to('user/profile')->with('status', 'Welcome Back!'); + +> **Note:** For more information regarding Sessions and flash data, check out the [Session documentation](/docs/session/config). + + +## Downloads + +Perhaps you just want to force the web browser to download a file? Check out the **download** method on the **File** class: + + 'GET /file' => function() + { + return File::download('path/to/your/file.jpg'); + } + +In the example above, the image will be downloaded as "file.jpg", however, you can easily specify a different filename for the download in the second parameter to the method: + + 'GET /file' => function() + { + return File::download('path/to/your/file.jpg', 'photo.jpg'); + } + + +## Building URLs + +- [Generating URLs Using URIs](#uri-urls) +- [Generating URLs Using Named Routes](#named-urls) +- [URL Slugs](#url-slugs) + +While developing your application, you will probably need to generate a large number of URLs. Hard-coding your URLs can cause headaches if you switch domains or application locations. Nobody wants to dig through every view in their application to change URLs. Thankfully, the **URL** class provides some simple methods that allow you to easily generate URLs for your application. + + +### Generating URLs Using URIs + +To generate a URL for your application, use the **to** method on the URL class: + + echo URL::to(); + +The statement above will simply return the URL specified in your **application/config/application.php** file. + +However, this method can also append a specified string to the end of your application URL: + + echo URL::to('user/profile'); + +Now the statement will generate a URL something like this: + + http://example.com/index.php/user/login + +Need to generate a HTTPS URL? No problem. Check out the **to\_secure** method: + + echo URL::to_secure('user/profile'); + +Often, you will need to generate URLs to assets such as images, scripts, or styles. You don't want "index.php" inserted into these URLs. So, use the **to_asset** method: + + echo URL::to_asset('img/picture.jpg'); + + +### Generating URLs To Named Routes + +Alright, you have learned how to generate URLs from URIs, but what about from named routes? You can do it by making a dynamic, static method call to the URL class. The syntax is beautiful: + + echo URL::to_profile(); + +Have a route that needs to be accessed over HTTPS? No problem: + + echo URL::to_secure_profile(); + +Could it get any easier? Now, imagine a route that is defined like this: + + 'GET /user/profile/(:any)/(:any)' => array('name' => 'profile', 'do' => function() + { + // + }) + +To generate a URL for the route, you need to replace the **(:any)** place-holders with actual values. It's easy. Just pass the parameters to the method in an array: + + echo URL::to_profile(array('taylor', 'otwell')); + +The method above will generate the following URL: + + http://example.com/index.php/user/profile/taylor/otwell + +If you don't want to use dynamic methods, you can simply call the **to_route** method: + + echo URL::to_route('profile', array('taylor', 'otwell')); + + +### URL Slugs + +When writing an application like a blog, it is often necessary to generate URL "slugs". Slugs are URL friendly strings of text that represent something like a blog post title. Generating slugs is a piece of cake using the **slug** method: + + echo URL::slug('My blog post title!'); + +The statement above will return the following string: + + my-blog-post-title + +Want to use a different separator character? Just mention it to the method: + + echo URL::slug('My blog post title!', '_'); + + +## Building HTML + +- [Entities](#html-entities) +- [JavaScript & Style Sheets](#html-styles) +- [Links](#html-links) +- [Links To Named Routes](#html-route-links) +- [Mail-To Links](#html-mailto) +- [Images](#html-images) +- [Lists](#html-lists) + +Need some convenient methods that make writing HTML a little less painful? Introducing the **HTML** class. Enjoy. + + +### Entities + +When displaying user input in your Views, it is important to convert all characters which have signifance in HTML to their "entity" representation. + +For example, the < symbol should be converted to its entity representation. Converting HTML characters to their entity representation helps protect your application from cross-site scripting. Thankfully, using the **entities** method on the HTML class, it's easy to add this layer of protection to your Views: + + echo HTML::entities(''); + + +### JavaScript & Style Sheets + +What trendy web application doesn't use JavaScript? Generating a reference to a JavaScript file is as simple using the **script** method. + + echo HTML::script('js/scrollTo.js'); + +Referencing cascading style sheets is just as simple. Check out the **style method**: + + echo HTML::style('css/common.css'); + +Need to specify a media type on your style sheet? No problem. Just pass it in the second parameter to the method: + + echo HTML::style('css/common.css', 'print'); + +> **Note:** All scripts and stylesheets should live in the **public** directory of your application. + + +### Links + +Generating links couldn't be easier. Just mention the URL and title to the **link** method: + + echo HTML::link('user/profile', 'User Profile'); + +Need to generate a link to a URL that should be accessed over HTTPS? Use the **secure_link** method: + + echo HTML::secure_link('user/profile', 'User Profile'); + +Of course, you may generate links to locations outside of your application: + + echo HTML::link('http://www.google.com', 'Search the Intrawebs!'); + +Any other attributes that should be applied to the link may be passed in the third parameter to the method: + + echo HTML::link('http://www.google.com', 'Search the Intrawebs!', array('id' => 'google')); + + +### Links To Named Routes + +If you are using [named routes](#routes-named), you use intuitive, expressive syntax to create links to those routes via dynamic methods: + + echo HTML::link_to_login('Login'); + +Or, if you need an HTTPS link: + + echo HTML::link_to_secure_login('Login'); + +Now let's assume the **login** route is defined like this: + + 'GET /login/(:any)' => array('name' => 'login', 'do' => function() {}) + +To generate a link to the route, you need to replace the **(:any)** place-holder with an actual value. It's easy. Just pass the parameters to the method in an array: + + echo HTML::link_to_login(array('user')); + +This statement will generate a link to the following URL: + + http://example.com/login/user + + +### Mail-To Links + +It's great to allow your users to get in touch with you, but you don't want to be bombarded by spam. Laravel doesn't want you to be either. The **mailto** method allows you to create a safe mail-to link that obfuscates your e-mail address: + + echo HTML::mailto('example@gmail.com'); + +Want the link text to be something besides your e-mail address? No problem. Just mention it to the method: + + echo HTML::mailto('example@gmail.com', 'E-Mail Me!'); + +Need to obfuscate an e-mail address but don't want to generate an entire mail-to link? Simply pass the e-mail address to the **email** method: + + echo HTML::email('example@gmail.com'); + + +### Images + +Since you're building a creative application, you will need to include some images. The **image** method makes simple. Just pass the URL and Alt text to the method: + + echo HTML::image('img/smile.jpg', 'A Smiling Face'); + +Like the link method, any other attributes that should be applied to the image may be passed in the third parameter to the method: + + echo HTML::image('img/smile.jpg', 'A Smiling Face', array('id' => 'smile')); + + +### Lists + +Generating HTML lists doesn't have to be a headache. Check out the **ol** method. All it needs is an array of items: + + echo HTML::ol(array('Get Peanut Butter', 'Get Chocolate', 'Feast')); + +As usual, you may specify any other attributes that should be applied to the list: + + echo HTML::ol(array('Get Peanut Butter', 'Get Chocolate', 'Feast'), array('class' => 'awesome')); + +Need an un-ordered list? No problem. Just use the **ul** method: + + echo HTML::ul(array('Ubuntu', 'Snow Leopard', 'Windows')); \ No newline at end of file From 48706caeb01e0b03fb0e094e3a4ea1506f92ac0a Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 15 Jul 2011 19:27:06 -0500 Subject: [PATCH 206/206] incremented version. --- public/index.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/index.php b/public/index.php index fbb7901f..38576157 100644 --- a/public/index.php +++ b/public/index.php @@ -3,7 +3,7 @@ * Laravel - A clean and classy framework for PHP web development. * * @package Laravel - * @version 1.1.1 + * @version 1.2.0 * @author Taylor Otwell * @license MIT License * @link http://laravel.com