Completed
Push — master ( 115248...91d2c9 )
by Henry
78:30 queued 58:40
created

includes/Html/Form.php (2 issues)

Labels
Severity

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
namespace Redaxscript\Html;
3
4
use Redaxscript\Captcha;
5
use Redaxscript\Hash;
6
use Redaxscript\Language;
7
use Redaxscript\Module;
8
use Redaxscript\Registry;
9
10
/**
11
 * children class to create a form
12
 *
13
 * @since 2.6.0
14
 *
15
 * @package Redaxscript
16
 * @category Html
17
 * @author Henry Ruhs
18
 *
19
 * @method $this button(string $text = null, ?array $attributeArray = [])
20
 * @method $this cancel(string $text = null, ?array $attributeArray = [])
21
 * @method $this checkbox(?array $attributeArray = [])
22
 * @method $this color(?array $attributeArray = [])
23
 * @method $this date(?array $attributeArray = [])
24
 * @method $this datetime(?array $attributeArray = [])
25
 * @method $this email(?array $attributeArray = [])
26
 * @method $this file(?array $attributeArray = [])
27
 * @method $this hidden(?array $attributeArray = [])
28
 * @method $this number(?array $attributeArray = [])
29
 * @method $this password(?array $attributeArray = [])
30
 * @method $this radio(?array $attributeArray = [])
31
 * @method $this range(?array $attributeArray = [])
32
 * @method $this reset(string $text = null, ?array $attributeArray = [])
33
 * @method $this search(?array $attributeArray = [])
34
 * @method $this submit(string $text = null, ?array $attributeArray = [])
35
 * @method $this time(?array $attributeArray = [])
36
 * @method $this tel(?array $attributeArray = [])
37
 * @method $this text(?array $attributeArray = [])
38
 * @method $this url(?array $attributeArray = [])
39
 * @method $this week(?array $attributeArray = [])
40
 */
41
42
class Form extends HtmlAbstract
43
{
44
	/**
45
	 * instance of the registry class
46
	 *
47
	 * @var Registry
48
	 */
49
50
	protected $_registry;
51
52
	/**
53
	 * instance of the language class
54
	 *
55
	 * @var Language
56
	 */
57
58
	protected $_language;
59
60
	/**
61
	 * captcha of the form
62
	 *
63
	 * @var object
64
	 */
65
66
	protected $_captcha;
67
68
	/**
69
	 * languages of the form
70
	 *
71
	 * @var array
72
	 */
73
74
	protected $_languageArray =
75
	[
76
		'legend' => 'fields_required',
77
		'button' =>
78
		[
79
			'button' => 'ok',
80
			'reset' => 'reset',
81
			'submit' => 'submit'
82
		],
83
		'link' =>
84
		[
85
			'cancel' => 'cancel'
86
		]
87
	];
88
89
	/**
90
	 * attributes of the form
91
	 *
92
	 * @var array
93
	 */
94
95
	protected $_attributeArray =
96
	[
97
		'form' =>
98
		[
99
			'class' => 'rs-js-validate rs-form-default',
100
			'method' => 'post'
101
		],
102
		'legend' =>
103
		[
104
			'class' => 'rs-legend-default'
105
		],
106
		'label' =>
107
		[
108
			'class' => 'rs-label-default'
109
		],
110
		'select' =>
111
		[
112
			'class' => 'rs-field-select'
113
		],
114
		'textarea' =>
115
		[
116
			'class' => 'rs-js-resize rs-field-textarea',
117
			'cols' => 100,
118
			'rows' => 5
119
		],
120
		'input' =>
121
		[
122
			'checkbox' =>
123
			[
124
				'class' => 'rs-field-checkbox',
125
				'type' => 'checkbox'
126
			],
127
			'color' =>
128
			[
129
				'class' => 'rs-field-color',
130
				'type' => 'color'
131
			],
132
			'date' =>
133
			[
134
				'class' => 'rs-field-default rs-field-date',
135
				'type' => 'date'
136
			],
137
			'datetime' =>
138
			[
139
				'class' => 'rs-field-default rs-field-date',
140
				'type' => 'datetime-local'
141
			],
142
			'email' =>
143
			[
144
				'class' => 'rs-field-default rs-field-email',
145
				'type' => 'email'
146
			],
147
			'file' =>
148
			[
149
				'class' => 'rs-field-file',
150
				'type' => 'file'
151
			],
152
			'hidden' =>
153
			[
154
				'class' => 'rs-field-hidden',
155
				'type' => 'hidden'
156
			],
157
			'number' =>
158
			[
159
				'class' => 'rs-field-default rs-field-number',
160
				'type' => 'number'
161
			],
162
			'password' =>
163
			[
164
				'class' => 'rs-field-default rs-field-password',
165
				'type' => 'password'
166
			],
167
			'radio' =>
168
			[
169
				'class' => 'rs-field-radio',
170
				'type' => 'radio'
171
			],
172
			'range' =>
173
			[
174
				'class' => 'rs-field-range',
175
				'type' => 'range'
176
			],
177
			'search' =>
178
			[
179
				'class' => 'rs-js-search rs-field-search',
180
				'type' => 'search'
181
			],
182
			'tel' =>
183
			[
184
				'class' => 'rs-field-default rs-field-tel',
185
				'type' => 'tel'
186
			],
187
			'time' =>
188
			[
189
				'class' => 'rs-field-default rs-field-date',
190
				'type' => 'time'
191
			],
192
			'text' =>
193
			[
194
				'class' => 'rs-field-default rs-field-text',
195
				'type' => 'text'
196
			],
197
			'url' =>
198
			[
199
				'class' => 'rs-field-default rs-field-url',
200
				'type' => 'url'
201
			],
202
			'week' =>
203
			[
204
				'class' => 'rs-field-default rs-field-date',
205
				'type' => 'week'
206
			]
207
		],
208
		'button' =>
209
		[
210
			'button' =>
211
			[
212
				'class' => 'rs-js-button rs-button-default',
213
				'type' => 'button'
214
			],
215
			'reset' =>
216
			[
217
				'class' => 'rs-js-reset rs-button-default rs-button-reset',
218
				'type' => 'reset'
219
			],
220
			'submit' =>
221
			[
222
				'class' => 'rs-js-button rs-button-default rs-button-submit',
223
				'type' => 'submit',
224
				'value' => 'submit'
225
			]
226
		],
227
		'link' =>
228
		[
229
			'cancel' =>
230
			[
231
				'class' => 'rs-js-cancel rs-button-default rs-button-cancel',
232
				'href' => 'javascript:history.back()'
233
			]
234
		]
235
	];
236
237
	/**
238
	 * options of the form
239
	 *
240
	 * @var array
241
	 */
242
243
	protected $_optionArray =
244
	[
245
		'captcha' => 0
246
	];
247
248
	/**
249
	 * constructor of the class
250
	 *
251
	 * @since 2.6.0
252
	 *
253
	 * @param Registry $registry instance of the registry class
254
	 * @param Language $language instance of the language class
255
	 */
256
257 67
	public function __construct(Registry $registry, Language $language)
258
	{
259 67
		$this->_registry = $registry;
260 67
		$this->_language = $language;
261 67
	}
262
263
	/**
264
	 * call method as needed
265
	 *
266
	 * @since 2.6.0
267
	 *
268
	 * @param string $method name of the method
269
	 * @param array $argumentArray arguments of the method
270
	 *
271
	 * @return self
272
	 */
273
274 47
	public function __call(string $method = null, array $argumentArray = []) : self
275
	{
276
		/* input */
277
278 47
		if (is_array($this->_attributeArray['input']) && array_key_exists($method, $this->_attributeArray['input']))
279
		{
280 37
			return $this->_createInput($method, $argumentArray[0]);
281
		}
282
283
		/* button */
284
285 10
		if (is_array($this->_attributeArray['button']) && array_key_exists($method, $this->_attributeArray['button']))
286
		{
287 6
			return $this->_createButton($method, $argumentArray[0], $argumentArray[1]);
288
		}
289
290
		/* link */
291
292 4
		if (is_array($this->_attributeArray['link']) && array_key_exists($method, $this->_attributeArray['link']))
293
		{
294 2
			return $this->_createLink($method, $argumentArray[0], $argumentArray[1]);
295
		}
296 2
		return $this;
297
	}
298
299
	/**
300
	 * stringify the form
301
	 *
302
	 * @since 2.6.0
303
	 *
304
	 * @return string
305
	 */
306
307 30
	public function __toString() : string
308
	{
309 30
		return $this->render();
310
	}
311
312
	/**
313
	 * init the class
314
	 *
315
	 * @since 2.6.0
316
	 *
317
	 * @param array $attributeArray attributes of the form
318
	 * @param array $optionArray options of the form
319
	 *
320
	 * @return self
321
	 */
322
323 67
	public function init(array $attributeArray = [], array $optionArray = []) : self
324
	{
325 67
		$this->_attributeArray = array_replace_recursive($this->_attributeArray, $attributeArray);
326 67
		$this->_optionArray = array_replace_recursive($this->_optionArray, $optionArray);
327
328
		/* captcha */
329
330 67
		if ($this->_optionArray['captcha'] > 0)
331
		{
332 3
			$this->_captcha = new Captcha($this->_language->getInstance());
333 3
			$this->_captcha->init();
334
		}
335 67
		return $this;
336
	}
337
338
	/**
339
	 * append the legend
340
	 *
341
	 * @since 3.0.0
342
	 *
343
	 * @param string $html html of the legend
344
	 * @param array|null $attributeArray attributes of the legend
345
	 *
346
	 * @return self
347
	 */
348
349 2
	public function legend(string $html = null, ?array $attributeArray = []) : self
350
	{
351 2
		if (is_array($attributeArray))
352
		{
353 2
			$attributeArray = array_merge($this->_attributeArray['legend'], $attributeArray);
354
		}
355
		else
356
		{
357
			$attributeArray = $this->_attributeArray['legend'];
358
		}
359 2
		$legendElement = new Element();
360
		$legendElement
361 2
			->init('legend', $attributeArray)
362 2
			->html($html ? $html : $this->_language->get($this->_languageArray['legend']) . $this->_language->get('point'));
363 2
		$this->append($legendElement);
364 2
		return $this;
365
	}
366
367
	/**
368
	 * append the label
369
	 *
370
	 * @since 3.0.0
371
	 *
372
	 * @param string $html html of the label
373
	 * @param array|null $attributeArray attributes of the label
374
	 *
375
	 * @return self
376
	 */
377
378 3
	public function label(string $html = null, ?array $attributeArray = []) : self
379
	{
380 3
		if (is_array($attributeArray))
381
		{
382 3
			$attributeArray = array_merge($this->_attributeArray['label'], $attributeArray);
383
		}
384
		else
385
		{
386
			$attributeArray = $this->_attributeArray['label'];
387
		}
388 3
		$labelElement = new Element();
389
		$labelElement
390 3
			->init('label', $attributeArray)
391 3
			->html($html);
392 3
		$this->append($labelElement);
393 3
		return $this;
394
	}
395
396
	/**
397
	 * append the textarea
398
	 *
399
	 * @since 2.6.0
400
	 *
401
	 * @param array|null $attributeArray attributes of the textarea
402
	 *
403
	 * @return self
404
	 */
405
406 3
	public function textarea(array $attributeArray = []) : self
407
	{
408 3
		if (is_array($attributeArray))
409
		{
410 3
			$attributeArray = array_merge($this->_attributeArray['textarea'], $attributeArray);
411
		}
412
		else
413
		{
414
			$attributeArray = $this->_attributeArray['textarea'];
415
		}
416 3
		$textareaElement = new Element();
417
		$textareaElement
418 3
			->init('textarea', $attributeArray)
419 3
			->text($attributeArray['value'])
420 3
			->val(null);
421 3
		$this->append($textareaElement);
422 3
		return $this;
423
	}
424
425
	/**
426
	 * append the select
427
	 *
428
	 * @since 2.6.0
429
	 *
430
	 * @param array $optionArray option of the select
431
	 * @param array $selectArray values to be selected
432
	 * @param array|null $attributeArray attributes of the select
433
	 *
434
	 * @return self
435
	 */
436
437 10
	public function select(array $optionArray = [], array $selectArray = [], ?array $attributeArray = []) : self
438
	{
439 10
		if (is_array($attributeArray))
440
		{
441 10
			$attributeArray = array_merge($this->_attributeArray['select'], $attributeArray);
442
		}
443
		else
444
		{
445
			$attributeArray = $this->_attributeArray['select'];
446
		}
447 10
		$selectElement = new Element();
448
		$selectElement
449 10
			->init('select', $attributeArray)
450 10
			->html($this->_createOption($optionArray, $selectArray));
451 10
		$this->append($selectElement);
452 10
		return $this;
453
	}
454
455
	/**
456
	 * append the select range
457
	 *
458
	 * @since 3.0.0
459
	 *
460
	 * @param array $rangeArray range of the select
461
	 * @param array $selectArray values to be selected
462
	 * @param array|null $attributeArray attributes of the select
463
	 *
464
	 * @return self
465
	 */
466
467 4
	public function selectRange(array $rangeArray = [], array $selectArray = [], ?array $attributeArray = []) : self
468
	{
469 4
		$this->select(range($rangeArray['min'], $rangeArray['max']), $selectArray, $attributeArray);
470 4
		return $this;
471
	}
472
473
	/**
474
	 * append the captcha
475
	 *
476
	 * @since 2.6.0
477
	 *
478
	 * @param string $type type of the captcha
479
	 *
480
	 * @return self
481
	 */
482
483 2
	public function captcha(string $type = null) : self
484
	{
485
		/* task */
486
487 2
		if ($this->_optionArray['captcha'] > 0 && $type === 'task')
488
		{
489 1
			$this->label('* ' . $this->_captcha->getTask(),
490
			[
491 1
				'for' => 'task'
492
			]);
493
494
			/* number */
495
496 1
			$this->number(
497
			[
498 1
				'id' => 'task',
499 1
				'min' => $this->_captcha->getMin(),
500 1
				'max' => $this->_captcha->getMax() * 2,
501 1
				'name' => 'task',
502 1
				'required' => 'required'
503
			]);
504
		}
505
506
		/* solution */
507
508 2
		if ($this->_optionArray['captcha'] > 0 && $type === 'solution')
509
		{
510 1
			$captchaHash = new Hash();
511 1
			$captchaHash->init($this->_captcha->getSolution());
512
513
			/* hidden */
514
515 1
			$this->hidden(
516
			[
517 1
				'name' => 'solution',
518 1
				'value' => $captchaHash->getHash()
519
			]);
520
		}
521 2
		return $this;
522
	}
523
524
	/**
525
	 * append the token
526
	 *
527
	 * @since 2.6.0
528
	 *
529
	 * @return self
530
	 */
531
532 2
	public function token() : self
533
	{
534 2
		$token = $this->_registry->get('token');
535 2
		if ($token)
536
		{
537 1
			$this->hidden(
538
			[
539 1
				'name' => 'token',
540 1
				'value' => $token
541
			]);
542
		}
543 2
		return $this;
544
	}
545
546
	/**
547
	 * render the form
548
	 *
549
	 * @since 2.6.0
550
	 *
551
	 * @return string
552
	 */
553
554 67
	public function render() : string
555
	{
556 67
		$output = Module\Hook::trigger('formStart');
557 67
		$formElement = new Element();
558 67
		$formElement->init('form', $this->_attributeArray['form']);
559
560
		/* collect output */
561
562 67
		$output .= $formElement->html($this->_html);
563 67
		$output .= Module\Hook::trigger('formEnd');
564 67
		return $output;
565
	}
566
567
	/**
568
	 * create the input
569
	 *
570
	 * @since 2.6.0
571
	 *
572
	 * @param string $type type of the input
573
	 * @param array|null $attributeArray attributes of the input
574
	 *
575
	 * @return self
576
	 */
577
578 37
	protected function _createInput(string $type = 'text', ?array $attributeArray = []) : self
579
	{
580 37
		if (is_array($attributeArray))
581
		{
582
583 37
			$attributeArray = array_merge($this->_attributeArray['input'][$type], $attributeArray);
584
		}
585
		else
586
		{
587
			$attributeArray = $this->_attributeArray['input'][$type];
588
		}
589 37
		$inputElement = new Element();
590 37
		$inputElement->init('input', $attributeArray);
591 37
		$this->append($inputElement);
592 37
		return $this;
593
	}
594
595
	/**
596
	 * create the option
597
	 *
598
	 * @since 3.0.0
599
	 *
600
	 * @param array $optionArray option of the select
601
	 * @param array $selectArray values to be selected
602
	 *
603
	 * @return string|null
604
	 */
605
606 10
	protected function _createOption(array $optionArray = [], array $selectArray = []) : ?string
607
	{
608 10
		$output = null;
609 10
		$optionElement = new Element();
610 10
		$optionElement->init('option');
611
612
		/* process values */
613
614 10
		foreach ($optionArray as $key => $value)
615
		{
616 8
			if ($key || $value)
617
			{
618
				$output .= $optionElement
619 6
					->copy()
620 6
					->attr(
621
					[
622 6
						'selected' => is_array($selectArray) && in_array($value, $selectArray) ? 'selected' : null,
623 6
						'value' => $value
624
					])
625 8
					->text(is_string($key) ? $key : $value);
626
			}
627
		}
628 10
		return $output;
629
	}
630
631
	/**
632
	 * create the button
633
	 *
634
	 * @since 2.6.0
635
	 *
636
	 * @param string $type type of the button
637
	 * @param string $text text of the button
638
	 * @param array|null $attributeArray attributes of the button
639
	 *
640
	 * @return self
641
	 */
642
643 6
	protected function _createButton(string $type = null, string $text = null, ?array $attributeArray = []) : self
644
	{
645 6
		if (is_array($attributeArray))
646
		{
647 6
			$attributeArray = array_merge($this->_attributeArray['button'][$type], $attributeArray);
648
		}
649
		else
650
		{
651
			$attributeArray = $this->_attributeArray['button'][$type];
652
		}
653 6
		$buttonElement = new Element();
654
		$buttonElement
655 6
			->init('button', $attributeArray)
656 6
			->text($text ? $text : $this->_language->get($this->_languageArray['button'][$type]));
0 ignored issues
show
It seems like $text ? $text : $this->_...Array['button'][$type]) can also be of type array; however, Redaxscript\Html\Element::text() does only seem to accept string|integer|null, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
657 6
		$this->append($buttonElement);
658 6
		return $this;
659
	}
660
661
	/**
662
	 * create the link
663
	 *
664
	 * @since 3.0.0
665
	 *
666
	 * @param string $type type of the link
667
	 * @param string $text text of the link
668
	 * @param array|null $attributeArray attributes of the link
669
	 *
670
	 * @return self
671
	 */
672
673 2
	protected function _createLink(string $type = null, string $text = null, ?array $attributeArray = []) : self
674
	{
675 2
		if (is_array($attributeArray))
676
		{
677 2
			$attributeArray = array_merge($this->_attributeArray['link'][$type], $attributeArray);
678
		}
679
		else
680
		{
681
			$attributeArray = $this->_attributeArray['link'][$type];
682
		}
683 2
		$linkElement = new Element();
684
		$linkElement
685 2
			->init('a', $attributeArray)
686 2
			->text($text ? $text : $this->_language->get($this->_languageArray['link'][$type]));
0 ignored issues
show
It seems like $text ? $text : $this->_...geArray['link'][$type]) can also be of type array; however, Redaxscript\Html\Element::text() does only seem to accept string|integer|null, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
687 2
		$this->append($linkElement);
688 2
		return $this;
689
	}
690
}
691