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

includes/Html/Form.php (2 issues)

call_checks.maybe_mismatching_type_passed

Bug Minor

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