From 83abced287feba5e94551a82657b9143fe82cecd Mon Sep 17 00:00:00 2001 From: mark_story Date: Sat, 29 Dec 2012 11:57:52 -0500 Subject: [PATCH 1/9] Add tests for save() and multiple locales. Refs #3498 --- .../Model/Behavior/TranslateBehaviorTest.php | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/lib/Cake/Test/Case/Model/Behavior/TranslateBehaviorTest.php b/lib/Cake/Test/Case/Model/Behavior/TranslateBehaviorTest.php index 50d897d60..0a4bf9e15 100644 --- a/lib/Cake/Test/Case/Model/Behavior/TranslateBehaviorTest.php +++ b/lib/Cake/Test/Case/Model/Behavior/TranslateBehaviorTest.php @@ -530,6 +530,44 @@ class TranslateBehaviorTest extends CakeTestCase { $this->assertEquals($expected, $result); } +/** + * test save multiple locales method + * + * @return void + */ + public function testSaveMultipleLocales() { + $this->loadFixtures('Translate', 'TranslatedItem'); + + $TestModel = new TranslatedItem(); + $data = array( + 'slug' => 'fourth_translated', + 'title' => array( + 'eng' => 'Title #4', + 'spa' => 'Leyenda #4', + ), + 'content' => array( + 'eng' => 'Content #4', + 'spa' => 'Contenido #4', + ), + 'translated_article_id' => 1, + ); + $TestModel->create(); + $TestModel->save($data); + + $translations = array('title' => 'Title', 'content' => 'Content'); + $TestModel->bindTranslation($translations, false); + $TestModel->locale = array('eng', 'spa'); + $result = $TestModel->read(); + + $this->assertCount(2, $result['Title']); + $this->assertEquals($result['Title'][0]['locale'], 'eng'); + $this->assertEquals($result['Title'][0]['content'], 'Title #4'); + $this->assertEquals($result['Title'][1]['locale'], 'spa'); + $this->assertEquals($result['Title'][1]['content'], 'Leyenda #4'); + + $this->assertCount(2, $result['Content']); + } + /** * testSaveAssociatedCreate method * From c72c612625588a17a0d4371688f4845699a683cd Mon Sep 17 00:00:00 2001 From: and-ers Date: Wed, 2 Jan 2013 23:47:27 +0100 Subject: [PATCH 2/9] Replaced simple and therefore pointless array_push function references with the ordinary way of adding an array element. This will increase the performance and the code looks much cleaner this way IMHO --- lib/Cake/Console/ConsoleOptionParser.php | 4 ++-- lib/Cake/Utility/String.php | 2 +- lib/Cake/View/ScaffoldView.php | 2 +- lib/Cake/View/View.php | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/Cake/Console/ConsoleOptionParser.php b/lib/Cake/Console/ConsoleOptionParser.php index 97726f0be..8d3cdeeed 100644 --- a/lib/Cake/Console/ConsoleOptionParser.php +++ b/lib/Cake/Console/ConsoleOptionParser.php @@ -627,7 +627,7 @@ class ConsoleOptionParser { */ protected function _parseArg($argument, $args) { if (empty($this->_args)) { - array_push($args, $argument); + $args[] = $argument; return $args; } $next = count($args); @@ -636,7 +636,7 @@ class ConsoleOptionParser { } if ($this->_args[$next]->validChoice($argument)) { - array_push($args, $argument); + $args[] = $argument; return $args; } } diff --git a/lib/Cake/Utility/String.php b/lib/Cake/Utility/String.php index 01f9acbbb..65969b69c 100644 --- a/lib/Cake/Utility/String.php +++ b/lib/Cake/Utility/String.php @@ -523,7 +523,7 @@ class String { } } else { foreach ($droppedTags as $closingTag) { - array_push($openTags, $closingTag[1]); + $openTags[] = $closingTag[1]; } } } diff --git a/lib/Cake/View/ScaffoldView.php b/lib/Cake/View/ScaffoldView.php index 5cb9fb677..04674a338 100644 --- a/lib/Cake/View/ScaffoldView.php +++ b/lib/Cake/View/ScaffoldView.php @@ -69,7 +69,7 @@ class ScaffoldView extends ThemeView { $paths = $this->_paths($this->plugin); $exts = array($this->ext); if ($this->ext !== '.ctp') { - array_push($exts, '.ctp'); + $exts[] = '.ctp'; } foreach ($exts as $ext) { foreach ($paths as $path) { diff --git a/lib/Cake/View/View.php b/lib/Cake/View/View.php index 408212584..475f02bb3 100644 --- a/lib/Cake/View/View.php +++ b/lib/Cake/View/View.php @@ -1058,7 +1058,7 @@ class View extends Object { protected function _getExtensions() { $exts = array($this->ext); if ($this->ext !== '.ctp') { - array_push($exts, '.ctp'); + $exts[] = '.ctp'; } return $exts; } From 75f654b87bc91286baae58eb89f3f5d04cf20e13 Mon Sep 17 00:00:00 2001 From: mark_story Date: Wed, 2 Jan 2013 23:09:37 -0500 Subject: [PATCH 3/9] Fix double encoding in JsHelper request methods. The urls were being HTML & URL encoded, this causes issues with URL's containing query string parameters. Remove HTML entities as they aren't required in the Javascript context. Fixes #3495 --- .../Test/Case/View/Helper/JqueryEngineHelperTest.php | 12 ++++++++++++ lib/Cake/View/Helper/JqueryEngineHelper.php | 2 +- lib/Cake/View/Helper/MootoolsEngineHelper.php | 2 +- lib/Cake/View/Helper/PrototypeEngineHelper.php | 3 ++- 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/lib/Cake/Test/Case/View/Helper/JqueryEngineHelperTest.php b/lib/Cake/Test/Case/View/Helper/JqueryEngineHelperTest.php index 4823f1ec5..4114b65e3 100644 --- a/lib/Cake/Test/Case/View/Helper/JqueryEngineHelperTest.php +++ b/lib/Cake/Test/Case/View/Helper/JqueryEngineHelperTest.php @@ -215,6 +215,18 @@ class JqueryEngineHelperTest extends CakeTestCase { $this->assertEquals($expected, $result); } +/** + * Test that querystring arguments are not double escaped. + * + * @return void + */ + public function testRequestWithQueryStringArguments() { + $url = '/users/search/sort:User.name/direction:desc?nome=&cpm=&audience=public'; + $result = $this->Jquery->request($url); + $expected = '$.ajax({url:"\\/users\\/search\\/sort:User.name\\/direction:desc?nome=&cpm=&audience=public"});'; + $this->assertEquals($expected, $result); + } + /** * test that alternate jQuery object values work for request() * diff --git a/lib/Cake/View/Helper/JqueryEngineHelper.php b/lib/Cake/View/Helper/JqueryEngineHelper.php index d79a8ef88..bfa029c24 100644 --- a/lib/Cake/View/Helper/JqueryEngineHelper.php +++ b/lib/Cake/View/Helper/JqueryEngineHelper.php @@ -250,7 +250,7 @@ class JqueryEngineHelper extends JsBaseEngineHelper { * @see JsBaseEngineHelper::request() for options list. */ public function request($url, $options = array()) { - $url = $this->url($url); + $url = html_entity_decode($this->url($url), ENT_COMPAT, Configure::read('App.encoding')); $options = $this->_mapOptions('request', $options); if (isset($options['data']) && is_array($options['data'])) { $options['data'] = $this->_toQuerystring($options['data']); diff --git a/lib/Cake/View/Helper/MootoolsEngineHelper.php b/lib/Cake/View/Helper/MootoolsEngineHelper.php index cc0340b6e..69e886b13 100644 --- a/lib/Cake/View/Helper/MootoolsEngineHelper.php +++ b/lib/Cake/View/Helper/MootoolsEngineHelper.php @@ -234,7 +234,7 @@ class MootoolsEngineHelper extends JsBaseEngineHelper { * @return string The completed ajax call. */ public function request($url, $options = array()) { - $url = $this->url($url); + $url = html_entity_decode($this->url($url), ENT_COMPAT, Configure::read('App.encoding')); $options = $this->_mapOptions('request', $options); $type = $data = null; if (isset($options['type']) || isset($options['update'])) { diff --git a/lib/Cake/View/Helper/PrototypeEngineHelper.php b/lib/Cake/View/Helper/PrototypeEngineHelper.php index 6c483aa13..4dd2c4e03 100644 --- a/lib/Cake/View/Helper/PrototypeEngineHelper.php +++ b/lib/Cake/View/Helper/PrototypeEngineHelper.php @@ -234,7 +234,8 @@ class PrototypeEngineHelper extends JsBaseEngineHelper { * @return string The completed ajax call. */ public function request($url, $options = array()) { - $url = '"' . $this->url($url) . '"'; + $url = html_entity_decode($this->url($url), ENT_COMPAT, Configure::read('App.encoding')); + $url = '"' . $url . '"'; $options = $this->_mapOptions('request', $options); $type = '.Request'; if (isset($options['type']) && strtolower($options['type']) == 'json') { From 6d743971e976cee8eaf8eb577240d1eb17be1e67 Mon Sep 17 00:00:00 2001 From: mark_story Date: Thu, 3 Jan 2013 21:06:46 -0500 Subject: [PATCH 4/9] Consistently inflect theme names. Themes should be consistently converted into CamelCase, this makes the camelization consitent with the treatment in App::themePath(). Fixes #3508 --- lib/Cake/Test/Case/View/ViewTest.php | 13 +++---------- lib/Cake/View/View.php | 5 +++-- 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/lib/Cake/Test/Case/View/ViewTest.php b/lib/Cake/Test/Case/View/ViewTest.php index ffcbb3ef9..f7048ffe4 100644 --- a/lib/Cake/Test/Case/View/ViewTest.php +++ b/lib/Cake/Test/Case/View/ViewTest.php @@ -300,7 +300,7 @@ class ViewTest extends CakeTestCase { $this->Controller->params['pass'] = array('home'); $ThemeView = new TestThemeView($this->Controller); - $ThemeView->theme = 'TestTheme'; + $ThemeView->theme = 'test_theme'; $expected = CAKE . 'Test' . DS . 'test_app' . DS . 'View' . DS . 'Pages' . DS . 'home.ctp'; $result = $ThemeView->getViewFileName('home'); $this->assertEquals($expected, $result); @@ -309,6 +309,7 @@ class ViewTest extends CakeTestCase { $result = $ThemeView->getViewFileName('/Posts/index'); $this->assertEquals($expected, $result); + $ThemeView->theme = 'TestTheme'; $expected = CAKE . 'Test' . DS . 'test_app' . DS . 'View' . DS . 'Themed' . DS . 'TestTheme' . DS . 'Layouts' . DS . 'default.ctp'; $result = $ThemeView->getLayoutFileName(); $this->assertEquals($expected, $result); @@ -546,11 +547,7 @@ class ViewTest extends CakeTestCase { $this->ThemeController->params['pass'] = array('home'); $View = new TestThemeView($this->ThemeController); - ob_start(); - $result = $View->getViewFileName('does_not_exist'); - $expected = ob_get_clean(); - $this->assertRegExp("/PagesController::/", $expected); - $this->assertRegExp("/views(\/|\\\)themed(\/|\\\)my_theme(\/|\\\)pages(\/|\\\)does_not_exist.ctp/", $expected); + $View->getViewFileName('does_not_exist'); } /** @@ -577,11 +574,7 @@ class ViewTest extends CakeTestCase { $this->ThemeController->theme = 'my_theme'; $View = new TestThemeView($this->ThemeController); - ob_start(); $result = $View->getLayoutFileName(); - $expected = ob_get_clean(); - $this->assertRegExp("/Missing Layout/", $expected); - $this->assertRegExp("/views(\/|\\\)themed(\/|\\\)my_theme(\/|\\\)layouts(\/|\\\)whatever.ctp/", $expected); } /** diff --git a/lib/Cake/View/View.php b/lib/Cake/View/View.php index 475f02bb3..6c08cd067 100644 --- a/lib/Cake/View/View.php +++ b/lib/Cake/View/View.php @@ -1111,13 +1111,14 @@ class View extends Object { $paths = array_unique(array_merge($paths, $viewPaths)); if (!empty($this->theme)) { + $theme = Inflector::camelize($this->theme); $themePaths = array(); foreach ($paths as $path) { if (strpos($path, DS . 'Plugin' . DS) === false) { if ($plugin) { - $themePaths[] = $path . 'Themed' . DS . $this->theme . DS . 'Plugin' . DS . $plugin . DS; + $themePaths[] = $path . 'Themed' . DS . $theme . DS . 'Plugin' . DS . $plugin . DS; } - $themePaths[] = $path . 'Themed' . DS . $this->theme . DS; + $themePaths[] = $path . 'Themed' . DS . $theme . DS; } } $paths = array_merge($themePaths, $paths); From ac1f64abc9ee3253e0a40ae07f614a4b1fd4035e Mon Sep 17 00:00:00 2001 From: mark_story Date: Sat, 5 Jan 2013 14:50:09 -0500 Subject: [PATCH 5/9] Start and end transactions on the fixtures datasource. Fixes #3490 --- lib/Cake/TestSuite/Fixture/CakeFixtureManager.php | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/lib/Cake/TestSuite/Fixture/CakeFixtureManager.php b/lib/Cake/TestSuite/Fixture/CakeFixtureManager.php index d5998ecda..4d726efbb 100644 --- a/lib/Cake/TestSuite/Fixture/CakeFixtureManager.php +++ b/lib/Cake/TestSuite/Fixture/CakeFixtureManager.php @@ -204,19 +204,16 @@ class CakeFixtureManager { return; } - $nested = $test->db->useNestedTransactions; - $test->db->useNestedTransactions = false; - $test->db->begin(); foreach ($fixtures as $f) { if (!empty($this->_loaded[$f])) { $fixture = $this->_loaded[$f]; $db = ConnectionManager::getDataSource($fixture->useDbConfig); + $db->begin(); $this->_setupTable($fixture, $db, $test->dropTables); $fixture->insert($db); + $db->commit(); } } - $test->db->commit(); - $test->db->useNestedTransactions = $nested; } /** From e132a7c85655ef1bba3ca9e9b3cce58d2d7b1692 Mon Sep 17 00:00:00 2001 From: mark_story Date: Mon, 7 Jan 2013 21:00:14 -0500 Subject: [PATCH 6/9] Remove duplicated assertions. These assertions are effectively duplicated in the next test method. --- .../Test/Case/View/Helper/FormHelperTest.php | 77 ------------------- 1 file changed, 77 deletions(-) diff --git a/lib/Cake/Test/Case/View/Helper/FormHelperTest.php b/lib/Cake/Test/Case/View/Helper/FormHelperTest.php index 7f179f55e..2dfe3c6a9 100644 --- a/lib/Cake/Test/Case/View/Helper/FormHelperTest.php +++ b/lib/Cake/Test/Case/View/Helper/FormHelperTest.php @@ -3294,83 +3294,6 @@ class FormHelperTest extends CakeTestCase { ); $this->assertTags($result, $expected); - $result = $this->Form->input('Newsletter.subscribe', array('legend' => 'Legend title', 'type' => 'radio', 'options' => array('0' => 'Unsubscribe', '1' => 'Subscribe'))); - $expected = array( - 'div' => array('class' => 'input radio'), - 'fieldset' => array(), - 'legend' => array(), - 'Legend title', - '/legend', - 'input' => array('type' => 'hidden', 'name' => 'data[Newsletter][subscribe]', 'value' => '', 'id' => 'NewsletterSubscribe_'), - array('input' => array('type' => 'radio', 'name' => 'data[Newsletter][subscribe]', 'value' => '0', 'id' => 'NewsletterSubscribe0')), - array('label' => array('for' => 'NewsletterSubscribe0')), - 'Unsubscribe', - '/label', - array('input' => array('type' => 'radio', 'name' => 'data[Newsletter][subscribe]', 'value' => '1', 'id' => 'NewsletterSubscribe1')), - array('label' => array('for' => 'NewsletterSubscribe1')), - 'Subscribe', - '/label', - '/fieldset', - '/div' - ); - $this->assertTags($result, $expected); - - $result = $this->Form->input('Newsletter.subscribe', array('legend' => false, 'type' => 'radio', 'options' => array('0' => 'Unsubscribe', '1' => 'Subscribe'))); - $expected = array( - 'div' => array('class' => 'input radio'), - 'input' => array('type' => 'hidden', 'name' => 'data[Newsletter][subscribe]', 'value' => '', 'id' => 'NewsletterSubscribe_'), - array('input' => array('type' => 'radio', 'name' => 'data[Newsletter][subscribe]', 'value' => '0', 'id' => 'NewsletterSubscribe0')), - array('label' => array('for' => 'NewsletterSubscribe0')), - 'Unsubscribe', - '/label', - array('input' => array('type' => 'radio', 'name' => 'data[Newsletter][subscribe]', 'value' => '1', 'id' => 'NewsletterSubscribe1')), - array('label' => array('for' => 'NewsletterSubscribe1')), - 'Subscribe', - '/label', - '/div' - ); - $this->assertTags($result, $expected); - - $result = $this->Form->input('Newsletter.subscribe', array('legend' => 'Legend title', 'label' => false, 'type' => 'radio', 'options' => array('0' => 'Unsubscribe', '1' => 'Subscribe'))); - $expected = array( - 'div' => array('class' => 'input radio'), - 'fieldset' => array(), - 'legend' => array(), - 'Legend title', - '/legend', - 'input' => array('type' => 'hidden', 'name' => 'data[Newsletter][subscribe]', 'value' => '', 'id' => 'NewsletterSubscribe_'), - array('input' => array('type' => 'radio', 'name' => 'data[Newsletter][subscribe]', 'value' => '0', 'id' => 'NewsletterSubscribe0')), - 'Unsubscribe', - array('input' => array('type' => 'radio', 'name' => 'data[Newsletter][subscribe]', 'value' => '1', 'id' => 'NewsletterSubscribe1')), - 'Subscribe', - '/fieldset', - '/div' - ); - $this->assertTags($result, $expected); - - $result = $this->Form->input('Newsletter.subscribe', array('legend' => false, 'label' => false, 'type' => 'radio', 'options' => array('0' => 'Unsubscribe', '1' => 'Subscribe'))); - $expected = array( - 'div' => array('class' => 'input radio'), - 'input' => array('type' => 'hidden', 'name' => 'data[Newsletter][subscribe]', 'value' => '', 'id' => 'NewsletterSubscribe_'), - array('input' => array('type' => 'radio', 'name' => 'data[Newsletter][subscribe]', 'value' => '0', 'id' => 'NewsletterSubscribe0')), - 'Unsubscribe', - array('input' => array('type' => 'radio', 'name' => 'data[Newsletter][subscribe]', 'value' => '1', 'id' => 'NewsletterSubscribe1')), - 'Subscribe', - '/div' - ); - $this->assertTags($result, $expected); - - $result = $this->Form->input('Newsletter.subscribe', array('legend' => false, 'label' => false, 'type' => 'radio', 'value' => '1', 'options' => array('0' => 'Unsubscribe', '1' => 'Subscribe'))); - $expected = array( - 'div' => array('class' => 'input radio'), - array('input' => array('type' => 'radio', 'name' => 'data[Newsletter][subscribe]', 'value' => '0', 'id' => 'NewsletterSubscribe0')), - 'Unsubscribe', - array('input' => array('type' => 'radio', 'name' => 'data[Newsletter][subscribe]', 'value' => '1', 'id' => 'NewsletterSubscribe1', 'checked' => 'checked')), - 'Subscribe', - '/div' - ); - $this->assertTags($result, $expected); - $result = $this->Form->radio('Employee.gender', array('male' => 'Male', 'female' => 'Female')); $expected = array( 'fieldset' => array(), From 3f21d09c1d91ddfbddce6608ecf89811a412d4eb Mon Sep 17 00:00:00 2001 From: mark_story Date: Mon, 7 Jan 2013 21:12:53 -0500 Subject: [PATCH 7/9] Fix radio() and boolean values. Boolean values should be cast to integer equivalents, which allows for correct handling of boolean columns and their string equivalents used in form options. Fixes #3512 --- .../Test/Case/View/Helper/FormHelperTest.php | 30 ++++++++++++++++++- lib/Cake/View/Helper/FormHelper.php | 4 +++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/lib/Cake/Test/Case/View/Helper/FormHelperTest.php b/lib/Cake/Test/Case/View/Helper/FormHelperTest.php index 2dfe3c6a9..ae5b8fc91 100644 --- a/lib/Cake/Test/Case/View/Helper/FormHelperTest.php +++ b/lib/Cake/Test/Case/View/Helper/FormHelperTest.php @@ -3407,10 +3407,11 @@ class FormHelperTest extends CakeTestCase { /** * Test that radios with a 0 value are selected under the correct conditions. + * Also ensure that values that are booleanish are handled correctly. * * @return void */ - public function testRadioOptionWithZeroValue() { + public function testRadioOptionWithBooleanishValues() { $expected = array( 'fieldset' => array(), 'legend' => array(), @@ -3432,6 +3433,9 @@ class FormHelperTest extends CakeTestCase { $result = $this->Form->radio('Model.field', array('1' => 'Yes', '0' => 'No'), array('value' => 0)); $this->assertTags($result, $expected); + $result = $this->Form->radio('Model.field', array('1' => 'Yes', '0' => 'No'), array('value' => false)); + $this->assertTags($result, $expected); + $expected = array( 'fieldset' => array(), 'legend' => array(), @@ -3456,6 +3460,30 @@ class FormHelperTest extends CakeTestCase { $result = $this->Form->radio('Model.field', array('1' => 'Yes', '0' => 'No')); $this->assertTags($result, $expected); + + $expected = array( + 'fieldset' => array(), + 'legend' => array(), + 'Field', + '/legend', + array('input' => array('type' => 'radio', 'name' => 'data[Model][field]', 'checked' => 'checked', 'value' => '1', 'id' => 'ModelField1')), + array('label' => array('for' => 'ModelField1')), + 'Yes', + '/label', + array('input' => array('type' => 'radio', 'name' => 'data[Model][field]', 'value' => '0', 'id' => 'ModelField0')), + array('label' => array('for' => 'ModelField0')), + 'No', + '/label', + '/fieldset' + ); + $result = $this->Form->radio('Model.field', array('1' => 'Yes', '0' => 'No'), array('value' => 1)); + $this->assertTags($result, $expected); + + $result = $this->Form->radio('Model.field', array('1' => 'Yes', '0' => 'No'), array('value' => '1')); + $this->assertTags($result, $expected); + + $result = $this->Form->radio('Model.field', array('1' => 'Yes', '0' => 'No'), array('value' => true)); + $this->assertTags($result, $expected); } /** diff --git a/lib/Cake/View/Helper/FormHelper.php b/lib/Cake/View/Helper/FormHelper.php index a3374f58d..b7961142f 100644 --- a/lib/Cake/View/Helper/FormHelper.php +++ b/lib/Cake/View/Helper/FormHelper.php @@ -1385,6 +1385,10 @@ class FormHelper extends AppHelper { $hiddenField = isset($attributes['hiddenField']) ? $attributes['hiddenField'] : true; unset($attributes['hiddenField']); + if (isset($value) && is_bool($value)) { + $value = $value ? 1 : 0; + } + foreach ($options as $optValue => $optTitle) { $optionsHere = array('value' => $optValue); From 8af76a566286d2b88eaa87a3f6aa372a164f4943 Mon Sep 17 00:00:00 2001 From: Marco Tisi Date: Tue, 8 Jan 2013 11:49:52 +0100 Subject: [PATCH 8/9] Closes ticket #3080 in CakePHP Lighthouse (http://cakephp.lighthouseapp.com/projects/42648/tickets/3080-hashflatten-endless-loop-on-single-0-int-keys). Hash::flatten has a bug which causes an endless loop when try to flatten an integer key. Probably the $data array pointer won't reset itself when doing: $data = $element and list($data, $path) = array_pop($stack) The solution is to reset the pointer after the assignments. --- lib/Cake/Utility/Hash.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/Cake/Utility/Hash.php b/lib/Cake/Utility/Hash.php index b3f3052b3..278a01aa9 100644 --- a/lib/Cake/Utility/Hash.php +++ b/lib/Cake/Utility/Hash.php @@ -532,6 +532,7 @@ class Hash { $stack[] = array($data, $path); } $data = $element; + reset($data); $path .= $key . $separator; } else { $result[$path . $key] = $element; @@ -539,6 +540,7 @@ class Hash { if (empty($data) && !empty($stack)) { list($data, $path) = array_pop($stack); + reset($data); } } return $result; From c2e087dc9b54a3b051b58076d062f9009f9c71de Mon Sep 17 00:00:00 2001 From: mark_story Date: Tue, 8 Jan 2013 19:42:12 -0500 Subject: [PATCH 9/9] Add test for recursion issues in Hash::expand(). Refs #3080 --- lib/Cake/Test/Case/Utility/HashTest.php | 138 ++++++++++++++++++++++++ 1 file changed, 138 insertions(+) diff --git a/lib/Cake/Test/Case/Utility/HashTest.php b/lib/Cake/Test/Case/Utility/HashTest.php index 17b436239..909687bb4 100644 --- a/lib/Cake/Test/Case/Utility/HashTest.php +++ b/lib/Cake/Test/Case/Utility/HashTest.php @@ -2165,4 +2165,142 @@ class HashTest extends CakeTestCase { $this->assertEquals($result, $expected); } +/** + * Test that flattening a large complex set doesn't loop forever. + * + * @return void + */ + public function testFlattenInfiniteLoop() { + $data = array( + 'Order.ASI' => '0', + 'Order.Accounting' => '0', + 'Order.Admin' => '0', + 'Order.Art' => '0', + 'Order.ArtChecker' => '0', + 'Order.Canned' => '0', + 'Order.Customer_Tags' => '', + 'Order.Embroidery' => '0', + 'Order.Item.0.Product.style_number' => 'a11222', + 'Order.Item.0.Product.slug' => 'a11222', + 'Order.Item.0.Product._id' => '4ff8b8d3d7bbe8ad30000000', + 'Order.Item.0.Product.Color.slug' => 'kelly_green', + 'Order.Item.0.Product.ColorSizes.0.Color.color' => 'Sport Grey', + 'Order.Item.0.Product.ColorSizes.0.Color.slug' => 'sport_grey', + 'Order.Item.0.Product.ColorSizes.1.Color.color' => 'Kelly Green', + 'Order.Item.0.Product.ColorSizes.1.Color.slug' => 'kelly_green', + 'Order.Item.0.Product.ColorSizes.2.Color.color' => 'Orange', + 'Order.Item.0.Product.ColorSizes.2.Color.slug' => 'orange', + 'Order.Item.0.Product.ColorSizes.3.Color.color' => 'Yellow Haze', + 'Order.Item.0.Product.ColorSizes.3.Color.slug' => 'yellow_haze', + 'Order.Item.0.Product.brand' => 'OUTER BANKS', + 'Order.Item.0.Product.style' => 'T-shirt', + 'Order.Item.0.Product.description' => 'uhiuhuih oin ooi ioo ioio', + 'Order.Item.0.Product.sizes.0.Size.qty' => '', + 'Order.Item.0.Product.sizes.0.Size.size' => '0-3mo', + 'Order.Item.0.Product.sizes.0.Size.id' => '38', + 'Order.Item.0.Product.sizes.1.Size.qty' => '', + 'Order.Item.0.Product.sizes.1.Size.size' => '3-6mo', + 'Order.Item.0.Product.sizes.1.Size.id' => '39', + 'Order.Item.0.Product.sizes.2.Size.qty' => '78', + 'Order.Item.0.Product.sizes.2.Size.size' => '6-9mo', + 'Order.Item.0.Product.sizes.2.Size.id' => '40', + 'Order.Item.0.Product.sizes.3.Size.qty' => '', + 'Order.Item.0.Product.sizes.3.Size.size' => '6-12mo', + 'Order.Item.0.Product.sizes.3.Size.id' => '41', + 'Order.Item.0.Product.sizes.4.Size.qty' => '', + 'Order.Item.0.Product.sizes.4.Size.size' => '12-18mo', + 'Order.Item.0.Product.sizes.4.Size.id' => '42', + 'Order.Item.0.Art.imprint_locations.0.id' => (int) 2, + 'Order.Item.0.Art.imprint_locations.0.name' => 'Left Chest', + 'Order.Item.0.Art.imprint_locations.0.imprint_type.id' => (int) 7, + 'Order.Item.0.Art.imprint_locations.0.imprint_type.type' => 'Embroidery', + 'Order.Item.0.Art.imprint_locations.0.art' => '', + 'Order.Item.0.Art.imprint_locations.0.num_colors' => (int) 3, + 'Order.Item.0.Art.imprint_locations.0.description' => 'Wooo! This is Embroidery!!', + 'Order.Item.0.Art.imprint_locations.0.lines.0' => 'Platen', + 'Order.Item.0.Art.imprint_locations.0.lines.1' => 'Logo', + 'Order.Item.0.Art.imprint_locations.0.height' => (int) 4, + 'Order.Item.0.Art.imprint_locations.0.width' => (int) 5, + 'Order.Item.0.Art.imprint_locations.0.stitch_density' => 'Light', + 'Order.Item.0.Art.imprint_locations.0.metallic_thread' => true, + 'Order.Item.0.Art.imprint_locations.1.id' => (int) 4, + 'Order.Item.0.Art.imprint_locations.1.name' => 'Full Back', + 'Order.Item.0.Art.imprint_locations.1.imprint_type.id' => (int) 6, + 'Order.Item.0.Art.imprint_locations.1.imprint_type.type' => 'Screenprinting', + 'Order.Item.0.Art.imprint_locations.1.art' => '', + 'Order.Item.0.Art.imprint_locations.1.num_colors' => (int) 3, + 'Order.Item.0.Art.imprint_locations.1.description' => 'Wooo! This is Screenprinting!!', + 'Order.Item.0.Art.imprint_locations.1.lines.0' => 'Platen', + 'Order.Item.0.Art.imprint_locations.1.lines.1' => 'Logo', + 'Order.Item.0.Art.imprint_locations.2.id' => (int) 26, + 'Order.Item.0.Art.imprint_locations.2.name' => 'HS - JSY Name Below', + 'Order.Item.0.Art.imprint_locations.2.imprint_type.id' => (int) 9, + 'Order.Item.0.Art.imprint_locations.2.imprint_type.type' => 'Names', + 'Order.Item.0.Art.imprint_locations.2.description' => 'Wooo! This is Names!!', + 'Order.Item.0.Art.imprint_locations.2.sizes.S.0.active' => (int) 1, + 'Order.Item.0.Art.imprint_locations.2.sizes.S.0.name' => 'Benjamin Talavera', + 'Order.Item.0.Art.imprint_locations.2.sizes.S.0.color' => 'Red', + 'Order.Item.0.Art.imprint_locations.2.sizes.S.0.height' => '3', + 'Order.Item.0.Art.imprint_locations.2.sizes.S.0.layout' => 'Arched', + 'Order.Item.0.Art.imprint_locations.2.sizes.S.0.style' => 'Classic', + 'Order.Item.0.Art.imprint_locations.2.sizes.S.1.active' => (int) 0, + 'Order.Item.0.Art.imprint_locations.2.sizes.S.1.name' => 'Rishi Narayan', + 'Order.Item.0.Art.imprint_locations.2.sizes.S.1.color' => 'Cardinal', + 'Order.Item.0.Art.imprint_locations.2.sizes.S.1.height' => '4', + 'Order.Item.0.Art.imprint_locations.2.sizes.S.1.layout' => 'Straight', + 'Order.Item.0.Art.imprint_locations.2.sizes.S.1.style' => 'Team US', + 'Order.Item.0.Art.imprint_locations.2.sizes.M.0.active' => (int) 1, + 'Order.Item.0.Art.imprint_locations.2.sizes.M.0.name' => 'Brandon Plasters', + 'Order.Item.0.Art.imprint_locations.2.sizes.M.0.color' => 'Red', + 'Order.Item.0.Art.imprint_locations.2.sizes.M.0.height' => '3', + 'Order.Item.0.Art.imprint_locations.2.sizes.M.0.layout' => 'Arched', + 'Order.Item.0.Art.imprint_locations.2.sizes.M.0.style' => 'Classic', + 'Order.Item.0.Art.imprint_locations.2.sizes.M.1.active' => (int) 0, + 'Order.Item.0.Art.imprint_locations.2.sizes.M.1.name' => 'Andrew Reed', + 'Order.Item.0.Art.imprint_locations.2.sizes.M.1.color' => 'Cardinal', + 'Order.Item.0.Art.imprint_locations.2.sizes.M.1.height' => '4', + 'Order.Item.0.Art.imprint_locations.2.sizes.M.1.layout' => 'Straight', + 'Order.Item.0.Art.imprint_locations.2.sizes.M.1.style' => 'Team US', + 'Order.Job.0._id' => 'job-1', + 'Order.Job.0.type' => 'screenprinting', + 'Order.Job.0.postPress' => 'job-2', + 'Order.Job.1._id' => 'job-2', + 'Order.Job.1.type' => 'embroidery', + 'Order.Postpress' => '0', + 'Order.PriceAdjustment.0._id' => 'price-adjustment-1', + 'Order.PriceAdjustment.0.adjustment' => '-20', + 'Order.PriceAdjustment.0.adjustment_type' => 'percent', + 'Order.PriceAdjustment.0.type' => 'grand_total', + 'Order.PriceAdjustment.1.adjustment' => '20', + 'Order.PriceAdjustment.1.adjustment_type' => 'flat', + 'Order.PriceAdjustment.1.min-items' => '10', + 'Order.PriceAdjustment.1.type' => 'min-items', + 'Order.PriceAdjustment.1._id' => 'another-test-adjustment', + 'Order.Purchasing' => '0', + 'Order.QualityControl' => '0', + 'Order.Receiving' => '0', + 'Order.ScreenPrinting' => '0', + 'Order.Stage.art_approval' => (int) 0, + 'Order.Stage.draft' => (int) 1, + 'Order.Stage.quote' => (int) 1, + 'Order.Stage.order' => (int) 1, + 'Order.StoreLiason' => '0', + 'Order.Tag_UI_Email' => '', + 'Order.Tags' => '', + 'Order._id' => 'test-2', + 'Order.add_print_location' => '', + 'Order.created' => '2011-Dec-29 05:40:18', + 'Order.force_admin' => '0', + 'Order.modified' => '2012-Jul-25 01:24:49', + 'Order.name' => 'towering power', + 'Order.order_id' => '135961', + 'Order.slug' => 'test-2', + 'Order.title' => 'test job 2', + 'Order.type' => 'ttt' + ); + $expanded = Hash::expand($data); + $flattened = Hash::flatten($expanded); + $this->assertEquals($data, $flattened); + } + }