From 4c554da8eba4e319b3b1c8c4a5c9b13b515ea27f Mon Sep 17 00:00:00 2001 From: Jeremy Bush Date: Thu, 23 Aug 2012 21:56:20 -0500 Subject: [PATCH 001/106] bump version to 3.2.1 --- classes/kohana/core.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/classes/kohana/core.php b/classes/kohana/core.php index 674c6b52e..f98e5bd8c 100644 --- a/classes/kohana/core.php +++ b/classes/kohana/core.php @@ -16,8 +16,8 @@ class Kohana_Core { // Release version and codename - const VERSION = '3.2.0'; - const CODENAME = 'Kolibri'; + const VERSION = '3.2.1'; + const CODENAME = 'Milvago'; // Common environment type constants for consistency and convenience const PRODUCTION = 10; From 97ccdecb053025e8287f1e5dfed520a81f864c96 Mon Sep 17 00:00:00 2001 From: Thomas Buck Date: Mon, 27 Aug 2012 16:06:58 +0000 Subject: [PATCH 002/106] Add Windows 8 support --- config/user_agents.php | 1 + 1 file changed, 1 insertion(+) diff --git a/config/user_agents.php b/config/user_agents.php index e92a632ca..6f929458d 100644 --- a/config/user_agents.php +++ b/config/user_agents.php @@ -3,6 +3,7 @@ return array( 'platform' => array( + 'windows nt 6.2' => 'Windows 8', 'windows nt 6.1' => 'Windows 7', 'windows nt 6.0' => 'Windows Vista', 'windows nt 5.2' => 'Windows 2003', From 7da9089767f3333d136c6ae214d7e43a4d822d0d Mon Sep 17 00:00:00 2001 From: Woody Gilk Date: Mon, 27 Aug 2012 12:52:24 -0500 Subject: [PATCH 003/106] Revert "Add Windows 8 support" This reverts commit 97ccdecb053025e8287f1e5dfed520a81f864c96. --- config/user_agents.php | 1 - 1 file changed, 1 deletion(-) diff --git a/config/user_agents.php b/config/user_agents.php index 6f929458d..e92a632ca 100644 --- a/config/user_agents.php +++ b/config/user_agents.php @@ -3,7 +3,6 @@ return array( 'platform' => array( - 'windows nt 6.2' => 'Windows 8', 'windows nt 6.1' => 'Windows 7', 'windows nt 6.0' => 'Windows Vista', 'windows nt 5.2' => 'Windows 2003', From 92a1724c1a35783e2cf352f92eb29aa81828d570 Mon Sep 17 00:00:00 2001 From: MichalM Date: Mon, 22 Oct 2012 17:05:08 +0200 Subject: [PATCH 004/106] stdout and stderr now use file log writer's format to display readable log levels --- classes/kohana/log/stderr.php | 5 +---- classes/kohana/log/stdout.php | 5 +---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/classes/kohana/log/stderr.php b/classes/kohana/log/stderr.php index 715ce902d..6c5210906 100644 --- a/classes/kohana/log/stderr.php +++ b/classes/kohana/log/stderr.php @@ -19,13 +19,10 @@ class Kohana_Log_StdErr extends Log_Writer { */ public function write(array $messages) { - // Set the log line format - $format = 'time --- type: body'; - foreach ($messages as $message) { // Writes out each message - fwrite(STDERR, PHP_EOL.strtr($format, $message)); + fwrite(STDERR, PHP_EOL.$message['time'].' --- '.$this->_log_levels[$message['level']].': '.$message['body']); } } } // End Kohana_Log_StdErr diff --git a/classes/kohana/log/stdout.php b/classes/kohana/log/stdout.php index 26c9668b5..0ef61c4a2 100644 --- a/classes/kohana/log/stdout.php +++ b/classes/kohana/log/stdout.php @@ -19,13 +19,10 @@ class Kohana_Log_StdOut extends Log_Writer { */ public function write(array $messages) { - // Set the log line format - $format = 'time --- type: body'; - foreach ($messages as $message) { // Writes out each message - fwrite(STDOUT, PHP_EOL.strtr($format, $message)); + fwrite(STDOUT, PHP_EOL.$message['time'].' --- '.$this->_log_levels[$message['level']].': '.$message['body']); } } } // End Kohana_Log_StdOut From 0e52dfc09f6ae50d830f102829a59af182ac8ee6 Mon Sep 17 00:00:00 2001 From: Evan Purkhiser Date: Sun, 4 Nov 2012 16:51:24 -0500 Subject: [PATCH 005/106] Use the current exception handler during shutdown It's possible that a different exception handler is defined than the default `Kohana_Exception::hander`. Refs #4627 --- classes/kohana/core.php | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/classes/kohana/core.php b/classes/kohana/core.php index 58c3481fc..12af46a86 100644 --- a/classes/kohana/core.php +++ b/classes/kohana/core.php @@ -968,7 +968,6 @@ public static function error_handler($code, $error, $file = NULL, $line = NULL) /** * Catches errors that are not caught by the error handler, such as E_PARSE. * - * @uses Kohana_Exception::handler * @return void */ public static function shutdown_handler() @@ -979,6 +978,12 @@ public static function shutdown_handler() return; } + // Retrieve the current exception handler + $handler = set_exception_handler(array('Kohana_Exception', 'handler')); + + // Restore it back to it's previous state + restore_exception_handler(); + try { if (Kohana::$caching === TRUE AND Kohana::$_files_changed === TRUE) @@ -990,7 +995,7 @@ public static function shutdown_handler() catch (Exception $e) { // Pass the exception to the handler - Kohana_Exception::handler($e); + call_user_func($handler, $e); } if (Kohana::$errors AND $error = error_get_last() AND in_array($error['type'], Kohana::$shutdown_errors)) @@ -999,7 +1004,7 @@ public static function shutdown_handler() ob_get_level() and ob_clean(); // Fake an exception for nice debugging - Kohana_Exception::handler(new ErrorException($error['message'], $error['type'], 0, $error['file'], $error['line'])); + call_user_func($handler, new ErrorException($error['message'], $error['type'], 0, $error['file'], $error['line'])); // Shutdown now to avoid a "death loop" exit(1); From 9aa78a82ba50522788de8260ca184ac6ba6c64bf Mon Sep 17 00:00:00 2001 From: Ken Dreyer Date: Thu, 14 Mar 2013 15:21:17 -0600 Subject: [PATCH 006/106] doc: fix HTML in validation view example The unordered list in the HTML example was unclosed. Add a closing ul element to fix it. --- guide/kohana/security/validation.md | 1 + 1 file changed, 1 insertion(+) diff --git a/guide/kohana/security/validation.md b/guide/kohana/security/validation.md index af67f229b..3ef504918 100644 --- a/guide/kohana/security/validation.md +++ b/guide/kohana/security/validation.md @@ -182,6 +182,7 @@ First, we need a [View] that contains the HTML form, which will be placed in `ap
  • +
    From 4cf93923ced3afbaa7086d53f6c1aa5175b96d3f Mon Sep 17 00:00:00 2001 From: Marek Szymczuk Date: Sat, 7 Sep 2013 22:06:16 +0200 Subject: [PATCH 007/106] Update the license url, fixes #4778 --- classes/kohana/config/group.php | 2 +- classes/kohana/config/source.php | 2 +- classes/kohana/config/writer.php | 2 +- classes/kohana/debug.php | 2 +- classes/kohana/http.php | 2 +- classes/kohana/http/header.php | 2 +- classes/kohana/http/message.php | 2 +- classes/kohana/http/request.php | 2 +- classes/kohana/http/response.php | 2 +- classes/kohana/log/stderr.php | 2 +- classes/kohana/log/stdout.php | 2 +- classes/kohana/response.php | 2 +- tests/kohana/Config/File/ReaderTest.php | 2 +- tests/kohana/Config/GroupTest.php | 2 +- tests/kohana/DebugTest.php | 2 +- tests/kohana/Http/HeaderTest.php | 2 +- 16 files changed, 16 insertions(+), 16 deletions(-) diff --git a/classes/kohana/config/group.php b/classes/kohana/config/group.php index 3adfbba69..f6e00ac66 100644 --- a/classes/kohana/config/group.php +++ b/classes/kohana/config/group.php @@ -12,7 +12,7 @@ * @category Configuration * @author Kohana Team * @copyright (c) 2012 Kohana Team - * @license http://kohanaphp.com/license + * @license http://kohanaframework.org/license */ class Kohana_Config_Group extends ArrayObject { diff --git a/classes/kohana/config/source.php b/classes/kohana/config/source.php index fcc57f3e3..1ed18a58f 100644 --- a/classes/kohana/config/source.php +++ b/classes/kohana/config/source.php @@ -8,7 +8,7 @@ * @category Configuration * @author Kohana Team * @copyright (c) 2012 Kohana Team - * @license http://kohanaphp.com/license + * @license http://kohanaframework.org/license */ interface Kohana_Config_Source {} diff --git a/classes/kohana/config/writer.php b/classes/kohana/config/writer.php index ea2b19ab8..cc7ae5291 100644 --- a/classes/kohana/config/writer.php +++ b/classes/kohana/config/writer.php @@ -8,7 +8,7 @@ * @package Kohana * @author Kohana Team * @copyright (c) 2008-2012 Kohana Team - * @license http://kohanaphp.com/license + * @license http://kohanaframework.org/license */ interface Kohana_Config_Writer extends Kohana_Config_Source { diff --git a/classes/kohana/debug.php b/classes/kohana/debug.php index 435387d67..fd8d795b0 100644 --- a/classes/kohana/debug.php +++ b/classes/kohana/debug.php @@ -6,7 +6,7 @@ * @category Base * @author Kohana Team * @copyright (c) 2008-2012 Kohana Team - * @license http://kohanaphp.com/license + * @license http://kohanaframework.org/license */ class Kohana_Debug { diff --git a/classes/kohana/http.php b/classes/kohana/http.php index 066464d1a..b254fd074 100644 --- a/classes/kohana/http.php +++ b/classes/kohana/http.php @@ -12,7 +12,7 @@ * @author Kohana Team * @since 3.1.0 * @copyright (c) 2008-2012 Kohana Team - * @license http://kohanaphp.com/license + * @license http://kohanaframework.org/license */ abstract class Kohana_HTTP { diff --git a/classes/kohana/http/header.php b/classes/kohana/http/header.php index 96d5e124a..7e8573f7b 100644 --- a/classes/kohana/http/header.php +++ b/classes/kohana/http/header.php @@ -10,7 +10,7 @@ * @author Kohana Team * @since 3.1.0 * @copyright (c) 2008-2012 Kohana Team - * @license http://kohanaphp.com/license + * @license http://kohanaframework.org/license */ class Kohana_HTTP_Header extends ArrayObject { diff --git a/classes/kohana/http/message.php b/classes/kohana/http/message.php index 9cbf18008..b699cbd57 100644 --- a/classes/kohana/http/message.php +++ b/classes/kohana/http/message.php @@ -8,7 +8,7 @@ * @author Kohana Team * @since 3.1.0 * @copyright (c) 2008-2012 Kohana Team - * @license http://kohanaphp.com/license + * @license http://kohanaframework.org/license */ interface Kohana_HTTP_Message { diff --git a/classes/kohana/http/request.php b/classes/kohana/http/request.php index b5b283eac..88eaa64c3 100644 --- a/classes/kohana/http/request.php +++ b/classes/kohana/http/request.php @@ -9,7 +9,7 @@ * @author Kohana Team * @since 3.1.0 * @copyright (c) 2008-2012 Kohana Team - * @license http://kohanaphp.com/license + * @license http://kohanaframework.org/license */ interface Kohana_HTTP_Request extends HTTP_Message { diff --git a/classes/kohana/http/response.php b/classes/kohana/http/response.php index 06a67fd78..8a337bce6 100644 --- a/classes/kohana/http/response.php +++ b/classes/kohana/http/response.php @@ -9,7 +9,7 @@ * @author Kohana Team * @since 3.1.0 * @copyright (c) 2008-2012 Kohana Team - * @license http://kohanaphp.com/license + * @license http://kohanaframework.org/license */ interface Kohana_HTTP_Response extends HTTP_Message { diff --git a/classes/kohana/log/stderr.php b/classes/kohana/log/stderr.php index 715ce902d..57bca9e3e 100644 --- a/classes/kohana/log/stderr.php +++ b/classes/kohana/log/stderr.php @@ -6,7 +6,7 @@ * @category Logging * @author Kohana Team * @copyright (c) 2008-2012 Kohana Team - * @license http://kohanaphp.com/license + * @license http://kohanaframework.org/license */ class Kohana_Log_StdErr extends Log_Writer { /** diff --git a/classes/kohana/log/stdout.php b/classes/kohana/log/stdout.php index 26c9668b5..2911ac717 100644 --- a/classes/kohana/log/stdout.php +++ b/classes/kohana/log/stdout.php @@ -6,7 +6,7 @@ * @category Logging * @author Kohana Team * @copyright (c) 2008-2012 Kohana Team - * @license http://kohanaphp.com/license + * @license http://kohanaframework.org/license */ class Kohana_Log_StdOut extends Log_Writer { /** diff --git a/classes/kohana/response.php b/classes/kohana/response.php index 51381fc20..2a820d828 100644 --- a/classes/kohana/response.php +++ b/classes/kohana/response.php @@ -8,7 +8,7 @@ * @category Base * @author Kohana Team * @copyright (c) 2008-2012 Kohana Team - * @license http://kohanaphp.com/license + * @license http://kohanaframework.org/license * @since 3.1.0 */ class Kohana_Response implements HTTP_Response { diff --git a/tests/kohana/Config/File/ReaderTest.php b/tests/kohana/Config/File/ReaderTest.php index ab22d14ae..7a365d183 100644 --- a/tests/kohana/Config/File/ReaderTest.php +++ b/tests/kohana/Config/File/ReaderTest.php @@ -11,7 +11,7 @@ * @author Jeremy Bush * @author Matt Button * @copyright (c) 2008-2012 Kohana Team - * @license http://kohanaphp.com/license + * @license http://kohanaframework.org/license */ class Kohana_Config_File_ReaderTest extends Kohana_Unittest_TestCase { diff --git a/tests/kohana/Config/GroupTest.php b/tests/kohana/Config/GroupTest.php index f18e44e1f..b78fbcfbc 100644 --- a/tests/kohana/Config/GroupTest.php +++ b/tests/kohana/Config/GroupTest.php @@ -11,7 +11,7 @@ * @author Jeremy Bush * @author Matt Button * @copyright (c) 2008-2012 Kohana Team - * @license http://kohanaphp.com/license + * @license http://kohanaframework.org/license */ class Kohana_Config_GroupTest extends Kohana_Unittest_TestCase { diff --git a/tests/kohana/DebugTest.php b/tests/kohana/DebugTest.php index 218309db4..ed9d09b54 100644 --- a/tests/kohana/DebugTest.php +++ b/tests/kohana/DebugTest.php @@ -14,7 +14,7 @@ * @author Kohana Team * @author Jeremy Bush * @copyright (c) 2008-2012 Kohana Team - * @license http://kohanaphp.com/license + * @license http://kohanaframework.org/license */ class Kohana_DebugTest extends Unittest_TestCase { diff --git a/tests/kohana/Http/HeaderTest.php b/tests/kohana/Http/HeaderTest.php index c7dffdaa5..4c82172e2 100644 --- a/tests/kohana/Http/HeaderTest.php +++ b/tests/kohana/Http/HeaderTest.php @@ -12,7 +12,7 @@ * @category Tests * @author Kohana Team * @copyright (c) 2008-2012 Kohana Team - * @license http://kohanaphp.com/license + * @license http://kohanaframework.org/license */ class Kohana_HTTP_HeaderTest extends Unittest_TestCase { From 463053faff4b3e4b482714f9848998033d5bd3ef Mon Sep 17 00:00:00 2001 From: Andrew Coulton Date: Thu, 26 Sep 2013 10:51:43 +0100 Subject: [PATCH 008/106] Only copy original request headers when following redirects [fixes #4790] When following a redirect, the client should not add NULL headers from the follow_headers list that were not set on the original request. This prevents problems when requesting URLs that include an authorization signature calculated from the request URL and headers. --- classes/Kohana/Request/Client.php | 7 +++++-- tests/kohana/request/ClientTest.php | 23 +++++++++++++++++++++++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/classes/Kohana/Request/Client.php b/classes/Kohana/Request/Client.php index 26b744240..1190a30fc 100644 --- a/classes/Kohana/Request/Client.php +++ b/classes/Kohana/Request/Client.php @@ -405,10 +405,13 @@ public static function on_header_location(Request $request, Response $response, break; } - // Prepare the additional request + // Prepare the additional request, copying any follow_headers that were present on the original request + $orig_headers = $request->headers()->getArrayCopy(); + $follow_headers = array_intersect_assoc($orig_headers, array_fill_keys($client->follow_headers(), TRUE)); + $follow_request = Request::factory($response->headers('Location')) ->method($follow_method) - ->headers(Arr::extract($request->headers(), $client->follow_headers())); + ->headers($follow_headers); if ($follow_method !== Request::GET) { diff --git a/tests/kohana/request/ClientTest.php b/tests/kohana/request/ClientTest.php index fffc1d5d3..9ad870297 100644 --- a/tests/kohana/request/ClientTest.php +++ b/tests/kohana/request/ClientTest.php @@ -175,6 +175,29 @@ public function test_follows_with_headers() $this->assertFalse(isset($headers['x-not-in-follow']), 'X-Not-In-Follow should not be passed to next request'); } + /** + * Tests that the follow_headers are only added to a redirect request if they were present in the original + * + * @ticket 4790 + */ + public function test_follow_does_not_add_extra_headers() + { + $response = Request::factory( + $this->_dummy_redirect_uri(301), + array( + 'follow' => TRUE, + 'follow_headers' => array('Authorization') + )) + ->headers(array()) + ->execute(); + + $data = json_decode($response->body(),TRUE); + $headers = $data['rq_headers']; + + $this->assertArrayNotHasKey('authorization', $headers, 'Empty headers should not be added when following redirects'); + } + + /** * Provider for test_follows_with_strict_method * From f7a4f3622fd186937622a3e42b37747e8f27dd24 Mon Sep 17 00:00:00 2001 From: Andrew Coulton Date: Thu, 26 Sep 2013 11:43:20 +0100 Subject: [PATCH 009/106] Don't set empty body on external curl requests [Fixes #4791] When the CURLOPT_POSTFIELDS option is present, curl adds a default Content-Type header which can be changed but not removed, causing authentication problems with signed requests. The option should only be set if a request body is being sent. --- classes/kohana/request/client/curl.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/classes/kohana/request/client/curl.php b/classes/kohana/request/client/curl.php index 2dd6ea3cc..5a92b5ab4 100644 --- a/classes/kohana/request/client/curl.php +++ b/classes/kohana/request/client/curl.php @@ -33,7 +33,10 @@ public function _send_message(Request $request) // if using a request other than POST. PUT does support this method // and DOES NOT require writing data to disk before putting it, if // reading the PHP docs you may have got that impression. SdF - $options[CURLOPT_POSTFIELDS] = $request->body(); + // This will also add a Content-Type: application/x-www-form-urlencoded header unless you override it + if ($body = $request->body()) { + $options[CURLOPT_POSTFIELDS] = $body; + } // Process headers if ($headers = $request->headers()) From c90b6fd7189c74c9c8a566e072977d5acfbe6a7b Mon Sep 17 00:00:00 2001 From: tmihalik Date: Thu, 12 Dec 2013 19:38:49 +0100 Subject: [PATCH 010/106] Update Exception.php --- classes/Kohana/HTTP/Exception.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/classes/Kohana/HTTP/Exception.php b/classes/Kohana/HTTP/Exception.php index cb292e3ec..65c4f31d9 100644 --- a/classes/Kohana/HTTP/Exception.php +++ b/classes/Kohana/HTTP/Exception.php @@ -46,7 +46,7 @@ public function __construct($message = NULL, array $variables = NULL, Exception * Store the Request that triggered this exception. * * @param Request $request Request object that triggered this exception. - * @return Response + * @return HTTP_Exception */ public function request(Request $request = NULL) { From a0ffbaa8c2d8f0d0e46afe4e88dac329c38d814d Mon Sep 17 00:00:00 2001 From: Samuel Demirdjian Date: Thu, 9 Jan 2014 06:34:08 -0800 Subject: [PATCH 011/106] Ref #4802: HTTP_Header not compatible with ArrayObject HHVM complains about this. The Zend engine silently ignore the bug, probably casting the NULL internally to int 0. Could not create a test for it, as this works with Zend, but it's fatal in HHVM. --- classes/Kohana/HTTP/Header.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/classes/Kohana/HTTP/Header.php b/classes/Kohana/HTTP/Header.php index a74fbb2c6..6a35e9138 100644 --- a/classes/Kohana/HTTP/Header.php +++ b/classes/Kohana/HTTP/Header.php @@ -287,7 +287,7 @@ public static function parse_cache_control($cache_control) * @param int $flags Flags * @param string $iterator_class The iterator class to use */ - public function __construct(array $input = array(), $flags = NULL, $iterator_class = 'ArrayIterator') + public function __construct(array $input = array(), $flags = 0, $iterator_class = 'ArrayIterator') { /** * @link http://www.w3.org/Protocols/rfc2616/rfc2616.html From 3be2d3d22d912d629b2a76fe90865d65c9305cef Mon Sep 17 00:00:00 2001 From: Jack Ellis Date: Sat, 11 Jan 2014 16:52:15 +0000 Subject: [PATCH 012/106] #4201 - Issue with validation labels --- classes/kohana/validation.php | 2 +- tests/kohana/ValidationTest.php | 44 +++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/classes/kohana/validation.php b/classes/kohana/validation.php index 50c29eee1..f71c576fe 100644 --- a/classes/kohana/validation.php +++ b/classes/kohana/validation.php @@ -219,7 +219,7 @@ public function rule($field, $rule, array $params = NULL) if ($field !== TRUE AND ! isset($this->_labels[$field])) { // Set the field label to the field name - $this->_labels[$field] = preg_replace('/[^\pL]+/u', ' ', $field); + $this->_labels[$field] = $field); } // Store the rule and params for this rule diff --git a/tests/kohana/ValidationTest.php b/tests/kohana/ValidationTest.php index 05258495a..b29aaecb3 100644 --- a/tests/kohana/ValidationTest.php +++ b/tests/kohana/ValidationTest.php @@ -665,4 +665,48 @@ public function test_error_type_check() $this->assertSame($errors, $validation->errors('validation')); } + /** + * Provides test data for test_rule_label_regex + * + * @return array + */ + public function provider_rule_label_regex() + { + // $data, $field, $rules, $expected + return array( + array( + array( + 'email1' => '', + ), + 'email1', + array( + array( + 'not_empty' + ) + ), + array( + 'email1' => 'email1 must not be empty' + ), + ) + ); + } + + /** + * http://dev.kohanaframework.org/issues/4201 + * + * @test + * @ticket 4201 + * @covers Validation::rule + * @dataProvider provider_rule_label_regex + */ + public function test_rule_label_regex($data, $field, $rules, $expected) + { + $validation = Validation::factory($data)->rules($field, $rules); + + $validation->check(); + + $errors = $validation->errors(''); + + $this->assertSame($errors, $expected); + } } From fb52a03da4e0e4af02501ab2edd414588329641c Mon Sep 17 00:00:00 2001 From: Jack Ellis Date: Sat, 11 Jan 2014 17:08:24 +0000 Subject: [PATCH 013/106] #4079 - Route::uri() Should encode parameters Route::uri() does not handle special characters well, so now it uses rawurlencode to fix this problem See: http://dev.kohanaframework.org/issues/4079 --- classes/kohana/route.php | 5 +++++ tests/kohana/RouteTest.php | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/classes/kohana/route.php b/classes/kohana/route.php index 39f7720c8..19d47e970 100644 --- a/classes/kohana/route.php +++ b/classes/kohana/route.php @@ -456,6 +456,11 @@ public function is_external() */ public function uri(array $params = NULL) { + if (is_array($params)) + { + $params = array_map('rawurlencode', $params); + } + // Start with the routed URI $uri = $this->_uri; diff --git a/tests/kohana/RouteTest.php b/tests/kohana/RouteTest.php index e57c45a9e..30104cd39 100644 --- a/tests/kohana/RouteTest.php +++ b/tests/kohana/RouteTest.php @@ -715,4 +715,41 @@ public function test_external_route_includes_params_in_uri($route, $defaults, $e $this->assertSame($expected_uri, Route::get('test')->uri()); } + + /** + * Provides test data for test_route_uri_encode_parameters + * + * @return array + */ + public function provider_route_uri_encode_parameters() + { + return array( + array( + 'article', + 'blog/article/', + array( + 'controller' => 'home', + 'action' => 'index' + ), + 'blog/article/Article%20name%20with%20special%20chars%20%5C%20%23%23' + ) + ); + } + + /** + * http://dev.kohanaframework.org/issues/4079 + * + * @test + * @covers Route::get + * @ticket 4079 + * @dataProvider provider_route_uri_encode_parameters + */ + public function test_route_uri_encode_parameters($name, $uri_callback, $defaults, $expected) + { + Route::set($name, $uri_callback)->defaults($defaults); + + $get_route_uri = Route::get($name)->uri(array('article_name' => 'Article name with special chars \\ ##')); + + $this->assertSame($expected, $get_route_uri); + } } From ce4ab50c08f57a37d0d8fd080e0cb64bc6dd76f7 Mon Sep 17 00:00:00 2001 From: Jack Ellis Date: Sat, 11 Jan 2014 19:20:05 +0000 Subject: [PATCH 014/106] Fixed the typo with additional braclet --- classes/kohana/validation.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/classes/kohana/validation.php b/classes/kohana/validation.php index f71c576fe..b0232686d 100644 --- a/classes/kohana/validation.php +++ b/classes/kohana/validation.php @@ -219,7 +219,7 @@ public function rule($field, $rule, array $params = NULL) if ($field !== TRUE AND ! isset($this->_labels[$field])) { // Set the field label to the field name - $this->_labels[$field] = $field); + $this->_labels[$field] = $field; } // Store the rule and params for this rule From 91202dd18178ad2e3c710677f3f11bf994a85707 Mon Sep 17 00:00:00 2001 From: Jack Ellis Date: Sat, 11 Jan 2014 21:22:19 +0000 Subject: [PATCH 015/106] #4482 - Array to string conversion in deep Arr::merge When using Arr::merge to merge arrays, the deeper array merges failed and caused a ErrorException [ Notice ]: Array to string conversion See: http://dev.kohanaframework.org/issues/4482 --- classes/kohana/arr.php | 10 +++++++++- tests/kohana/ArrTest.php | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/classes/kohana/arr.php b/classes/kohana/arr.php index 9df715ee6..e45f6df5f 100644 --- a/classes/kohana/arr.php +++ b/classes/kohana/arr.php @@ -441,7 +441,15 @@ public static function merge(array $a1, array $a2) else { // Find the values that are not already present - $diff = array_diff($val, $result[$key]); + $diff = array(); + + foreach ($val as $val_val) + { + if ( ! in_array($val_val, $result[$key])) + { + $diff[] = $val_val; + } + } // Indexed arrays are merged to prevent duplicates $result[$key] = array_merge($result[$key], $diff); diff --git a/tests/kohana/ArrTest.php b/tests/kohana/ArrTest.php index 54bdc7cea..435cc470c 100644 --- a/tests/kohana/ArrTest.php +++ b/tests/kohana/ArrTest.php @@ -280,6 +280,45 @@ public function provider_merge() array('foo' => array('bar')), array('foo' => 'bar'), ), + /** + * @ticket 4482 + */ + array( + array( + 'foo' => array( + 'bar' => array( + array( + 'foo' => 'bar', + 'bar' => TRUE, + ), + array( + 'foo1' => 'foo', + 'bar1' => FALSE, + ), + ), + ), + ), + array( + 'foo' => array( + 'bar' => array( + array( + 'foo' => 'bar', + 'bar' => TRUE, + ), + ), + ), + ), + array( + 'foo' => array( + 'bar' => array( + array( + 'foo1' => 'foo', + 'bar1' => FALSE, + ), + ), + ), + ) + ) ); } From 00df092ca43faeb6cac33da10e0c28e11440a072 Mon Sep 17 00:00:00 2001 From: Jack Ellis Date: Sat, 11 Jan 2014 22:09:51 +0000 Subject: [PATCH 016/106] Added strict in in_array Added extra entry to array in test and changed in_array to strict. Performance: Non Strinct: http://i.imgur.com/JiRtUxQ.png Strict: http://i.imgur.com/nRmgHVS.png --- classes/kohana/arr.php | 2 +- tests/kohana/ArrTest.php | 13 +++++-------- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/classes/kohana/arr.php b/classes/kohana/arr.php index e45f6df5f..945247114 100644 --- a/classes/kohana/arr.php +++ b/classes/kohana/arr.php @@ -445,7 +445,7 @@ public static function merge(array $a1, array $a2) foreach ($val as $val_val) { - if ( ! in_array($val_val, $result[$key])) + if ( ! in_array($val_val, $result[$key], TRUE)) { $diff[] = $val_val; } diff --git a/tests/kohana/ArrTest.php b/tests/kohana/ArrTest.php index 435cc470c..2260cd3c6 100644 --- a/tests/kohana/ArrTest.php +++ b/tests/kohana/ArrTest.php @@ -287,22 +287,19 @@ public function provider_merge() array( 'foo' => array( 'bar' => array( + 'foo' => TRUE, array( - 'foo' => 'bar', 'bar' => TRUE, - ), - array( - 'foo1' => 'foo', 'bar1' => FALSE, ), ), - ), + ) ), array( 'foo' => array( 'bar' => array( + 'foo' => 'php', array( - 'foo' => 'bar', 'bar' => TRUE, ), ), @@ -311,14 +308,14 @@ public function provider_merge() array( 'foo' => array( 'bar' => array( + 'foo' => TRUE, array( - 'foo1' => 'foo', 'bar1' => FALSE, ), ), ), ) - ) + ), ); } From 7ca1fe68ada69cee45f631c49cf0885de7d8d040 Mon Sep 17 00:00:00 2001 From: Lorenzo Pisani Date: Sun, 30 Sep 2012 21:30:28 -0700 Subject: [PATCH 017/106] the third parameter is the one you are matching, not the second refs (#4432) --- messages/validation.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/messages/validation.php b/messages/validation.php index dcaa38987..345a5e8e0 100644 --- a/messages/validation.php +++ b/messages/validation.php @@ -15,7 +15,7 @@ 'exact_length' => ':field must be exactly :param2 characters long', 'in_array' => ':field must be one of the available options', 'ip' => ':field must be an ip address', - 'matches' => ':field must be the same as :param2', + 'matches' => ':field must be the same as :param3', 'min_length' => ':field must be at least :param2 characters long', 'max_length' => ':field must not exceed :param2 characters long', 'not_empty' => ':field must not be empty', From 29073be965cbaa84e685d33307d9a1a935d662d7 Mon Sep 17 00:00:00 2001 From: Lorenzo Pisani Date: Sat, 11 Jan 2014 15:39:48 -0800 Subject: [PATCH 018/106] refs #3896 - fix unit tests for proper matches validation --- tests/kohana/CoreTest.php | 2 +- tests/kohana/ValidationTest.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/kohana/CoreTest.php b/tests/kohana/CoreTest.php index 5f4adf0d8..cd70bd024 100644 --- a/tests/kohana/CoreTest.php +++ b/tests/kohana/CoreTest.php @@ -171,7 +171,7 @@ public function provider_message() 'exact_length' => ':field must be exactly :param2 characters long', 'in_array' => ':field must be one of the available options', 'ip' => ':field must be an ip address', - 'matches' => ':field must be the same as :param2', + 'matches' => ':field must be the same as :param3', 'min_length' => ':field must be at least :param2 characters long', 'max_length' => ':field must not exceed :param2 characters long', 'not_empty' => ':field must not be empty', diff --git a/tests/kohana/ValidationTest.php b/tests/kohana/ValidationTest.php index b29aaecb3..6acb292fa 100644 --- a/tests/kohana/ValidationTest.php +++ b/tests/kohana/ValidationTest.php @@ -542,7 +542,7 @@ public function test_data_stays_unaltered() public function test_object_parameters_not_in_messages() { $validation = Validation::factory(array('foo' => 'foo')) - ->rule('bar', 'matches', array(':validation', 'foo', ':field')); + ->rule('bar', 'matches', array(':validation', ':field', 'foo')); $validation->check(); $errors = $validation->errors('validation'); From 8f99de40049c39932faa0f568de422e5b82294f6 Mon Sep 17 00:00:00 2001 From: Lorenzo Pisani Date: Sat, 11 Jan 2014 17:32:59 -0800 Subject: [PATCH 019/106] refs #4287 - fix memory leak in validation class --- classes/kohana/validation.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/classes/kohana/validation.php b/classes/kohana/validation.php index b0232686d..1c8d8abb3 100644 --- a/classes/kohana/validation.php +++ b/classes/kohana/validation.php @@ -430,6 +430,13 @@ public function check() } } + // Unbind all the automatic bindings to avoid memory leaks. + unset($this->_bound[':validation']); + unset($this->_bound[':data']); + unset($this->_bound[':field']); + unset($this->_bound[':value']); + + // Restore the data to its original form $this->_data = $original; From 14342a5f9678d243f5483303c80b15c4cef87085 Mon Sep 17 00:00:00 2001 From: Samuel Demirdjian Date: Sun, 12 Jan 2014 12:58:59 -0800 Subject: [PATCH 020/106] Avoid division by zero --- views/profiler/stats.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/views/profiler/stats.php b/views/profiler/stats.php index ebfb489b7..c7cc550f9 100755 --- a/views/profiler/stats.php +++ b/views/profiler/stats.php @@ -35,7 +35,7 @@
    s
    -
    +
    @@ -47,7 +47,7 @@
    kB
    -
    +
    @@ -71,4 +71,4 @@ - \ No newline at end of file + From 1ad289c152b8622a31b549c847a3e85f3e007c08 Mon Sep 17 00:00:00 2001 From: Lorenzo Pisani Date: Sun, 12 Jan 2014 19:35:33 -0800 Subject: [PATCH 021/106] Follow up to 3bc535eb536e54451b722da9699d052c863ac1b4, determine the download mime by the extension rather than the actual download file (which will probably not exist). Refs #4344 refs #4592 Conflicts: classes/kohana/response.php --- classes/kohana/response.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/classes/kohana/response.php b/classes/kohana/response.php index 51381fc20..420dc9a07 100644 --- a/classes/kohana/response.php +++ b/classes/kohana/response.php @@ -445,8 +445,8 @@ public function send_file($filename, $download = NULL, array $options = NULL) if ( ! isset($mime)) { - // Get the mime type - $mime = File::mime_by_ext(strtolower(pathinfo($download, PATHINFO_EXTENSION))); + // Get the mime type from the extension of the download file + $mime = File::mime_by_ext(pathinfo($download, PATHINFO_EXTENSION)); } // Open the file for reading From db3e91fc7a15b2a47f33004b36cd7a2ac57af55c Mon Sep 17 00:00:00 2001 From: Samuel Demirdjian Date: Sun, 12 Jan 2014 20:51:15 -0800 Subject: [PATCH 022/106] Replaced empty string with zero Outputing as inline style, so empty string doesn't help --- views/profiler/stats.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/views/profiler/stats.php b/views/profiler/stats.php index c7cc550f9..f134cf824 100755 --- a/views/profiler/stats.php +++ b/views/profiler/stats.php @@ -35,7 +35,7 @@
    s
    -
    +
    @@ -47,7 +47,7 @@
    kB
    -
    +
    From d93caff9f563c6ee830d7ada01607b1906bdb09e Mon Sep 17 00:00:00 2001 From: Samuel Demirdjian Date: Sun, 12 Jan 2014 23:21:03 -0800 Subject: [PATCH 023/106] replaced occurances of kohanaphp.com with kohanaframework.org except the occurance: github.com/isaiahdw/kohanaphp.com/blob/f2afe8e28b/application/bootstrap.php was not replaced --- guide/kohana/bootstrap.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/guide/kohana/bootstrap.md b/guide/kohana/bootstrap.md index 2b8c26ad5..19379249a 100644 --- a/guide/kohana/bootstrap.md +++ b/guide/kohana/bootstrap.md @@ -52,7 +52,7 @@ You can add conditional statements to make the bootstrap have different values b /** * Set the environment status by the domain. */ -if (strpos($_SERVER['HTTP_HOST'], 'kohanaphp.com') !== FALSE) +if (strpos($_SERVER['HTTP_HOST'], 'kohanaframework.org') !== FALSE) { // We are live! Kohana::$environment = Kohana::PRODUCTION; @@ -66,7 +66,7 @@ if (strpos($_SERVER['HTTP_HOST'], 'kohanaphp.com') !== FALSE) ... [trimmed] */ Kohana::init(array( - 'base_url' => Kohana::$environment === Kohana::PRODUCTION ? '/' : '/kohanaphp.com/', + 'base_url' => Kohana::$environment === Kohana::PRODUCTION ? '/' : '/kohanaframework.org/', 'caching' => Kohana::$environment === Kohana::PRODUCTION, 'profile' => Kohana::$environment !== Kohana::PRODUCTION, 'index_file' => FALSE, From 4fc1eaba0bb4bd93f8548d01cc8908e43b3505f6 Mon Sep 17 00:00:00 2001 From: Samuel Demirdjian Date: Sun, 12 Jan 2014 23:25:35 -0800 Subject: [PATCH 024/106] replaced occurances of kohanaphp.com with kohanaframework.org --- guide/kohana/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/guide/kohana/index.md b/guide/kohana/index.md index 102439634..32c308751 100644 --- a/guide/kohana/index.md +++ b/guide/kohana/index.md @@ -20,4 +20,4 @@ We are working very hard to provide complete documentation. To help improve the ## Unofficial Documentation -If you are having trouble finding an answer here, have a look through the [unofficial wiki](http://kerkness.ca/wiki/doku.php). Your answer may also be found by searching the [forum](http://forum.kohanaphp.com/) or [stackoverflow](http://stackoverflow.com/questions/tagged/kohana) followed by asking your question on either. Additionally, you can chat with the community of developers on the freenode [#kohana](irc://irc.freenode.net/kohana) IRC channel. \ No newline at end of file +If you are having trouble finding an answer here, have a look through the [unofficial wiki](http://kerkness.ca/wiki/doku.php). Your answer may also be found by searching the [forum](http://forum.kohanaframework.org/) or [stackoverflow](http://stackoverflow.com/questions/tagged/kohana) followed by asking your question on either. Additionally, you can chat with the community of developers on the freenode [#kohana](irc://irc.freenode.net/kohana) IRC channel. From 6f73da3eaa66c833addd753af93420e90a0fd147 Mon Sep 17 00:00:00 2001 From: Jack Ellis Date: Mon, 13 Jan 2014 09:15:21 +0000 Subject: [PATCH 025/106] Removed pointless array check and moved uri's key & value to provider --- classes/kohana/route.php | 2 +- tests/kohana/RouteTest.php | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/classes/kohana/route.php b/classes/kohana/route.php index 19d47e970..b4c5dfc8f 100644 --- a/classes/kohana/route.php +++ b/classes/kohana/route.php @@ -456,7 +456,7 @@ public function is_external() */ public function uri(array $params = NULL) { - if (is_array($params)) + if ($params) { $params = array_map('rawurlencode', $params); } diff --git a/tests/kohana/RouteTest.php b/tests/kohana/RouteTest.php index 30104cd39..86ad5ac7c 100644 --- a/tests/kohana/RouteTest.php +++ b/tests/kohana/RouteTest.php @@ -731,6 +731,8 @@ public function provider_route_uri_encode_parameters() 'controller' => 'home', 'action' => 'index' ), + 'article_name', + 'Article name with special chars \\ ##', 'blog/article/Article%20name%20with%20special%20chars%20%5C%20%23%23' ) ); @@ -744,11 +746,11 @@ public function provider_route_uri_encode_parameters() * @ticket 4079 * @dataProvider provider_route_uri_encode_parameters */ - public function test_route_uri_encode_parameters($name, $uri_callback, $defaults, $expected) + public function test_route_uri_encode_parameters($name, $uri_callback, $defaults, $uri_key, $uri_value, $expected) { Route::set($name, $uri_callback)->defaults($defaults); - $get_route_uri = Route::get($name)->uri(array('article_name' => 'Article name with special chars \\ ##')); + $get_route_uri = Route::get($name)->uri(array($uri_key => $uri_value)); $this->assertSame($expected, $get_route_uri); } From 6fb0eff9b9c314d536d0f1d6963a25044309dd70 Mon Sep 17 00:00:00 2001 From: biakaveron Date: Mon, 13 Jan 2014 22:10:57 +0400 Subject: [PATCH 026/106] Internal routing must ignore external routes, fixes #4235 --- classes/kohana/request.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/classes/kohana/request.php b/classes/kohana/request.php index c362076f5..5436c7474 100644 --- a/classes/kohana/request.php +++ b/classes/kohana/request.php @@ -578,6 +578,12 @@ public static function process_uri($uri, $routes = NULL) foreach ($routes as $name => $route) { + // Use external routes for reverse routing only + if ($route->is_external()) + { + continue; + } + // We found something suitable if ($params = $route->matches($uri)) { From 83f8f781b98217c0bd9d32f7cdc815ff9dbd051c Mon Sep 17 00:00:00 2001 From: Samuel Demirdjian Date: Thu, 23 Jan 2014 16:41:25 +0200 Subject: [PATCH 027/106] created failing test --- tests/kohana/TextTest.php | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/tests/kohana/TextTest.php b/tests/kohana/TextTest.php index 1d5d867c9..93ce7ee5a 100644 --- a/tests/kohana/TextTest.php +++ b/tests/kohana/TextTest.php @@ -384,9 +384,23 @@ function provider_widont() { return array ( - array('No gain, no pain', 'No gain, no pain'), - array("spaces?what'rethey?", "spaces?what'rethey?"), - array('', ''), + array( + 'No gain, no pain', + 'No gain, no pain', + ), + array( + "spaces?what'rethey?", + "spaces?what'rethey?", + ), + // @issue 3499 + array( + 'with HTML at the end: Kohana', + 'with HTML at the end: Kohana', + ), + array( + '', + '', + ), ); } From c0cc9a97e59c799e24ba5d4966d58f16a0f23b31 Mon Sep 17 00:00:00 2001 From: Samuel Demirdjian Date: Thu, 23 Jan 2014 16:43:54 +0200 Subject: [PATCH 028/106] fixed with regex courtesy of the Typogrify project --- classes/kohana/text.php | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/classes/kohana/text.php b/classes/kohana/text.php index 17dcca3a8..e3d48fd43 100644 --- a/classes/kohana/text.php +++ b/classes/kohana/text.php @@ -587,20 +587,23 @@ public static function number($number) * * echo Text::widont($text); * + * regex courtesy of the Typogrify project + * @link http://code.google.com/p/typogrify/ + * * @param string $str text to remove widows from * @return string */ public static function widont($str) { - $str = rtrim($str); - $space = strrpos($str, ' '); - - if ($space !== FALSE) - { - $str = substr($str, 0, $space).' '.substr($str, $space + 1); - } - - return $str; + $widont_regex = " + ((?:]*>)|[^<>\s]) # must be proceeded by an approved inline opening or closing tag or a nontag/nonspace + \s+ # the space to replace + ([^<>\s]+ # must be flollowed by non-tag non-space characters + \s* # optional white space! + (\s*)* # optional closing inline tags with optional white space after each + (()|$)) # end with a closing p, h1-6, li or the end of the string + "; + return preg_replace($widont_regex, ' $2', $str); } } // End text From 8d7b304181c0a637fdfea55c13a7630ba7a3384c Mon Sep 17 00:00:00 2001 From: Samuel Demirdjian Date: Fri, 24 Jan 2014 15:54:23 +0200 Subject: [PATCH 029/106] fixed phpdoc for Request::url function params Removed * @param array $params URI parameters --- classes/kohana/request.php | 1 - 1 file changed, 1 deletion(-) diff --git a/classes/kohana/request.php b/classes/kohana/request.php index c362076f5..a2993e51f 100644 --- a/classes/kohana/request.php +++ b/classes/kohana/request.php @@ -899,7 +899,6 @@ public function uri() * * echo URL::site($this->request->uri(), $protocol); * - * @param array $params URI parameters * @param mixed $protocol protocol string or Request object * @return string * @since 3.0.7 From 89d0d99df4cf82003fabade3bde64fce41fde7ff Mon Sep 17 00:00:00 2001 From: Samuel Demirdjian Date: Fri, 24 Jan 2014 15:58:04 +0200 Subject: [PATCH 030/106] fixed phpdoc for test_url function --- tests/kohana/RequestTest.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/kohana/RequestTest.php b/tests/kohana/RequestTest.php index 3941d5488..6e332f0f6 100644 --- a/tests/kohana/RequestTest.php +++ b/tests/kohana/RequestTest.php @@ -310,10 +310,10 @@ public function provider_url() * @test * @dataProvider provider_url * @covers Request::url - * @param string $route the route to use - * @param array $params params to pass to route::uri - * @param string $protocol the protocol to use - * @param array $expected The string we expect + * @param string $uri the uri to use + * @param mixed $protocol the protocol to use (string or Request object) + * @param bool $is_cli whether the call is from CLI or not + * @param string $expected The url string we expect */ public function test_url($uri, $protocol, $is_cli, $expected) { From 27f3f75114d69e37c74dbadb8f490dbc5b4fa5c3 Mon Sep 17 00:00:00 2001 From: Samuel Demirdjian Date: Fri, 24 Jan 2014 16:04:57 +0200 Subject: [PATCH 031/106] add the failing test --- tests/kohana/RequestTest.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/kohana/RequestTest.php b/tests/kohana/RequestTest.php index 6e332f0f6..a3a3c1fbf 100644 --- a/tests/kohana/RequestTest.php +++ b/tests/kohana/RequestTest.php @@ -301,6 +301,12 @@ public function provider_url() TRUE, 'http://localhost/kohana/foo' ), + array( + 'http://www.google.com', + 'http', + TRUE, + 'http://www.google.com' + ), ); } From c14bbbddc34717636111c2bc90a3a90869be13ea Mon Sep 17 00:00:00 2001 From: Samuel Demirdjian Date: Fri, 24 Jan 2014 16:05:46 +0200 Subject: [PATCH 032/106] add a condition to test if the url is external --- classes/kohana/request.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/classes/kohana/request.php b/classes/kohana/request.php index a2993e51f..9ee2854ee 100644 --- a/classes/kohana/request.php +++ b/classes/kohana/request.php @@ -906,8 +906,11 @@ public function uri() */ public function url($protocol = NULL) { - // Create a URI with the current route and convert it to a URL - return URL::site($this->uri(), $protocol); + return $this->is_external() ? + // if it is an external request return the uri + $this->uri() : + // Create a URI with the current route and convert it to a URL + URL::site($this->uri(), $protocol); } /** From 1f86402f4735a74077acac0ff24d7b2e2a632195 Mon Sep 17 00:00:00 2001 From: Samuel Demirdjian Date: Fri, 24 Jan 2014 22:44:01 +0200 Subject: [PATCH 033/106] Use Kohana_UTF8 from version 3.3 to apply all the patches Fixes Kohana_UTF8Test::test_clean with data set no.3 and Kohana_DebugTest::test_dump with data set no.6 --- classes/kohana/utf8.php | 112 +++++++++++++++++++++------------------- 1 file changed, 60 insertions(+), 52 deletions(-) diff --git a/classes/kohana/utf8.php b/classes/kohana/utf8.php index c3135d3fd..386ed8a87 100644 --- a/classes/kohana/utf8.php +++ b/classes/kohana/utf8.php @@ -63,21 +63,21 @@ public static function clean($var, $charset = NULL) foreach ($var as $key => $val) { // Recursion! - $var[self::clean($key)] = self::clean($val); + $var[UTF8::clean($key)] = UTF8::clean($val); } } elseif (is_string($var) AND $var !== '') { // Remove control characters - $var = self::strip_ascii_ctrl($var); + $var = UTF8::strip_ascii_ctrl($var); - if ( ! self::is_ascii($var)) + if ( ! UTF8::is_ascii($var)) { // Disable notices $error_reporting = error_reporting(~E_NOTICE); // iconv is expensive, so it is only used when needed - $var = iconv($charset, $charset.'//IGNORE', $var); + $var = mb_convert_encoding($var, $charset, $charset); // Turn notices back on error_reporting($error_reporting); @@ -144,12 +144,12 @@ public static function strip_non_ascii($str) */ public static function transliterate_to_ascii($str, $case = 0) { - if ( ! isset(self::$called[__FUNCTION__])) + if ( ! isset(UTF8::$called[__FUNCTION__])) { require Kohana::find_file('utf8', __FUNCTION__); // Function has been called - self::$called[__FUNCTION__] = TRUE; + UTF8::$called[__FUNCTION__] = TRUE; } return _transliterate_to_ascii($str, $case); @@ -170,12 +170,12 @@ public static function strlen($str) if (UTF8::$server_utf8) return mb_strlen($str, Kohana::$charset); - if ( ! isset(self::$called[__FUNCTION__])) + if ( ! isset(UTF8::$called[__FUNCTION__])) { require Kohana::find_file('utf8', __FUNCTION__); // Function has been called - self::$called[__FUNCTION__] = TRUE; + UTF8::$called[__FUNCTION__] = TRUE; } return _strlen($str); @@ -200,12 +200,12 @@ public static function strpos($str, $search, $offset = 0) if (UTF8::$server_utf8) return mb_strpos($str, $search, $offset, Kohana::$charset); - if ( ! isset(self::$called[__FUNCTION__])) + if ( ! isset(UTF8::$called[__FUNCTION__])) { require Kohana::find_file('utf8', __FUNCTION__); // Function has been called - self::$called[__FUNCTION__] = TRUE; + UTF8::$called[__FUNCTION__] = TRUE; } return _strpos($str, $search, $offset); @@ -230,12 +230,12 @@ public static function strrpos($str, $search, $offset = 0) if (UTF8::$server_utf8) return mb_strrpos($str, $search, $offset, Kohana::$charset); - if ( ! isset(self::$called[__FUNCTION__])) + if ( ! isset(UTF8::$called[__FUNCTION__])) { require Kohana::find_file('utf8', __FUNCTION__); // Function has been called - self::$called[__FUNCTION__] = TRUE; + UTF8::$called[__FUNCTION__] = TRUE; } return _strrpos($str, $search, $offset); @@ -262,12 +262,12 @@ public static function substr($str, $offset, $length = NULL) ? mb_substr($str, $offset, mb_strlen($str), Kohana::$charset) : mb_substr($str, $offset, $length, Kohana::$charset); - if ( ! isset(self::$called[__FUNCTION__])) + if ( ! isset(UTF8::$called[__FUNCTION__])) { require Kohana::find_file('utf8', __FUNCTION__); // Function has been called - self::$called[__FUNCTION__] = TRUE; + UTF8::$called[__FUNCTION__] = TRUE; } return _substr($str, $offset, $length); @@ -287,12 +287,12 @@ public static function substr($str, $offset, $length = NULL) */ public static function substr_replace($str, $replacement, $offset, $length = NULL) { - if ( ! isset(self::$called[__FUNCTION__])) + if ( ! isset(UTF8::$called[__FUNCTION__])) { require Kohana::find_file('utf8', __FUNCTION__); // Function has been called - self::$called[__FUNCTION__] = TRUE; + UTF8::$called[__FUNCTION__] = TRUE; } return _substr_replace($str, $replacement, $offset, $length); @@ -314,12 +314,12 @@ public static function strtolower($str) if (UTF8::$server_utf8) return mb_strtolower($str, Kohana::$charset); - if ( ! isset(self::$called[__FUNCTION__])) + if ( ! isset(UTF8::$called[__FUNCTION__])) { require Kohana::find_file('utf8', __FUNCTION__); // Function has been called - self::$called[__FUNCTION__] = TRUE; + UTF8::$called[__FUNCTION__] = TRUE; } return _strtolower($str); @@ -340,12 +340,12 @@ public static function strtoupper($str) if (UTF8::$server_utf8) return mb_strtoupper($str, Kohana::$charset); - if ( ! isset(self::$called[__FUNCTION__])) + if ( ! isset(UTF8::$called[__FUNCTION__])) { require Kohana::find_file('utf8', __FUNCTION__); // Function has been called - self::$called[__FUNCTION__] = TRUE; + UTF8::$called[__FUNCTION__] = TRUE; } return _strtoupper($str); @@ -363,12 +363,12 @@ public static function strtoupper($str) */ public static function ucfirst($str) { - if ( ! isset(self::$called[__FUNCTION__])) + if ( ! isset(UTF8::$called[__FUNCTION__])) { require Kohana::find_file('utf8', __FUNCTION__); // Function has been called - self::$called[__FUNCTION__] = TRUE; + UTF8::$called[__FUNCTION__] = TRUE; } return _ucfirst($str); @@ -387,12 +387,12 @@ public static function ucfirst($str) */ public static function ucwords($str) { - if ( ! isset(self::$called[__FUNCTION__])) + if ( ! isset(UTF8::$called[__FUNCTION__])) { require Kohana::find_file('utf8', __FUNCTION__); // Function has been called - self::$called[__FUNCTION__] = TRUE; + UTF8::$called[__FUNCTION__] = TRUE; } return _ucwords($str); @@ -413,12 +413,12 @@ public static function ucwords($str) */ public static function strcasecmp($str1, $str2) { - if ( ! isset(self::$called[__FUNCTION__])) + if ( ! isset(UTF8::$called[__FUNCTION__])) { require Kohana::find_file('utf8', __FUNCTION__); // Function has been called - self::$called[__FUNCTION__] = TRUE; + UTF8::$called[__FUNCTION__] = TRUE; } return _strcasecmp($str1, $str2); @@ -442,12 +442,12 @@ public static function strcasecmp($str1, $str2) */ public static function str_ireplace($search, $replace, $str, & $count = NULL) { - if ( ! isset(self::$called[__FUNCTION__])) + if ( ! isset(UTF8::$called[__FUNCTION__])) { require Kohana::find_file('utf8', __FUNCTION__); // Function has been called - self::$called[__FUNCTION__] = TRUE; + UTF8::$called[__FUNCTION__] = TRUE; } return _str_ireplace($search, $replace, $str, $count); @@ -468,12 +468,12 @@ public static function str_ireplace($search, $replace, $str, & $count = NULL) */ public static function stristr($str, $search) { - if ( ! isset(self::$called[__FUNCTION__])) + if ( ! isset(UTF8::$called[__FUNCTION__])) { require Kohana::find_file('utf8', __FUNCTION__); // Function has been called - self::$called[__FUNCTION__] = TRUE; + UTF8::$called[__FUNCTION__] = TRUE; } return _stristr($str, $search); @@ -494,12 +494,12 @@ public static function stristr($str, $search) */ public static function strspn($str, $mask, $offset = NULL, $length = NULL) { - if ( ! isset(self::$called[__FUNCTION__])) + if ( ! isset(UTF8::$called[__FUNCTION__])) { require Kohana::find_file('utf8', __FUNCTION__); // Function has been called - self::$called[__FUNCTION__] = TRUE; + UTF8::$called[__FUNCTION__] = TRUE; } return _strspn($str, $mask, $offset, $length); @@ -520,12 +520,12 @@ public static function strspn($str, $mask, $offset = NULL, $length = NULL) */ public static function strcspn($str, $mask, $offset = NULL, $length = NULL) { - if ( ! isset(self::$called[__FUNCTION__])) + if ( ! isset(UTF8::$called[__FUNCTION__])) { require Kohana::find_file('utf8', __FUNCTION__); // Function has been called - self::$called[__FUNCTION__] = TRUE; + UTF8::$called[__FUNCTION__] = TRUE; } return _strcspn($str, $mask, $offset, $length); @@ -546,12 +546,12 @@ public static function strcspn($str, $mask, $offset = NULL, $length = NULL) */ public static function str_pad($str, $final_str_length, $pad_str = ' ', $pad_type = STR_PAD_RIGHT) { - if ( ! isset(self::$called[__FUNCTION__])) + if ( ! isset(UTF8::$called[__FUNCTION__])) { require Kohana::find_file('utf8', __FUNCTION__); // Function has been called - self::$called[__FUNCTION__] = TRUE; + UTF8::$called[__FUNCTION__] = TRUE; } return _str_pad($str, $final_str_length, $pad_str, $pad_type); @@ -570,12 +570,12 @@ public static function str_pad($str, $final_str_length, $pad_str = ' ', $pad_typ */ public static function str_split($str, $split_length = 1) { - if ( ! isset(self::$called[__FUNCTION__])) + if ( ! isset(UTF8::$called[__FUNCTION__])) { require Kohana::find_file('utf8', __FUNCTION__); // Function has been called - self::$called[__FUNCTION__] = TRUE; + UTF8::$called[__FUNCTION__] = TRUE; } return _str_split($str, $split_length); @@ -592,12 +592,12 @@ public static function str_split($str, $split_length = 1) */ public static function strrev($str) { - if ( ! isset(self::$called[__FUNCTION__])) + if ( ! isset(UTF8::$called[__FUNCTION__])) { require Kohana::find_file('utf8', __FUNCTION__); // Function has been called - self::$called[__FUNCTION__] = TRUE; + UTF8::$called[__FUNCTION__] = TRUE; } return _strrev($str); @@ -616,12 +616,12 @@ public static function strrev($str) */ public static function trim($str, $charlist = NULL) { - if ( ! isset(self::$called[__FUNCTION__])) + if ( ! isset(UTF8::$called[__FUNCTION__])) { require Kohana::find_file('utf8', __FUNCTION__); // Function has been called - self::$called[__FUNCTION__] = TRUE; + UTF8::$called[__FUNCTION__] = TRUE; } return _trim($str, $charlist); @@ -640,12 +640,12 @@ public static function trim($str, $charlist = NULL) */ public static function ltrim($str, $charlist = NULL) { - if ( ! isset(self::$called[__FUNCTION__])) + if ( ! isset(UTF8::$called[__FUNCTION__])) { require Kohana::find_file('utf8', __FUNCTION__); // Function has been called - self::$called[__FUNCTION__] = TRUE; + UTF8::$called[__FUNCTION__] = TRUE; } return _ltrim($str, $charlist); @@ -664,12 +664,12 @@ public static function ltrim($str, $charlist = NULL) */ public static function rtrim($str, $charlist = NULL) { - if ( ! isset(self::$called[__FUNCTION__])) + if ( ! isset(UTF8::$called[__FUNCTION__])) { require Kohana::find_file('utf8', __FUNCTION__); // Function has been called - self::$called[__FUNCTION__] = TRUE; + UTF8::$called[__FUNCTION__] = TRUE; } return _rtrim($str, $charlist); @@ -687,12 +687,12 @@ public static function rtrim($str, $charlist = NULL) */ public static function ord($chr) { - if ( ! isset(self::$called[__FUNCTION__])) + if ( ! isset(UTF8::$called[__FUNCTION__])) { require Kohana::find_file('utf8', __FUNCTION__); // Function has been called - self::$called[__FUNCTION__] = TRUE; + UTF8::$called[__FUNCTION__] = TRUE; } return _ord($chr); @@ -717,12 +717,12 @@ public static function ord($chr) */ public static function to_unicode($str) { - if ( ! isset(self::$called[__FUNCTION__])) + if ( ! isset(UTF8::$called[__FUNCTION__])) { require Kohana::find_file('utf8', __FUNCTION__); // Function has been called - self::$called[__FUNCTION__] = TRUE; + UTF8::$called[__FUNCTION__] = TRUE; } return _to_unicode($str); @@ -747,12 +747,12 @@ public static function to_unicode($str) */ public static function from_unicode($arr) { - if ( ! isset(self::$called[__FUNCTION__])) + if ( ! isset(UTF8::$called[__FUNCTION__])) { require Kohana::find_file('utf8', __FUNCTION__); // Function has been called - self::$called[__FUNCTION__] = TRUE; + UTF8::$called[__FUNCTION__] = TRUE; } return _from_unicode($arr); @@ -760,6 +760,14 @@ public static function from_unicode($arr) } // End UTF8 +/** + * Set the mb_substitute_character to "none" + * + * @link http://www.php.net/manual/function.mb-substitute-character.php + */ + +mb_substitute_character('none'); + if (Kohana_UTF8::$server_utf8 === NULL) { // Determine if this server supports UTF-8 natively From eba1b22f9c85ab40ae7e7f80a9a736fd14b87441 Mon Sep 17 00:00:00 2001 From: Samuel Demirdjian Date: Sat, 25 Jan 2014 09:26:47 +0200 Subject: [PATCH 034/106] Set and reset mb_substitute_character To disable substituting illigal characters with the default `'?'` character. I think this solution is better than to set `mb_substitute_character('none');` at the end of the file. I also think this solution makes the setting of `mb_substitute_character('none');` in `bootstrap.php` redundant. --- classes/kohana/utf8.php | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/classes/kohana/utf8.php b/classes/kohana/utf8.php index 386ed8a87..4e5a26282 100644 --- a/classes/kohana/utf8.php +++ b/classes/kohana/utf8.php @@ -73,14 +73,19 @@ public static function clean($var, $charset = NULL) if ( ! UTF8::is_ascii($var)) { - // Disable notices - $error_reporting = error_reporting(~E_NOTICE); + // Set the mb_substitute_character() value into temporary variable + $mb_substitute_character = mb_substitute_character(); + + // Disable substituting illigal characters with the default '?' character + mb_substitute_character('none'); + // iconv is expensive, so it is only used when needed $var = mb_convert_encoding($var, $charset, $charset); + + // Reset mb_substitute_character() value back to the original setting + mb_substitute_character($mb_substitute_character); - // Turn notices back on - error_reporting($error_reporting); } } @@ -760,14 +765,6 @@ public static function from_unicode($arr) } // End UTF8 -/** - * Set the mb_substitute_character to "none" - * - * @link http://www.php.net/manual/function.mb-substitute-character.php - */ - -mb_substitute_character('none'); - if (Kohana_UTF8::$server_utf8 === NULL) { // Determine if this server supports UTF-8 natively From 3fb562f9c12cd222526aa64e29e0aa84da5114e1 Mon Sep 17 00:00:00 2001 From: Samuel Demirdjian Date: Sat, 25 Jan 2014 09:28:32 +0200 Subject: [PATCH 035/106] replaced iconv with mb_convert_encoding in comments --- classes/kohana/utf8.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/classes/kohana/utf8.php b/classes/kohana/utf8.php index 4e5a26282..72fba8966 100644 --- a/classes/kohana/utf8.php +++ b/classes/kohana/utf8.php @@ -80,7 +80,7 @@ public static function clean($var, $charset = NULL) // Disable substituting illigal characters with the default '?' character mb_substitute_character('none'); - // iconv is expensive, so it is only used when needed + // mb_convert_encoding is expensive, so it is only used when needed $var = mb_convert_encoding($var, $charset, $charset); // Reset mb_substitute_character() value back to the original setting From 3a8443e53c2fe462af3cd9e22fc2e29828ecfa2c Mon Sep 17 00:00:00 2001 From: Samuel Demirdjian Date: Tue, 28 Jan 2014 05:56:37 -0800 Subject: [PATCH 036/106] default to original ini setting if Cookie::$domain not set We should not let PHP override the default setting if Cookie::$domain is not set. Otherwise it will send cookies with the host name of the server which generated the cookie. --- classes/kohana/session/native.php | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/classes/kohana/session/native.php b/classes/kohana/session/native.php index 0858f2ab7..e7edf057f 100644 --- a/classes/kohana/session/native.php +++ b/classes/kohana/session/native.php @@ -24,8 +24,29 @@ public function id() */ protected function _read($id = NULL) { + // session_set_cookie_params will override php ini settings + // If Cookie::$domain is NULL or empty and is passed, PHP + // will override ini and sent cookies with the host name + // of the server which generated the cookie + // + // see issue #3604 + // + // see http://www.php.net/manual/en/function.session-set-cookie-params.php + // see http://www.php.net/manual/en/session.configuration.php#ini.session.cookie-domain + // + // set to Cookie::$domain if available, otherwise default to ini setting + $session_cookie_domain = empty(Cookie::$domain) + ? ini_get('session.cookie_domain') + : Cookie::$domain; + // Sync up the session cookie with Cookie parameters - session_set_cookie_params($this->_lifetime, Cookie::$path, Cookie::$domain, Cookie::$secure, Cookie::$httponly); + session_set_cookie_params( + $this->_lifetime, + Cookie::$path, + $session_cookie_domain, + Cookie::$secure, + Cookie::$httponly + ); // Do not allow PHP to send Cache-Control headers session_cache_limiter(FALSE); From 84ebc36a082ca805c009f2ed59ea4cc38a820cc0 Mon Sep 17 00:00:00 2001 From: Samuel Demirdjian Date: Wed, 29 Jan 2014 07:56:55 -0800 Subject: [PATCH 037/106] Adjusted ternary operator to comply with coding standards --- classes/kohana/request.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/classes/kohana/request.php b/classes/kohana/request.php index 9ee2854ee..df562e335 100644 --- a/classes/kohana/request.php +++ b/classes/kohana/request.php @@ -906,11 +906,12 @@ public function uri() */ public function url($protocol = NULL) { - return $this->is_external() ? + // test if the request is external + return $this->is_external() // if it is an external request return the uri - $this->uri() : + ? $this->uri() // Create a URI with the current route and convert it to a URL - URL::site($this->uri(), $protocol); + : URL::site($this->uri(), $protocol); } /** From 071fab978ba43c44d70cbb87c838fdb78eb93eca Mon Sep 17 00:00:00 2001 From: Samuel Demirdjian Date: Wed, 29 Jan 2014 08:01:20 -0800 Subject: [PATCH 038/106] Make it more readable, as suggested by @WinterSilence --- classes/kohana/request.php | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/classes/kohana/request.php b/classes/kohana/request.php index df562e335..3e33cd939 100644 --- a/classes/kohana/request.php +++ b/classes/kohana/request.php @@ -906,14 +906,15 @@ public function uri() */ public function url($protocol = NULL) { - // test if the request is external - return $this->is_external() - // if it is an external request return the uri - ? $this->uri() - // Create a URI with the current route and convert it to a URL - : URL::site($this->uri(), $protocol); - } + if ($this->is_external()) + { + // If it's an external request return the URI + return $this->uri(); + } + // Create a URI with the current route, convert to a URL and returns + return URL::site($this->uri(), $protocol); + } /** * Retrieves a value from the route parameters. * From c17a98407dc4ea653dbcc7160725f62bf850a66e Mon Sep 17 00:00:00 2001 From: Rob Taylor Date: Wed, 29 Jan 2014 16:34:09 +0000 Subject: [PATCH 039/106] Fixed typo in comments for Kohana_View::__toString() --- classes/Kohana/View.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/classes/Kohana/View.php b/classes/Kohana/View.php index 9a45c3548..662a9327a 100644 --- a/classes/Kohana/View.php +++ b/classes/Kohana/View.php @@ -232,7 +232,7 @@ public function __toString() /** * Display the exception message. * - * We use this method here because it's impossible to throw and + * We use this method here because it's impossible to throw an * exception from __toString(). */ $error_response = Kohana_Exception::_handler($e); From e55bffd1f78e5e1a179c70c328eccc25cb951333 Mon Sep 17 00:00:00 2001 From: Samuel Demirdjian Date: Wed, 29 Jan 2014 10:06:01 -0800 Subject: [PATCH 040/106] commented comments to a multi line comment block as suggested by @Zeelot --- classes/kohana/session/native.php | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/classes/kohana/session/native.php b/classes/kohana/session/native.php index e7edf057f..626f6bd16 100644 --- a/classes/kohana/session/native.php +++ b/classes/kohana/session/native.php @@ -24,17 +24,19 @@ public function id() */ protected function _read($id = NULL) { - // session_set_cookie_params will override php ini settings - // If Cookie::$domain is NULL or empty and is passed, PHP - // will override ini and sent cookies with the host name - // of the server which generated the cookie - // - // see issue #3604 - // - // see http://www.php.net/manual/en/function.session-set-cookie-params.php - // see http://www.php.net/manual/en/session.configuration.php#ini.session.cookie-domain - // - // set to Cookie::$domain if available, otherwise default to ini setting + /** + * session_set_cookie_params will override php ini settings + * If Cookie::$domain is NULL or empty and is passed, PHP + * will override ini and sent cookies with the host name + * of the server which generated the cookie + * + * see issue #3604 + * + * see http://www.php.net/manual/en/function.session-set-cookie-params.php + * see http://www.php.net/manual/en/session.configuration.php#ini.session.cookie-domain + * + * set to Cookie::$domain if available, otherwise default to ini setting + * / $session_cookie_domain = empty(Cookie::$domain) ? ini_get('session.cookie_domain') : Cookie::$domain; From 4d13bfbfc4ddba45ce45d30c27dc9de385823888 Mon Sep 17 00:00:00 2001 From: Samuel Demirdjian Date: Mon, 3 Feb 2014 03:44:20 -0800 Subject: [PATCH 041/106] Properly close multi-line comment (remove space) --- classes/kohana/session/native.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/classes/kohana/session/native.php b/classes/kohana/session/native.php index 626f6bd16..172d3b9e6 100644 --- a/classes/kohana/session/native.php +++ b/classes/kohana/session/native.php @@ -36,7 +36,7 @@ protected function _read($id = NULL) * see http://www.php.net/manual/en/session.configuration.php#ini.session.cookie-domain * * set to Cookie::$domain if available, otherwise default to ini setting - * / + */ $session_cookie_domain = empty(Cookie::$domain) ? ini_get('session.cookie_domain') : Cookie::$domain; From e722128f645a0b391f620a3bf6b49d5cb81ee721 Mon Sep 17 00:00:00 2001 From: Samuel Demirdjian Date: Wed, 5 Feb 2014 10:56:50 +0200 Subject: [PATCH 042/106] Added a bunch of tests from the Typogrify project Also, comment out the tests from issue no. 3499 --- tests/kohana/TextTest.php | 77 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 72 insertions(+), 5 deletions(-) diff --git a/tests/kohana/TextTest.php b/tests/kohana/TextTest.php index 93ce7ee5a..2084229c4 100644 --- a/tests/kohana/TextTest.php +++ b/tests/kohana/TextTest.php @@ -384,6 +384,66 @@ function provider_widont() { return array ( + // A very simple widont test + array( + 'A very simple test', + 'A very simple test', + ), + // Single word items shouldn't be changed + array( + 'Test', + 'Test', + ), + // Single word after single space shouldn't be changed either + array( + ' Test', + ' Test', + ), + // Single word with HTML all around + array( + '
    • Test

      • ', + '
        • Test

          • ', + ), + // Single word after single space with HTML all around + array( + '
            • Test

              • ', + '
                • Test

                  • ', + ), + // Widont with more than one paragraph + array( + '

                    In a couple of paragraphs

                    paragraph two

                    ', + '

                    In a couple of paragraphs

                    paragraph two

                    ', + ), + // a link inside a heading + array( + '

                    In a link inside a heading

                    ', + '

                    In a link inside a heading

                    ', + ), + // a link followed by text + array( + '

                    In a link followed by other text

                    ', + '

                    In a link followed by other text

                    ', + ), + // empty html, with no text inside + array( + '

                    ', + '

                    ', + ), + // apparently, we don't love DIVs + array( + '
                    Divs get no love!
                    ', + '
                    Divs get no love!
                    ', + ), + // we don't love PREs, either + array( + '
                    Neither do PREs
                    ', + '
                    Neither do PREs
                    ', + ), + // but we love DIVs with paragraphs + array( + '

                    But divs with paragraphs do!

                    ', + '

                    But divs with paragraphs do!

                    ', + ), array( 'No gain, no pain', 'No gain, no pain', @@ -392,11 +452,18 @@ function provider_widont() "spaces?what'rethey?", "spaces?what'rethey?", ), - // @issue 3499 - array( - 'with HTML at the end: Kohana', - 'with HTML at the end: Kohana', - ), + /* + * // @issue 3499, with HTML at the end + * array( + * 'with HTML at the end  Kohana', + * 'with HTML at the end Kohana', + * ), + * // @issue 3499, with HTML with attributes at the end + * array( + * 'with HTML at the end: Kohana', + * 'with HTML at the end: Kohana', + * ), + */ array( '', '', From 9f21e222bc1ef790fed3b4ba69c609808642c8c9 Mon Sep 17 00:00:00 2001 From: Samuel Demirdjian Date: Wed, 5 Feb 2014 10:57:48 +0200 Subject: [PATCH 043/106] re-bugfix use '%' as delimiter and 'x' as modifier Also, replace with '$1 $2' instead of ' $2' --- classes/kohana/text.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/classes/kohana/text.php b/classes/kohana/text.php index e3d48fd43..eb12254cf 100644 --- a/classes/kohana/text.php +++ b/classes/kohana/text.php @@ -595,15 +595,16 @@ public static function number($number) */ public static function widont($str) { - $widont_regex = " + // use '%' as delimiter and 'x' as modifier + $widont_regex = "% ((?:]*>)|[^<>\s]) # must be proceeded by an approved inline opening or closing tag or a nontag/nonspace \s+ # the space to replace ([^<>\s]+ # must be flollowed by non-tag non-space characters \s* # optional white space! (\s*)* # optional closing inline tags with optional white space after each (()|$)) # end with a closing p, h1-6, li or the end of the string - "; - return preg_replace($widont_regex, ' $2', $str); + %x"; + return preg_replace($widont_regex, '$1 $2', $str); } } // End text From 07e3986e23bba068096370bc104ffed6cc765727 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Marinetti?= Date: Tue, 11 Feb 2014 18:16:23 +0100 Subject: [PATCH 044/106] Fix documentation mention of Request::instance Various place in guide and php comments were mentionning Request::instance() to be used. I replaced it either by Request::factory() or something more appropriate. --- classes/kohana/form.php | 2 +- classes/kohana/request.php | 4 ++-- guide/kohana/flow.md | 4 ++-- guide/kohana/mvc/controllers.md | 2 +- guide/kohana/routing.md | 8 ++++---- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/classes/kohana/form.php b/classes/kohana/form.php index f9606f1f1..3e6109d91 100644 --- a/classes/kohana/form.php +++ b/classes/kohana/form.php @@ -28,7 +28,7 @@ class Kohana_Form { * @param mixed $action form action, defaults to the current request URI, or [Request] class to use * @param array $attributes html attributes * @return string - * @uses Request::instance + * @uses Request * @uses URL::site * @uses HTML::attributes */ diff --git a/classes/kohana/request.php b/classes/kohana/request.php index fb59cd428..bd1986717 100644 --- a/classes/kohana/request.php +++ b/classes/kohana/request.php @@ -38,7 +38,7 @@ class Kohana_Request implements HTTP_Request { /** * Creates a new request object for the given URI. New requests should be - * created using the [Request::instance] or [Request::factory] methods. + * created using the [Request::factory] method. * * $request = Request::factory($uri); * @@ -758,7 +758,7 @@ protected static function _parse_accept( & $header, array $accepts = NULL) /** * Creates a new request object for the given URI. New requests should be - * created using the [Request::instance] or [Request::factory] methods. + * created using the [Request::factory] method. * * $request = new Request($uri); * diff --git a/guide/kohana/flow.md b/guide/kohana/flow.md index 81a2e54b1..76fdfe8cb 100644 --- a/guide/kohana/flow.md +++ b/guide/kohana/flow.md @@ -16,7 +16,7 @@ Every application follows the same flow: * Includes each module's `init.php` file, if it exists. * The `init.php` file can perform additional environment setup, including adding routes. 10. [Route::set] is called multiple times to define the [application routes](routing). - 11. [Request::instance] is called to start processing the request. + 11. [Request::factory] is called to start processing the request. 1. Checks each route that has been set until a match is found. 2. Creates the controller instance and passes the request to it. 3. Calls the [Controller::before] method. @@ -24,4 +24,4 @@ Every application follows the same flow: 5. Calls the [Controller::after] method. * The above 5 steps can be repeated multiple times when using [HMVC sub-requests](requests). 3. Application flow returns to index.php - 12. The main [Request] response is displayed \ No newline at end of file + 12. The main [Request] response is displayed diff --git a/guide/kohana/mvc/controllers.md b/guide/kohana/mvc/controllers.md index 29414825a..47af6077b 100644 --- a/guide/kohana/mvc/controllers.md +++ b/guide/kohana/mvc/controllers.md @@ -55,7 +55,7 @@ You can also have a controller extend another controller to share common things, Every controller has the `$this->request` property which is the [Request] object that called the controller. You can use this to get information about the current request, as well as set the response body via `$this->response->body($ouput)`. -Here is a partial list of the properties and methods available to `$this->request`. These can also be accessed via `Request::instance()`, but `$this->request` is provided as a shortcut. See the [Request] class for more information on any of these. +Here is a partial list of the properties and methods available to `$this->request`. See the [Request] class for more information on any of these. Property/method | What it does --- | --- diff --git a/guide/kohana/routing.md b/guide/kohana/routing.md index 07c9f5f77..24c411552 100644 --- a/guide/kohana/routing.md +++ b/guide/kohana/routing.md @@ -248,14 +248,14 @@ Let's say you decided later to make that route definition more verbose by changi One method you might use frequently is the shortcut [Request::uri] which is the same as the above except it assumes the current route, directory, controller and action. If our current route is the default and the uri was `users/list`, we can do the following to generate uris in the format `users/view/$id`: - $this->request->uri(array('action' => 'view', 'id' => $user_id)); - + $this->request->route()->uri(array('action' => 'view', 'id' => $user_id)); + Or if within a view, the preferable method is: - Request::instance()->uri(array('action' => 'view', 'id' => $user_id)); + Route::url('default', array('action' => 'view', 'id' => $user_id)); TODO: examples of using html::anchor in addition to the above examples ## Testing routes -TODO: mention bluehawk's devtools module \ No newline at end of file +TODO: mention bluehawk's devtools module From a8b2fe80aecabd7724ad1103b266cef5e1ef010f Mon Sep 17 00:00:00 2001 From: Samuel Demirdjian Date: Thu, 13 Feb 2014 11:17:11 +0200 Subject: [PATCH 045/106] inject routes to avoid conflict with application's routes --- tests/kohana/RequestTest.php | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/tests/kohana/RequestTest.php b/tests/kohana/RequestTest.php index a3a3c1fbf..74c05e251 100644 --- a/tests/kohana/RequestTest.php +++ b/tests/kohana/RequestTest.php @@ -335,7 +335,14 @@ public function test_url($uri, $protocol, $is_cli, $expected) 'Kohana::$is_cli' => $is_cli, )); - $this->assertEquals(Request::factory($uri)->url($protocol), $expected); + // issue #3967: inject the route so that we don't conflict with the application's default route + $route = new Route('((/))'); + $route->defaults(array( + 'controller' => 'welcome', + 'action' => 'index', + )); + + $this->assertEquals(Request::factory($uri, NULL, array($route))->url($protocol), $expected); } /** @@ -436,8 +443,15 @@ public function test_post_max_size_exceeded($content_length, $expected) */ public function provider_uri_only_trimed_on_internal() { + // issue #3967: inject the route so that we don't conflict with the application's default route + $route = new Route('((/))'); + $route->defaults(array( + 'controller' => 'welcome', + 'action' => 'index', + )); + $old_request = Request::$initial; - Request::$initial = new Request(TRUE); + Request::$initial = new Request(TRUE, NULL, array($route)); $result = array( array( From f698da1aca22e24af711d68064625f5449907738 Mon Sep 17 00:00:00 2001 From: Samuel Demirdjian Date: Fri, 14 Feb 2014 14:28:16 +0200 Subject: [PATCH 046/106] allow Kohana shutdown properly, exit(1) in case of exception --- classes/kohana/core.php | 19 +++++++++++++++++++ classes/kohana/kohana/exception.php | 29 ++++++++++++++++++++++++----- 2 files changed, 43 insertions(+), 5 deletions(-) diff --git a/classes/kohana/core.php b/classes/kohana/core.php index 647aa6440..c4b6e8378 100644 --- a/classes/kohana/core.php +++ b/classes/kohana/core.php @@ -972,6 +972,25 @@ public static function error_handler($code, $error, $file = NULL, $line = NULL) */ public static function shutdown_handler() { + /* + * $shutdown_handler_visited is a static local function + * that will test if this function is already passed + * through by PHP runtime. + * + * If it is passed through already, we should exit with + * an error status. + * + * The second registration occurs inside Kohana's default + * exception handler. + * + * @see issue #3931 + */ + static $shutdown_handler_visited = FALSE; + if ($shutdown_handler_visited) { + exit(1); + } + $shutdown_handler_visited = TRUE; + if ( ! Kohana::$_init) { // Do not execute when not active diff --git a/classes/kohana/kohana/exception.php b/classes/kohana/kohana/exception.php index 6af406d47..65fa39ffb 100644 --- a/classes/kohana/kohana/exception.php +++ b/classes/kohana/kohana/exception.php @@ -89,6 +89,25 @@ public static function handler(Exception $e) { try { + /* + * + * We are registering our shutdown function again. + * By re-registering our shutdown function, we are + * basically asking PHP to run the shutdown function + * twice before exiting. + * + * The $shutdown_for_exception_registered is needed + * to ensure that we are not re-registering our shutdown + * function more than once. + * + * @see issue #3931 + */ + static $shutdown_for_exception_registered = FALSE; + if (!$shutdown_for_exception_registered) { + register_shutdown_function(array('Kohana', 'shutdown_handler')); + $shutdown_for_exception_registered = TRUE; + } + // Get the exception information $type = get_class($e); $code = $e->getCode(); @@ -146,7 +165,7 @@ public static function handler(Exception $e) // Just display the text of the exception echo "\n{$error}\n"; - exit(1); + return TRUE; } if ( ! headers_sent()) @@ -162,7 +181,7 @@ public static function handler(Exception $e) // Just display the text of the exception echo "\n{$error}\n"; - exit(1); + return TRUE; } // Start an output buffer @@ -183,7 +202,7 @@ public static function handler(Exception $e) // Display the contents of the output buffer echo ob_get_clean(); - exit(1); + return TRUE; } catch (Exception $e) { @@ -193,8 +212,8 @@ public static function handler(Exception $e) // Display the exception text echo Kohana_Exception::text($e), "\n"; - // Exit with an error status - exit(1); + // return + return TRUE; } } From e72b0ca8daa2a6aef7fda96e08b3c47d81a9e0be Mon Sep 17 00:00:00 2001 From: Samuel Demirdjian Date: Fri, 14 Feb 2014 13:32:08 -0800 Subject: [PATCH 047/106] fix typo --- classes/kohana/core.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/classes/kohana/core.php b/classes/kohana/core.php index c4b6e8378..1dd28c7f3 100644 --- a/classes/kohana/core.php +++ b/classes/kohana/core.php @@ -973,7 +973,7 @@ public static function error_handler($code, $error, $file = NULL, $line = NULL) public static function shutdown_handler() { /* - * $shutdown_handler_visited is a static local function + * $shutdown_handler_visited is a static function variable * that will test if this function is already passed * through by PHP runtime. * From 7ed5c65bbc3ac682eb1022c145f5914acb23495d Mon Sep 17 00:00:00 2001 From: Samuel Demirdjian Date: Mon, 17 Feb 2014 10:04:53 +0200 Subject: [PATCH 048/106] some cleanup no need to ensure if we're re-registering the shutdown function more than once. --- classes/kohana/core.php | 20 +++++++------------- classes/kohana/kohana/exception.php | 25 ++++++------------------- 2 files changed, 13 insertions(+), 32 deletions(-) diff --git a/classes/kohana/core.php b/classes/kohana/core.php index 1dd28c7f3..4d7c7d539 100644 --- a/classes/kohana/core.php +++ b/classes/kohana/core.php @@ -967,24 +967,18 @@ public static function error_handler($code, $error, $file = NULL, $line = NULL) /** * Catches errors that are not caught by the error handler, such as E_PARSE. + * + * In case of an exception, the Kohana exception handler registers + * this shutdown_handler twice. We'll count the number of visits to + * this function using a local static function variable, and exit(1) + * on the second visit. + * @see issue #3931 * * @return void */ public static function shutdown_handler() { - /* - * $shutdown_handler_visited is a static function variable - * that will test if this function is already passed - * through by PHP runtime. - * - * If it is passed through already, we should exit with - * an error status. - * - * The second registration occurs inside Kohana's default - * exception handler. - * - * @see issue #3931 - */ + // test if we have already shutdown, if TRUE, exit(1) static $shutdown_handler_visited = FALSE; if ($shutdown_handler_visited) { exit(1); diff --git a/classes/kohana/kohana/exception.php b/classes/kohana/kohana/exception.php index 65fa39ffb..03a1ed599 100644 --- a/classes/kohana/kohana/exception.php +++ b/classes/kohana/kohana/exception.php @@ -80,7 +80,11 @@ public function __toString() /** * Inline exception handler, displays the error message, source of the * exception, and the stack trace of the error. - * + * + * Re-registers Kohana::shutdown_handler to make the PHP engine + * run the shutdown twice. We'll be exit(1)-ing from there. + * @see issue #3931 + * * @uses Kohana_Exception::text * @param Exception $e * @return boolean @@ -89,24 +93,7 @@ public static function handler(Exception $e) { try { - /* - * - * We are registering our shutdown function again. - * By re-registering our shutdown function, we are - * basically asking PHP to run the shutdown function - * twice before exiting. - * - * The $shutdown_for_exception_registered is needed - * to ensure that we are not re-registering our shutdown - * function more than once. - * - * @see issue #3931 - */ - static $shutdown_for_exception_registered = FALSE; - if (!$shutdown_for_exception_registered) { - register_shutdown_function(array('Kohana', 'shutdown_handler')); - $shutdown_for_exception_registered = TRUE; - } + register_shutdown_function(array('Kohana', 'shutdown_handler')); // Get the exception information $type = get_class($e); From 49c8c46bcb373d91bf61850f444269f8842338f4 Mon Sep 17 00:00:00 2001 From: Samuel Demirdjian Date: Mon, 24 Feb 2014 05:51:14 -0800 Subject: [PATCH 049/106] fixed a brace according to the coding standard changed the word "twice" to "again" in phpdoc also the brace of `if` should go on the next line thanks @EvanPurkhiser --- classes/kohana/core.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/classes/kohana/core.php b/classes/kohana/core.php index 4d7c7d539..a36092983 100644 --- a/classes/kohana/core.php +++ b/classes/kohana/core.php @@ -969,7 +969,7 @@ public static function error_handler($code, $error, $file = NULL, $line = NULL) * Catches errors that are not caught by the error handler, such as E_PARSE. * * In case of an exception, the Kohana exception handler registers - * this shutdown_handler twice. We'll count the number of visits to + * this shutdown_handler again. We'll count the number of visits to * this function using a local static function variable, and exit(1) * on the second visit. * @see issue #3931 @@ -980,7 +980,8 @@ public static function shutdown_handler() { // test if we have already shutdown, if TRUE, exit(1) static $shutdown_handler_visited = FALSE; - if ($shutdown_handler_visited) { + if ($shutdown_handler_visited) + { exit(1); } $shutdown_handler_visited = TRUE; From 4eeacd46f45b6717ecbec30f5013e2c7dfd483a7 Mon Sep 17 00:00:00 2001 From: Samuel Demirdjian Date: Wed, 26 Feb 2014 22:44:52 +0200 Subject: [PATCH 050/106] replace preg_replace /e with preg_replace_callback uses `create_function` to support both PHP 5.2 and PHP 5.5 `create_function` uses `eval` internally so probably it's better to have this fix only for Kohana v.3.2 Thank you @Zeelot for the help --- classes/kohana/text.php | 11 +++++++++-- utf8/ucwords.php | 9 +++++---- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/classes/kohana/text.php b/classes/kohana/text.php index eb12254cf..a9cc91f20 100644 --- a/classes/kohana/text.php +++ b/classes/kohana/text.php @@ -269,6 +269,9 @@ public static function reduce_slashes($str) * 'frick' => '#####', * )); * + * If argument $replacement is a single character, it will be used to replace + * the characters in the badword, otherwise it will replace the badword completely + * * @param string $str phrase to replace words in * @param array $badwords words to replace * @param string $replacement replacement string @@ -293,12 +296,16 @@ public static function censor($str, $badwords, $replacement = '#', $replace_part $regex = '!'.$regex.'!ui'; + // if $replacement is a single character: replace each of the characters of the badword with $replacement if (UTF8::strlen($replacement) == 1) { - $regex .= 'e'; - return preg_replace($regex, 'str_repeat($replacement, UTF8::strlen(\'$1\'))', $str); + // issue #4819: use preg_replace_callback, preg_replace with /e modifier is depricated for PHP 5.5.0 + $callback = create_function('$matches', 'return str_repeat("' . $replacement . '", UTF8::strlen($matches[1]));'); + + return preg_replace_callback($regex, $callback, $str); } + // if $replacement is not a single character, fully replace the badword with $replacement return preg_replace($regex, $replacement, $str); } diff --git a/utf8/ucwords.php b/utf8/ucwords.php index a266702fa..987b36bd1 100644 --- a/utf8/ucwords.php +++ b/utf8/ucwords.php @@ -15,9 +15,10 @@ function _ucwords($str) // [\x0c\x09\x0b\x0a\x0d\x20] matches form feeds, horizontal tabs, vertical tabs, linefeeds and carriage returns. // This corresponds to the definition of a 'word' defined at http://php.net/ucwords - return preg_replace( - '/(?<=^|[\x0c\x09\x0b\x0a\x0d\x20])[^\x0c\x09\x0b\x0a\x0d\x20]/ue', - 'UTF8::strtoupper(\'$0\')', + $callback = create_function('$matches', 'return UTF8::strtoupper($matches[0]);'); + return preg_replace_callback( + '/(?<=^|[\x0c\x09\x0b\x0a\x0d\x20])[^\x0c\x09\x0b\x0a\x0d\x20]/u', + $callback, $str ); -} \ No newline at end of file +} From c2227827ab4c994f7b62dc8781df6bf6b9c9687d Mon Sep 17 00:00:00 2001 From: Soon Van Date: Fri, 28 Feb 2014 13:28:53 -0500 Subject: [PATCH 051/106] Update license URLs in PHPDoc - Ref: #4821 Found links pointing to old location of license instead of where they are now at http://kohanaframework.org/license --- classes/Kohana/Config/Group.php | 4 ++-- classes/Kohana/Config/Source.php | 4 ++-- classes/Kohana/Config/Writer.php | 4 ++-- classes/Kohana/Debug.php | 4 ++-- classes/Kohana/HTTP.php | 4 ++-- classes/Kohana/HTTP/Header.php | 4 ++-- classes/Kohana/HTTP/Message.php | 4 ++-- classes/Kohana/HTTP/Request.php | 4 ++-- classes/Kohana/HTTP/Response.php | 4 ++-- classes/Kohana/Log/StdErr.php | 4 ++-- classes/Kohana/Log/StdOut.php | 4 ++-- classes/Kohana/Response.php | 4 ++-- tests/kohana/Config/File/ReaderTest.php | 4 ++-- tests/kohana/Config/GroupTest.php | 4 ++-- tests/kohana/DebugTest.php | 4 ++-- tests/kohana/Http/HeaderTest.php | 4 ++-- 16 files changed, 32 insertions(+), 32 deletions(-) diff --git a/classes/Kohana/Config/Group.php b/classes/Kohana/Config/Group.php index 0460c898d..3e35fdf55 100644 --- a/classes/Kohana/Config/Group.php +++ b/classes/Kohana/Config/Group.php @@ -11,8 +11,8 @@ * @package Kohana * @category Configuration * @author Kohana Team - * @copyright (c) 2012 Kohana Team - * @license http://kohanaphp.com/license + * @copyright (c) 2012-2014 Kohana Team + * @license http://kohanaframework.org/license */ class Kohana_Config_Group extends ArrayObject { diff --git a/classes/Kohana/Config/Source.php b/classes/Kohana/Config/Source.php index cfce07609..388986085 100644 --- a/classes/Kohana/Config/Source.php +++ b/classes/Kohana/Config/Source.php @@ -7,8 +7,8 @@ * @package Kohana * @category Configuration * @author Kohana Team - * @copyright (c) 2012 Kohana Team - * @license http://kohanaphp.com/license + * @copyright (c) 2012-2014 Kohana Team + * @license http://kohanaframework.org/license */ interface Kohana_Config_Source {} diff --git a/classes/Kohana/Config/Writer.php b/classes/Kohana/Config/Writer.php index 3db22c037..856ebde8d 100644 --- a/classes/Kohana/Config/Writer.php +++ b/classes/Kohana/Config/Writer.php @@ -7,8 +7,8 @@ * * @package Kohana * @author Kohana Team - * @copyright (c) 2008-2012 Kohana Team - * @license http://kohanaphp.com/license + * @copyright (c) 2008-2014 Kohana Team + * @license http://kohanaframework.org/license */ interface Kohana_Config_Writer extends Kohana_Config_Source { diff --git a/classes/Kohana/Debug.php b/classes/Kohana/Debug.php index 9d1efdf52..5a82bb623 100644 --- a/classes/Kohana/Debug.php +++ b/classes/Kohana/Debug.php @@ -5,8 +5,8 @@ * @package Kohana * @category Base * @author Kohana Team - * @copyright (c) 2008-2012 Kohana Team - * @license http://kohanaphp.com/license + * @copyright (c) 2008-2014 Kohana Team + * @license http://kohanaframework.org/license */ class Kohana_Debug { diff --git a/classes/Kohana/HTTP.php b/classes/Kohana/HTTP.php index 083b43589..eca52c4e6 100644 --- a/classes/Kohana/HTTP.php +++ b/classes/Kohana/HTTP.php @@ -11,8 +11,8 @@ * @category HTTP * @author Kohana Team * @since 3.1.0 - * @copyright (c) 2008-2012 Kohana Team - * @license http://kohanaphp.com/license + * @copyright (c) 2008-2014 Kohana Team + * @license http://kohanaframework.org/license */ abstract class Kohana_HTTP { diff --git a/classes/Kohana/HTTP/Header.php b/classes/Kohana/HTTP/Header.php index 6a35e9138..0debabf6c 100644 --- a/classes/Kohana/HTTP/Header.php +++ b/classes/Kohana/HTTP/Header.php @@ -9,8 +9,8 @@ * @category HTTP * @author Kohana Team * @since 3.1.0 - * @copyright (c) 2008-2012 Kohana Team - * @license http://kohanaphp.com/license + * @copyright (c) 2008-2014 Kohana Team + * @license http://kohanaframework.org/license */ class Kohana_HTTP_Header extends ArrayObject { diff --git a/classes/Kohana/HTTP/Message.php b/classes/Kohana/HTTP/Message.php index 0861a733a..c24046ec1 100644 --- a/classes/Kohana/HTTP/Message.php +++ b/classes/Kohana/HTTP/Message.php @@ -7,8 +7,8 @@ * @category HTTP * @author Kohana Team * @since 3.1.0 - * @copyright (c) 2008-2012 Kohana Team - * @license http://kohanaphp.com/license + * @copyright (c) 2008-2014 Kohana Team + * @license http://kohanaframework.org/license */ interface Kohana_HTTP_Message { diff --git a/classes/Kohana/HTTP/Request.php b/classes/Kohana/HTTP/Request.php index 8b2169c5f..b8992725f 100644 --- a/classes/Kohana/HTTP/Request.php +++ b/classes/Kohana/HTTP/Request.php @@ -8,8 +8,8 @@ * @category HTTP * @author Kohana Team * @since 3.1.0 - * @copyright (c) 2008-2012 Kohana Team - * @license http://kohanaphp.com/license + * @copyright (c) 2008-2014 Kohana Team + * @license http://kohanaframework.org/license */ interface Kohana_HTTP_Request extends HTTP_Message { diff --git a/classes/Kohana/HTTP/Response.php b/classes/Kohana/HTTP/Response.php index 71058ac49..ddce6b4b9 100644 --- a/classes/Kohana/HTTP/Response.php +++ b/classes/Kohana/HTTP/Response.php @@ -8,8 +8,8 @@ * @category HTTP * @author Kohana Team * @since 3.1.0 - * @copyright (c) 2008-2012 Kohana Team - * @license http://kohanaphp.com/license + * @copyright (c) 2008-2014 Kohana Team + * @license http://kohanaframework.org/license */ interface Kohana_HTTP_Response extends HTTP_Message { diff --git a/classes/Kohana/Log/StdErr.php b/classes/Kohana/Log/StdErr.php index 5097bb75d..53840387a 100644 --- a/classes/Kohana/Log/StdErr.php +++ b/classes/Kohana/Log/StdErr.php @@ -5,8 +5,8 @@ * @package Kohana * @category Logging * @author Kohana Team - * @copyright (c) 2008-2012 Kohana Team - * @license http://kohanaphp.com/license + * @copyright (c) 2008-2014 Kohana Team + * @license http://kohanaframework.org/license */ class Kohana_Log_StdErr extends Log_Writer { /** diff --git a/classes/Kohana/Log/StdOut.php b/classes/Kohana/Log/StdOut.php index b0cfeb380..608c65389 100644 --- a/classes/Kohana/Log/StdOut.php +++ b/classes/Kohana/Log/StdOut.php @@ -5,8 +5,8 @@ * @package Kohana * @category Logging * @author Kohana Team - * @copyright (c) 2008-2012 Kohana Team - * @license http://kohanaphp.com/license + * @copyright (c) 2008-2014 Kohana Team + * @license http://kohanaframework.org/license */ class Kohana_Log_StdOut extends Log_Writer { diff --git a/classes/Kohana/Response.php b/classes/Kohana/Response.php index 2a12e5e40..686997915 100644 --- a/classes/Kohana/Response.php +++ b/classes/Kohana/Response.php @@ -7,8 +7,8 @@ * @package Kohana * @category Base * @author Kohana Team - * @copyright (c) 2008-2012 Kohana Team - * @license http://kohanaphp.com/license + * @copyright (c) 2008-2014 Kohana Team + * @license http://kohanaframework.org/license * @since 3.1.0 */ class Kohana_Response implements HTTP_Response { diff --git a/tests/kohana/Config/File/ReaderTest.php b/tests/kohana/Config/File/ReaderTest.php index ab22d14ae..bfc755eda 100644 --- a/tests/kohana/Config/File/ReaderTest.php +++ b/tests/kohana/Config/File/ReaderTest.php @@ -10,8 +10,8 @@ * @author Kohana Team * @author Jeremy Bush * @author Matt Button - * @copyright (c) 2008-2012 Kohana Team - * @license http://kohanaphp.com/license + * @copyright (c) 2008-2014 Kohana Team + * @license http://kohanaframework.org/license */ class Kohana_Config_File_ReaderTest extends Kohana_Unittest_TestCase { diff --git a/tests/kohana/Config/GroupTest.php b/tests/kohana/Config/GroupTest.php index 66fd05856..6222c8df6 100644 --- a/tests/kohana/Config/GroupTest.php +++ b/tests/kohana/Config/GroupTest.php @@ -10,8 +10,8 @@ * @author Kohana Team * @author Jeremy Bush * @author Matt Button - * @copyright (c) 2008-2012 Kohana Team - * @license http://kohanaphp.com/license + * @copyright (c) 2008-2014 Kohana Team + * @license http://kohanaframework.org/license */ class Kohana_Config_GroupTest extends Kohana_Unittest_TestCase { diff --git a/tests/kohana/DebugTest.php b/tests/kohana/DebugTest.php index 39176ec17..3f845361e 100644 --- a/tests/kohana/DebugTest.php +++ b/tests/kohana/DebugTest.php @@ -13,8 +13,8 @@ * @category Tests * @author Kohana Team * @author Jeremy Bush - * @copyright (c) 2008-2012 Kohana Team - * @license http://kohanaphp.com/license + * @copyright (c) 2008-2014 Kohana Team + * @license http://kohanaframework.org/license */ class Kohana_DebugTest extends Unittest_TestCase { diff --git a/tests/kohana/Http/HeaderTest.php b/tests/kohana/Http/HeaderTest.php index 0894b468e..8f241d3a5 100644 --- a/tests/kohana/Http/HeaderTest.php +++ b/tests/kohana/Http/HeaderTest.php @@ -11,8 +11,8 @@ * @package Kohana * @category Tests * @author Kohana Team - * @copyright (c) 2008-2012 Kohana Team - * @license http://kohanaphp.com/license + * @copyright (c) 2008-2014 Kohana Team + * @license http://kohanaframework.org/license */ class Kohana_HTTP_HeaderTest extends Unittest_TestCase { From 5c5aac1b7103172b08908813afddc78785592cef Mon Sep 17 00:00:00 2001 From: Samuel Demirdjian Date: Wed, 5 Mar 2014 09:25:55 +0200 Subject: [PATCH 052/106] Decode slashes back after rawurlencode --- classes/kohana/route.php | 3 +++ tests/kohana/RouteTest.php | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/classes/kohana/route.php b/classes/kohana/route.php index b4c5dfc8f..51e45dd2d 100644 --- a/classes/kohana/route.php +++ b/classes/kohana/route.php @@ -458,7 +458,10 @@ public function uri(array $params = NULL) { if ($params) { + // @issue #4079 rawurlencode parameters $params = array_map('rawurlencode', $params); + // decode slashes back, see Apache docs about AllowEncodedSlashes and AcceptPathInfo + $params = str_replace(array('%2F', '%5C'), array('/', '\\'), $params); } // Start with the routed URI diff --git a/tests/kohana/RouteTest.php b/tests/kohana/RouteTest.php index 86ad5ac7c..a4747ccd9 100644 --- a/tests/kohana/RouteTest.php +++ b/tests/kohana/RouteTest.php @@ -733,7 +733,7 @@ public function provider_route_uri_encode_parameters() ), 'article_name', 'Article name with special chars \\ ##', - 'blog/article/Article%20name%20with%20special%20chars%20%5C%20%23%23' + 'blog/article/Article%20name%20with%20special%20chars%20\\%20%23%23' ) ); } From c011806aaf3e92b80d30f442dc6bb6d1383117b4 Mon Sep 17 00:00:00 2001 From: Samuel Demirdjian Date: Wed, 5 Mar 2014 11:39:11 +0200 Subject: [PATCH 053/106] add generating URIs section to the upgrade doc --- guide/kohana/upgrading.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/guide/kohana/upgrading.md b/guide/kohana/upgrading.md index c09a5b00a..9dd2662af 100644 --- a/guide/kohana/upgrading.md +++ b/guide/kohana/upgrading.md @@ -58,6 +58,15 @@ Alternatively it is possible to set a specific client to an individual Request. ->client(new Request_Client_Stream) ->execute(); +## Generating URIs + +In Kohana 3.1, you could generate URIs by passing parameters to the `Request::uri` method. In Kohana 3.2 [Request::uri] +just returns the URI of the current route. URIs can **NOT** be generated using the `Request::uri` method anymore. + +Instead, you should generate your URIs by passing your parameters to the [Route::uri] method. + +For more information, please read the section about [creating URLs and links using routes](routing#creating-urls-and-links-using-routes). + ## HTTP cache control Kohana 3.1 introduced HTTP cache control, providing RFC 2616 fully compliant transparent caching of responses. Kohana 3.2 builds on this moving all caching logic out of `Request_Client` into `HTTP_Cache`. From b109d1ea57acbd7517174acca452773370ecce8d Mon Sep 17 00:00:00 2001 From: Jeff Chan Date: Sat, 12 Apr 2014 04:56:38 +0000 Subject: [PATCH 054/106] Simplify Kohana::globals() test This simplifies the `test_globals_removes_user_def_globals` test to take advantage of PHPUnit's backupGlobals option which is on by default. There's no need to backup globals. In addition, changed to avoid directly assigning to $GLOBALS, since it is not allowed in HHVM. See: https://github.com/facebook/hhvm/blob/master/hphp/doc/inconsistencies#L127 --- tests/kohana/CoreTest.php | 30 ++++++------------------------ 1 file changed, 6 insertions(+), 24 deletions(-) diff --git a/tests/kohana/CoreTest.php b/tests/kohana/CoreTest.php index a0099d103..0b93ac755 100644 --- a/tests/kohana/CoreTest.php +++ b/tests/kohana/CoreTest.php @@ -107,33 +107,15 @@ public function test_list_files_returns_array_on_success_and_failure() */ public function test_globals_removes_user_def_globals() { - // Store the globals - $temp_globals = array( - 'cookie' => $_COOKIE, - 'get' => $_GET, - 'files' => $_FILES, - 'post' => $_POST, - 'request' => $_REQUEST, - 'server' => $_SERVER, - 'session' => $_SESSION, - 'globals' => $GLOBALS, - ); - - $GLOBALS = array('hackers' => 'foobar','name' => array('','',''), '_POST' => array()); + $GLOBALS['hackers'] = 'foobar'; + $GLOBALS['name'] = array('','',''); + $GLOBALS['_POST'] = array(); Kohana::globals(); - $this->assertEquals(array('_POST' => array()), $GLOBALS); - - // Reset the globals for other tests - $_COOKIE = $temp_globals['cookie']; - $_GET = $temp_globals['get']; - $_FILES = $temp_globals['files']; - $_POST = $temp_globals['post']; - $_REQUEST = $temp_globals['request']; - $_SERVER = $temp_globals['server']; - $_SESSION = $temp_globals['session']; - $GLOBALS = $temp_globals['globals']; + $this->assertFalse(isset($GLOBALS['hackers'])); + $this->assertFalse(isset($GLOBALS['name'])); + $this->assertTrue(isset($GLOBALS['_POST'])); } /** From 611d56989103789944ab9aa91904a5fdc7a40ecb Mon Sep 17 00:00:00 2001 From: Kemo Date: Thu, 17 Apr 2014 01:45:34 +0200 Subject: [PATCH 055/106] Use `preg_replace_callback()` in `_ucwords()` /e modifier is deprecated since PHP 5.4 --- utf8/ucwords.php | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/utf8/ucwords.php b/utf8/ucwords.php index 1411ff1d4..2cc8b35f9 100644 --- a/utf8/ucwords.php +++ b/utf8/ucwords.php @@ -15,9 +15,10 @@ function _ucwords($str) // [\x0c\x09\x0b\x0a\x0d\x20] matches form feeds, horizontal tabs, vertical tabs, linefeeds and carriage returns. // This corresponds to the definition of a 'word' defined at http://php.net/ucwords - return preg_replace( - '/(?<=^|[\x0c\x09\x0b\x0a\x0d\x20])[^\x0c\x09\x0b\x0a\x0d\x20]/ue', - 'UTF8::strtoupper(\'$0\')', - $str - ); + return preg_replace_callback( + '/(?<=^|[\x0c\x09\x0b\x0a\x0d\x20])[^\x0c\x09\x0b\x0a\x0d\x20]/u', + function($matches){ + return UTF8::strtoupper($matches[0]); + }, + $str); } From 9e192d779b0f62d323dd588f1f3c5192fb55c8d7 Mon Sep 17 00:00:00 2001 From: Woody Gilk Date: Wed, 7 May 2014 14:11:46 -0500 Subject: [PATCH 056/106] Bump version --- classes/kohana/core.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/classes/kohana/core.php b/classes/kohana/core.php index 6b33f4e1b..4d34a78a3 100644 --- a/classes/kohana/core.php +++ b/classes/kohana/core.php @@ -16,8 +16,8 @@ class Kohana_Core { // Release version and codename - const VERSION = '3.2.2'; - const CODENAME = 'hypoleucos'; + const VERSION = '3.2.3.1'; + const CODENAME = 'turdus'; // Common environment type constants for consistency and convenience const PRODUCTION = 10; From 122899681eb7073fc72c0a62d8df66fea035f56d Mon Sep 17 00:00:00 2001 From: Scott Arciszewski Date: Mon, 12 May 2014 21:15:56 -0400 Subject: [PATCH 057/106] Update Security.php --- classes/Kohana/Security.php | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/classes/Kohana/Security.php b/classes/Kohana/Security.php index b8f66c635..b07e499e4 100644 --- a/classes/Kohana/Security.php +++ b/classes/Kohana/Security.php @@ -81,8 +81,29 @@ public static function token($new = FALSE) */ public static function check($token) { - return Security::token() === $token; + return Security::slow_equals(Security::token(), $token); } + + + + /** + * Compare two hashes in a time-invariant manner. + * Prevents cryptographic side-channel attacks (timing attacks, specifically) + * + * @param string $a cryptographic hash + * @param string $b cryptographic hash + * @return boolean + */ + public static function slow_equals($a, $b) + { + $diff = strlen($a) ^ strlen($b); + for($i = 0; $i < strlen($a) AND $i < strlen($b); $i++) + { + $diff |= ord($a[$i]) ^ ord($b[$i]); + } + return $diff === 0; + } + /** * Remove image tags from a string. From ee0e121c665100b8f6899149db3cdea4cf0f7f6b Mon Sep 17 00:00:00 2001 From: Scott Arciszewski Date: Mon, 12 May 2014 21:16:19 -0400 Subject: [PATCH 058/106] Update Cookie.php --- classes/Kohana/Cookie.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/classes/Kohana/Cookie.php b/classes/Kohana/Cookie.php index dafb7f5a4..fe4cc490d 100644 --- a/classes/Kohana/Cookie.php +++ b/classes/Kohana/Cookie.php @@ -71,7 +71,7 @@ public static function get($key, $default = NULL) // Separate the salt and the value list ($hash, $value) = explode('~', $cookie, 2); - if (Cookie::salt($key, $value) === $hash) + if (Security::slow_equals(Cookie::salt($key, $value), $hash)) { // Cookie signature is valid return $value; From 31d8b4a8ee1032b2b150354e215cda9246e60062 Mon Sep 17 00:00:00 2001 From: Samuel Demirdjian Date: Mon, 9 Jun 2014 09:45:46 +0300 Subject: [PATCH 059/106] Allow query-only links in HTML::anchor Added tests for query-only and fragment-only links --- classes/Kohana/HTML.php | 4 ++-- tests/kohana/HTMLTest.php | 14 ++++++++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/classes/Kohana/HTML.php b/classes/Kohana/HTML.php index a78bc7621..ef226c2c8 100644 --- a/classes/Kohana/HTML.php +++ b/classes/Kohana/HTML.php @@ -126,9 +126,9 @@ public static function anchor($uri, $title = NULL, array $attributes = NULL, $pr $attributes['target'] = '_blank'; } } - elseif ($uri[0] !== '#') + elseif ($uri[0] !== '#' AND $uri[0] !== '?') { - // Make the URI absolute for non-id anchors + // Make the URI absolute for non-fragment and non-query anchors $uri = URL::site($uri, $protocol, $index); } } diff --git a/tests/kohana/HTMLTest.php b/tests/kohana/HTMLTest.php index 015a65da4..c4505bc40 100644 --- a/tests/kohana/HTMLTest.php +++ b/tests/kohana/HTMLTest.php @@ -223,6 +223,20 @@ public function test_style($expected, $file, array $attributes = NULL, $protocol public function provider_anchor() { return array( + // a fragment-only anchor + array( + 'Kohana', + array(), + '#go-to-section-kohana', + 'Kohana', + ), + // a query-only anchor + array( + 'Category A', + array(), + '?cat=a', + 'Category A', + ), array( 'Kohana', array(), From e9d912da372900c6904c5131a8ea6fb10365978e Mon Sep 17 00:00:00 2001 From: Kemo Date: Sun, 15 Jun 2014 00:33:24 +0200 Subject: [PATCH 060/106] Fix for issue #496 - bad Request::url() docblock --- classes/Kohana/Request.php | 1 - 1 file changed, 1 deletion(-) diff --git a/classes/Kohana/Request.php b/classes/Kohana/Request.php index ba7738548..bdc059ecf 100644 --- a/classes/Kohana/Request.php +++ b/classes/Kohana/Request.php @@ -740,7 +740,6 @@ public function uri($uri = NULL) * * echo URL::site($this->request->uri(), $protocol); * - * @param array $params URI parameters * @param mixed $protocol protocol string or Request object * @return string * @since 3.0.7 From fd77a4b4afe6cf7615ec23be73ab10d43ca2b287 Mon Sep 17 00:00:00 2001 From: Sam Sanchez Date: Fri, 4 Jul 2014 16:10:05 +0800 Subject: [PATCH 061/106] don't empty the error message refer from the menu: [htmlspecialchars](http://cn2.php.net/htmlspecialchars#refsect1-function.htmlspecialchars-returnvalues) > If the input string contains an invalid code unit sequence within the given encoding an empty string will be returned --- views/kohana/error.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/kohana/error.php b/views/kohana/error.php index 4d8b1d8f5..f31b5f191 100644 --- a/views/kohana/error.php +++ b/views/kohana/error.php @@ -49,7 +49,7 @@ function koggle(elem) }
                    -

                    [ ]:

                    +

                    [ ]:

                    [ ]

                    From 60c3105c8ac96c97b56766a7c8459f5b32d45e65 Mon Sep 17 00:00:00 2001 From: Andrew Coulton Date: Sat, 9 Aug 2014 23:40:41 +0100 Subject: [PATCH 062/106] Fix resending headers following redirect Fixes failing test - all header keys are lowercase internally so comparison was not working as expected and the header values were not being properly copied by array_intersect_assoc as that function compares both key and value. --- classes/Kohana/Request/Client.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/classes/Kohana/Request/Client.php b/classes/Kohana/Request/Client.php index 1190a30fc..3c35faf61 100644 --- a/classes/Kohana/Request/Client.php +++ b/classes/Kohana/Request/Client.php @@ -26,7 +26,7 @@ abstract class Kohana_Request_Client { /** * @var array Headers to preserve when following a redirect */ - protected $_follow_headers = array('Authorization'); + protected $_follow_headers = array('authorization'); /** * @var bool Follow 302 redirect with original request method? @@ -205,7 +205,7 @@ public function follow_headers($follow_headers = NULL) if ($follow_headers === NULL) return $this->_follow_headers; - $this->_follow_headers = $follow_headers; + $this->_follow_headers = array_map('strtolower', $follow_headers); return $this; } @@ -407,7 +407,8 @@ public static function on_header_location(Request $request, Response $response, // Prepare the additional request, copying any follow_headers that were present on the original request $orig_headers = $request->headers()->getArrayCopy(); - $follow_headers = array_intersect_assoc($orig_headers, array_fill_keys($client->follow_headers(), TRUE)); + $follow_header_keys = array_intersect(array_keys($orig_headers), $client->follow_headers()); + $follow_headers = \Arr::extract($orig_headers, $follow_header_keys); $follow_request = Request::factory($response->headers('Location')) ->method($follow_method) From 6b519fb074b7f378615e50dee47504e98dc1735d Mon Sep 17 00:00:00 2001 From: slik Date: Tue, 22 Jul 2014 22:05:36 +0300 Subject: [PATCH 063/106] since php 5.5.0 /e modifier is deprecated --- classes/Kohana/Text.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/classes/Kohana/Text.php b/classes/Kohana/Text.php index 7514fd66c..942538d42 100644 --- a/classes/Kohana/Text.php +++ b/classes/Kohana/Text.php @@ -295,8 +295,9 @@ public static function censor($str, $badwords, $replacement = '#', $replace_part if (UTF8::strlen($replacement) == 1) { - $regex .= 'e'; - return preg_replace($regex, 'str_repeat($replacement, UTF8::strlen(\'$1\'))', $str); + return preg_replace_callback($regex, function($matches) use ($replacement) { + return str_repeat($replacement, UTF8::strlen($matches[1])); + }, $str); } return preg_replace($regex, $replacement, $str); From ce8288fd583ebe881c9516adb285a196a0527ec6 Mon Sep 17 00:00:00 2001 From: Andrew Coulton Date: Tue, 12 Aug 2014 11:28:51 +0100 Subject: [PATCH 064/106] Fix Arr::get for ArrayObject on HHVM - fixes HTTP_Header behaviour HHVM 3.2.0 may emit a PHP Notice (Undefined Index) when calling isset on an ArrayObject that maps the keys in some way - for example the HTTP_Header object which converts all keys to lowercase - see https://github.com/facebook/hhvm/issues/3437 Implemented a workaround in Arr::get to resolve breaking tests for Kohana_Request_Client on HHVM. --- classes/Kohana/Arr.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/classes/Kohana/Arr.php b/classes/Kohana/Arr.php index 766369a81..fec1c2fa9 100644 --- a/classes/Kohana/Arr.php +++ b/classes/Kohana/Arr.php @@ -279,7 +279,13 @@ public static function range($step = 10, $max = 100) */ public static function get($array, $key, $default = NULL) { - return isset($array[$key]) ? $array[$key] : $default; + if ($array instanceof ArrayObject) { + // This is a workaround for inconsistent implementation of isset between PHP and HHVM + // See https://github.com/facebook/hhvm/issues/3437 + return $array->offsetExists($key) ? $array->offsetGet($key) : $default; + } else { + return isset($array[$key]) ? $array[$key] : $default; + } } /** From 952d6a888faaed4b214928b3642f15e328a7cb70 Mon Sep 17 00:00:00 2001 From: Andrew Coulton Date: Tue, 12 Aug 2014 13:12:04 +0100 Subject: [PATCH 065/106] Ensure Date::formatted_time always has expected timezone Always re-assign the requested timezone in Date::formatted_time - this will be a no-op if the timezone already matches, otherwise it will convert the time as required. It is not currently safe to make this a conditional call based on comparing the existing timezone name, because of facebook/hhvm#2302. --- classes/Kohana/Date.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/classes/Kohana/Date.php b/classes/Kohana/Date.php index 692d658cb..930edc381 100644 --- a/classes/Kohana/Date.php +++ b/classes/Kohana/Date.php @@ -592,10 +592,10 @@ public static function formatted_time($datetime_str = 'now', $timestamp_format = $tz = new DateTimeZone($timezone ? $timezone : date_default_timezone_get()); $time = new DateTime($datetime_str, $tz); - if ($time->getTimeZone()->getName() !== $tz->getName()) - { - $time->setTimeZone($tz); - } + // Convert the time back to the expected timezone if required (in case the datetime_str provided a timezone, + // offset or unix timestamp. This also ensures that the timezone reported by the object is correct on HHVM + // (see https://github.com/facebook/hhvm/issues/2302). + $time->setTimeZone($tz); return $time->format($timestamp_format); } From e472ecfb540ce0130509187a6cbcc3aa822939e1 Mon Sep 17 00:00:00 2001 From: Zogame Date: Fri, 5 Sep 2014 15:26:23 +0900 Subject: [PATCH 066/106] Update Text.php $agent has been already defined --- classes/Kohana/Text.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/classes/Kohana/Text.php b/classes/Kohana/Text.php index 942538d42..9a7cd5ac1 100644 --- a/classes/Kohana/Text.php +++ b/classes/Kohana/Text.php @@ -608,15 +608,16 @@ public static function widont($str) * Returns information about the client user agent. * * // Returns "Chrome" when using Google Chrome - * $browser = Text::user_agent('browser'); + * $browser = Text::user_agent($agent, 'browser'); * * Multiple values can be returned at once by using an array: * * // Get the browser and platform with a single call - * $info = Text::user_agent(array('browser', 'platform')); + * $info = Text::user_agent($agent, array('browser', 'platform')); * * When using an array for the value, an associative array will be returned. * + * @param string $agent user_agent * @param mixed $value array or string to return: browser, version, robot, mobile, platform * @return mixed requested information, FALSE if nothing is found * @uses Kohana::$config @@ -650,7 +651,7 @@ public static function user_agent($agent, $value) // Set the browser name $info['browser'] = $name; - if (preg_match('#'.preg_quote($search).'[^0-9.]*+([0-9.][0-9.a-z]*)#i', Request::$user_agent, $matches)) + if (preg_match('#'.preg_quote($search).'[^0-9.]*+([0-9.][0-9.a-z]*)#i', $agent, $matches)) { // Set the version number $info['version'] = $matches[1]; From b8ba46bdc28072c2297ac33305ead6ad768da843 Mon Sep 17 00:00:00 2001 From: Andrew Coulton Date: Wed, 17 Sep 2014 13:07:52 +0100 Subject: [PATCH 067/106] Configure standalone travis build for kohana/core Adds and configures kohana/koharness to build a skeleton Kohana application and then uses that environment to run the unit tests on travis (and locally, if required). --- .gitignore | 3 +++ .travis.yml | 24 ++++++++++++++++++++++++ composer.json | 7 +++++++ koharness.php | 8 ++++++++ 4 files changed, 42 insertions(+) create mode 100644 .travis.yml create mode 100644 koharness.php diff --git a/.gitignore b/.gitignore index b72f9be20..09b35f5ba 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,5 @@ *~ *.swp +/composer.lock +/vendor +/koharness_bootstrap.php diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 000000000..8f9e0fcae --- /dev/null +++ b/.travis.yml @@ -0,0 +1,24 @@ +language: php + +php: + - 5.3 + - 5.4 + - 5.5 + - 5.6 + - hhvm + +before_script: + - composer install --prefer-dist + - vendor/bin/koharness + +script: + - cd /tmp/koharness && ./vendor/bin/phpunit --bootstrap=modules/unittest/bootstrap.php modules/unittest/tests.php + +notifications: + irc: + channels: + - "irc.freenode.org#kohana" + template: + - "%{repository}/%{branch} (%{commit}) - %{author}: %{message}" + - "Build details: %{build_url}" + email: false diff --git a/composer.json b/composer.json index 2c07aa849..32d2a3252 100644 --- a/composer.json +++ b/composer.json @@ -21,12 +21,19 @@ "require": { "php": ">=5.3.3" }, + "require-dev": { + "kohana/unittest": "3.3.*@dev", + "kohana/koharness": "*@dev" + }, "suggest": { "ext-http": "*", "ext-curl": "*", "ext-mcrypt": "*" }, "extra": { + "installer-paths": { + "vendor/{$vendor}/{$name}": ["type:kohana-module"] + }, "branch-alias": { "dev-3.3/develop": "3.3.x-dev", "dev-3.4/develop": "3.4.x-dev" diff --git a/koharness.php b/koharness.php new file mode 100644 index 000000000..7845498db --- /dev/null +++ b/koharness.php @@ -0,0 +1,8 @@ + array( + 'unittest' => __DIR__ . '/vendor/kohana/unittest' + ), + 'syspath' => __DIR__, +); From 58e842d439ce9f5447d2789885c3dce8955c53d3 Mon Sep 17 00:00:00 2001 From: Andrew Coulton Date: Wed, 17 Sep 2014 13:11:16 +0100 Subject: [PATCH 068/106] Remove physical path dependency in Kohana::module tests Backports dd926956 to 3.3 - MODPATH is unreliable in a koharness environment as it will usually be a symlink. With this change the test of Kohana::modules will pass wherever the code happens to be deployed on the disk. This will conflict when we merge up to 3.4 because that commit uses short array syntax which is not available in the 3.3 series. --- tests/kohana/CoreTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/kohana/CoreTest.php b/tests/kohana/CoreTest.php index 0b93ac755..04b06dfcd 100644 --- a/tests/kohana/CoreTest.php +++ b/tests/kohana/CoreTest.php @@ -296,7 +296,7 @@ public function provider_modules_sets_and_returns_valid_modules() { return array( array(array(), array()), - array(array('unittest' => MODPATH.'unittest'), array('unittest' => $this->dirSeparator(MODPATH.'unittest/'))), + array(array('module' => __DIR__), array('module' => $this->dirSeparator(__DIR__.'/'))), ); } From b880a3eaea7ee553ab78674eee0e1b036658ed8f Mon Sep 17 00:00:00 2001 From: Sakto Date: Wed, 17 Sep 2014 17:09:18 +0200 Subject: [PATCH 069/106] Added failing test for urls starting with // in HTML --- tests/kohana/HTMLTest.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/kohana/HTMLTest.php b/tests/kohana/HTMLTest.php index c4505bc40..ef7c881d5 100644 --- a/tests/kohana/HTMLTest.php +++ b/tests/kohana/HTMLTest.php @@ -117,6 +117,10 @@ public function provider_script() 'https', FALSE ), + array( + '', + '//google.com/script.js', + ), ); } From 9bfb502ad60891f90a8951c9f78c2cf87f944e8e Mon Sep 17 00:00:00 2001 From: Sakto Date: Wed, 17 Sep 2014 17:10:29 +0200 Subject: [PATCH 070/106] Support urls starting with // in HTML::script and HTML::style --- classes/Kohana/HTML.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/classes/Kohana/HTML.php b/classes/Kohana/HTML.php index ef226c2c8..9752a1e65 100644 --- a/classes/Kohana/HTML.php +++ b/classes/Kohana/HTML.php @@ -206,7 +206,7 @@ public static function mailto($email, $title = NULL, array $attributes = NULL) */ public static function style($file, array $attributes = NULL, $protocol = NULL, $index = FALSE) { - if (strpos($file, '://') === FALSE) + if (strpos($file, '://') === FALSE AND strpos($file, '//') !== 0) { // Add the base URL $file = URL::site($file, $protocol, $index); @@ -239,7 +239,7 @@ public static function style($file, array $attributes = NULL, $protocol = NULL, */ public static function script($file, array $attributes = NULL, $protocol = NULL, $index = FALSE) { - if (strpos($file, '://') === FALSE) + if (strpos($file, '://') === FALSE AND strpos($file, '//') !== 0) { // Add the base URL $file = URL::site($file, $protocol, $index); From 621b8981905e0f0b12c3465d58169d7db469e82b Mon Sep 17 00:00:00 2001 From: Sakto Date: Wed, 17 Sep 2014 17:36:24 +0200 Subject: [PATCH 071/106] Added failing test to provider_style for urls starting with // --- tests/kohana/HTMLTest.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/kohana/HTMLTest.php b/tests/kohana/HTMLTest.php index ef7c881d5..c92ad1bbe 100644 --- a/tests/kohana/HTMLTest.php +++ b/tests/kohana/HTMLTest.php @@ -197,6 +197,13 @@ public function provider_style() 'https', TRUE ), + array( + '', + '//google.com/style.css', + array(), + NULL, + FALSE + ), ); } From 12516f05bc3d4ac758f7ecad69e76b524269d552 Mon Sep 17 00:00:00 2001 From: Samuel Demirdjian Date: Thu, 18 Sep 2014 11:15:37 +0300 Subject: [PATCH 072/106] incorrect mention of the missing method Request::instance --- classes/Kohana/Form.php | 2 +- classes/Kohana/Request.php | 4 ++-- guide/kohana/flow.md | 2 +- guide/kohana/mvc/controllers.md | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/classes/Kohana/Form.php b/classes/Kohana/Form.php index 510bd8109..65ed30bb6 100644 --- a/classes/Kohana/Form.php +++ b/classes/Kohana/Form.php @@ -28,7 +28,7 @@ class Kohana_Form { * @param mixed $action form action, defaults to the current request URI, or [Request] class to use * @param array $attributes html attributes * @return string - * @uses Request::instance + * @uses Request * @uses URL::site * @uses HTML::attributes */ diff --git a/classes/Kohana/Request.php b/classes/Kohana/Request.php index bdc059ecf..5343a9c27 100644 --- a/classes/Kohana/Request.php +++ b/classes/Kohana/Request.php @@ -38,7 +38,7 @@ class Kohana_Request implements HTTP_Request { /** * Creates a new request object for the given URI. New requests should be - * created using the [Request::instance] or [Request::factory] methods. + * Created using the [Request::factory] method. * * $request = Request::factory($uri); * @@ -631,7 +631,7 @@ protected static function _parse_accept( & $header, array $accepts = NULL) /** * Creates a new request object for the given URI. New requests should be - * created using the [Request::instance] or [Request::factory] methods. + * Created using the [Request::factory] method. * * $request = new Request($uri); * diff --git a/guide/kohana/flow.md b/guide/kohana/flow.md index 81a2e54b1..e478b2e7f 100644 --- a/guide/kohana/flow.md +++ b/guide/kohana/flow.md @@ -16,7 +16,7 @@ Every application follows the same flow: * Includes each module's `init.php` file, if it exists. * The `init.php` file can perform additional environment setup, including adding routes. 10. [Route::set] is called multiple times to define the [application routes](routing). - 11. [Request::instance] is called to start processing the request. + 11. [Request::factory] is called to start processing the request. 1. Checks each route that has been set until a match is found. 2. Creates the controller instance and passes the request to it. 3. Calls the [Controller::before] method. diff --git a/guide/kohana/mvc/controllers.md b/guide/kohana/mvc/controllers.md index 3a155dbff..2c9f8ab10 100644 --- a/guide/kohana/mvc/controllers.md +++ b/guide/kohana/mvc/controllers.md @@ -55,7 +55,7 @@ You can also have a controller extend another controller to share common things, Every controller has the `$this->request` property which is the [Request] object that called the controller. You can use this to get information about the current request, as well as set the response body via `$this->response->body($ouput)`. -Here is a partial list of the properties and methods available to `$this->request`. These can also be accessed via `Request::instance()`, but `$this->request` is provided as a shortcut. See the [Request] class for more information on any of these. +Here is a partial list of the properties and methods available to `$this->request`. See the [Request] class for more information on any of these. Property/method | What it does --- | --- From 9c377058db39083131d8a44c89a4343ce9180d46 Mon Sep 17 00:00:00 2001 From: Samuel Demirdjian Date: Thu, 18 Sep 2014 12:36:29 +0300 Subject: [PATCH 073/106] related to R4813, removes redundant notice disabling lines `mb_convert_encoding`, unlike `iconv`, does not emit `E_NOTICE` --- classes/Kohana/UTF8.php | 6 ------ 1 file changed, 6 deletions(-) diff --git a/classes/Kohana/UTF8.php b/classes/Kohana/UTF8.php index ca5e315a8..d18b31bd2 100644 --- a/classes/Kohana/UTF8.php +++ b/classes/Kohana/UTF8.php @@ -70,13 +70,7 @@ public static function clean($var, $charset = NULL) if ( ! UTF8::is_ascii($var)) { - // Disable notices - $error_reporting = error_reporting(~E_NOTICE); - $var = mb_convert_encoding($var, $charset, $charset); - - // Turn notices back on - error_reporting($error_reporting); } } From a0d06fd01cdad46adf1f9ad1131585b9cc20824e Mon Sep 17 00:00:00 2001 From: Samuel Demirdjian Date: Mon, 22 Sep 2014 14:38:25 +0300 Subject: [PATCH 074/106] Xdebug returns dynamic and static instead of using -> and :: --- classes/Kohana/Kohana/Exception.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/classes/Kohana/Kohana/Exception.php b/classes/Kohana/Kohana/Exception.php index d54b46436..3cbe166bb 100644 --- a/classes/Kohana/Kohana/Exception.php +++ b/classes/Kohana/Kohana/Exception.php @@ -217,6 +217,16 @@ public static function response(Exception $e) $frame['type'] = '??'; } + // Xdebug returns the words 'dynamic' and 'static' instead of using '->' and '::' symbols + if ('dynamic' === $frame['type']) + { + $frame['type'] = '->'; + } + elseif ('static' === $frame['type']) + { + $frame['type'] = '::'; + } + // XDebug also has a different name for the parameters array if (isset($frame['params']) AND ! isset($frame['args'])) { From 2faa4dca5d75f1e2edf0f6a691bc1c1ffee8c272 Mon Sep 17 00:00:00 2001 From: Andrew Coulton Date: Mon, 22 Sep 2014 17:35:42 +0100 Subject: [PATCH 075/106] Specify the COMPOSER_ROOT_VERSION for travis installation This works around the circular dependency on kohana/unittest > kohana/core which leads to resolution problems when working on a feature branch. --- .travis.yml | 2 +- README.md | 28 ++++++++++++++++++++++++++++ composer.json | 1 + 3 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 README.md diff --git a/.travis.yml b/.travis.yml index 8f9e0fcae..862a7b467 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,7 +8,7 @@ php: - hhvm before_script: - - composer install --prefer-dist + - COMPOSER_ROOT_VERSION=3.3.x-dev composer install --prefer-dist - vendor/bin/koharness script: diff --git a/README.md b/README.md new file mode 100644 index 000000000..4fe265fe5 --- /dev/null +++ b/README.md @@ -0,0 +1,28 @@ +# Kohana PHP Framework - core + +This is the core package for the [Kohana](http://kohanaframework.org/) object oriented HMVC framework built using PHP5. +It aims to be swift, secure, and small. + +Released under a [BSD license](http://kohanaframework.org/license), Kohana can be used legally for any open source, +commercial, or personal project. + +## Documentation and installation + +See the [sample application repository](https://github.com/kohana/kohana) for full readme and contributing information. +You will usually add `kohana/core` as a dependency in your own project's composer.json to install and work with this +pacakge. + +## Installation for development + +To work on this package, you'll want to install it with composer to get the required dependencies. Note that there are +currently circular dependencies between this module and kohana/unittest. These may cause you problems if you are working +on a feature branch, because composer may not be able to figure out which version of kohana core you have. + +To work around this, run composer like: `COMPOSER_ROOT_VERSION=3.3.x-dev composer install`. This tells composer that the +current checkout is a 3.3.* development version. Obviously change the argument if your branch is based on a different +version. + +After installing the dependencies, you'll need a skeleton Kohana application before you can run the unit tests etc. The +simplest way to do this is to use kohana/koharness to build a bare project in `/tmp/koharness`. + +If in doubt, check the install and test steps in the [.travis.yml](.travis.yml) file. diff --git a/composer.json b/composer.json index 32d2a3252..9a88f4012 100644 --- a/composer.json +++ b/composer.json @@ -1,4 +1,5 @@ { + "_readme": "NOTE: see readme for COMPOSER_ROOT_VERSION instructions if you have dependency issues", "name": "kohana/core", "description": "Core system classes for the Kohana application framework", "homepage": "http://kohanaframework.org", From a9db554dedb1d9e8aded970f08bc3b748bca5bd3 Mon Sep 17 00:00:00 2001 From: Andrew Coulton Date: Mon, 22 Sep 2014 17:36:10 +0100 Subject: [PATCH 076/106] Remove IRC notifications from travis --- .travis.yml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index 862a7b467..603c929a3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,10 +15,4 @@ script: - cd /tmp/koharness && ./vendor/bin/phpunit --bootstrap=modules/unittest/bootstrap.php modules/unittest/tests.php notifications: - irc: - channels: - - "irc.freenode.org#kohana" - template: - - "%{repository}/%{branch} (%{commit}) - %{author}: %{message}" - - "Build details: %{build_url}" email: false From 526fe4d14435722f2f118d4f6a2bd759643dd89f Mon Sep 17 00:00:00 2001 From: Andrew Coulton Date: Tue, 23 Sep 2014 08:51:24 +0100 Subject: [PATCH 077/106] Update .gitignore for consistency with other modules --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 09b35f5ba..093963d5e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ *~ *.swp /composer.lock -/vendor +/vendor/* /koharness_bootstrap.php From f6aee425f376983d80de607da0cdc9e6c8ac7dd1 Mon Sep 17 00:00:00 2001 From: Andrew Coulton Date: Thu, 18 Sep 2014 14:31:32 +0100 Subject: [PATCH 078/106] Rewrite Cookie tests to be runnable and cover current behaviour The cookie tests were all being skipped because headers are always sent by the PHPUnit test runner (fixes kohana/kohana#47). Additionally even when they ran they were covering very little of the actual logic of the class, mostly just asserting that setcookie returns TRUE. Rewrote the tests completely and slightly refactored the Cookie class to allow mocking of the timestamp and setcookie calls. Refs #547. --- classes/Kohana/Cookie.php | 60 +++++-- tests/kohana/CookieTest.php | 314 ++++++++++++++++++++++++++---------- 2 files changed, 280 insertions(+), 94 deletions(-) diff --git a/classes/Kohana/Cookie.php b/classes/Kohana/Cookie.php index fe4cc490d..8ee3d239e 100644 --- a/classes/Kohana/Cookie.php +++ b/classes/Kohana/Cookie.php @@ -78,7 +78,7 @@ public static function get($key, $default = NULL) } // The cookie signature is invalid, delete it - Cookie::delete($key); + static::delete($key); } return $default; @@ -88,33 +88,38 @@ public static function get($key, $default = NULL) * Sets a signed cookie. Note that all cookie values must be strings and no * automatic serialization will be performed! * + * [!!] By default, Cookie::$expiration is 0 - if you skip/pass NULL for the optional + * lifetime argument your cookies will expire immediately unless you have separately + * configured Cookie::$expiration. + * + * * // Set the "theme" cookie * Cookie::set('theme', 'red'); * * @param string $name name of cookie * @param string $value value of cookie - * @param integer $expiration lifetime in seconds + * @param integer $lifetime lifetime in seconds * @return boolean * @uses Cookie::salt */ - public static function set($name, $value, $expiration = NULL) + public static function set($name, $value, $lifetime = NULL) { - if ($expiration === NULL) + if ($lifetime === NULL) { // Use the default expiration - $expiration = Cookie::$expiration; + $lifetime = Cookie::$expiration; } - if ($expiration !== 0) + if ($lifetime !== 0) { // The expiration is expected to be a UNIX timestamp - $expiration += time(); + $lifetime += static::_time(); } // Add the salt to the cookie value $value = Cookie::salt($name, $value).'~'.$value; - return setcookie($name, $value, $expiration, Cookie::$path, Cookie::$domain, Cookie::$secure, Cookie::$httponly); + return static::_setcookie($name, $value, $lifetime, Cookie::$path, Cookie::$domain, Cookie::$secure, Cookie::$httponly); } /** @@ -131,7 +136,7 @@ public static function delete($name) unset($_COOKIE[$name]); // Nullify the cookie and make it expire - return setcookie($name, NULL, -86400, Cookie::$path, Cookie::$domain, Cookie::$secure, Cookie::$httponly); + return static::_setcookie($name, NULL, -86400, Cookie::$path, Cookie::$domain, Cookie::$secure, Cookie::$httponly); } /** @@ -139,8 +144,10 @@ public static function delete($name) * * $salt = Cookie::salt('theme', 'red'); * - * @param string $name name of cookie - * @param string $value value of cookie + * @param string $name name of cookie + * @param string $value value of cookie + * + * @throws Kohana_Exception if Cookie::$salt is not configured * @return string */ public static function salt($name, $value) @@ -157,4 +164,35 @@ public static function salt($name, $value) return sha1($agent.$name.$value.Cookie::$salt); } + /** + * Proxy for the native setcookie function - to allow mocking in unit tests so that they do not fail when headers + * have been sent. + * + * @param string $name + * @param string $value + * @param integer $expire + * @param string $path + * @param string $domain + * @param boolean $secure + * @param boolean $httponly + * + * @return bool + * @see setcookie + */ + protected static function _setcookie($name, $value, $expire, $path, $domain, $secure, $httponly) + { + return setcookie($name, $value, $expire, $path, $domain, $secure, $httponly); + } + + /** + * Proxy for the native time function - to allow mocking of time-related logic in unit tests + * + * @return int + * @see time + */ + protected static function _time() + { + return time(); + } + } diff --git a/tests/kohana/CookieTest.php b/tests/kohana/CookieTest.php index 9c1fe400c..25159b9eb 100644 --- a/tests/kohana/CookieTest.php +++ b/tests/kohana/CookieTest.php @@ -11,13 +11,15 @@ * @category Tests * @author Kohana Team * @author Jeremy Bush - * @copyright (c) 2008-2012 Kohana Team + * @author Andrew Coulton + * @copyright (c) 2008-2014 Kohana Team * @license http://kohanaframework.org/license */ class Kohana_CookieTest extends Unittest_TestCase { + const UNIX_TIMESTAMP = 1411040141; + const COOKIE_EXPIRATION = 60; - protected $_default_salt = 'AdaoidadnA£ASDNadnaoiwdnawd'; /** * Sets up the environment */ @@ -26,152 +28,298 @@ public function setUp() // @codingStandardsIgnoreEnd { parent::setUp(); + Kohana_CookieTest_TestableCookie::$_mock_cookies_set = array(); - Cookie::$salt = $this->_default_salt; + $this->setEnvironment(array( + 'Cookie::$salt' => 'some-random-salt', + 'HTTP_USER_AGENT' => 'cli' + )); } /** - * Tears down the environment + * Tests that cookies are set with the global path, domain, etc options. + * + * @covers Cookie::set */ - // @codingStandardsIgnoreStart - public function tearDown() - // @codingStandardsIgnoreEnd + public function test_set_creates_cookie_with_configured_cookie_options() { - parent::tearDown(); + $this->setEnvironment(array( + 'Cookie::$path' => '/path', + 'Cookie::$domain' => 'my.domain', + 'Cookie::$secure' => TRUE, + 'Cookie::$httponly' => FALSE, + )); - Cookie::$salt = NULL; + Kohana_CookieTest_TestableCookie::set('cookie', 'value'); + + $this->assertSetCookieWith(array( + 'path' => '/path', + 'domain' => 'my.domain', + 'secure' => TRUE, + 'httponly' => FALSE + )); } /** - * Provides test data for test_set() + * Provider for test_set_calculates_expiry_from_lifetime * - * @return array + * @return array of $lifetime, $expect_expiry */ - public function provider_set() + public function provider_set_calculates_expiry_from_lifetime() { return array( - array('foo', 'bar', NULL, TRUE), - array('foo', 'bar', 10, TRUE), + array(NULL, self::COOKIE_EXPIRATION + self::UNIX_TIMESTAMP), + array(0, 0), + array(10, 10 + self::UNIX_TIMESTAMP), ); } /** - * Tests cookie::set() + * @param int $expiration + * @param int $expect_expiry * - * @test - * @dataProvider provider_set - * @covers cookie::set - * @param mixed $key key to use - * @param mixed $value value to set - * @param mixed $exp exp to set - * @param boolean $expected Output for cookie::set() - */ - public function test_set($key, $value, $exp, $expected) - { - if (headers_sent()) { - $this->markTestSkipped('Cannot test setting cookies as headers have already been sent'); - } + * @dataProvider provider_set_calculates_expiry_from_lifetime + * @covers Cookie::set + */ + public function test_set_calculates_expiry_from_lifetime($expiration, $expect_expiry) + { + $this->setEnvironment(array('Cookie::$expiration' => self::COOKIE_EXPIRATION)); + Kohana_CookieTest_TestableCookie::set('foo', 'bar', $expiration); + $this->assertSetCookieWith(array('expire' => $expect_expiry)); + } + + /** + * @covers Cookie::get + */ + public function test_get_returns_default_if_cookie_missing() + { + unset($_COOKIE['missing_cookie']); + $this->assertEquals('default', Cookie::get('missing_cookie', 'default')); + } - $this->assertSame($expected, cookie::set($key, $value, $exp)); + /** + * @covers Cookie::get + */ + public function test_get_returns_value_if_cookie_present_and_signed() + { + Kohana_CookieTest_TestableCookie::set('cookie', 'value'); + $cookie = Kohana_CookieTest_TestableCookie::$_mock_cookies_set[0]; + $_COOKIE[$cookie['name']] = $cookie['value']; + $this->assertEquals('value', Cookie::get('cookie', 'default')); } /** - * Provides test data for test_get() + * Provider for test_get_returns_default_without_deleting_if_cookie_unsigned * * @return array */ - public function provider_get() + public function provider_get_returns_default_without_deleting_if_cookie_unsigned() { - // setUp is called after the provider so we need to specify a - // salt here in order to use it in the provider - Cookie::$salt = $this->_default_salt; - return array( - array('foo', Cookie::salt('foo', 'bar').'~bar', 'bar'), - array('bar', Cookie::salt('foo', 'bar').'~bar', NULL), - array(NULL, Cookie::salt('foo', 'bar').'~bar', NULL), + array('unsalted'), + array('un~salted'), ); } /** - * Tests cookie::set() + * Verifies that unsigned cookies are not available to the kohana application, but are not affected for other + * consumers. + * + * @param string $unsigned_value * - * @test - * @dataProvider provider_get - * @covers cookie::get - * @param mixed $key key to use - * @param mixed $value value to set - * @param boolean $expected Output for cookie::get() + * @dataProvider provider_get_returns_default_without_deleting_if_cookie_unsigned + * @covers Cookie::get */ - public function test_get($key, $value, $expected) + public function test_get_returns_default_without_deleting_if_cookie_unsigned($unsigned_value) { - if (headers_sent()) { - $this->markTestSkipped('Cannot test setting cookies as headers have already been sent'); - } + $_COOKIE['cookie'] = $unsigned_value; + $this->assertEquals('default', Kohana_CookieTest_TestableCookie::get('cookie', 'default')); + $this->assertEquals($unsigned_value, $_COOKIE['cookie'], '$_COOKIE not affected'); + $this->assertEmpty(Kohana_CookieTest_TestableCookie::$_mock_cookies_set, 'No cookies set or changed'); + } - // Force $_COOKIE - if ($key !== NULL) - { - $_COOKIE[$key] = $value; - } + /** + * If a cookie looks like a signed cookie but the signature no longer matches, it should be deleted. + * + * @covers Cookie::get + */ + public function test_get_returns_default_and_deletes_tampered_signed_cookie() + { + $_COOKIE['cookie'] = Cookie::salt('cookie', 'value').'~tampered'; + $this->assertEquals('default', Kohana_CookieTest_TestableCookie::get('cookie', 'default')); + $this->assertDeletedCookie('cookie'); + } + + /** + * @covers Cookie::delete + */ + public function test_delete_removes_cookie_from_globals_and_expires_cookie() + { + $_COOKIE['cookie'] = Cookie::salt('cookie', 'value').'~tampered'; + $this->assertTrue(Kohana_CookieTest_TestableCookie::delete('cookie')); + $this->assertDeletedCookie('cookie'); + } + + /** + * @covers Cookie::delete + * @link http://dev.kohanaframework.org/issues/3501 + * @link http://dev.kohanaframework.org/issues/3020 + */ + public function test_delete_does_not_require_configured_salt() + { + Cookie::$salt = NULL; + $this->assertTrue(Kohana_CookieTest_TestableCookie::delete('cookie')); + $this->assertDeletedCookie('cookie'); + } + + /** + * @covers Cookie::salt + * @expectedException Kohana_Exception + */ + public function test_salt_throws_with_no_configured_salt() + { + Cookie::$salt = NULL; + Cookie::salt('key', 'value'); + } - $this->assertSame($expected, cookie::get($key)); + /** + * @covers Cookie::salt + */ + public function test_salt_creates_same_hash_for_same_values_and_state() + { + $name = 'cookie'; + $value = 'value'; + $this->assertEquals(Cookie::salt($name, $value), Cookie::salt($name, $value)); } /** - * Provides test data for test_delete() + * Provider for test_salt_creates_different_hash_for_different_data * * @return array */ - public function provider_delete() + public function provider_salt_creates_different_hash_for_different_data() { return array( - array('foo', TRUE), + array(array('name' => 'foo', 'value' => 'bar', 'salt' => 'our-salt', 'user-agent' => 'Chrome'), array('name' => 'changed')), + array(array('name' => 'foo', 'value' => 'bar', 'salt' => 'our-salt', 'user-agent' => 'Chrome'), array('value' => 'changed')), + array(array('name' => 'foo', 'value' => 'bar', 'salt' => 'our-salt', 'user-agent' => 'Chrome'), array('salt' => 'changed-salt')), + array(array('name' => 'foo', 'value' => 'bar', 'salt' => 'our-salt', 'user-agent' => 'Chrome'), array('user-agent' => 'Firefox')), + array(array('name' => 'foo', 'value' => 'bar', 'salt' => 'our-salt', 'user-agent' => 'Chrome'), array('user-agent' => NULL)), ); } /** - * Tests cookie::delete() + * @param array $first_args + * @param array $changed_args * - * @test - * @dataProvider provider_delete - * @covers cookie::delete - * @param mixed $key key to use - * @param boolean $expected Output for cookie::delete() + * @dataProvider provider_salt_creates_different_hash_for_different_data + * @covers Cookie::salt */ - public function test_delete($key, $expected) + public function test_salt_creates_different_hash_for_different_data($first_args, $changed_args) { - if (headers_sent()) { - $this->markTestSkipped('Cannot test setting cookies as headers have already been sent'); + $second_args = array_merge($first_args, $changed_args); + $hashes = array(); + foreach (array($first_args, $second_args) as $args) + { + Cookie::$salt = $args['salt']; + $this->set_or_remove_http_user_agent($args['user-agent']); + + $hashes[] = Cookie::salt($args['name'], $args['value']); } - $this->assertSame($expected, cookie::delete($key)); + $this->assertNotEquals($hashes[0], $hashes[1]); } /** - * Provides test data for test_salt() + * Verify that a cookie was deleted from the global $_COOKIE array, and that a setcookie call was made to remove it + * from the client. * - * @return array + * @param string $name */ - public function provider_salt() + // @codingStandardsIgnoreStart + protected function assertDeletedCookie($name) + // @codingStandardsIgnoreEnd { - return array( - array('foo', 'bar', 'b5773a6255d1deefc23f9f69bcc40fdc998e5802'), - ); + $this->assertArrayNotHasKey($name, $_COOKIE); + // To delete the client-side cookie, Cookie::delete should send a new cookie with value NULL and expiry in the past + $this->assertSetCookieWith(array( + 'name' => $name, + 'value' => NULL, + 'expire' => -86400, + 'path' => Cookie::$path, + 'domain' => Cookie::$domain, + 'secure' => Cookie::$secure, + 'httponly' => Cookie::$httponly + )); } /** - * Tests cookie::salt() + * Verify that there was a single call to setcookie including the provided named arguments * - * @test - * @dataProvider provider_salt - * @covers cookie::salt - * @param mixed $key key to use - * @param mixed $value value to salt with - * @param boolean $expected Output for cookie::delete() + * @param array $expected + */ + // @codingStandardsIgnoreStart + protected function assertSetCookieWith($expected) + // @codingStandardsIgnoreEnd + { + $this->assertCount(1, Kohana_CookieTest_TestableCookie::$_mock_cookies_set); + $relevant_values = array_intersect_key(Kohana_CookieTest_TestableCookie::$_mock_cookies_set[0], $expected); + $this->assertEquals($expected, $relevant_values); + } + + /** + * Configure the $_SERVER[HTTP_USER_AGENT] environment variable for the test + * + * @param string $user_agent + */ + protected function set_or_remove_http_user_agent($user_agent) + { + if ($user_agent === NULL) + { + unset($_SERVER['HTTP_USER_AGENT']); + } + else + { + $_SERVER['HTTP_USER_AGENT'] = $user_agent; + } + } +} + +/** + * Class Kohana_CookieTest_TestableCookie wraps the cookie class to mock out the actual setcookie and time calls for + * unit testing. + */ +class Kohana_CookieTest_TestableCookie extends Cookie { + + /** + * @var array setcookie calls that were made */ - public function test_salt($key, $value, $expected) + public static $_mock_cookies_set = array(); + + /** + * {@inheritdoc} + */ + protected static function _setcookie($name, $value, $expire, $path, $domain, $secure, $httponly) { - $this->assertSame($expected, cookie::salt($key, $value)); + self::$_mock_cookies_set[] = array( + 'name' => $name, + 'value' => $value, + 'expire' => $expire, + 'path' => $path, + 'domain' => $domain, + 'secure' => $secure, + 'httponly' => $httponly + ); + + return TRUE; } + + /** + * @return int + */ + protected static function _time() + { + return Kohana_CookieTest::UNIX_TIMESTAMP; + } + } From 200a25cbe76f06c7341e60bfdf5e6756af70da1d Mon Sep 17 00:00:00 2001 From: Andrew Coulton Date: Thu, 18 Sep 2014 14:44:37 +0100 Subject: [PATCH 079/106] Reinstate skipped Security::csrf tests These tests were disabled in 46e2ee9946 because they were generating an error if headers had already been sent. http://dev.kohanaframework.org/issues/4155 This appears no longer the case - presumably the Session class or test configuration/environment has changed since then. The tests could probably still be clearer and more comprehensive, but they run. --- tests/kohana/SecurityTest.php | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/tests/kohana/SecurityTest.php b/tests/kohana/SecurityTest.php index 4c3b368f5..25eb020b5 100644 --- a/tests/kohana/SecurityTest.php +++ b/tests/kohana/SecurityTest.php @@ -67,17 +67,6 @@ public function test_strip_image_tags($expected, $input) */ public function provider_csrf_token() { - // Unfortunately this data provider has to use the session in order to - // generate its data. If headers have already been sent then this method - // throws an error, even if the test is does not run. If we return an - // empty array then this also causes an error, so the only way to get - // around it is to return an array of misc data and have the test skip - // if headers have been sent. It's annoying this hack has to be - // implemented, but the security code isn't exactly brilliantly - // implemented. Ideally we'd be able to inject a session instance - if (headers_sent()) - return array(array('', '', 0)); - $array = array(); for ($i = 0; $i <= 4; $i++) { @@ -96,10 +85,7 @@ public function provider_csrf_token() */ public function test_csrf_token($expected, $input, $iteration) { - if (headers_sent()) { - $this->markTestSkipped('Headers have already been sent, session not available'); - } - + //@todo: the Security::token tests need to be reviewed to check how much of the logic they're actually covering Security::$token_name = 'token_'.$iteration; $this->assertSame(TRUE, $input); $this->assertSame($expected, Security::token(FALSE)); From fe41689f0700465f2730523ce3ecd578388c1043 Mon Sep 17 00:00:00 2001 From: Andrew Coulton Date: Thu, 18 Sep 2014 15:00:05 +0100 Subject: [PATCH 080/106] Reinstate one skipped test for File::mime These tests aren't great, and the PHP mime-type test is particularly likely to be unreliable. We should be able to get a valid (eg image/png) result in all current environments though for the moment. Probably the File::mime method would be best reimplemented with some third-party component, surely this is a solved problem by now. --- tests/kohana/FileTest.php | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/tests/kohana/FileTest.php b/tests/kohana/FileTest.php index 8d4c49112..712bb2cdd 100644 --- a/tests/kohana/FileTest.php +++ b/tests/kohana/FileTest.php @@ -5,7 +5,7 @@ * * @group kohana * @group kohana.core - * @group kohana.core.url + * @group kohana.core.file * * @package Kohana * @category Tests @@ -25,8 +25,7 @@ public function provider_mime() { return array( // $value, $result - array(Kohana::find_file('classes', 'File')), - array(Kohana::find_file('tests', 'test_data/github', 'png')), + array(Kohana::find_file('tests', 'test_data/github', 'png'), 'image/png'), ); } @@ -38,12 +37,10 @@ public function provider_mime() * @param boolean $input Input for File::mime * @param boolean $expected Output for File::mime */ - public function test_mime($input) + public function test_mime($input, $expected) { - $this->markTestSkipped( - 'This test doesn\'t do anything useful!' - ); - $this->assertSame(1, preg_match('/^(?:application|audio|image|message|multipart|text|video)\/[a-z.+0-9-]+$/i', File::mime($input))); + //@todo: File::mime coverage needs significant improvement or to be dropped for a composer package - it's a "horribly unreliable" method with very little testing + $this->assertSame($expected, File::mime($input)); } /** From 5a24c6e185203638d3e92445e415834a536a1683 Mon Sep 17 00:00:00 2001 From: Andrew Coulton Date: Thu, 18 Sep 2014 15:10:28 +0100 Subject: [PATCH 081/106] Fix mocking for incomplete Session test --- tests/kohana/SessionTest.php | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/tests/kohana/SessionTest.php b/tests/kohana/SessionTest.php index 1f34fa807..83a35921d 100644 --- a/tests/kohana/SessionTest.php +++ b/tests/kohana/SessionTest.php @@ -95,20 +95,14 @@ public function test_constructor_uses_settings_from_config_and_casts($expected, */ public function test_constructor_loads_session_with_session_id() { - $this->markTestIncomplete( - 'Need to work out why constructor is not being called' - ); - $config = array(); $session_id = 'lolums'; // Don't auto-call constructor, we need to setup the mock first - $session = $this->getMockForAbstractClass( - 'Session', - array(), - '', - FALSE - ); + $session = $this->getMockBuilder('Session') + ->disableOriginalConstructor() + ->setMethods(array('read')) + ->getMockForAbstractClass(); $session ->expects($this->once()) From c7bc251f3d035cfaddbfa75eaca27a6a6dc59041 Mon Sep 17 00:00:00 2001 From: Andrew Coulton Date: Thu, 18 Sep 2014 15:37:35 +0100 Subject: [PATCH 082/106] Rewrite Kohana::message tests for fragility and coverage These tests were vulnerable to breakage if modules provide their own validation message files, so were being skipped. Fixed to use (hopefully) collision-proof message files with known values and to cover a broader range of cases including missing file and missing key. Would ideally be further refactored to mock/have complete control of the module search path outside global state. --- tests/kohana/CoreTest.php | 82 +++++++++++-------- .../messages/kohana_core_message_tests.php | 6 ++ .../messages/kohana_core_message_tests.php | 6 ++ 3 files changed, 59 insertions(+), 35 deletions(-) create mode 100644 tests/test_data/message_tests/bottom_module/messages/kohana_core_message_tests.php create mode 100644 tests/test_data/message_tests/top_module/messages/kohana_core_message_tests.php diff --git a/tests/kohana/CoreTest.php b/tests/kohana/CoreTest.php index 04b06dfcd..bdb750914 100644 --- a/tests/kohana/CoreTest.php +++ b/tests/kohana/CoreTest.php @@ -18,6 +18,32 @@ */ class Kohana_CoreTest extends Unittest_TestCase { + protected $old_modules = array(); + + /** + * Captures the module list as it was before this test + * + * @return null + */ + // @codingStandardsIgnoreStart + public function setUp() + // @codingStandardsIgnoreEnd + { + parent::setUp(); + $this->old_modules = Kohana::modules(); + } + + /** + * Restores the module list + * + * @return null + */ + // @codingStandardsIgnoreStart + public function tearDown() + // @codingStandardsIgnoreEnd + { + Kohana::modules($this->old_modules); + } /** * Provides test data for test_sanitize() @@ -157,35 +183,18 @@ public function test_cache($key, $value, $lifetime) public function provider_message() { return array( - // $value, $result - array(':field must not be empty', 'validation', 'not_empty'), - array( + array('no_message_file', 'anything', 'default', 'default'), + array('no_message_file', NULL, 'anything', array()), + array('kohana_core_message_tests', 'bottom_only', 'anything', 'inherited bottom message'), + array('kohana_core_message_tests', 'cfs_replaced', 'anything', 'overriding cfs_replaced message'), + array('kohana_core_message_tests', 'top_only', 'anything', 'top only message'), + array('kohana_core_message_tests', 'missing', 'default', 'default'), + array('kohana_core_message_tests', NULL, 'anything', array( - 'alpha' => ':field must contain only letters', - 'alpha_dash' => ':field must contain only numbers, letters and dashes', - 'alpha_numeric' => ':field must contain only letters and numbers', - 'color' => ':field must be a color', - 'credit_card' => ':field must be a credit card number', - 'date' => ':field must be a date', - 'decimal' => ':field must be a decimal with :param2 places', - 'digit' => ':field must be a digit', - 'email' => ':field must be a email address', - 'email_domain' => ':field must contain a valid email domain', - 'equals' => ':field must equal :param2', - 'exact_length' => ':field must be exactly :param2 characters long', - 'in_array' => ':field must be one of the available options', - 'ip' => ':field must be an ip address', - 'matches' => ':field must be the same as :param2', - 'min_length' => ':field must be at least :param2 characters long', - 'max_length' => ':field must not exceed :param2 characters long', - 'not_empty' => ':field must not be empty', - 'numeric' => ':field must be numeric', - 'phone' => ':field must be a phone number', - 'range' => ':field must be within the range of :param2 to :param3', - 'regex' => ':field does not match the required format', - 'url' => ':field must be a url', - ), - 'validation', NULL, + 'bottom_only' => 'inherited bottom message', + 'cfs_replaced' => 'overriding cfs_replaced message', + 'top_only' => 'top only message' + ) ), ); } @@ -195,15 +204,18 @@ public function provider_message() * * @test * @dataProvider provider_message - * @covers Kohana::message - * @param boolean $expected Output for Kohana::message - * @param boolean $file File to look in for Kohana::message - * @param boolean $key Key for Kohana::message + * @covers Kohana::message + * @param string $file to pass to Kohana::message + * @param string $key to pass to Kohana::message + * @param string $default to pass to Kohana::message + * @param string $expected Output for Kohana::message */ - public function test_message($expected, $file, $key) + public function test_message($file, $key, $default, $expected) { - $this->markTestSkipped('This test is incredibly fragile and needs to be re-done'); - $this->assertEquals($expected, Kohana::message($file, $key)); + $test_path = realpath(dirname(__FILE__).'/../test_data/message_tests'); + Kohana::modules(array('top' => "$test_path/top_module", 'bottom' => "$test_path/bottom_module")); + + $this->assertEquals($expected, Kohana::message($file, $key, $default, $expected)); } /** diff --git a/tests/test_data/message_tests/bottom_module/messages/kohana_core_message_tests.php b/tests/test_data/message_tests/bottom_module/messages/kohana_core_message_tests.php new file mode 100644 index 000000000..efd167999 --- /dev/null +++ b/tests/test_data/message_tests/bottom_module/messages/kohana_core_message_tests.php @@ -0,0 +1,6 @@ + 'inherited bottom message', + 'cfs_replaced' => 'inherited cfs_replaced message', +); diff --git a/tests/test_data/message_tests/top_module/messages/kohana_core_message_tests.php b/tests/test_data/message_tests/top_module/messages/kohana_core_message_tests.php new file mode 100644 index 000000000..07fcd7e2b --- /dev/null +++ b/tests/test_data/message_tests/top_module/messages/kohana_core_message_tests.php @@ -0,0 +1,6 @@ + 'top only message', + 'cfs_replaced' => 'overriding cfs_replaced message', +); From e9cd5ed9301c6abc4240e87ccca3b79077803a1e Mon Sep 17 00:00:00 2001 From: Andrew Coulton Date: Thu, 18 Sep 2014 16:10:07 +0100 Subject: [PATCH 083/106] Reinstate and improve Feed::parse tests Original test was being skipped because it was hitting the internet - the major relevant functionality here is the parsing rather than the remote access. Added local example feeds for RSS2.0 as well as the previously tested atom feed. Also improved the tests to check for the title as well as the number of elements since we now have known content to work with. --- tests/kohana/FeedTest.php | 13 +++++-- tests/test_data/feeds/activity.atom | 58 +++++++++++++++++++++++++++++ tests/test_data/feeds/example.rss20 | 20 ++++++++++ 3 files changed, 87 insertions(+), 4 deletions(-) create mode 100644 tests/test_data/feeds/activity.atom create mode 100644 tests/test_data/feeds/example.rss20 diff --git a/tests/kohana/FeedTest.php b/tests/kohana/FeedTest.php index 40986303b..e4279dd51 100644 --- a/tests/kohana/FeedTest.php +++ b/tests/kohana/FeedTest.php @@ -25,7 +25,8 @@ public function provider_parse() { return array( // $source, $expected - array('http://dev.kohanaframework.org/projects/kohana3/activity.atom', 15), + array(realpath(__DIR__.'/../test_data/feeds/activity.atom'), array('Proposals (Political/Workflow) #4839 (New)', 'Proposals (Political/Workflow) #4782')), + array(realpath(__DIR__.'/../test_data/feeds/example.rss20'), array('Example entry')), ); } @@ -38,11 +39,15 @@ public function provider_parse() * @param string $source URL to test * @param integer $expected Count of items */ - public function test_parse($source, $expected) + public function test_parse($source, $expected_titles) { - $this->markTestSkipped('We don\'t go to the internet for tests.'); + $titles = array(); + foreach (Feed::parse($source) as $item) + { + $titles[] = $item['title']; + } - $this->assertEquals($expected, count(Feed::parse($source))); + $this->assertSame($expected_titles, $titles); } /** diff --git a/tests/test_data/feeds/activity.atom b/tests/test_data/feeds/activity.atom new file mode 100644 index 000000000..7fe42c6b1 --- /dev/null +++ b/tests/test_data/feeds/activity.atom @@ -0,0 +1,58 @@ + + + Kohana v3.x: Activity + + + http://dev.kohanaframework.org/ + http://dev.kohanaframework.org/favicon.ico?1392677580 + 2014-08-28T01:52:12Z + + Kohana Development + + +Redmine + + Proposals (Political/Workflow) #4839 (New) + + http://dev.kohanaframework.org/issues/4839 + 2014-08-28T01:52:12Z + + Guillaume Poirier-Morency + guillaumepoiriermorency@gmail.com + + +<p>I have a prototype here <a class="external" href="https://github.com/arteymix/kohana-makefile">https://github.com/arteymix/kohana-makefile</a></p> + + + <p>The tool is very useful for settings permissions and running tests.</p> + + + <p>I think we should consider having a good make tool in the sample application for the 3.4.*.</p> + + + Proposals (Political/Workflow) #4782 + + http://dev.kohanaframework.org/issues/4782#change-17279 + 2014-08-28T01:44:26Z + + Guillaume Poirier-Morency + guillaumepoiriermorency@gmail.com + + +<p>Moving to composer is a nice idea. This will allow Kohana modules to define a wide range of dependencies.</p> + + + <p>Although, I think that modules designed specifically for Kohana should end in modules and external libraries in application/vendor. This makes a clear dinsinction between what gets autoloaded by the CFS and what gets loaded by composer. Technically, we add "vendor-dir": "application/vendor" in "config" in composer.json.</p> + + + <p>Then, only add a line after the modules loading in bootstrap.php</p> + + +<pre> +// Autoloading composer packages +require Kohana::find_file('vendor', 'autoload'); +</pre> + + <p>This is pretty much what I do right now. This doesn't break anything and allow a full access to composer.</p> + + diff --git a/tests/test_data/feeds/example.rss20 b/tests/test_data/feeds/example.rss20 new file mode 100644 index 000000000..9fc6c39f1 --- /dev/null +++ b/tests/test_data/feeds/example.rss20 @@ -0,0 +1,20 @@ + + + + RSS Title + This is an example of an RSS feed + http://www.example.com/main.html + Mon, 06 Sep 2010 00:01:00 +0000 + Sun, 06 Sep 2009 16:20:00 +0000 + 1800 + + + Example entry + Here is some text containing an interesting description. + http://www.example.com/blog/post/1 + 7bd204c6-1655-4c27-aeee-53f933c5395f + Sun, 06 Sep 2009 16:20:00 +0000 + + + + From 1631e0bf4496bad1f0d3152da90c6e39f84786c1 Mon Sep 17 00:00:00 2001 From: Andrew Coulton Date: Thu, 18 Sep 2014 16:37:06 +0100 Subject: [PATCH 084/106] Remove invalid skipped test - Response does send headers in CLI This test was originally implemented in bf0175f6cd to fix http://dev.kohanaframework.org/issues/3767 - it was required because headers were sent directly by the response class, and once they had been sent it was then very difficult to test other variations. The fact that headers are not relevant in CLI appears to have been a side concern. Subsequently in a1943b0d7d26cf5a header sending moved to HTTP_Header which mocks out `header` with a callback interface for testing instead. Since that refactoring (released in v3.2.0) the headers are always sent in CLI unless headers_sent is already true. Therefore this test is invalid and can be removed. --- tests/kohana/ResponseTest.php | 23 +---------------------- 1 file changed, 1 insertion(+), 22 deletions(-) diff --git a/tests/kohana/ResponseTest.php b/tests/kohana/ResponseTest.php index d5e8870c1..998916314 100644 --- a/tests/kohana/ResponseTest.php +++ b/tests/kohana/ResponseTest.php @@ -171,27 +171,6 @@ public function test_cookie_get() $this->assertSame(Cookie::$expiration, $cookie['expiration']); } - /** - * Tests that the headers are not sent by PHP in CLI mode - * - * @return void - */ - public function test_send_headers_cli() - { - if (headers_sent()) - { - $this->markTestSkipped('Cannot test this feature as headers have already been sent!'); - } - - $content_type = 'application/json'; - $response = new Response; - $response->headers('content-type', $content_type) - ->send_headers(); - - $this->assertFalse(headers_sent()); - - } - /** * Test the content type is sent when set * @@ -205,4 +184,4 @@ public function test_content_type_when_set() $headers = $response->send_headers()->headers(); $this->assertSame($content_type, (string) $headers['content-type']); } -} \ No newline at end of file +} From 67da593df7230a97f13075a17d9fe0d236d51188 Mon Sep 17 00:00:00 2001 From: Kevin Daudt Date: Wed, 24 Sep 2014 22:15:42 +0200 Subject: [PATCH 085/106] Text: provide tests for user_agent method --- tests/kohana/TextTest.php | 107 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) diff --git a/tests/kohana/TextTest.php b/tests/kohana/TextTest.php index a59bd5939..5ede0b077 100644 --- a/tests/kohana/TextTest.php +++ b/tests/kohana/TextTest.php @@ -639,4 +639,111 @@ public function test_auto_link($text, $urls = array(), $emails = array()) } + + public function provider_user_agents() + { + return array( + array( + "Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2049.0 Safari/537.36", + array( + 'browser' => 'Chrome', + 'version' => '37.0.2049.0', + 'platform' => "Windows 8.1" + ) + ), + array( + "Mozilla/5.0 (Macintosh; U; Mac OS X 10_6_1; en-US) AppleWebKit/530.5 (KHTML, like Gecko) Chrome/ Safari/530.5", + array( + 'browser' => 'Chrome', + 'version' => '530.5', + 'platform' => "Mac OS X" + ) + ), + array( + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/537.13+ (KHTML, like Gecko) Version/5.1.7 Safari/534.57.2", + array( + 'browser' => 'Safari', + 'version' => '534.57.2', + 'platform' => 'Mac OS X' + ) + ), + array( + "Lynx/2.8.8dev.3 libwww-FM/2.14 SSL-MM/1.4.1", + array( + 'browser' => 'Lynx', + 'version' => '2.8.8dev.3', + 'platform' => false + ) + ) + ); + } + + /** + * Tests Text::user_agent + * + * @dataProvider provider_user_agents + * @group current + */ + public function test_user_agent_returns_correct_browser($userAgent, $expectedData) + { + $browser = Text::user_agent($userAgent, 'browser'); + + $this->assertEquals($expectedData['browser'], $browser); + } + + /** + * Tests Text::user_agent + * + * @dataProvider provider_user_agents + * @test + */ + public function test_user_agent_returns_correct_version($userAgent, $expectedData) + { + $version = Text::user_agent($userAgent, 'version'); + + $this->assertEquals($expectedData['version'], $version); + } + + /** + * Tests Text::user_agent + * @test + */ + public function test_user_agent_recognizes_robots() + { + $bot = Text::user_agent('Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)', 'robot'); + + $this->assertEquals('Googlebot', $bot); + } + + /** + * Tests Text::user_agent + * + * @dataProvider provider_user_agents + * @test + */ + public function test_user_agent_returns_correct_platform($userAgent, $expectedData) + { + $platform = Text::user_agent($userAgent, 'platform'); + + $this->assertEquals($expectedData['platform'], $platform); + } + + + /** + * Tests Text::user_agent + * @test + */ + public function test_user_agent_accepts_array() + { + $agent_info = Text::user_agent( + 'Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 '. + '(KHTML, like Gecko) Chrome/37.0.2049.0 Safari/537.36', + array('browser', 'version', 'platform')); + + $this->assertArrayHasKey('browser', $agent_info); + $this->assertArrayHasKey('version', $agent_info); + $this->assertArrayHasKey('platform', $agent_info); + + } + } From 0699cbf0dc2464a38f28ca3dc072254b6e6c10f2 Mon Sep 17 00:00:00 2001 From: Kevin Daudt Date: Wed, 24 Sep 2014 22:16:05 +0200 Subject: [PATCH 086/106] user_agents: add new windows platform windows nt 6.3 equals Windows 8.1 --- config/user_agents.php | 1 + 1 file changed, 1 insertion(+) diff --git a/config/user_agents.php b/config/user_agents.php index f4b92eac4..3f631a4f0 100644 --- a/config/user_agents.php +++ b/config/user_agents.php @@ -3,6 +3,7 @@ return array( 'platform' => array( + 'windows nt 6.3' => 'Windows 8.1', 'windows nt 6.2' => 'Windows 8', 'windows nt 6.1' => 'Windows 7', 'windows nt 6.0' => 'Windows Vista', From 7bbc0d2067df64ae550e465567bf0c52b8a0402b Mon Sep 17 00:00:00 2001 From: Andrew Coulton Date: Fri, 26 Sep 2014 11:40:52 +0100 Subject: [PATCH 087/106] Force Debug::dump marker to alpha for type safety [Fixes #559] Resolves a flickering test (and flickering Debug::dump output) where internal recursion markers are left in dumped arrays if the current system microtime produces a uniqid with only numeric characters. --- classes/Kohana/Debug.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/classes/Kohana/Debug.php b/classes/Kohana/Debug.php index 5a82bb623..9106b00c1 100644 --- a/classes/Kohana/Debug.php +++ b/classes/Kohana/Debug.php @@ -133,8 +133,8 @@ protected static function _dump( & $var, $length = 128, $limit = 10, $level = 0) if ($marker === NULL) { - // Make a unique marker - $marker = uniqid("\x00"); + // Make a unique marker - force it to be alphanumeric so that it is always treated as a string array key + $marker = uniqid("\x00")."x"; } if (empty($var)) From 7d640dfe54f72cb0c27624e2be5708feb12d3cd1 Mon Sep 17 00:00:00 2001 From: Samuel Demirdjian Date: Tue, 7 Oct 2014 10:09:26 +0300 Subject: [PATCH 088/106] add protected function _create_iv to proxy mcrypt_create_iv added a protected function `_create_iv` This is to proxy the mcrypt_create_iv function and to allow mocking and testing against KAT vectors. --- classes/Kohana/Encrypt.php | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/classes/Kohana/Encrypt.php b/classes/Kohana/Encrypt.php index 642860769..b37ace7c1 100644 --- a/classes/Kohana/Encrypt.php +++ b/classes/Kohana/Encrypt.php @@ -164,8 +164,8 @@ public function encode($data) mt_srand(); } - // Create a random initialization vector of the proper size for the current cipher - $iv = mcrypt_create_iv($this->_iv_size, Encrypt::$_rand); + // Get an initialization vector + $iv = $this->_create_iv(); // Encrypt the data using the configured options and generated iv $data = mcrypt_encrypt($this->_cipher, $this->_key, $data, $this->_mode, $iv); @@ -210,4 +210,13 @@ public function decode($data) return rtrim(mcrypt_decrypt($this->_cipher, $this->_key, $data, $this->_mode, $iv), "\0"); } + /** + * Proxy for the mcrypt_create_iv function - to allow mocking and testing against KAT vectors + * + * @return string the initialization vector or FALSE on error + */ + protected function _create_iv() { + // Create a random initialization vector of the proper size for the current cipher + return mcrypt_create_iv($this->_iv_size, Encrypt::$_rand); + } } From 5f8f8bdb9d102b6864857e9d9073ce4fbd4806ec Mon Sep 17 00:00:00 2001 From: Samuel Demirdjian Date: Tue, 7 Oct 2014 10:13:47 +0300 Subject: [PATCH 089/106] Unit test for the Encrypt class Adds a class Kohana_EncryptTest_TestableEncrypt that wraps the Encrypt class to mock out the actual mcrypt_create_iv calls. The test data are provided by Cryptographic Algorithm Validation Program National Institute of Standards and Technology http://csrc.nist.gov/groups/STM/cavp/index.html --- tests/kohana/EncryptTest.php | 410 +++++++++++++++++++++++++++++++++++ 1 file changed, 410 insertions(+) create mode 100644 tests/kohana/EncryptTest.php diff --git a/tests/kohana/EncryptTest.php b/tests/kohana/EncryptTest.php new file mode 100644 index 000000000..919dddf64 --- /dev/null +++ b/tests/kohana/EncryptTest.php @@ -0,0 +1,410 @@ + + * @copyright (c) 2014 Kohana Team + * @license http://kohanaframework.org/license + */ +class Kohana_EncryptTest extends Unittest_TestCase +{ + + /** + * Provider for test_encode + * AES Multiblock Message Test (MMT) Sample Vectors - Known Answer Test (KAT) + * @link http://csrc.nist.gov/groups/STM/cavp/index.html NIST - Cryptographic Algorithm Validation Program + * @link http://csrc.nist.gov/groups/STM/cavp/documents/aes/aesmmt.zip file used CBCMMT128.rsp + * + * @return array of $mode, $cipher, $key, $iv, $txt_plain, $txt_encoded + */ + public function provider_encode() + { + return array( + array( + // mode + MCRYPT_MODE_CBC, + // cypher + MCRYPT_RIJNDAEL_128, + // key + pack("H*", "1f8e4973953f3fb0bd6b16662e9a3c17"), + // IV + pack("H*", "2fe2b333ceda8f98f4a99b40d2cd34a8"), + // txt_plain + pack("H*", "45cf12964fc824ab76616ae2f4bf0822"), + // txt_encoded + pack("H*", "0f61c4d44c5147c03c195ad7e2cc12b2"), + ), + array( + // mode + MCRYPT_MODE_CBC, + // cypher + MCRYPT_RIJNDAEL_128, + // key + pack("H*", "1f8e4973953f3fb0bd6b16662e9a3c17"), + // IV + pack("H*", "2fe2b333ceda8f98f4a99b40d2cd34a8"), + // txt_plain + pack("H*", "45cf12964fc824ab76616ae2f4bf0822"), + // txt_encoded + pack("H*", "0f61c4d44c5147c03c195ad7e2cc12b2"), + ), + array( + // mode + MCRYPT_MODE_CBC, + // cypher + MCRYPT_RIJNDAEL_128, + // key + pack("H*", "0700d603a1c514e46b6191ba430a3a0c"), + // IV + pack("H*", "aad1583cd91365e3bb2f0c3430d065bb"), + // txt_plain + pack("H*", "068b25c7bfb1f8bdd4cfc908f69dffc5ddc726a197f0e5f720f730393279be91"), + // txt_encoded + pack("H*", "c4dc61d9725967a3020104a9738f23868527ce839aab1752fd8bdb95a82c4d00"), + ), + array( + // mode + MCRYPT_MODE_CBC, + // cypher + MCRYPT_RIJNDAEL_128, + // key + pack("H*", "3348aa51e9a45c2dbe33ccc47f96e8de"), + // IV + pack("H*", "19153c673160df2b1d38c28060e59b96"), + // txt_plain + pack("H*", "9b7cee827a26575afdbb7c7a329f887238052e3601a7917456ba61251c214763d5e1847a6ad5d54127a399ab07ee3599"), + // txt_encoded + pack("H*", "d5aed6c9622ec451a15db12819952b6752501cf05cdbf8cda34a457726ded97818e1f127a28d72db5652749f0c6afee5"), + ), + array( + // mode + MCRYPT_MODE_CBC, + // cypher + MCRYPT_RIJNDAEL_128, + // key + pack("H*", "b7f3c9576e12dd0db63e8f8fac2b9a39"), + // IV + pack("H*", "c80f095d8bb1a060699f7c19974a1aa0"), + // txt_plain + pack("H*", "9ac19954ce1319b354d3220460f71c1e373f1cd336240881160cfde46ebfed2e791e8d5a1a136ebd1dc469dec00c4187722b841cdabcb22c1be8a14657da200e"), + // txt_encoded + pack("H*", "19b9609772c63f338608bf6eb52ca10be65097f89c1e0905c42401fd47791ae2c5440b2d473116ca78bd9ff2fb6015cfd316524eae7dcb95ae738ebeae84a467"), + ), + array( + // mode + MCRYPT_MODE_CBC, + // cypher + MCRYPT_RIJNDAEL_128, + // key + pack("H*", "b6f9afbfe5a1562bba1368fc72ac9d9c"), + // IV + pack("H*", "3f9d5ebe250ee7ce384b0d00ee849322"), + // txt_plain + pack("H*", "db397ec22718dbffb9c9d13de0efcd4611bf792be4fce0dc5f25d4f577ed8cdbd4eb9208d593dda3d4653954ab64f05676caa3ce9bfa795b08b67ceebc923fdc89a8c431188e9e482d8553982cf304d1"), + // txt_encoded + pack("H*", "10ea27b19e16b93af169c4a88e06e35c99d8b420980b058e34b4b8f132b13766f72728202b089f428fecdb41c79f8aa0d0ef68f5786481cca29e2126f69bc14160f1ae2187878ba5c49cf3961e1b7ee9"), + ), + array( + // mode + MCRYPT_MODE_CBC, + // cypher + MCRYPT_RIJNDAEL_128, + // key + pack("H*", "bbe7b7ba07124ff1ae7c3416fe8b465e"), + // IV + pack("H*", "7f65b5ee3630bed6b84202d97fb97a1e"), + // txt_plain + pack("H*", "2aad0c2c4306568bad7447460fd3dac054346d26feddbc9abd9110914011b4794be2a9a00a519a51a5b5124014f4ed2735480db21b434e99a911bb0b60fe0253763725b628d5739a5117b7ee3aefafc5b4c1bf446467e7bf5f78f31ff7caf187"), + // txt_encoded + pack("H*", "3b8611bfc4973c5cd8e982b073b33184cd26110159172e44988eb5ff5661a1e16fad67258fcbfee55469267a12dc374893b4e3533d36f5634c3095583596f135aa8cd1138dc898bc5651ee35a92ebf89ab6aeb5366653bc60a70e0074fc11efe"), + ), + array( + // mode + MCRYPT_MODE_CBC, + // cypher + MCRYPT_RIJNDAEL_128, + // key + pack("H*", "89a553730433f7e6d67d16d373bd5360"), + // IV + pack("H*", "f724558db3433a523f4e51a5bea70497"), + // txt_plain + pack("H*", "807bc4ea684eedcfdcca30180680b0f1ae2814f35f36d053c5aea6595a386c1442770f4d7297d8b91825ee7237241da8925dd594ccf676aecd46ca2068e8d37a3a0ec8a7d5185a201e663b5ff36ae197110188a23503763b8218826d23ced74b31e9f6e2d7fbfa6cb43420c7807a8625"), + // txt_encoded + pack("H*", "406af1429a478c3d07e555c5287a60500d37fc39b68e5bbb9bafd6ddb223828561d6171a308d5b1a4551e8a5e7d572918d25c968d3871848d2f16635caa9847f38590b1df58ab5efb985f2c66cfaf86f61b3f9c0afad6c963c49cee9b8bc81a2ddb06c967f325515a4849eec37ce721a"), + ), + array( + // mode + MCRYPT_MODE_CBC, + // cypher + MCRYPT_RIJNDAEL_128, + // key + pack("H*", "c491ca31f91708458e29a925ec558d78"), + // IV + pack("H*", "9ef934946e5cd0ae97bd58532cb49381"), + // txt_plain + pack("H*", "cb6a787e0dec56f9a165957f81af336ca6b40785d9e94093c6190e5152649f882e874d79ac5e167bd2a74ce5ae088d2ee854f6539e0a94796b1e1bd4c9fcdbc79acbef4d01eeb89776d18af71ae2a4fc47dd66df6c4dbe1d1850e466549a47b636bcc7c2b3a62495b56bb67b6d455f1eebd9bfefecbca6c7f335cfce9b45cb9d"), + // txt_encoded + pack("H*", "7b2931f5855f717145e00f152a9f4794359b1ffcb3e55f594e33098b51c23a6c74a06c1d94fded7fd2ae42c7db7acaef5844cb33aeddc6852585ed0020a6699d2cb53809cefd169148ce42292afab063443978306c582c18b9ce0da3d084ce4d3c482cfd8fcf1a85084e89fb88b40a084d5e972466d07666126fb761f84078f2"), + ), + array( + // mode + MCRYPT_MODE_CBC, + // cypher + MCRYPT_RIJNDAEL_128, + // key + pack("H*", "f6e87d71b0104d6eb06a68dc6a71f498"), + // IV + pack("H*", "1c245f26195b76ebebc2edcac412a2f8"), + // txt_plain + pack("H*", "f82bef3c73a6f7f80db285726d691db6bf55eec25a859d3ba0e0445f26b9bb3b16a3161ed1866e4dd8f2e5f8ecb4e46d74a7a78c20cdfc7bcc9e479ba7a0caba9438238ad0c01651d5d98de37f03ddce6e6b4bd4ab03cf9e8ed818aedfa1cf963b932067b97d776dce1087196e7e913f7448e38244509f0caf36bd8217e15336d35c149fd4e41707893fdb84014f8729"), + // txt_encoded + pack("H*", "b09512f3eff9ed0d85890983a73dadbb7c3678d52581be64a8a8fc586f490f2521297a478a0598040ebd0f5509fafb0969f9d9e600eaef33b1b93eed99687b167f89a5065aac439ce46f3b8d22d30865e64e45ef8cd30b6984353a844a11c8cd60dba0e8866b3ee30d24b3fa8a643b328353e06010fa8273c8fd54ef0a2b6930e5520aae5cd5902f9b86a33592ca4365"), + ), + array( + // mode + MCRYPT_MODE_CBC, + // cypher + MCRYPT_RIJNDAEL_128, + // key + pack("H*", "2c14413751c31e2730570ba3361c786b"), + // IV + pack("H*", "1dbbeb2f19abb448af849796244a19d7"), + // txt_plain + pack("H*", "40d930f9a05334d9816fe204999c3f82a03f6a0457a8c475c94553d1d116693adc618049f0a769a2eed6a6cb14c0143ec5cccdbc8dec4ce560cfd206225709326d4de7948e54d603d01b12d7fed752fb23f1aa4494fbb00130e9ded4e77e37c079042d828040c325b1a5efd15fc842e44014ca4374bf38f3c3fc3ee327733b0c8aee1abcd055772f18dc04603f7b2c1ea69ff662361f2be0a171bbdcea1e5d3f"), + // txt_encoded + pack("H*", "6be8a12800455a320538853e0cba31bd2d80ea0c85164a4c5c261ae485417d93effe2ebc0d0a0b51d6ea18633d210cf63c0c4ddbc27607f2e81ed9113191ef86d56f3b99be6c415a4150299fb846ce7160b40b63baf1179d19275a2e83698376d28b92548c68e06e6d994e2c1501ed297014e702cdefee2f656447706009614d801de1caaf73f8b7fa56cf1ba94b631933bbe577624380850f117435a0355b2b"), + ), + ); + } + + /** + * @param string $mode + * @param string $cipher + * @param string $key Encryption key + * @param string $iv Initialization vector + * @param string $txt_plain Plain text to be encrypted + * @param string $txt_encoded Known ecrypted text + * + * @dataProvider provider_encode + * @covers Encrypt::encode + */ + public function test_encode($mode, $cipher, $key, $iv, $txt_plain, $txt_encoded) + { + // initialize + $e = new Kohana_EncryptTest_TestableEncrypt($key, $mode, $cipher); + $e->_iv = $iv; + + // prepare data + $expected = base64_encode($iv . $txt_encoded); + $actual = $e->encode($txt_plain); + + // assert + $this->assertSame($expected, $actual); + } + + /** + * Provider for test_decode + * AES Multiblock Message Test (MMT) Sample Vectors - Known Answer Test (KAT) + * @link http://csrc.nist.gov/groups/STM/cavp/index.html NIST - Cryptographic Algorithm Validation Program + * @link http://csrc.nist.gov/groups/STM/cavp/documents/aes/aesmmt.zip file used CBCMMT128.rsp + * + * @return array of $mode, $cipher, $key, $iv, $txt_encoded, $txt_plain + */ + public function provider_decode() + { + return array( + array( + // mode + MCRYPT_MODE_CBC, + // cypher + MCRYPT_RIJNDAEL_128, + // key + pack("H*", "6a7082cf8cda13eff48c8158dda206ae"), + // IV + pack("H*", "bd4172934078c2011cb1f31cffaf486e"), + // txt_encoded + pack("H*", "f8eb31b31e374e960030cd1cadb0ef0c"), + // txt_plain + pack("H*", "940bc76d61e2c49dddd5df7f37fcf105"), + ), + array( + // mode + MCRYPT_MODE_CBC, + // cypher + MCRYPT_RIJNDAEL_128, + // key + pack("H*", "625eefa18a4756454e218d8bfed56e36"), + // IV + pack("H*", "73d9d0e27c2ec568fbc11f6a0998d7c8"), + // txt_encoded + pack("H*", "5d6fed86f0c4fe59a078d6361a142812514b295dc62ff5d608a42ea37614e6a1"), + // txt_plain + pack("H*", "360dc1896ce601dfb2a949250067aad96737847a4580ede2654a329b842fe81e"), + ), + array( + // mode + MCRYPT_MODE_CBC, + // cypher + MCRYPT_RIJNDAEL_128, + // key + pack("H*", "fd6e0b954ae2e3b723d6c9fcae6ab09b"), + // IV + pack("H*", "f08b65c9f4dd950039941da2e8058c4e"), + // txt_encoded + pack("H*", "e29e3114c8000eb484395b256b1b3267894f290d3999819ff35da03e6463c186c4d7ebb964941f1986a2d69572fcaba8"), + // txt_plain + pack("H*", "a206385945b21f812a9475f47fddbb7fbdda958a8d14c0dbcdaec36e8b28f1f6ececa1ceae4ce17721d162c1d42a66c1"), + ), + array( + // mode + MCRYPT_MODE_CBC, + // cypher + MCRYPT_RIJNDAEL_128, + // key + pack("H*", "7b1ab9144b0239315cd5eec6c75663bd"), + // IV + pack("H*", "0b1e74f45c17ff304d99c059ce5cde09"), + // txt_encoded + pack("H*", "d3f89b71e033070f9d7516a6cb4ea5ef51d6fb63d4f0fea089d0a60e47bbb3c2e10e9ba3b282c7cb79aefe3068ce228377c21a58fe5a0f8883d0dbd3d096beca"), + // txt_plain + pack("H*", "b968aeb199ad6b3c8e01f26c2edad444538c78bfa36ed68ca76123b8cdce615a01f6112bb80bfc3f17490578fb1f909a52e162637b062db04efee291a1f1af60"), + ), + array( + // mode + MCRYPT_MODE_CBC, + // cypher + MCRYPT_RIJNDAEL_128, + // key + pack("H*", "36466b6bd25ea3857ea42f0cac1919b1"), + // IV + pack("H*", "7186fb6bdfa98a16189544b228f3bcd3"), + // txt_encoded + pack("H*", "9ed957bd9bc52bba76f68cfbcde52157a8ca4f71ac050a3d92bdebbfd7c78316b4c9f0ba509fad0235fdafe90056ad115dfdbf08338b2acb1c807a88182dd2a882d1810d4302d598454e34ef2b23687d"), + // txt_plain + pack("H*", "999983467c47bb1d66d7327ab5c58f61ddb09b93bd2460cb78cbc12b5fa1ea0c5f759ccc5e478697687012ff4673f6e61eecaeda0ccad2d674d3098c7d17f887b62b56f56b03b4d055bf3a4460e83efa"), + ), + array( + // mode + MCRYPT_MODE_CBC, + // cypher + MCRYPT_RIJNDAEL_128, + // key + pack("H*", "89373ee6e28397640d5082eed4123239"), + // IV + pack("H*", "1a74d7c859672c804b82472f7e6d3c6b"), + // txt_encoded + pack("H*", "1bcba44ddff503db7c8c2ec4c4eea0e827957740cce125c1e11769842fa97e25f1b89269e6d77923a512a358312f4ba1cd33f2d111280cd83e1ef9e7cf7036d55048d5c273652afa611cc81b4e9dac7b5078b7c4716062e1032ead1e3329588a"), + // txt_plain + pack("H*", "45efd00daa4cdc8273ef785cae9e944a7664a2391e1e2c449f475acec0124bbc22944331678617408a1702917971f4654310ffb9229bec6173715ae512d37f93aaa6abf009f7e30d65669d1db0366b5bce4c7b00f871014f5753744a1878dc57"), + ), + array( + // mode + MCRYPT_MODE_CBC, + // cypher + MCRYPT_RIJNDAEL_128, + // key + pack("H*", "bab0cceddc0abd63e3f82e9fbff7b8aa"), + // IV + pack("H*", "68b9140f300490c5c942f66e777eb806"), + // txt_encoded + pack("H*", "c65b94b1f291fa9f0600f22c3c0432c895ad5d177bcccc9ea44e8ec339c9adf43855b326179d6d81aa36ef59462fd86127e9d81b0f286f93306bf74d4c79e47c1b3d4b74edd3a16290e3c63b742e41f20d66ceee794316bb63d3bd002712a1b136ba6185bd5c1dab81b07db90d2af5e5"), + // txt_plain + pack("H*", "c5585ff215bbb73ba5393440852fb199436de0d15e55c631f877670aa3eda9f672eb1f876f09544e63558436b8928000db2f02a5ad90f95b05ac4cf49e198e617e7678480fdf0efacc6aae691271e6cdd3541ebf719a1ccaedb24e2f80f92455dd5910cb5086b0960a3942ec182dcbd7"), + ), + array( + // mode + MCRYPT_MODE_CBC, + // cypher + MCRYPT_RIJNDAEL_128, + // key + pack("H*", "9c702898efa44557b29ed283f5bc0293"), + // IV + pack("H*", "cec6e1b82e8b2a591a9fa5ff1cf5cc51"), + // txt_encoded + pack("H*", "ba9f646755dacc22911f51d7de2f7e7cb0bc0b75257ea44fe883edb055c7c28ede04c3a0adcb10128ad4517d0093fa16bb0bcd2635e7a0ba92c7609bc8d8568002a7a983473724d256513aa7d51b477aabec1975ab5faf2872a6407e922180eff02f1ef86a4591c8bd3d143da6f0ef0e4806f94ace0d5b0151c99640fccbc843"), + // txt_plain + pack("H*", "1d1f8d81bdc3e2c7cb057f408e6450000c5aaed3260ff1e87fbb6f324df6887ffd8f78d7e2a04c9ed9deda9d64482d2b002f4a2b78d8b4f691875c8295d4a64b22257ceaf713ed2f4b92530d7ad7151d629acda882b4829577a43990b0948c1149c22fe4273656d1b08833930e8b06709a94579a78fc220f7057bbc1fa9f6563"), + ), + array( + // mode + MCRYPT_MODE_CBC, + // cypher + MCRYPT_RIJNDAEL_128, + // key + pack("H*", "5674636dbdb38f705f0b08c372ef4785"), + // IV + pack("H*", "3f20ce0509b57420d53b6be4d0b7f0a9"), + // txt_encoded + pack("H*", "198351f453103face6655666fe90bdbd9630e3733b2d66c013a634e91f2bf015bd2d975d71b26322e44defa32d4e9dce50363557046ece08ba38f258dae5fd3e5049c647476c81e73482e40c171d89f9fea29452caf995733589b0061464fbd5dabe27dc5ea463a3deeb7dcb43664ae6a65c498c143883ab8e83b51e5410b181647602443dc3cfffe86f0205398fa83c"), + // txt_plain + pack("H*", "6d40fd2f908f48ce19241b6b278b1b1676dffd4a97ce9f8a1574c33bc59237deb536bee376fd6c381e6987700e39283aa111cf1a59f26fae6fb6700bf012646a2ab80239bf5e1632329043aa87d7911978b36523a2bc0bed9a9737ccf7a00baa2f3822b4e9e742e168e7069290705fed2eb63aa044b78f97dd33a8d6b24741ec1fd8c8db79d93b884e762dba0f406961"), + ), + array( + // mode + MCRYPT_MODE_CBC, + // cypher + MCRYPT_RIJNDAEL_128, + // key + pack("H*", "97a1025529b9925e25bbe78770ca2f99"), + // IV + pack("H*", "d4b4eab92aa9637e87d366384ed6915c"), + // txt_encoded + pack("H*", "22cdc3306fcd4d31ccd32720cbb61bad28d855670657c48c7b88c31f4fa1f93c01b57da90be63ead67d6a325525e6ed45083e6fb70a53529d1fa0f55653b942af59d78a2660361d63a7290155ac5c43312a25b235dacbbc863faf00940c99624076dfa44068e7c554c9038176953e571751dfc0954d41d113771b06466b1c8d13e0d4cb675ed58d1a619e1540970983781dc11d2dd8525ab5745958d615defda"), + // txt_plain + pack("H*", "e8b89150d8438bf5b17449d6ed26bd72127e10e4aa57cad85283e8359e089208e84921649f5b60ea21f7867cbc9620560c4c6238db021216db453c9943f1f1a60546173daef2557c3cdd855031b353d4bf176f28439e48785c37d38f270aa4a6faad2baabcb0c0b2d1dd5322937498ce803ba1148440a52e227ddba4872fe4d81d2d76a939d24755adb8a7b8452ceed2d179e1a5848f316f5c016300a390bfa7"), + ), + ); + } + + /** + * @param string $mode + * @param string $cipher + * @param string $key Encryption key + * @param string $iv Initialization vector + * @param string $txt_encoded ecrypted text + * @param string $txt_plain Known plain text that is decripted + * + * @dataProvider provider_decode + * @covers Encrypt::decode + */ + public function test_decode($mode, $cipher, $key, $iv, $txt_encoded, $txt_plain) + { + // initialize + $e = new Encrypt($key, $mode, $cipher); + + // prepare data + $expected = $txt_plain; + $actual = $e->decode(base64_encode($iv . $txt_encoded)); + + // assert + $this->assertSame($expected, $actual); + } + +} + +/** + * Class Kohana_EncryptTest_TestableEncrypt wraps the Encrypt class to mock out + * the actual mcrypt_create_iv calls for unit testing. + */ +class Kohana_EncryptTest_TestableEncrypt extends Encrypt +{ + + /** + * Fake a random initialization vector by returning a known one + * + * @return string a known IV + */ + protected function _create_iv() + { + return isset($this->_iv) ? $this->_iv : FALSE; + } + +} From 26e83a43674f4790a46ef59d18c3fb8766fc38b6 Mon Sep 17 00:00:00 2001 From: Andrew Coulton Date: Fri, 10 Oct 2014 15:38:26 +0100 Subject: [PATCH 090/106] Add travis build badges [skip ci] --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 4fe265fe5..4689a2182 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,10 @@ # Kohana PHP Framework - core +| ver | Stable | Develop | +|-------|------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------| +| 3.3.x | [![Build Status - 3.3/master](https://travis-ci.org/kohana/core.svg?branch=3.3%2Fmaster)](https://travis-ci.org/kohana/core) | [![Build Status - 3.3/develop](https://travis-ci.org/kohana/core.svg?branch=3.3%2Fdevelop)](https://travis-ci.org/kohana/core) | +| 3.4.x | [![Build Status - 3.4/master](https://travis-ci.org/kohana/core.svg?branch=3.4%2Fmaster)](https://travis-ci.org/kohana/core) | [![Build Status - 3.4/develop](https://travis-ci.org/kohana/core.svg?branch=3.4%2Fdevelop)](https://travis-ci.org/kohana/core) | + This is the core package for the [Kohana](http://kohanaframework.org/) object oriented HMVC framework built using PHP5. It aims to be swift, secure, and small. From 61aefe3f69d05bfc19cc8483cb971d35975eb46e Mon Sep 17 00:00:00 2001 From: Samuel Demirdjian Date: Fri, 24 Oct 2014 15:57:49 +0300 Subject: [PATCH 091/106] More Encrypt tests test_encode_decode: Explicitly verifying that value > encode > decode === value test_decode_invalid_data: Tests for decode when the string is not valid base64, or is too short to contain a valid IV test_consecutive_encode_produce_different_results: A test without the iv stubbed to verify that consecutive calls to encode the same value produce different results test_instance_throw_exception_when_no_key_provided When an encryption key is not configured, the method `instance` should throw a Kohana_Exception test_instance_returns_singleton Tests for method instance - that it returns singleton, that an instance created through the instance method has the expected configuration --- tests/kohana/EncryptTest.php | 213 +++++++++++++++++++++++++++++++++++ 1 file changed, 213 insertions(+) diff --git a/tests/kohana/EncryptTest.php b/tests/kohana/EncryptTest.php index 919dddf64..f73f235e7 100644 --- a/tests/kohana/EncryptTest.php +++ b/tests/kohana/EncryptTest.php @@ -388,6 +388,219 @@ public function test_decode($mode, $cipher, $key, $iv, $txt_encoded, $txt_plain) $this->assertSame($expected, $actual); } + /** + * Provider for test_encode_decode, test_consecutive_encode_different_results + * + * @return array of $key, $mode, $cipher, $txt_plain + */ + public function provider_encode_decode() + { + return array( + array( + // key + "Some super secret key", + // mode + MCRYPT_MODE_NOFB, + // cypher + MCRYPT_RIJNDAEL_128, + // txt_plain + "The quick brown fox jumps over the lazy dog", + ), + array( + // key + "De finibus bonorum et malorum", + // mode + MCRYPT_MODE_NOFB, + // cypher + MCRYPT_RIJNDAEL_128, + // txt_plain + "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.", + ), + ); + } + + /** + * @param type $key Encryption Key + * @param type $mode Encryption Mode + * @param type $cipher Encryption Cipher + * @param type $txt_plain Plain text to encode and then decode back + * + * @dataProvider provider_encode_decode + * @covers Encrypt::encode + * @covers Encrypt::decode + */ + public function test_encode_decode($key, $mode, $cipher, $txt_plain) + { + // initialize, encode + $e = new Encrypt($key, $mode, $cipher); + $txt_encoded = $e->encode($txt_plain); + + // prepare data + $expected = $txt_plain; + $actual = $e->decode($txt_encoded); + + // assert + $this->assertSame($expected, $actual); + } + + /** + * Provider for test_decode_invalid_data + * + * @return array of $key, $mode, $cipher, $txt_invalid_encoded + */ + public function provider_decode_invalid_data() + { + return array( + array( + // key + "Some super secret key", + // mode + MCRYPT_MODE_NOFB, + // cypher + MCRYPT_RIJNDAEL_128, + // txt_invalid_encoded + ".:This data is not a valid base 64 string:.", + ), + array( + // key + "Some super secret key", + // mode + MCRYPT_MODE_NOFB, + // cypher + MCRYPT_RIJNDAEL_128, + // txt_invalid_encoded + base64_encode("too short"), + ), + ); + } + + /** + * Tests for decode when the string is not valid base64, + * or is too short to contain a valid IV + * + * @param type $key + * @param type $mode + * @param type $cipher + * @param type $txt_encoded + * + * @dataProvider provider_decode_invalid_data + */ + public function test_decode_invalid_data($key, $mode, $cipher, $txt_invalid_encoded) + { + // initialize + $e = new Encrypt($key, $mode, $cipher); + + // assert + $this->AssertFalse($e->decode($txt_invalid_encoded)); + } + + /** + * @param type $key Encryption Key + * @param type $mode Encryption Mode + * @param type $cipher Encryption Cipher + * @param type $txt_plain Plain text to encode and then decode back + * + * @dataProvider provider_encode_decode + * @covers Encrypt::encode + */ + public function test_consecutive_encode_produce_different_results($key, $mode, $cipher, $txt_plain) + { + // initialize, encode twice + $e = new Encrypt($key, $mode, $cipher); + $txt_encoded_first = $e->encode($txt_plain); + $txt_encoded_second = $e->encode($txt_plain); + + // assert + $this->assertNotEquals($txt_encoded_first, $txt_encoded_second); + } + + /** + * @expectedException Kohana_Exception + * @expectedExceptionMessage No encryption key is defined in the encryption configuration group + */ + public function test_instance_throw_exception_when_no_key_provided() + { + Encrypt::instance(); + } + + /** + * Provider for test_instance_returns_singleton + * + * @return array of $instance_name, $missing_config + */ + public function provider_instance_returns_singleton() + { + return array( + array( + 'default', + array( + 'key' => 'trwQwVXX96TIJoKxyBHB9AJkwAOHixuV1ENZmIWyanI0j1zNgSVvqywy044Agaj', + ) + ), + array( + 'blowfish', + array( + 'key' => '7bZJJkmNrelj5NaKoY6h6rMSRSmeUlJuTeOd5HHka5XknyMX4uGSfeVolTz4IYy', + 'cipher' => MCRYPT_BLOWFISH, + 'mode' => MCRYPT_MODE_ECB, + ) + ), + array( + 'tripledes', + array( + 'key' => 'a9hcSLRvA3LkFc7EJgxXIKQuz1ec91J7P6WNq1IaxMZp4CTj5m31gZLARLxI1jD', + 'cipher' => MCRYPT_3DES, + 'mode' => MCRYPT_MODE_CBC, + ) + ), + ); + } + + /** + * Test to multiple calls to the instance() method returns same instance + * also test if the instances are appropriately configured. + * + * @param string $instance_name instance name + * @param array $config_array array of config variables missing from config + * + * @dataProvider provider_instance_returns_singleton + */ + public function test_instance_returns_singleton($instance_name, array $config_array) + { + // load config + $config = Kohana::$config->load('encrypt'); + // if instance name is NULL the config group should be the default + $config_group = $instance_name ? : Encrypt::$default; + // if config group does not exists, create + if (!array_key_exists($config_group, $config)) + { + $config[$config_group] = array(); + } + // fill in the missing config variables + $config[$config_group] = $config[$config_group] + $config_array; + + // call instance twice + $e = Encrypt::instance($instance_name); + $e2 = Encrypt::instance($instance_name); + + // assert instances + $this->assertInstanceOf('Encrypt', $e); + $this->assertInstanceOf('Encrypt', $e2); + $this->assertSame($e, $e2); + + // test if instances are well configured + // prepare expected variables + $expected_cipher = $config[$config_group]['cipher']; + $expected_mode = $config[$config_group]['mode']; + $expected_key_size = mcrypt_get_key_size($expected_cipher, $expected_mode); + $expected_key = substr($config[$config_group]['key'], 0, $expected_key_size); + + // assert + $this->assertSame($expected_key, $e->_key); + $this->assertSame($config[$config_group]['cipher'], $e->_cipher); + $this->assertSame($config[$config_group]['mode'], $e->_mode); + } + } /** From 4d7bc8552dae8810b67929058ca1a74caafa9499 Mon Sep 17 00:00:00 2001 From: Samuel Demirdjian Date: Tue, 28 Oct 2014 12:42:19 +0200 Subject: [PATCH 092/106] minor readability changes --- tests/kohana/EncryptTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/kohana/EncryptTest.php b/tests/kohana/EncryptTest.php index f73f235e7..9cad3bbb2 100644 --- a/tests/kohana/EncryptTest.php +++ b/tests/kohana/EncryptTest.php @@ -597,8 +597,8 @@ public function test_instance_returns_singleton($instance_name, array $config_ar // assert $this->assertSame($expected_key, $e->_key); - $this->assertSame($config[$config_group]['cipher'], $e->_cipher); - $this->assertSame($config[$config_group]['mode'], $e->_mode); + $this->assertSame($expected_cipher, $e->_cipher); + $this->assertSame($expected_mode, $e->_mode); } } From 3d644610d62d7ad06682c7b5dabfb10a01fe6363 Mon Sep 17 00:00:00 2001 From: Samuel Demirdjian Date: Wed, 29 Oct 2014 04:09:32 +0200 Subject: [PATCH 093/106] Kohana_EncryptTest_TestableEncrypt to Kohana_EncryptTest_IvStubbed Renamed Kohana_EncryptTest_TestableEncrypt to Kohana_EncryptTest_IvStubbed so that I can add another testable class Also feed the known IV from the constructor now instead of setting the property separately --- tests/kohana/EncryptTest.php | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/tests/kohana/EncryptTest.php b/tests/kohana/EncryptTest.php index 9cad3bbb2..ed786d292 100644 --- a/tests/kohana/EncryptTest.php +++ b/tests/kohana/EncryptTest.php @@ -199,8 +199,7 @@ public function provider_encode() public function test_encode($mode, $cipher, $key, $iv, $txt_plain, $txt_encoded) { // initialize - $e = new Kohana_EncryptTest_TestableEncrypt($key, $mode, $cipher); - $e->_iv = $iv; + $e = new Kohana_EncryptTest_IvStubbed($key, $iv, $mode, $cipher); // prepare data $expected = base64_encode($iv . $txt_encoded); @@ -604,12 +603,27 @@ public function test_instance_returns_singleton($instance_name, array $config_ar } /** - * Class Kohana_EncryptTest_TestableEncrypt wraps the Encrypt class to mock out + * Class Kohana_EncryptTest_IvStubbed wraps the Encrypt class to mock out * the actual mcrypt_create_iv calls for unit testing. */ -class Kohana_EncryptTest_TestableEncrypt extends Encrypt +class Kohana_EncryptTest_IvStubbed extends Encrypt { + /** + * override constructor to force class use known IVs + * + * @param string $key encryption key + * @param string $iv feed a known IV + * @param string $mode mcrypt mode + * @param string $cipher mcrypt cipher + */ + public function __construct($key, $iv, $mode, $cipher) + { + parent::__construct($key, $mode, $cipher); + + $this->_iv = $iv; + } + /** * Fake a random initialization vector by returning a known one * From 4b853e0373518ab08c01e290b3f2745c94cd0b72 Mon Sep 17 00:00:00 2001 From: Samuel Demirdjian Date: Wed, 29 Oct 2014 04:33:06 +0200 Subject: [PATCH 094/106] Encryption key B/C compatible padding for PHP 5.6 Added a protected `_normalize_key` method to pad keys with null bytes similar to PHP versions older than PHP 5.6 this is called from the constructor when PHP version is above 5.6.0 Added Kohana_EncryptTest_KeyNormalized that always forces key normalization, used in test_key_normalization. Added test_key_normalization which tests our null byte padding for encryption keys only against lower versions of PHP (< 5.6.0) to see that it matches the internal key padding those PHP versions already have. --- classes/Kohana/Encrypt.php | 36 +++++++++++++- tests/kohana/EncryptTest.php | 95 ++++++++++++++++++++++++++++++++++++ 2 files changed, 130 insertions(+), 1 deletion(-) diff --git a/classes/Kohana/Encrypt.php b/classes/Kohana/Encrypt.php index b37ace7c1..7dd58afd7 100644 --- a/classes/Kohana/Encrypt.php +++ b/classes/Kohana/Encrypt.php @@ -105,6 +105,10 @@ public function __construct($key, $mode, $cipher) // Shorten the key to the maximum size $key = substr($key, 0, $size); } + else if (version_compare(PHP_VERSION, '5.6.0', '>=')) + { + $key = $this->_normalize_key($key, $cipher, $mode); + } // Store the key, mode, and cipher $this->_key = $key; @@ -215,8 +219,38 @@ public function decode($data) * * @return string the initialization vector or FALSE on error */ - protected function _create_iv() { + protected function _create_iv() + { // Create a random initialization vector of the proper size for the current cipher return mcrypt_create_iv($this->_iv_size, Encrypt::$_rand); } + + /** + * Normalize key for PHP 5.6 for backwards compatibility + * + * This method is a shim to make PHP 5.6 behave in a B/C way for + * legacy key padding when shorter-than-supported keys are used + * + * @param string $key encryption key + * @param string $cipher mcrypt cipher + * @param string $mode mcrypt mode + */ + protected function _normalize_key($key, $cipher, $mode) + { + // open the cipher + $td = mcrypt_module_open($cipher, '', $mode, ''); + + // loop through the supported key sizes + foreach (mcrypt_enc_get_supported_key_sizes($td) as $supported) { + // if key is short, needs padding + if (strlen($key) <= $supported) + { + return str_pad($key, $supported, "\0"); + } + } + + // at this point key must be greater than max supported size, shorten it + return substr($key, 0, mcrypt_get_key_size($cipher, $mode)); + } + } diff --git a/tests/kohana/EncryptTest.php b/tests/kohana/EncryptTest.php index ed786d292..5ea9d9127 100644 --- a/tests/kohana/EncryptTest.php +++ b/tests/kohana/EncryptTest.php @@ -513,6 +513,75 @@ public function test_consecutive_encode_produce_different_results($key, $mode, $ $this->assertNotEquals($txt_encoded_first, $txt_encoded_second); } + /** + * Provider for test_key_normalization + * + * @return array of $key, $iv, $mode, $cipher, $txt_plain + */ + public function provider_key_normalization() + { + return array( + array( + // key + "Some super secret key", + // IV + pack("H*", "2fe2b333ceda8f98f4a99b40d2cd34a8"), + // mode + MCRYPT_MODE_NOFB, + // cypher + MCRYPT_RIJNDAEL_128, + // txt_plain + "The quick brown fox jumps over the lazy dog", + ), + array( + // key + "De finibus bonorum et malorum", + // IV + pack("H*", "2fe2b333ceda8f98f4a99b40d2cd34a8"), + // mode + MCRYPT_MODE_NOFB, + // cypher + MCRYPT_RIJNDAEL_128, + // txt_plain + "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.", + ), + ); + } + + /** + * Test if key normalization logic behaves well + * Encrypt::_normalize_key was ment for PHP > 5.6.0 + * + * We are testing our key normalization only against lower versions of PHP + * (PHP < 5.6.0) to see if it matches the internal key padding those + * PHP versions already have + * + * @param type $key Encryption Key + * @param type $mode Encryption Mode + * @param type $cipher Encryption Cipher + * @param type $txt_plain Plain text to encode and then decode back + * + * @dataProvider provider_key_normalization + * @covers Encrypt::_normalize_key + */ + public function test_key_normalization($key, $iv, $mode, $cipher, $txt_plain) + { + if (version_compare(PHP_VERSION, '5.6.0', '>=')) + { + $this->markTestSkipped('Starting from PHP 5.6.0, mcrypt does not pad encryption keys with null bytes.'); + } + + // initialize, encode twice + $e1 = new Kohana_EncryptTest_IvStubbed($key, $iv, $mode, $cipher); + $e2 = new Kohana_EncryptTest_KeyNormalized($key, $iv, $mode, $cipher); + + $txt_encoded_1 = $e1->encode($txt_plain); + $txt_encoded_2 = $e2->encode($txt_plain); + + // assert + $this->assertSame($txt_encoded_1, $txt_encoded_2); + } + /** * @expectedException Kohana_Exception * @expectedExceptionMessage No encryption key is defined in the encryption configuration group @@ -635,3 +704,29 @@ protected function _create_iv() } } + +/** + * Class Kohana_EncryptTest_KeyNormalized wraps the Encrypt class to mock out + * the actual mcrypt_create_iv calls for unit testing, as well as to always + * normalize keys + */ +class Kohana_EncryptTest_KeyNormalized extends Kohana_EncryptTest_IvStubbed +{ + + /** + * override constructor to force key normalization + * + * @param string $key encryption key + * @param string $mode mcrypt mode + * @param string $cipher mcrypt cipher + */ + public function __construct($key, $iv, $mode, $cipher) + { + parent::__construct($key, $iv, $mode, $cipher); + + $this->_key = $this->_normalize_key($this->_key, $this->_cipher, $this->_mode); + } + + +} + From f3eea76b62e9b5a359c83f3106c621910c36e707 Mon Sep 17 00:00:00 2001 From: Samuel Demirdjian Date: Wed, 29 Oct 2014 11:03:10 +0200 Subject: [PATCH 095/106] tabs instead of whitespaces as per coding standards --- tests/kohana/EncryptTest.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/kohana/EncryptTest.php b/tests/kohana/EncryptTest.php index 5ea9d9127..3280c3cf1 100644 --- a/tests/kohana/EncryptTest.php +++ b/tests/kohana/EncryptTest.php @@ -566,10 +566,10 @@ public function provider_key_normalization() */ public function test_key_normalization($key, $iv, $mode, $cipher, $txt_plain) { - if (version_compare(PHP_VERSION, '5.6.0', '>=')) + if (version_compare(PHP_VERSION, '5.6.0', '>=')) { - $this->markTestSkipped('Starting from PHP 5.6.0, mcrypt does not pad encryption keys with null bytes.'); - } + $this->markTestSkipped('Starting from PHP 5.6.0, mcrypt does not pad encryption keys with null bytes.'); + } // initialize, encode twice $e1 = new Kohana_EncryptTest_IvStubbed($key, $iv, $mode, $cipher); From 9b967f5d26e1db619e8f679c47479ada87f42195 Mon Sep 17 00:00:00 2001 From: Samuel Demirdjian Date: Wed, 29 Oct 2014 12:11:36 +0200 Subject: [PATCH 096/106] Declare _key _mode _cipher _iv_size properties protected Declared `_key`, `_mode`, `_cipher`, and `_iv_size` properties that were originally created dynamically. Added `assertSameProtectedProperty` as a helper method for the tests, it uses reflection to access and test the values of those properties --- classes/Kohana/Encrypt.php | 20 ++++++++++++++++++++ tests/kohana/EncryptTest.php | 21 ++++++++++++++++++--- 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/classes/Kohana/Encrypt.php b/classes/Kohana/Encrypt.php index 7dd58afd7..fd52945d2 100644 --- a/classes/Kohana/Encrypt.php +++ b/classes/Kohana/Encrypt.php @@ -40,6 +40,26 @@ class Kohana_Encrypt { */ protected static $_rand; + /** + * @var string Encryption key + */ + protected $_key; + + /** + * @var string mcrypt mode + */ + protected $_mode; + + /** + * @var string mcrypt cipher + */ + protected $_cipher; + + /** + * @var int the size of the Initialization Vector (IV) in bytes + */ + protected $_iv_size; + /** * Returns a singleton instance of Encrypt. An encryption key must be * provided in your "encrypt" configuration file. diff --git a/tests/kohana/EncryptTest.php b/tests/kohana/EncryptTest.php index 3280c3cf1..9e93acabf 100644 --- a/tests/kohana/EncryptTest.php +++ b/tests/kohana/EncryptTest.php @@ -664,9 +664,24 @@ public function test_instance_returns_singleton($instance_name, array $config_ar $expected_key = substr($config[$config_group]['key'], 0, $expected_key_size); // assert - $this->assertSame($expected_key, $e->_key); - $this->assertSame($expected_cipher, $e->_cipher); - $this->assertSame($expected_mode, $e->_mode); + $this->assertSameProtectedProperty($expected_key, $e, '_key'); + $this->assertSameProtectedProperty($expected_cipher, $e, '_cipher'); + $this->assertSameProtectedProperty($expected_mode, $e, '_mode'); + } + + /** + * Helper method to test for private/protected properties + * + * @param mixed $expect Expected value + * @param mixed $object object that holds the private/protected property + * @param string $name the name of the private/protected property + */ + protected function assertSameProtectedProperty($expect, $object, $name) + { + $refl = new ReflectionClass($object); + $property = $refl->getProperty($name); + $property->setAccessible(TRUE); + $this->assertSame($expect, $property->getValue($object)); } } From 6798a94950296ffd5eca1bdadb621b38abd0881a Mon Sep 17 00:00:00 2001 From: Samuel Demirdjian Date: Thu, 6 Nov 2014 11:28:19 +0200 Subject: [PATCH 097/106] Don't allow insecure, predictable random numbers for IVs Silently use MCRYPT_DEV_URANDOM when the chosen random number generator is not one of those that are considered secure. --- classes/Kohana/Encrypt.php | 46 +++++++++----------------------------- 1 file changed, 11 insertions(+), 35 deletions(-) diff --git a/classes/Kohana/Encrypt.php b/classes/Kohana/Encrypt.php index fd52945d2..13fcdb9fa 100644 --- a/classes/Kohana/Encrypt.php +++ b/classes/Kohana/Encrypt.php @@ -153,41 +153,6 @@ public function __construct($key, $mode, $cipher) */ public function encode($data) { - // Set the rand type if it has not already been set - if (Encrypt::$_rand === NULL) - { - if (Kohana::$is_windows) - { - // Windows only supports the system random number generator - Encrypt::$_rand = MCRYPT_RAND; - } - else - { - if (defined('MCRYPT_DEV_URANDOM')) - { - // Use /dev/urandom - Encrypt::$_rand = MCRYPT_DEV_URANDOM; - } - elseif (defined('MCRYPT_DEV_RANDOM')) - { - // Use /dev/random - Encrypt::$_rand = MCRYPT_DEV_RANDOM; - } - else - { - // Use the system random number generator - Encrypt::$_rand = MCRYPT_RAND; - } - } - } - - if (Encrypt::$_rand === MCRYPT_RAND) - { - // The system random number generator must always be seeded each - // time it is used, or it will not produce true random results - mt_srand(); - } - // Get an initialization vector $iv = $this->_create_iv(); @@ -241,6 +206,17 @@ public function decode($data) */ protected function _create_iv() { + /* + * Silently use MCRYPT_DEV_URANDOM when the chosen random number generator + * is not one of those that are considered secure. + * + * Also sets Encrypt::$_rand to MCRYPT_DEV_URANDOM when it's not already set + */ + if ((Encrypt::$_rand !== MCRYPT_DEV_URANDOM) AND ( Encrypt::$_rand !== MCRYPT_DEV_RANDOM)) + { + Encrypt::$_rand = MCRYPT_DEV_URANDOM; + } + // Create a random initialization vector of the proper size for the current cipher return mcrypt_create_iv($this->_iv_size, Encrypt::$_rand); } From 70d4aab4190f7a8cb27b0760e5933e27e957b34b Mon Sep 17 00:00:00 2001 From: Samuel Demirdjian Date: Thu, 6 Nov 2014 12:29:39 +0200 Subject: [PATCH 098/106] default $_rand to MCRYPT_DEV_URANDOM and document that we're silently revert to MCRYPT_DEV_URANDOM when using MCRYPT_RAND --- classes/Kohana/Encrypt.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/classes/Kohana/Encrypt.php b/classes/Kohana/Encrypt.php index 13fcdb9fa..5c59f8306 100644 --- a/classes/Kohana/Encrypt.php +++ b/classes/Kohana/Encrypt.php @@ -36,9 +36,12 @@ class Kohana_Encrypt { public static $instances = array(); /** - * @var string OS-dependent RAND type to use + * @var string RAND type to use + * + * Only MCRYPT_DEV_URANDOM and MCRYPT_DEV_RANDOM are considered safe. + * Using MCRYPT_RAND will silently revert to MCRYPT_DEV_URANDOM */ - protected static $_rand; + protected static $_rand = MCRYPT_DEV_URANDOM; /** * @var string Encryption key From 337d812619b5c8e82abf59dd74e31ed3bbcdffcd Mon Sep 17 00:00:00 2001 From: Samuel Demirdjian Date: Tue, 18 Nov 2014 01:24:11 +0200 Subject: [PATCH 099/106] Move Cookie salt configuration guide up --- guide/kohana/install.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/guide/kohana/install.md b/guide/kohana/install.md index f8e0e420b..287d8c616 100644 --- a/guide/kohana/install.md +++ b/guide/kohana/install.md @@ -34,15 +34,15 @@ Kohana::init(array( )); ~~~ - - Make sure the `application/cache` and `application/logs` directories are writable by the web server. + - Define a salt for the `Cookie` class. ~~~ -sudo chmod -R a+rwx application/cache -sudo chmod -R a+rwx application/logs +Cookie::$salt = 'some-really-long-cookie-salt-here'; ~~~ - - Define a salt for the `Cookie` class. + - Make sure the `application/cache` and `application/logs` directories are writable by the web server. ~~~ -Cookie::$salt = [really-long-cookie-salt-here] +sudo chmod -R a+rwx application/cache +sudo chmod -R a+rwx application/logs ~~~ [!!] Make sure to use a unique salt for your application and never to share it. Take a look at the [Cookies](cookies) page for more information on how cookies work in Kohana. If you do not define a `Cookie::$salt` value, Kohana will throw an exception when it encounters any cookie on your domain. From 262a5a03b0fc1d06dacc1c0fa77ed693cb7fab95 Mon Sep 17 00:00:00 2001 From: Samuel Demirdjian Date: Tue, 18 Nov 2014 09:35:42 +0200 Subject: [PATCH 100/106] Prevent hash length extension attack refs Redmine #4701 refs Github #348 see http://blog.whitehatsec.com/hash-length-extension-attacks/ Thanks @birkir --- classes/Kohana/Cookie.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/classes/Kohana/Cookie.php b/classes/Kohana/Cookie.php index 8ee3d239e..e68f84fc0 100644 --- a/classes/Kohana/Cookie.php +++ b/classes/Kohana/Cookie.php @@ -161,7 +161,7 @@ public static function salt($name, $value) // Determine the user agent $agent = isset($_SERVER['HTTP_USER_AGENT']) ? strtolower($_SERVER['HTTP_USER_AGENT']) : 'unknown'; - return sha1($agent.$name.$value.Cookie::$salt); + return hash_hmac('sha1', $agent.$name.$value.Cookie::$salt, Cookie::$salt); } /** From 442aa204d003545c2a945f2162689664a0e66bd3 Mon Sep 17 00:00:00 2001 From: Samuel Demirdjian Date: Sat, 22 Nov 2014 06:29:08 +0200 Subject: [PATCH 101/106] Added tests for Text::ucfirst The test with UTF-8 characters is expectedly failing --- tests/kohana/TextTest.php | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/tests/kohana/TextTest.php b/tests/kohana/TextTest.php index 01bff2ba3..0c93270d5 100644 --- a/tests/kohana/TextTest.php +++ b/tests/kohana/TextTest.php @@ -196,6 +196,30 @@ function test_alternate_resets_when_called_with_no_params_and_returns_empty_stri $this->assertSame('yes', Text::alternate($val_a, $val_b, $val_c)); } + /** + * Provides test data for test_ucfirst + * + * @return array Test data + */ + public function provider_ucfirst() + { + return array( + array('Content-Type', 'content-type', '-'), + array('Բարեւ|Ձեզ', 'բարեւ|ձեզ', '|'), + ); + } + + /** + * Covers Text::ucfirst() + * + * @test + * @dataProvider provider_ucfirst + */ + public function test_ucfirst($expected, $string, $delimiter) + { + $this->assertSame($expected, Text::ucfirst($string, $delimiter)); + } + /** * Provides test data for test_reducde_slashes() * From 98c79f893de479ac7bd8cb050f105be8efaa075c Mon Sep 17 00:00:00 2001 From: Come2Daddy Date: Mon, 24 Nov 2014 02:04:14 +0100 Subject: [PATCH 102/106] Add UTF8 support to Text::ucfirst() Uses UTF8::ucfirst() --- classes/Kohana/Text.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/classes/Kohana/Text.php b/classes/Kohana/Text.php index df061eff8..828c5950d 100644 --- a/classes/Kohana/Text.php +++ b/classes/Kohana/Text.php @@ -240,12 +240,13 @@ public static function random($type = NULL, $length = 8) * * @param string $string string to transform * @param string $delimiter delimiter to use + * @uses UTF8::ucfirst * @return string */ public static function ucfirst($string, $delimiter = '-') { // Put the keys back the Case-Convention expected - return implode($delimiter, array_map('ucfirst', explode($delimiter, $string))); + return implode($delimiter, array_map('UTF8::ucfirst', explode($delimiter, $string))); } /** From e5ce1a337706493fea09ea499b0a982e5a2f9746 Mon Sep 17 00:00:00 2001 From: Samuel Demirdjian Date: Wed, 26 Nov 2014 11:21:45 +0200 Subject: [PATCH 103/106] Remove redundant version_compare() get_magic_quotes_gpc() exists in all versions of PHP and HHVM and returns the correct value. See http://php.net/manual/en/function.get-magic-quotes-gpc.php and https://github.com/kohana/core/commit/f770108ba0e4b31c52997ff878353850e8ffaa42#commitcomment-2054200 --- classes/Kohana/Core.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/classes/Kohana/Core.php b/classes/Kohana/Core.php index 081d4bb05..525e49975 100644 --- a/classes/Kohana/Core.php +++ b/classes/Kohana/Core.php @@ -322,7 +322,7 @@ public static function init(array $settings = NULL) } // Determine if the extremely evil magic quotes are enabled - Kohana::$magic_quotes = (version_compare(PHP_VERSION, '5.4') < 0 AND get_magic_quotes_gpc()); + Kohana::$magic_quotes = get_magic_quotes_gpc(); // Sanitize all request variables $_GET = Kohana::sanitize($_GET); From 7fbfcf4412ca2158a66579f4939a9f0b7201651f Mon Sep 17 00:00:00 2001 From: Serghei Iakovlev Date: Mon, 8 Dec 2014 09:21:54 +0200 Subject: [PATCH 104/106] Update View::set Fixed previous commit Improved tests Fixed previous commit Update Test Added Note Fixed indentation Update View::set_global --- classes/Kohana/View.php | 31 +++++++++++++++++++++---------- tests/kohana/ViewTest.php | 30 +++++++++++++++++++++++++++++- 2 files changed, 50 insertions(+), 11 deletions(-) diff --git a/classes/Kohana/View.php b/classes/Kohana/View.php index 662a9327a..3ca5ba0ca 100644 --- a/classes/Kohana/View.php +++ b/classes/Kohana/View.php @@ -40,6 +40,7 @@ public static function factory($file = NULL, array $data = NULL) * @param string $kohana_view_filename filename * @param array $kohana_view_data variables * @return string + * @throws Exception */ protected static function capture($kohana_view_filename, array $kohana_view_data) { @@ -79,17 +80,25 @@ protected static function capture($kohana_view_filename, array $kohana_view_data * * View::set_global($name, $value); * - * @param string $key variable name or an array of variables - * @param mixed $value value + * You can also use an array or Traversable object to set several values at once: + * + * // Create the values $food and $beverage in the view + * View::set_global(array('food' => 'bread', 'beverage' => 'water')); + * + * [!!] Note: When setting with using Traversable object we're not attaching the whole object to the view, + * i.e. the object's standard properties will not be available in the view context. + * + * @param string|array|Traversable $key variable name or an array of variables + * @param mixed $value value * @return void */ public static function set_global($key, $value = NULL) { - if (is_array($key)) + if (is_array($key) OR $key instanceof Traversable) { - foreach ($key as $key2 => $value) + foreach ($key as $name => $value) { - View::$_global_data[$key2] = $value; + View::$_global_data[$name] = $value; } } else @@ -127,7 +136,6 @@ public static function bind_global($key, & $value) * * @param string $file view filename * @param array $data array of values - * @return void * @uses View::set_filename */ public function __construct($file = NULL, array $data = NULL) @@ -272,18 +280,21 @@ public function set_filename($file) * // This value can be accessed as $foo within the view * $view->set('foo', 'my value'); * - * You can also use an array to set several values at once: + * You can also use an array or Traversable object to set several values at once: * * // Create the values $food and $beverage in the view * $view->set(array('food' => 'bread', 'beverage' => 'water')); * - * @param string $key variable name or an array of variables - * @param mixed $value value + * [!!] Note: When setting with using Traversable object we're not attaching the whole object to the view, + * i.e. the object's standard properties will not be available in the view context. + * + * @param string|array|Traversable $key variable name or an array of variables + * @param mixed $value value * @return $this */ public function set($key, $value = NULL) { - if (is_array($key)) + if (is_array($key) OR $key instanceof Traversable) { foreach ($key as $name => $value) { diff --git a/tests/kohana/ViewTest.php b/tests/kohana/ViewTest.php index 543f411e5..20dfe0ce1 100644 --- a/tests/kohana/ViewTest.php +++ b/tests/kohana/ViewTest.php @@ -60,9 +60,23 @@ public function provider_instantiate() ); } + /** + * Provider to test_set + * + * @return array + */ + public function provider_set() + { + return array( + array('foo', 'bar', 'foo', 'bar'), + array(array('foo' => 'bar'), NULL, 'foo', 'bar'), + array(new ArrayIterator(array('foo' => 'bar')), NULL, 'foo', 'bar'), + ); + } + /** * Tests that we can instantiate a view file - * + * * @test * @dataProvider provider_instantiate * @@ -80,4 +94,18 @@ public function test_instantiate($path, $expects_exception) $this->assertSame(TRUE, $expects_exception); } } + + /** + * Tests that we can set using string, array or Traversable object + * + * @test + * @dataProvider provider_set + * + * @return null + */ + public function test_set($data_key, $value, $test_key, $expected) + { + $view = View::factory()->set($data_key, $value); + $this->assertSame($expected, $view->$test_key); + } } From eff4bdfa61b942b6450c9cf9aa0affa66f1ff46f Mon Sep 17 00:00:00 2001 From: Serghei Iakovlev Date: Tue, 9 Dec 2014 12:01:40 +0200 Subject: [PATCH 105/106] Added test for View::set_global --- tests/kohana/ViewTest.php | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/tests/kohana/ViewTest.php b/tests/kohana/ViewTest.php index 20dfe0ce1..a20b4f623 100644 --- a/tests/kohana/ViewTest.php +++ b/tests/kohana/ViewTest.php @@ -99,7 +99,7 @@ public function test_instantiate($path, $expects_exception) * Tests that we can set using string, array or Traversable object * * @test - * @dataProvider provider_set + * @dataProvider provider_set * * @return null */ @@ -108,4 +108,19 @@ public function test_set($data_key, $value, $test_key, $expected) $view = View::factory()->set($data_key, $value); $this->assertSame($expected, $view->$test_key); } + + /** + * Tests that we can set global using string, array or Traversable object + * + * @test + * @dataProvider provider_set + * + * @return null + */ + public function test_set_global($data_key, $value, $test_key, $expected) + { + $view = View::factory(); + $view::set_global($data_key, $value); + $this->assertSame($expected, $view->$test_key); + } } From 9c380c13231aaa2ce0b89e7cfe983cde474bca5c Mon Sep 17 00:00:00 2001 From: Samuel Demirdjian Date: Thu, 11 Dec 2014 04:16:09 +0200 Subject: [PATCH 106/106] Bump minor version number, codename changed to uluru Codenamed uluru, the biggest rock on earth: Working hard to solve as many bugs as possible, we believe this release is a solid release. --- classes/Kohana/Core.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/classes/Kohana/Core.php b/classes/Kohana/Core.php index d77c74d96..30ef91755 100644 --- a/classes/Kohana/Core.php +++ b/classes/Kohana/Core.php @@ -16,8 +16,8 @@ class Kohana_Core { // Release version and codename - const VERSION = '3.3.2'; - const CODENAME = 'dryocopus'; + const VERSION = '3.3.3'; + const CODENAME = 'uluru'; // Common environment type constants for consistency and convenience const PRODUCTION = 10;