From 9da81894be1b0268eab2216b44c4bc48e5e24fd5 Mon Sep 17 00:00:00 2001 From: Alexander Makarov Date: Fri, 29 Mar 2013 18:23:50 +0400 Subject: [PATCH 1/4] moved helper test from util namespace to helpers namespace, added tests for StringHelper --- tests/unit/framework/helpers/ArrayHelperTest.php | 50 +++ tests/unit/framework/helpers/HtmlTest.php | 448 ++++++++++++++++++++++ tests/unit/framework/helpers/StringHelperTest.php | 73 ++++ tests/unit/framework/util/ArrayHelperTest.php | 50 --- tests/unit/framework/util/HtmlTest.php | 448 ---------------------- 5 files changed, 571 insertions(+), 498 deletions(-) create mode 100644 tests/unit/framework/helpers/ArrayHelperTest.php create mode 100644 tests/unit/framework/helpers/HtmlTest.php create mode 100644 tests/unit/framework/helpers/StringHelperTest.php delete mode 100644 tests/unit/framework/util/ArrayHelperTest.php delete mode 100644 tests/unit/framework/util/HtmlTest.php diff --git a/tests/unit/framework/helpers/ArrayHelperTest.php b/tests/unit/framework/helpers/ArrayHelperTest.php new file mode 100644 index 0000000..187217f --- /dev/null +++ b/tests/unit/framework/helpers/ArrayHelperTest.php @@ -0,0 +1,50 @@ + 'b', 'age' => 3), + array('name' => 'a', 'age' => 1), + array('name' => 'c', 'age' => 2), + ); + ArrayHelper::multisort($array, 'name'); + $this->assertEquals(array('name' => 'a', 'age' => 1), $array[0]); + $this->assertEquals(array('name' => 'b', 'age' => 3), $array[1]); + $this->assertEquals(array('name' => 'c', 'age' => 2), $array[2]); + + // multiple keys + $array = array( + array('name' => 'b', 'age' => 3), + array('name' => 'a', 'age' => 2), + array('name' => 'a', 'age' => 1), + ); + ArrayHelper::multisort($array, array('name', 'age')); + $this->assertEquals(array('name' => 'a', 'age' => 1), $array[0]); + $this->assertEquals(array('name' => 'a', 'age' => 2), $array[1]); + $this->assertEquals(array('name' => 'b', 'age' => 3), $array[2]); + + // case-insensitive + $array = array( + array('name' => 'a', 'age' => 3), + array('name' => 'b', 'age' => 2), + array('name' => 'A', 'age' => 1), + ); + ArrayHelper::multisort($array, array('name', 'age'), SORT_ASC, array(SORT_STRING|SORT_FLAG_CASE, SORT_REGULAR)); + $this->assertEquals(array('name' => 'A', 'age' => 1), $array[0]); + $this->assertEquals(array('name' => 'a', 'age' => 3), $array[1]); + $this->assertEquals(array('name' => 'b', 'age' => 2), $array[2]); + } +} diff --git a/tests/unit/framework/helpers/HtmlTest.php b/tests/unit/framework/helpers/HtmlTest.php new file mode 100644 index 0000000..2c3de72 --- /dev/null +++ b/tests/unit/framework/helpers/HtmlTest.php @@ -0,0 +1,448 @@ + array( + 'request' => array( + 'class' => 'yii\web\Request', + 'url' => '/test', + ), + ), + )); + } + + public function tearDown() + { + Yii::$app = null; + } + + public function testEncode() + { + $this->assertEquals("a<>&"'", Html::encode("a<>&\"'")); + } + + public function testDecode() + { + $this->assertEquals("a<>&\"'", Html::decode("a<>&"'")); + } + + public function testTag() + { + $this->assertEquals('
', Html::tag('br')); + $this->assertEquals('', Html::tag('span')); + $this->assertEquals('
content
', Html::tag('div', 'content')); + $this->assertEquals('', Html::tag('input', '', array('type' => 'text', 'name' => 'test', 'value' => '<>'))); + + Html::$closeVoidElements = false; + + $this->assertEquals('
', Html::tag('br')); + $this->assertEquals('', Html::tag('span')); + $this->assertEquals('
content
', Html::tag('div', 'content')); + $this->assertEquals('', Html::tag('input', '', array('type' => 'text', 'name' => 'test', 'value' => '<>'))); + + Html::$closeVoidElements = true; + + $this->assertEquals('', Html::tag('span', '', array('disabled' => true))); + Html::$showBooleanAttributeValues = false; + $this->assertEquals('', Html::tag('span', '', array('disabled' => true))); + Html::$showBooleanAttributeValues = true; + } + + public function testBeginTag() + { + $this->assertEquals('
', Html::beginTag('br')); + $this->assertEquals('', Html::beginTag('span', array('id' => 'test', 'class' => 'title'))); + } + + public function testEndTag() + { + $this->assertEquals('
', Html::endTag('br')); + $this->assertEquals('
', Html::endTag('span')); + } + + public function testCdata() + { + $data = 'test<>'; + $this->assertEquals('', Html::cdata($data)); + } + + public function testStyle() + { + $content = 'a <>'; + $this->assertEquals("", Html::style($content)); + $this->assertEquals("", Html::style($content, array('type' => 'text/less'))); + } + + public function testScript() + { + $content = 'a <>'; + $this->assertEquals("", Html::script($content)); + $this->assertEquals("", Html::script($content, array('type' => 'text/js'))); + } + + public function testCssFile() + { + $this->assertEquals('', Html::cssFile('http://example.com')); + $this->assertEquals('', Html::cssFile('')); + } + + public function testJsFile() + { + $this->assertEquals('', Html::jsFile('http://example.com')); + $this->assertEquals('', Html::jsFile('')); + } + + public function testBeginForm() + { + $this->assertEquals('
', Html::beginForm()); + $this->assertEquals('', Html::beginForm('/example', 'get')); + $hiddens = array( + '', + '', + ); + $this->assertEquals('' . "\n" . implode("\n", $hiddens), Html::beginForm('/example?id=1&title=%3C', 'get')); + } + + public function testEndForm() + { + $this->assertEquals('
', Html::endForm()); + } + + public function testA() + { + $this->assertEquals('something<>', Html::a('something<>')); + $this->assertEquals('something', Html::a('something', '/example')); + $this->assertEquals('something', Html::a('something', '')); + } + + public function testMailto() + { + $this->assertEquals('test<>', Html::mailto('test<>')); + $this->assertEquals('test<>', Html::mailto('test<>', 'test>')); + } + + public function testImg() + { + $this->assertEquals('', Html::img('/example')); + $this->assertEquals('', Html::img('')); + $this->assertEquals('something', Html::img('/example', array('alt' => 'something', 'width' => 10))); + } + + public function testLabel() + { + $this->assertEquals('', Html::label('something<>')); + $this->assertEquals('', Html::label('something<>', 'a')); + $this->assertEquals('', Html::label('something<>', 'a', array('class' => 'test'))); + } + + public function testButton() + { + $this->assertEquals('', Html::button()); + $this->assertEquals('', Html::button('test', 'value', 'content<>')); + $this->assertEquals('', Html::button('test', 'value', 'content<>', array('type' => 'submit', 'class' => "t"))); + } + + public function testSubmitButton() + { + $this->assertEquals('', Html::submitButton()); + $this->assertEquals('', Html::submitButton('test', 'value', 'content<>', array('class' => 't'))); + } + + public function testResetButton() + { + $this->assertEquals('', Html::resetButton()); + $this->assertEquals('', Html::resetButton('test', 'value', 'content<>', array('class' => 't'))); + } + + public function testInput() + { + $this->assertEquals('', Html::input('text')); + $this->assertEquals('', Html::input('text', 'test', 'value', array('class' => 't'))); + } + + public function testButtonInput() + { + $this->assertEquals('', Html::buttonInput('test')); + $this->assertEquals('', Html::buttonInput('test', 'text', array('class' => 'a'))); + } + + public function testSubmitInput() + { + $this->assertEquals('', Html::submitInput()); + $this->assertEquals('', Html::submitInput('test', 'text', array('class' => 'a'))); + } + + public function testResetInput() + { + $this->assertEquals('', Html::resetInput()); + $this->assertEquals('', Html::resetInput('test', 'text', array('class' => 'a'))); + } + + public function testTextInput() + { + $this->assertEquals('', Html::textInput('test')); + $this->assertEquals('', Html::textInput('test', 'value', array('class' => 't'))); + } + + public function testHiddenInput() + { + $this->assertEquals('', Html::hiddenInput('test')); + $this->assertEquals('', Html::hiddenInput('test', 'value', array('class' => 't'))); + } + + public function testPasswordInput() + { + $this->assertEquals('', Html::passwordInput('test')); + $this->assertEquals('', Html::passwordInput('test', 'value', array('class' => 't'))); + } + + public function testFileInput() + { + $this->assertEquals('', Html::fileInput('test')); + $this->assertEquals('', Html::fileInput('test', 'value', array('class' => 't'))); + } + + public function testTextarea() + { + $this->assertEquals('', Html::textarea('test')); + $this->assertEquals('', Html::textarea('test', 'value<>', array('class' => 't'))); + } + + public function testRadio() + { + $this->assertEquals('', Html::radio('test')); + $this->assertEquals('', Html::radio('test', true, null, array('class' => 'a'))); + $this->assertEquals('', Html::radio('test', true, 2, array('class' => 'a' , 'uncheck' => '0'))); + } + + public function testCheckbox() + { + $this->assertEquals('', Html::checkbox('test')); + $this->assertEquals('', Html::checkbox('test', true, null, array('class' => 'a'))); + $this->assertEquals('', Html::checkbox('test', true, 2, array('class' => 'a', 'uncheck' => '0'))); + } + + public function testDropDownList() + { + $expected = << + + +EOD; + $this->assertEquals($expected, Html::dropDownList('test')); + $expected = << + + + +EOD; + $this->assertEquals($expected, Html::dropDownList('test', null, $this->getDataItems())); + $expected = << + + + +EOD; + $this->assertEquals($expected, Html::dropDownList('test', 'value2', $this->getDataItems())); + } + + public function testListBox() + { + $expected = << + + +EOD; + $this->assertEquals($expected, Html::listBox('test')); + $expected = << + + + +EOD; + $this->assertEquals($expected, Html::listBox('test', null, $this->getDataItems(), array('size' => 5))); + $expected = << + + + +EOD; + $this->assertEquals($expected, Html::listBox('test', null, $this->getDataItems2())); + $expected = << + + + +EOD; + $this->assertEquals($expected, Html::listBox('test', 'value2', $this->getDataItems())); + $expected = << + + + +EOD; + $this->assertEquals($expected, Html::listBox('test', array('value1', 'value2'), $this->getDataItems())); + + $expected = << + + +EOD; + $this->assertEquals($expected, Html::listBox('test', null, array(), array('multiple' => true))); + $expected = << +EOD; + $this->assertEquals($expected, Html::listBox('test', '', array(), array('unselect' => '0'))); + } + + public function testCheckboxList() + { + $this->assertEquals('', Html::checkboxList('test')); + + $expected = << text1 + +EOD; + $this->assertEquals($expected, Html::checkboxList('test', array('value2'), $this->getDataItems())); + + $expected = << text1<> + +EOD; + $this->assertEquals($expected, Html::checkboxList('test', array('value2'), $this->getDataItems2())); + + $expected = <<
+ +EOD; + $this->assertEquals($expected, Html::checkboxList('test', array('value2'), $this->getDataItems(), array( + 'separator' => "
\n", + 'unselect' => '0', + ))); + + $expected = <<text1 +1 +EOD; + $this->assertEquals($expected, Html::checkboxList('test', array('value2'), $this->getDataItems(), array( + 'item' => function ($index, $label, $name, $checked, $value) { + return $index . Html::label($label . ' ' . Html::checkbox($name, $checked, $value)); + } + ))); + } + + public function testRadioList() + { + $this->assertEquals('', Html::radioList('test')); + + $expected = << text1 + +EOD; + $this->assertEquals($expected, Html::radioList('test', array('value2'), $this->getDataItems())); + + $expected = << text1<> + +EOD; + $this->assertEquals($expected, Html::radioList('test', array('value2'), $this->getDataItems2())); + + $expected = <<
+ +EOD; + $this->assertEquals($expected, Html::radioList('test', array('value2'), $this->getDataItems(), array( + 'separator' => "
\n", + 'unselect' => '0', + ))); + + $expected = <<text1 +1 +EOD; + $this->assertEquals($expected, Html::radioList('test', array('value2'), $this->getDataItems(), array( + 'item' => function ($index, $label, $name, $checked, $value) { + return $index . Html::label($label . ' ' . Html::radio($name, $checked, $value)); + } + ))); + } + + public function testRenderOptions() + { + $data = array( + 'value1' => 'label1', + 'group1' => array( + 'value11' => 'label11', + 'group11' => array( + 'value111' => 'label111', + ), + 'group12' => array(), + ), + 'value2' => 'label2', + 'group2' => array(), + ); + $expected = <<please select<> + + + + + + + + + + + + + + +EOD; + $attributes = array( + 'prompt' => 'please select<>', + 'options' => array( + 'value111' => array('class' => 'option'), + ), + 'groups' => array( + 'group12' => array('class' => 'group'), + ), + ); + $this->assertEquals($expected, Html::renderSelectOptions(array('value111', 'value1'), $data, $attributes)); + } + + public function testRenderAttributes() + { + $this->assertEquals('', Html::renderTagAttributes(array())); + $this->assertEquals(' name="test" value="1<>"', Html::renderTagAttributes(array('name' => 'test', 'empty' => null, 'value' => '1<>'))); + Html::$showBooleanAttributeValues = false; + $this->assertEquals(' checked disabled', Html::renderTagAttributes(array('checked' => 'checked', 'disabled' => true, 'hidden' => false))); + Html::$showBooleanAttributeValues = true; + } + + protected function getDataItems() + { + return array( + 'value1' => 'text1', + 'value2' => 'text2', + ); + } + + protected function getDataItems2() + { + return array( + 'value1<>' => 'text1<>', + 'value 2' => 'text 2', + ); + } +} diff --git a/tests/unit/framework/helpers/StringHelperTest.php b/tests/unit/framework/helpers/StringHelperTest.php new file mode 100644 index 0000000..4e1266f --- /dev/null +++ b/tests/unit/framework/helpers/StringHelperTest.php @@ -0,0 +1,73 @@ +assertEquals(4, StringHelper::strlen('this')); + $this->assertEquals(6, StringHelper::strlen('это')); + } + + public function testSubstr() + { + $this->assertEquals('th', StringHelper::substr('this', 0, 2)); + $this->assertEquals('э', StringHelper::substr('это', 0, 2)); + } + + public function testPluralize() + { + $testData = array( + 'move' => 'moves', + 'foot' => 'feet', + 'child' => 'children', + 'human' => 'humans', + 'man' => 'men', + 'staff' => 'staff', + 'tooth' => 'teeth', + 'person' => 'people', + 'mouse' => 'mice', + 'touch' => 'touches', + 'hash' => 'hashes', + 'shelf' => 'shelves', + 'potato' => 'potatoes', + 'bus' => 'buses', + 'test' => 'tests', + 'car' => 'cars', + ); + + foreach($testData as $testIn => $testOut) { + $this->assertEquals($testOut, StringHelper::pluralize($testIn)); + $this->assertEquals(ucfirst($testOut), ucfirst(StringHelper::pluralize($testIn))); + } + } + + public function testCamel2words() + { + $this->assertEquals('Camel Case', StringHelper::camel2words('camelCase')); + $this->assertEquals('Lower Case', StringHelper::camel2words('lower_case')); + $this->assertEquals('Tricky Stuff It Is Testing', StringHelper::camel2words(' tricky_stuff.it-is testing... ')); + } + + public function testCamel2id() + { + $this->assertEquals('post-tag', StringHelper::camel2id('PostTag')); + $this->assertEquals('post_tag', StringHelper::camel2id('PostTag', '_')); + + $this->assertEquals('post-tag', StringHelper::camel2id('postTag')); + $this->assertEquals('post_tag', StringHelper::camel2id('postTag', '_')); + } + + public function testId2camel() + { + $this->assertEquals('PostTag', StringHelper::id2camel('post-tag')); + $this->assertEquals('PostTag', StringHelper::id2camel('post_tag', '_')); + + $this->assertEquals('PostTag', StringHelper::id2camel('post-tag')); + $this->assertEquals('PostTag', StringHelper::id2camel('post_tag', '_')); + } +} \ No newline at end of file diff --git a/tests/unit/framework/util/ArrayHelperTest.php b/tests/unit/framework/util/ArrayHelperTest.php deleted file mode 100644 index 117c702..0000000 --- a/tests/unit/framework/util/ArrayHelperTest.php +++ /dev/null @@ -1,50 +0,0 @@ - 'b', 'age' => 3), - array('name' => 'a', 'age' => 1), - array('name' => 'c', 'age' => 2), - ); - ArrayHelper::multisort($array, 'name'); - $this->assertEquals(array('name' => 'a', 'age' => 1), $array[0]); - $this->assertEquals(array('name' => 'b', 'age' => 3), $array[1]); - $this->assertEquals(array('name' => 'c', 'age' => 2), $array[2]); - - // multiple keys - $array = array( - array('name' => 'b', 'age' => 3), - array('name' => 'a', 'age' => 2), - array('name' => 'a', 'age' => 1), - ); - ArrayHelper::multisort($array, array('name', 'age')); - $this->assertEquals(array('name' => 'a', 'age' => 1), $array[0]); - $this->assertEquals(array('name' => 'a', 'age' => 2), $array[1]); - $this->assertEquals(array('name' => 'b', 'age' => 3), $array[2]); - - // case-insensitive - $array = array( - array('name' => 'a', 'age' => 3), - array('name' => 'b', 'age' => 2), - array('name' => 'A', 'age' => 1), - ); - ArrayHelper::multisort($array, array('name', 'age'), SORT_ASC, array(SORT_STRING|SORT_FLAG_CASE, SORT_REGULAR)); - $this->assertEquals(array('name' => 'A', 'age' => 1), $array[0]); - $this->assertEquals(array('name' => 'a', 'age' => 3), $array[1]); - $this->assertEquals(array('name' => 'b', 'age' => 2), $array[2]); - } -} diff --git a/tests/unit/framework/util/HtmlTest.php b/tests/unit/framework/util/HtmlTest.php deleted file mode 100644 index eba1a20..0000000 --- a/tests/unit/framework/util/HtmlTest.php +++ /dev/null @@ -1,448 +0,0 @@ - array( - 'request' => array( - 'class' => 'yii\web\Request', - 'url' => '/test', - ), - ), - )); - } - - public function tearDown() - { - Yii::$app = null; - } - - public function testEncode() - { - $this->assertEquals("a<>&"'", Html::encode("a<>&\"'")); - } - - public function testDecode() - { - $this->assertEquals("a<>&\"'", Html::decode("a<>&"'")); - } - - public function testTag() - { - $this->assertEquals('
', Html::tag('br')); - $this->assertEquals('', Html::tag('span')); - $this->assertEquals('
content
', Html::tag('div', 'content')); - $this->assertEquals('', Html::tag('input', '', array('type' => 'text', 'name' => 'test', 'value' => '<>'))); - - Html::$closeVoidElements = false; - - $this->assertEquals('
', Html::tag('br')); - $this->assertEquals('', Html::tag('span')); - $this->assertEquals('
content
', Html::tag('div', 'content')); - $this->assertEquals('', Html::tag('input', '', array('type' => 'text', 'name' => 'test', 'value' => '<>'))); - - Html::$closeVoidElements = true; - - $this->assertEquals('', Html::tag('span', '', array('disabled' => true))); - Html::$showBooleanAttributeValues = false; - $this->assertEquals('', Html::tag('span', '', array('disabled' => true))); - Html::$showBooleanAttributeValues = true; - } - - public function testBeginTag() - { - $this->assertEquals('
', Html::beginTag('br')); - $this->assertEquals('', Html::beginTag('span', array('id' => 'test', 'class' => 'title'))); - } - - public function testEndTag() - { - $this->assertEquals('
', Html::endTag('br')); - $this->assertEquals('
', Html::endTag('span')); - } - - public function testCdata() - { - $data = 'test<>'; - $this->assertEquals('', Html::cdata($data)); - } - - public function testStyle() - { - $content = 'a <>'; - $this->assertEquals("", Html::style($content)); - $this->assertEquals("", Html::style($content, array('type' => 'text/less'))); - } - - public function testScript() - { - $content = 'a <>'; - $this->assertEquals("", Html::script($content)); - $this->assertEquals("", Html::script($content, array('type' => 'text/js'))); - } - - public function testCssFile() - { - $this->assertEquals('', Html::cssFile('http://example.com')); - $this->assertEquals('', Html::cssFile('')); - } - - public function testJsFile() - { - $this->assertEquals('', Html::jsFile('http://example.com')); - $this->assertEquals('', Html::jsFile('')); - } - - public function testBeginForm() - { - $this->assertEquals('
', Html::beginForm()); - $this->assertEquals('', Html::beginForm('/example', 'get')); - $hiddens = array( - '', - '', - ); - $this->assertEquals('' . "\n" . implode("\n", $hiddens), Html::beginForm('/example?id=1&title=%3C', 'get')); - } - - public function testEndForm() - { - $this->assertEquals('
', Html::endForm()); - } - - public function testA() - { - $this->assertEquals('something<>', Html::a('something<>')); - $this->assertEquals('something', Html::a('something', '/example')); - $this->assertEquals('something', Html::a('something', '')); - } - - public function testMailto() - { - $this->assertEquals('test<>', Html::mailto('test<>')); - $this->assertEquals('test<>', Html::mailto('test<>', 'test>')); - } - - public function testImg() - { - $this->assertEquals('', Html::img('/example')); - $this->assertEquals('', Html::img('')); - $this->assertEquals('something', Html::img('/example', array('alt' => 'something', 'width' => 10))); - } - - public function testLabel() - { - $this->assertEquals('', Html::label('something<>')); - $this->assertEquals('', Html::label('something<>', 'a')); - $this->assertEquals('', Html::label('something<>', 'a', array('class' => 'test'))); - } - - public function testButton() - { - $this->assertEquals('', Html::button()); - $this->assertEquals('', Html::button('test', 'value', 'content<>')); - $this->assertEquals('', Html::button('test', 'value', 'content<>', array('type' => 'submit', 'class' => "t"))); - } - - public function testSubmitButton() - { - $this->assertEquals('', Html::submitButton()); - $this->assertEquals('', Html::submitButton('test', 'value', 'content<>', array('class' => 't'))); - } - - public function testResetButton() - { - $this->assertEquals('', Html::resetButton()); - $this->assertEquals('', Html::resetButton('test', 'value', 'content<>', array('class' => 't'))); - } - - public function testInput() - { - $this->assertEquals('', Html::input('text')); - $this->assertEquals('', Html::input('text', 'test', 'value', array('class' => 't'))); - } - - public function testButtonInput() - { - $this->assertEquals('', Html::buttonInput('test')); - $this->assertEquals('', Html::buttonInput('test', 'text', array('class' => 'a'))); - } - - public function testSubmitInput() - { - $this->assertEquals('', Html::submitInput()); - $this->assertEquals('', Html::submitInput('test', 'text', array('class' => 'a'))); - } - - public function testResetInput() - { - $this->assertEquals('', Html::resetInput()); - $this->assertEquals('', Html::resetInput('test', 'text', array('class' => 'a'))); - } - - public function testTextInput() - { - $this->assertEquals('', Html::textInput('test')); - $this->assertEquals('', Html::textInput('test', 'value', array('class' => 't'))); - } - - public function testHiddenInput() - { - $this->assertEquals('', Html::hiddenInput('test')); - $this->assertEquals('', Html::hiddenInput('test', 'value', array('class' => 't'))); - } - - public function testPasswordInput() - { - $this->assertEquals('', Html::passwordInput('test')); - $this->assertEquals('', Html::passwordInput('test', 'value', array('class' => 't'))); - } - - public function testFileInput() - { - $this->assertEquals('', Html::fileInput('test')); - $this->assertEquals('', Html::fileInput('test', 'value', array('class' => 't'))); - } - - public function testTextarea() - { - $this->assertEquals('', Html::textarea('test')); - $this->assertEquals('', Html::textarea('test', 'value<>', array('class' => 't'))); - } - - public function testRadio() - { - $this->assertEquals('', Html::radio('test')); - $this->assertEquals('', Html::radio('test', true, null, array('class' => 'a'))); - $this->assertEquals('', Html::radio('test', true, 2, array('class' => 'a' , 'uncheck' => '0'))); - } - - public function testCheckbox() - { - $this->assertEquals('', Html::checkbox('test')); - $this->assertEquals('', Html::checkbox('test', true, null, array('class' => 'a'))); - $this->assertEquals('', Html::checkbox('test', true, 2, array('class' => 'a', 'uncheck' => '0'))); - } - - public function testDropDownList() - { - $expected = << - - -EOD; - $this->assertEquals($expected, Html::dropDownList('test')); - $expected = << - - - -EOD; - $this->assertEquals($expected, Html::dropDownList('test', null, $this->getDataItems())); - $expected = << - - - -EOD; - $this->assertEquals($expected, Html::dropDownList('test', 'value2', $this->getDataItems())); - } - - public function testListBox() - { - $expected = << - - -EOD; - $this->assertEquals($expected, Html::listBox('test')); - $expected = << - - - -EOD; - $this->assertEquals($expected, Html::listBox('test', null, $this->getDataItems(), array('size' => 5))); - $expected = << - - - -EOD; - $this->assertEquals($expected, Html::listBox('test', null, $this->getDataItems2())); - $expected = << - - - -EOD; - $this->assertEquals($expected, Html::listBox('test', 'value2', $this->getDataItems())); - $expected = << - - - -EOD; - $this->assertEquals($expected, Html::listBox('test', array('value1', 'value2'), $this->getDataItems())); - - $expected = << - - -EOD; - $this->assertEquals($expected, Html::listBox('test', null, array(), array('multiple' => true))); - $expected = << -EOD; - $this->assertEquals($expected, Html::listBox('test', '', array(), array('unselect' => '0'))); - } - - public function testCheckboxList() - { - $this->assertEquals('', Html::checkboxList('test')); - - $expected = << text1 - -EOD; - $this->assertEquals($expected, Html::checkboxList('test', array('value2'), $this->getDataItems())); - - $expected = << text1<> - -EOD; - $this->assertEquals($expected, Html::checkboxList('test', array('value2'), $this->getDataItems2())); - - $expected = <<
- -EOD; - $this->assertEquals($expected, Html::checkboxList('test', array('value2'), $this->getDataItems(), array( - 'separator' => "
\n", - 'unselect' => '0', - ))); - - $expected = <<text1 -1 -EOD; - $this->assertEquals($expected, Html::checkboxList('test', array('value2'), $this->getDataItems(), array( - 'item' => function ($index, $label, $name, $checked, $value) { - return $index . Html::label($label . ' ' . Html::checkbox($name, $checked, $value)); - } - ))); - } - - public function testRadioList() - { - $this->assertEquals('', Html::radioList('test')); - - $expected = << text1 - -EOD; - $this->assertEquals($expected, Html::radioList('test', array('value2'), $this->getDataItems())); - - $expected = << text1<> - -EOD; - $this->assertEquals($expected, Html::radioList('test', array('value2'), $this->getDataItems2())); - - $expected = <<
- -EOD; - $this->assertEquals($expected, Html::radioList('test', array('value2'), $this->getDataItems(), array( - 'separator' => "
\n", - 'unselect' => '0', - ))); - - $expected = <<text1 -1 -EOD; - $this->assertEquals($expected, Html::radioList('test', array('value2'), $this->getDataItems(), array( - 'item' => function ($index, $label, $name, $checked, $value) { - return $index . Html::label($label . ' ' . Html::radio($name, $checked, $value)); - } - ))); - } - - public function testRenderOptions() - { - $data = array( - 'value1' => 'label1', - 'group1' => array( - 'value11' => 'label11', - 'group11' => array( - 'value111' => 'label111', - ), - 'group12' => array(), - ), - 'value2' => 'label2', - 'group2' => array(), - ); - $expected = <<please select<> - - - - - - - - - - - - - - -EOD; - $attributes = array( - 'prompt' => 'please select<>', - 'options' => array( - 'value111' => array('class' => 'option'), - ), - 'groups' => array( - 'group12' => array('class' => 'group'), - ), - ); - $this->assertEquals($expected, Html::renderSelectOptions(array('value111', 'value1'), $data, $attributes)); - } - - public function testRenderAttributes() - { - $this->assertEquals('', Html::renderTagAttributes(array())); - $this->assertEquals(' name="test" value="1<>"', Html::renderTagAttributes(array('name' => 'test', 'empty' => null, 'value' => '1<>'))); - Html::$showBooleanAttributeValues = false; - $this->assertEquals(' checked disabled', Html::renderTagAttributes(array('checked' => 'checked', 'disabled' => true, 'hidden' => false))); - Html::$showBooleanAttributeValues = true; - } - - protected function getDataItems() - { - return array( - 'value1' => 'text1', - 'value2' => 'text2', - ); - } - - protected function getDataItems2() - { - return array( - 'value1<>' => 'text1<>', - 'value 2' => 'text 2', - ); - } -} From f69a73baf2bf377fbba09268335aec8228c35413 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Fri, 29 Mar 2013 13:31:08 -0400 Subject: [PATCH 2/4] refactored User and Identity classes. --- framework/web/Identity.php | 36 +++++++++++++- framework/web/User.php | 115 ++++++++++++++++++++++++--------------------- 2 files changed, 96 insertions(+), 55 deletions(-) diff --git a/framework/web/Identity.php b/framework/web/Identity.php index 89d4282..6d67bc0 100644 --- a/framework/web/Identity.php +++ b/framework/web/Identity.php @@ -8,6 +8,35 @@ namespace yii\web; /** + * Identity is the interface that should be implemented by a class providing identity information. + * + * This interface can typically be implemented by a user model class. For example, the following + * code shows how to implement this interface by a User ActiveRecord class: + * + * ~~~ + * class User extends ActiveRecord implements Identity + * { + * public static function findIdentity($id) + * { + * return static::find($id); + * } + * + * public function getId() + * { + * return $this->id; + * } + * + * public function getAuthKey() + * { + * return $this->authKey; + * } + * + * public function validateAuthKey($authKey) + * { + * return $this->authKey === $authKey; + * } + * } + * ~~~ * * @author Qiang Xue * @since 2.0 @@ -29,8 +58,11 @@ interface Identity public function getId(); /** * Returns a key that can be used to check the validity of a given identity ID. - * The space of such keys should be big and random enough to defeat potential identity attacks. - * The returned key can be a string, an integer, or any serializable data. + * + * The key should be unique for each individual user, and should be persistent + * so that it can be used to check the validity of the user identity. + * + * The space of such keys should be big enough to defeat potential identity attacks. * * This is required if [[User::enableAutoLogin]] is enabled. * @return string a key that is used to check the validity of a given identity ID. diff --git a/framework/web/User.php b/framework/web/User.php index 5b5f977..4dc2607 100644 --- a/framework/web/User.php +++ b/framework/web/User.php @@ -13,7 +13,7 @@ use yii\base\HttpException; use yii\base\InvalidConfigException; /** - * User is an application component that manages the user authentication status. + * User is the class for the "user" application component that manages the user authentication status. * * In particular, [[User::isGuest]] returns a value indicating whether the current user is a guest or not. * Through methods [[login()]] and [[logout()]], you can change the user authentication status. @@ -32,15 +32,6 @@ class User extends Component const EVENT_AFTER_LOGOUT = 'afterLogout'; /** - * @var Identity the identity object associated with the currently logged user. - * This property is set automatically be the User component. Do not modify it directly - * unless you understand the consequence. You should normally use [[login()]], [[logout()]], - * or [[switchIdentity()]] to update the identity associated with the current user. - * - * If this property is null, it means the current user is a guest (not authenticated). - */ - public $identity; - /** * @var string the class name of the [[identity]] object. */ public $identityClass; @@ -65,7 +56,7 @@ class User extends Component * @var array the configuration of the identity cookie. This property is used only when [[enableAutoLogin]] is true. * @see Cookie */ - public $identityCookie = array('name' => '__identity'); + public $identityCookie = array('name' => '__identity', 'httponly' => true); /** * @var integer the number of seconds in which the user will be logged out automatically if he * remains inactive. If this property is not set, the user will be logged out after @@ -112,8 +103,6 @@ class User extends Component Yii::$app->getSession()->open(); - $this->loadIdentity(); - $this->renewAuthStatus(); if ($this->enableAutoLogin) { @@ -125,19 +114,43 @@ class User extends Component } } + private $_identity = false; + /** - * Loads the [[identity]] object according to [[id]]. + * Returns the identity object associated with the currently logged user. + * @return Identity the identity object associated with the currently logged user. + * Null is returned if the user is not logged in (not authenticated). + * @see login + * @see logout */ - protected function loadIdentity() + public function getIdentity() { - $id = $this->getId(); - if ($id === null) { - $this->identity = null; - } else { - /** @var $class Identity */ - $class = $this->identityClass; - $this->identity = $class::findIdentity($id); + if ($this->_identity === false) { + $id = $this->getId(); + if ($id === null) { + $this->_identity = null; + } else { + /** @var $class Identity */ + $class = $this->identityClass; + $this->_identity = $class::findIdentity($id); + } } + return $this->_identity; + } + + /** + * Sets the identity object. + * This method should be mainly be used by the User component or its child class + * to maintain the identity object. + * + * You should normally update the user identity via methods [[login()]], [[logout()]] + * or [[switchIdentity()]]. + * + * @param Identity $identity the identity object associated with the currently logged user. + */ + public function setIdentity($identity) + { + $this->_identity = $identity; } /** @@ -157,10 +170,7 @@ class User extends Component public function login($identity, $duration = 0) { if ($this->beforeLogin($identity, false)) { - $this->switchIdentity($identity); - if ($duration > 0 && $this->enableAutoLogin) { - $this->sendIdentityCookie($identity, $duration); - } + $this->switchIdentity($identity, $duration); $this->afterLogin($identity, false); } return !$this->getIsGuest(); @@ -185,10 +195,7 @@ class User extends Component $identity = $class::findIdentity($id); if ($identity !== null && $identity->validateAuthKey($authKey)) { if ($this->beforeLogin($identity, true)) { - $this->switchIdentity($identity); - if ($this->autoRenewCookie) { - $this->sendIdentityCookie($identity, $duration); - } + $this->switchIdentity($identity, $this->autoRenewCookie ? $duration : 0); $this->afterLogin($identity, true); } } elseif ($identity !== null) { @@ -206,12 +213,9 @@ class User extends Component */ public function logout($destroySession = true) { - $identity = $this->identity; + $identity = $this->getIdentity(); if ($identity !== null && $this->beforeLogout($identity)) { $this->switchIdentity(null); - if ($this->enableAutoLogin) { - Yii::$app->getResponse()->getCookies()->remove(new Cookie($this->identityCookie)); - } if ($destroySession) { Yii::$app->getSession()->destroy(); } @@ -225,7 +229,7 @@ class User extends Component */ public function getIsGuest() { - return $this->identity === null; + return $this->getIdentity() === null; } /** @@ -238,14 +242,6 @@ class User extends Component } /** - * @param string|integer $value the unique identifier for the user. If null, it means the user is a guest. - */ - public function setId($value) - { - Yii::$app->getSession()->set($this->idVar, $value); - } - - /** * Returns the URL that the user should be redirected to after successful login. * This property is usually used by the login action. If the login is successful, * the action should read this property and use it to redirect the user browser. @@ -400,24 +396,37 @@ class User extends Component } /** - * Changes the current user with the specified identity information. - * This method is called by [[login()]] and [[loginByCookie()]] - * when the current user needs to be associated with the corresponding - * identity information. + * Switches to a new identity for the current user. + * + * This method will save necessary session information to keep track of the user authentication status. + * If `$duration` is provided, it will also send out appropriate identity cookie + * to support cookie-based login. + * + * This method is mainly called by [[login()]], [[logout()]] and [[loginByCookie()]] + * when the current user needs to be associated with the corresponding identity information. + * * @param Identity $identity the identity information to be associated with the current user. + * If null, it means switching to be a guest. + * @param integer $duration number of seconds that the user can remain in logged-in status. + * This parameter is used only when `$identity` is not null. */ - protected function switchIdentity($identity) + public function switchIdentity($identity, $duration = 0) { - Yii::$app->getSession()->regenerateID(true); - $this->identity = $identity; $session = Yii::$app->getSession(); + $session->regenerateID(true); + $this->setIdentity($identity); $session->remove($this->idVar); $session->remove($this->authTimeoutVar); if ($identity instanceof Identity) { - $this->setId($identity->getId()); + $session->set($this->idVar, $identity->getId()); if ($this->authTimeout !== null) { - Yii::$app->getSession()->set($this->authTimeoutVar, time() + $this->authTimeout); + $session->set($this->authTimeoutVar, time() + $this->authTimeout); + } + if ($duration > 0 && $this->enableAutoLogin) { + $this->sendIdentityCookie($identity, $duration); } + } elseif ($this->enableAutoLogin) { + Yii::$app->getResponse()->getCookies()->remove(new Cookie($this->identityCookie)); } } @@ -429,7 +438,7 @@ class User extends Component */ protected function renewAuthStatus() { - if ($this->authTimeout !== null && $this->identity !== null) { + if ($this->authTimeout !== null && !$this->getIsGuest()) { $expire = Yii::$app->getSession()->get($this->authTimeoutVar); if ($expire !== null && $expire < time()) { $this->logout(false); From 92e634db66272b6151392dbef74c1a40baa8437c Mon Sep 17 00:00:00 2001 From: Alexander Makarov Date: Fri, 29 Mar 2013 22:54:45 +0400 Subject: [PATCH 3/4] Ability to configure session cookie, httponly by default --- framework/web/Session.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/framework/web/Session.php b/framework/web/Session.php index 3e0f599..4c0505f 100644 --- a/framework/web/Session.php +++ b/framework/web/Session.php @@ -60,6 +60,13 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co public $flashVar = '__flash'; /** + * @var array parameter-value pairs to override default session cookie parameters + */ + public $cookieParams = array( + 'httponly' => true + ); + + /** * Initializes the application component. * This method is required by IApplicationComponent and is invoked by application. */ @@ -111,6 +118,8 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co ); } + $this->setCookieParams($this->cookieParams); + @session_start(); if (session_id() == '') { From 7d27d65a136ec85fe8d7331444fd35fcd0f1460e Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Fri, 29 Mar 2013 15:04:15 -0400 Subject: [PATCH 4/4] bug fix. --- framework/YiiBase.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/YiiBase.php b/framework/YiiBase.php index 16e237d..261a99e 100644 --- a/framework/YiiBase.php +++ b/framework/YiiBase.php @@ -238,7 +238,7 @@ class YiiBase { if ($path === null) { unset(self::$aliases[$alias]); - } elseif ($path[0] !== '@') { + } elseif (strncmp($path, '@', 1)) { self::$aliases[$alias] = rtrim($path, '\\/'); } else { self::$aliases[$alias] = static::getAlias($path);