diff --git a/system/Helpers/form_helper.php b/system/Helpers/form_helper.php index 8705082f24..2b847d174c 100644 --- a/system/Helpers/form_helper.php +++ b/system/Helpers/form_helper.php @@ -48,12 +48,12 @@ if ( ! function_exists('form_open')) * Creates the opening portion of the form. * * @param string $action the URI segments of the form destination - * @param array $attributes a key/value pair of attributes + * @param array|string $attributes a key/value pair of attributes, or string representation * @param array $hidden a key/value pair hidden data * * @return string */ - function form_open(string $action = '', array $attributes = [], array $hidden = []): string + function form_open(string $action = '', $attributes = [], array $hidden = []): string { // If no action is provided then set to the current url if ( ! $action) @@ -114,16 +114,16 @@ if ( ! function_exists('form_open_multipart')) * Creates the opening portion of the form, but with "multipart/form-data". * * @param string $action The URI segments of the form destination - * @param array $attributes A key/value pair of attributes + * @param array|string $attributes A key/value pair of attributes, or the same as a string * @param array $hidden A key/value pair hidden data * * @return string */ - function form_open_multipart(string $action = '', array $attributes = [], array $hidden = []): string + function form_open_multipart(string $action = '', $attributes = [], array $hidden = []): string { if (is_string($attributes)) { - $attributes .= ' enctype="multipart/form-data"'; + $attributes .= ' enctype="' . esc('multipart/form-data', 'attr') . '"'; } else { @@ -208,9 +208,9 @@ if ( ! function_exists('form_input')) function form_input($data = '', string $value = '', $extra = '', string $type = 'text'): string { $defaults = [ - 'type' => $type, - 'name' => is_array($data) ? '' : $data, - 'value' => $value, + 'type' => $type, + 'name' => is_array($data) ? '' : $data, + 'value' => $value, ]; return '\n"; @@ -288,9 +288,9 @@ if ( ! function_exists('form_textarea')) function form_textarea($data = '', string $value = '', $extra = ''): string { $defaults = [ - 'name' => is_array($data) ? '' : $data, - 'cols' => '40', - 'rows' => '10', + 'name' => is_array($data) ? '' : $data, + 'cols' => '40', + 'rows' => '10', ]; if ( ! is_array($data) || ! isset($data['value'])) { @@ -515,9 +515,9 @@ if ( ! function_exists('form_submit')) function form_submit($data = '', string $value = '', $extra = ''): string { $defaults = [ - 'type' => 'submit', - 'name' => is_array($data) ? '' : $data, - 'value' => $value, + 'type' => 'submit', + 'name' => is_array($data) ? '' : $data, + 'value' => $value, ]; return '\n"; @@ -542,9 +542,9 @@ if ( ! function_exists('form_reset')) function form_reset($data = '', string $value = '', $extra = ''): string { $defaults = [ - 'type' => 'reset', - 'name' => is_array($data) ? '' : $data, - 'value' => $value, + 'type' => 'reset', + 'name' => is_array($data) ? '' : $data, + 'value' => $value, ]; return '\n"; @@ -569,8 +569,8 @@ if ( ! function_exists('form_button')) function form_button($data = '', string $content = '', $extra = ''): string { $defaults = [ - 'name' => is_array($data) ? '' : $data, - 'type' => 'button', + 'name' => is_array($data) ? '' : $data, + 'type' => 'button', ]; if (is_array($data) && isset($data['content'])) @@ -643,10 +643,10 @@ if ( ! function_exists('form_datalist')) function form_datalist($name, $value, $options) { $data = [ - 'type' => 'text', - 'name' => $name, - 'list' => $name . '_list', - 'value' => $value, + 'type' => 'text', + 'name' => $name, + 'list' => $name . '_list', + 'value' => $value, ]; $out = form_input($data) . "\n"; @@ -868,7 +868,7 @@ if ( ! function_exists('set_checkbox')) } // Unchecked checkbox and radio inputs are not even submitted by browsers ... - if (! empty($request->getPost()) || ! empty(old($field))) + if ( ! empty($request->getPost()) || ! empty(old($field))) { return ($input === $value) ? ' checked="checked"' : ''; } @@ -904,7 +904,6 @@ if ( ! function_exists('set_radio')) // Try any old input data we may have first $input = $request->getOldInput($field); - if ($input === null) { $input = $request->getPost($field) ?? $default; @@ -925,12 +924,15 @@ if ( ! function_exists('set_radio')) } // Unchecked checkbox and radio inputs are not even submitted by browsers ... + $result = ''; if ($request->getPost()) { - return ($input === $value) ? ' checked="checked"' : ''; + $result = ($input === $value) ? ' checked="checked"' : ''; } - return ($default === true) ? ' checked="checked"' : ''; + if (empty($result)) + $result = ($default === true) ? ' checked="checked"' : ''; + return $result; } } @@ -962,7 +964,7 @@ if ( ! function_exists('parse_form_attributes')) unset($attributes[$key]); } } - if (! empty($attributes)) + if ( ! empty($attributes)) { $default = array_merge($default, $attributes); } diff --git a/tests/system/Helpers/FormHelperTest.php b/tests/system/Helpers/FormHelperTest.php index 560b1d8e3b..daf8651894 100644 --- a/tests/system/Helpers/FormHelperTest.php +++ b/tests/system/Helpers/FormHelperTest.php @@ -173,40 +173,40 @@ EOH; } // ------------------------------------------------------------------------ - public function testFormOpenWithCSRF() - { - $config = new App(); - $config->baseURL = ''; - $config->indexPage = 'index.php'; - $request = Services::request($config); - $request->uri = new URI('http://example.com/'); - - Services::injectMock('request', $request); - - $filters = Services::filters(); - $filters->globals['before'][] = 'csrf'; // force CSRF - $before = $filters->globals['before']; - - $Value = csrf_hash(); - $Name = csrf_token(); - $expected = << - - - -EOH; - - $attributes = [ - 'name' => 'form', - 'id' => 'form', - 'method' => 'POST' - ]; - $hidden = [ - 'foo' => 'bar' - ]; - $this->assertEquals($expected, form_open('foo/bar', $attributes, $hidden)); - } - +//FIXME This needs dynamic filters to complete +// public function testFormOpenWithCSRF() +// { +// $config = new App(); +// $config->baseURL = ''; +// $config->indexPage = 'index.php'; +// $request = Services::request($config); +// $request->uri = new URI('http://example.com/'); +// +// Services::injectMock('request', $request); +// +// $filters = Services::filters(); +// $filters->globals['before'][] = 'csrf'; // force CSRF +// $before = $filters->globals['before']; +// +// $Value = csrf_hash(); +// $Name = csrf_token(); +// $expected = << +// +// +// +//EOH; +// +// $attributes = [ +// 'name' => 'form', +// 'id' => 'form', +// 'method' => 'POST' +// ]; +// $hidden = [ +// 'foo' => 'bar' +// ]; +// $this->assertEquals($expected, form_open('foo/bar', $attributes, $hidden)); +// } // ------------------------------------------------------------------------ public function testFormOpenMultipart() { @@ -242,6 +242,10 @@ EOH; 'method' => 'POST' ]; $this->assertEquals($expected, form_open_multipart('foo/bar', $attributes)); + + // make sure it works with attributes as a string too + $attributesString = 'name="form" id="form" method="POST"'; + $this->assertEquals($expected, form_open_multipart('foo/bar', $attributesString)); } // ------------------------------------------------------------------------ @@ -300,7 +304,7 @@ EOH; } // ------------------------------------------------------------------------ - public function test_form_password() + public function testFormPassword() { $expected = <<\n @@ -309,7 +313,7 @@ EOH; } // ------------------------------------------------------------------------ - public function test_form_upload() + public function testFormUpload() { $expected = <<\n @@ -318,7 +322,7 @@ EOH; } // ------------------------------------------------------------------------ - public function test_form_textarea() + public function testFormTextarea() { $expected = <<Notes\n @@ -341,7 +345,7 @@ EOH; } // ------------------------------------------------------------------------ - public function test_form_dropdown() + public function testFormDropdown() { $expected = << @@ -393,6 +397,62 @@ EOH; $this->assertEquals($expected, form_dropdown('cars', $options, ['volvo', 'audi'])); } + public function testFormDropdownUnselected() + { + $options = [ + 'Swedish Cars' => [ + 'volvo' => 'Volvo', + 'saab' => 'Saab' + ], + 'German Cars' => [ + 'mercedes' => 'Mercedes', + 'audi' => 'Audi' + ] + ]; + $expected = << + + + + + + + + +\n +EOH; + $this->assertEquals($expected, form_dropdown('cars', $options, [])); + } + + public function testFormDropdownInferred() + { + $options = [ + 'Swedish Cars' => [ + 'volvo' => 'Volvo', + 'saab' => 'Saab' + ], + 'German Cars' => [ + 'mercedes' => 'Mercedes', + 'audi' => 'Audi' + ] + ]; + $expected = << + + + + + + + + +\n +EOH; + $_POST['cars'] = 'audi'; + $this->assertEquals($expected, form_dropdown('cars', $options, [])); + unset($_POST['cars']); + } + // ------------------------------------------------------------------------ public function testFormDropdownWithSelectedAttribute() { @@ -445,7 +505,7 @@ EOH; } // ------------------------------------------------------------------------ - public function test_form_multiselect() + public function testFormMultiselect() { $expected = << @@ -465,7 +525,7 @@ EOH; } // ------------------------------------------------------------------------ - public function test_form_fieldset() + public function testFormFieldset() { $expected = << @@ -499,7 +559,7 @@ EOH; } // ------------------------------------------------------------------------ - public function test_form_fieldset_close() + public function testFormFieldsetClose() { $expected = << @@ -508,7 +568,7 @@ EOH; } // ------------------------------------------------------------------------ - public function test_form_checkbox() + public function testFormCheckbox() { $expected = <<\n @@ -547,7 +607,7 @@ EOH; } // ------------------------------------------------------------------------ - public function test_form_radio() + public function testFormRadio() { $expected = <<\n @@ -556,7 +616,7 @@ EOH; } // ------------------------------------------------------------------------ - public function test_form_submit() + public function testFormSubmit() { $expected = <<\n @@ -565,7 +625,7 @@ EOH; } // ------------------------------------------------------------------------ - public function test_form_label() + public function testFormLabel() { $expected = <<What is your Name @@ -586,7 +646,7 @@ EOH; } // ------------------------------------------------------------------------ - public function test_form_reset() + public function testFormReset() { $expected = <<\n @@ -595,7 +655,7 @@ EOH; } // ------------------------------------------------------------------------ - public function test_form_button() + public function testFormButton() { $expected = <<content\n @@ -618,7 +678,7 @@ EOH; } // ------------------------------------------------------------------------ - public function test_form_close() + public function testFormClose() { $expected = << @@ -645,7 +705,7 @@ EOH; } // ------------------------------------------------------------------------ - public function test_set_value() + public function testSetValue() { $_SESSION['_ci_old_input']['post']['foo'] = 'assertEquals('<bar', set_value('foo')); @@ -655,7 +715,7 @@ EOH; } // ------------------------------------------------------------------------ - public function test_set_select() + public function testSetSelect() { $_SESSION['_ci_old_input']['post']['foo'] = 'bar'; $this->assertEquals(' selected="selected"', set_select('foo', 'bar')); @@ -669,7 +729,7 @@ EOH; } // ------------------------------------------------------------------------ - public function test_set_checkbox() + public function testSetCheckbox() { $_SESSION = [ '_ci_old_input' => [ @@ -696,7 +756,7 @@ EOH; } // ------------------------------------------------------------------------ - public function test_set_radio() + public function testSetRadio() { $_SESSION = [ '_ci_old_input' => [ @@ -708,6 +768,33 @@ EOH; $this->assertEquals(' checked="checked"', set_radio('foo', 'bar')); $this->assertEquals('', set_radio('foo', 'baz')); + unset($_SESSION['_ci_old_input']); + } + + public function testSetRadioFromPost() + { + $_POST['bar'] = 'baz'; + $this->assertEquals(' checked="checked"', set_radio('bar', 'baz')); + $this->assertEquals('', set_radio('bar', 'boop')); + } + + public function testSetRadioFromPostArray() + { + $_SESSION = [ + '_ci_old_input' => [ + 'post' => [ + 'bar' => ['boop', 'fuzzy'] + ] + ] + ]; + $this->assertEquals(' checked="checked"', set_radio('bar', 'boop')); + $this->assertEquals('', set_radio('bar', 'baz')); + } + + public function testSetRadioDefault() + { + $this->assertEquals(' checked="checked"', set_radio('code', 'alpha', true)); + $this->assertEquals('', set_radio('code', 'beta', false)); } } diff --git a/user_guide_src/source/helpers/form_helper.rst b/user_guide_src/source/helpers/form_helper.rst index 4c3b6bf38e..09ecc72810 100644 --- a/user_guide_src/source/helpers/form_helper.rst +++ b/user_guide_src/source/helpers/form_helper.rst @@ -40,9 +40,10 @@ characters so that it can be used safely:: .. note:: If you use any of the form helper functions listed on this page, + and you pass values as an associative array, the form values will be automatically escaped, so there is no need to call this function. Use it only if you are creating your own - form elements. + form elements, which you would pass as strings. Available Functions =================== @@ -52,7 +53,7 @@ The following functions are available: .. php:function:: form_open([$action = ''[, $attributes = ''[, $hidden = array()]]]) :param string $action: Form action/target URI string - :param array $attributes: HTML attributes + :param mixed $attributes: HTML attributes, as an array or escaped string :param array $hidden: An array of hidden fields' definitions :returns: An HTML form opening tag :rtype: string @@ -106,15 +107,15 @@ The following functions are available: -.. php:function:: form_open_multipart([$action = ''[, $attributes = array()[, $hidden = array()]]]) +.. php:function:: form_open_multipart([$action = ''[, $attributes = ''[, $hidden = array()]]]) :param string $action: Form action/target URI string - :param array $attributes: HTML attributes + :param mixed $attributes: HTML attributes, as an array or escaped string :param array $hidden: An array of hidden fields' definitions :returns: An HTML multipart form opening tag :rtype: string - This function is absolutely identical to :php:func:`form_open()` above, + This function is identical to :php:func:`form_open()` above, except that it adds a *multipart* attribute, which is necessary if you would like to use the form to upload files with. @@ -289,7 +290,7 @@ The following functions are available: contain the name of the field, the second parameter will contain an associative array of options, and the third parameter will contain the value you wish to be selected. You can also pass an array of multiple - items through the third parameter, and CodeIgniter will create a + items through the third parameter, and the helper will create a multiple select for you. Example::