Passed
Pull Request — master (#172)
by Wilmer
02:51 queued 28s
created

Form::legendAttributes()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 3
nc 1
nop 1
dl 0
loc 5
ccs 4
cts 4
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Form\Widget;
6
7
use InvalidArgumentException;
8
use Stringable;
9
use Yiisoft\Html\Html;
10
use Yiisoft\Html\Tag\CustomTag;
11
use Yiisoft\Http\Method;
12
use Yiisoft\Widget\Widget;
13
14
use function explode;
15
use function implode;
16
use function strpos;
17
use function substr;
18
use function urldecode;
19
20
/**
21
 *  Generates a form start tag.
22
 *
23
 *  @link https://www.w3.org/TR/html52/sec-forms.html
24
 */
25
final class Form extends Widget
26
{
27
    private string $action = '';
28
    private array $attributes = [];
29
    private string $csrfName = '';
30
    private string $csrfToken = '';
31
    private bool $fieldset = false;
32
    private array $fieldsetAttributes = [];
33
    private ?string $legend = null;
34
    private array $legendAttributes = [];
35
    private string $id = '';
36
    private string $method = Method::POST;
37
38
    /**
39
     * @return string the generated form start tag.
40
     *
41
     * {@see end())}
42
     */
43 20
    public function begin(): string
44
    {
45 20
        parent::begin();
46
47 20
        $attributes = $this->attributes;
48 20
        $action = $this->action;
49 20
        $hiddenInputs = [];
50
51 20
        if (!array_key_exists('id', $attributes) && $this->id !== '') {
52 2
            $attributes['id'] = $this->id;
53
        }
54
55 20
        if ($this->csrfToken !== '' && $this->method === Method::POST) {
56 2
            $hiddenInputs[] = Html::hiddenInput($this->csrfName, $this->csrfToken);
57
        }
58
59 20
        if ($this->method === Method::GET && ($pos = strpos($action, '?')) !== false) {
60
            /**
61
             * Query parameters in the action are ignored for GET method we use hidden fields to add them back.
62
             */
63 1
            foreach (explode('&', substr($action, $pos + 1)) as $pair) {
64 1
                if (($pos1 = strpos($pair, '=')) !== false) {
65 1
                    $hiddenInputs[] = Html::hiddenInput(
66 1
                        urldecode(substr($pair, 0, $pos1)),
67 1
                        urldecode(substr($pair, $pos1 + 1))
68
                    );
69
                } else {
70 1
                    $hiddenInputs[] = Html::hiddenInput(urldecode($pair), '');
71
                }
72
            }
73
74 1
            $action = substr($action, 0, $pos);
75
        }
76
77 20
        if ($action !== '') {
78 7
            $attributes['action'] = $action;
79
        }
80
81 20
        $attributes['method'] = $this->method;
82
83 20
        if ($this->csrfToken !== '') {
84
            /** @var string */
85 3
            $attributes[$this->csrfName] = $this->csrfToken;
86
        }
87
88 20
        $form = Html::openTag('form', $attributes);
89
90 20
        if ($this->fieldset) {
91 4
            $form .= PHP_EOL . Html::openTag('fieldset', $this->fieldsetAttributes);
92
        }
93
94 20
        if ($this->legend !== null) {
95 2
            $form .= PHP_EOL . CustomTag::name('legend')
96 2
                ->attributes($this->legendAttributes)
97 2
                ->content($this->legend)
98 2
                ->render();
99
        }
100
101 20
        if (!empty($hiddenInputs)) {
102 3
            $form .= PHP_EOL . implode(PHP_EOL, $hiddenInputs);
103
        }
104
105 20
        return $form;
106
    }
107
108
    /**
109
     * The accept-charset content attribute gives the character encodings that are to be used for the submission.
110
     * If specified, the value must be an ordered set of unique space-separated tokens that are ASCII case-insensitive,
111
     * and each token must be an ASCII case-insensitive match for one of the labels of an ASCII-compatible encoding.
112
     *
113
     * @param string $value the accept-charset attribute value.
114
     *
115
     * @return static
116
     *
117
     * @link https://www.w3.org/TR/html52/sec-forms.html#element-attrdef-form-accept-charset
118
     */
119 2
    public function acceptCharset(string $value): self
120
    {
121 2
        $new = clone $this;
122 2
        $new->attributes['accept-charset'] = $value;
123 2
        return $new;
124
    }
125
126
    /**
127
     * The action and formaction content attributes, if specified, must have a value that is a valid non-empty URL
128
     * potentially surrounded by spaces.
129
     *
130
     * @param string $value the action attribute value.
131
     *
132
     * @return static
133
     *
134
     * @link https://www.w3.org/TR/html52/sec-forms.html#element-attrdef-form-action
135
     */
136 10
    public function action(string $value): self
137
    {
138 10
        $new = clone $this;
139 10
        $new->action = $value;
140 10
        return $new;
141
    }
142
143
    /**
144
     * The HTML attributes. The following special options are recognized.
145
     *
146
     * @param array $value
147
     *
148
     * @return static
149
     *
150
     * See {@see \Yiisoft\Html\Html::renderTagAttributes()} for details on how attributes are being rendered.
151
     */
152 3
    public function attributes(array $value): self
153
    {
154 3
        $new = clone $this;
155 3
        $new->attributes = $value;
156 3
        return $new;
157
    }
158
159
    /**
160
     * Specifies whether the element represents an input control for which a UA is meant to store the value entered by
161
     * the user (so that the UA can prefill the form later).
162
     *
163
     * @param bool $value
164
     *
165
     * @return static
166
     *
167
     * @link https://www.w3.org/TR/html52/sec-forms.html#element-attrdef-autocompleteelements-autocomplete
168
     */
169 2
    public function autocomplete(bool $value = true): self
170
    {
171 2
        $new = clone $this;
172 2
        $new->attributes['autocomplete'] = $value ? 'on' : 'off';
173 2
        return $new;
174
    }
175
176
    /**
177
     * The CSRF-token content attribute token that are known to be safe to use for.
178
     *
179
     * @param mixed|string|Stringable $csrfToken the CSRF-token attribute value.
180
     * @param string $csrfName the CSRF-token attribute name.
181
     *
182
     * @return static
183
     */
184 8
    public function csrf($csrfToken, string $csrfName = '_csrf'): self
185
    {
186 8
        $new = clone $this;
187
188 8
        if (is_string($csrfToken) || (is_object($csrfToken) && method_exists($csrfToken, '__toString'))) {
189 6
            $new->csrfToken = (string) $csrfToken;
190
        } else {
191 2
            throw new InvalidArgumentException('$csrfToken must be a string or \Stringable object.');
192
        }
193
194 6
        $new->csrfName = $csrfName;
195 6
        return $new;
196
    }
197
198
    /**
199
     * The formenctype content attribute specifies the content type of the form submission.
200
     *
201
     * @param string $value the formenctype attribute value.
202
     *
203
     * @return static
204
     *
205
     * @link https://www.w3.org/TR/html52/sec-forms.html#element-attrdef-form-enctype
206
     */
207 2
    public function enctype(string $value): self
208
    {
209 2
        $new = clone $this;
210 2
        $new->id = $value;
211 2
        return $new;
212
    }
213
214
    /**
215
     * The <fieldset> HTML element is used to group several controls as well as labels (<label>) within a web form.
216
     *
217
     * @param bool $value whether the fieldset is enabled or disabled.
218
     *
219
     * @return static
220
     *
221
     * @link https://html.spec.whatwg.org/multipage/form-elements.html#the-fieldset-element
222
     */
223 5
    public function fieldset(bool $value): self
224
    {
225 5
        $new = clone $this;
226 5
        $new->fieldset = $value;
227 5
        return $new;
228
    }
229
230
    /**
231
     * The HTML attributes. The following special options are recognized.
232
     *
233
     * @param array $values Attribute values indexed by attribute names.
234
     *
235
     * @return static
236
     *
237
     * See {@see \Yiisoft\Html\Html::renderTagAttributes()} for details on how attributes are being rendered.
238
     */
239 2
    public function fieldsetAttributes(array $values): self
240
    {
241 2
        $new = clone $this;
242 2
        $new->fieldsetAttributes = $values;
243 2
        return $new;
244
    }
245
246
    /**
247
     * The id content attribute is a unique identifier for the element.
248
     *
249
     * @param string $value the id attribute value.
250
     *
251
     * @return static
252
     */
253 2
    public function id(string $value): self
254
    {
255 2
        $new = clone $this;
256 2
        $new->id = $value;
257 2
        return $new;
258
    }
259
260
    /**
261
     * The <legend> HTML element represents a caption for the content of its parent <fieldset>.
262
     *
263
     * @param string|null $value whether the legend is enabled or disabled.
264
     *
265
     * @return static
266
     *
267
     * @link https://html.spec.whatwg.org/multipage/form-elements.html#the-legend-element
268
     */
269 3
    public function legend(?string $value): self
270
    {
271 3
        $new = clone $this;
272 3
        $new->legend = $value;
273 3
        return $new;
274
    }
275
276
    /**
277
     * The HTML attributes. The following special options are recognized.
278
     *
279
     * @param array $values Attribute values indexed by attribute names.
280
     *
281
     * @return static
282
     *
283
     * See {@see \Yiisoft\Html\Html::renderTagAttributes()} for details on how attributes are being rendered.
284
     */
285 2
    public function legendAttributes(array $values): self
286
    {
287 2
        $new = clone $this;
288 2
        $new->legendAttributes = $values;
289 2
        return $new;
290
    }
291
292
    /**
293
     * The method content attribute specifies how the form-data should be submitted.
294
     *
295
     * @param string $value the method attribute value.
296
     *
297
     * @return static
298
     *
299
     * @link https://www.w3.org/TR/html52/sec-forms.html#element-attrdef-form-method
300
     */
301 8
    public function method(string $value): self
302
    {
303 8
        $new = clone $this;
304 8
        $new->method = strtoupper($value);
305 8
        return $new;
306
    }
307
308
    /**
309
     * The novalidate and formnovalidate content attributes are boolean attributes. If present, they indicate that the
310
     * form is not to be validated during submission.
311
     *
312
     * @return static
313
     *
314
     * @link https://www.w3.org/TR/html52/sec-forms.html#element-attrdef-form-novalidate
315
     */
316 2
    public function noHtmlValidation(): self
317
    {
318 2
        $new = clone $this;
319 2
        $new->attributes['novalidate'] = true;
320 2
        return $new;
321
    }
322
323
    /**
324
     * The target and formtarget content attributes, if specified, must have values that are valid browsing context
325
     * names or keywords.
326
     *
327
     * @param string $value the target attribute value, for default its `_blank`.
328
     *
329
     * @return static
330
     *
331
     * @link https://www.w3.org/TR/html52/sec-forms.html#element-attrdef-form-target
332
     */
333 2
    public function target(string $value): self
334
    {
335 2
        $new = clone $this;
336 2
        $new->attributes['target'] = $value;
337 2
        return $new;
338
    }
339
340
    /**
341
     * Generates a form end tag.
342
     *
343
     * @return string the generated tag.
344
     *
345
     * {@see beginForm()}
346
     */
347 5
    protected function run(): string
348
    {
349 5
        $html = '';
350
351 5
        if ($this->fieldset) {
352 4
            $html .= Html::closeTag('fieldset') . PHP_EOL;
353
        }
354
355 5
        return $html . Html::closeTag('form');
356
    }
357
}
358