depage-forms v2.0.0
html forms made easy
Loading...
Searching...
No Matches
HtmlForm.php
Go to the documentation of this file.
1<?php
2
14
64
65namespace Depage\HtmlForm;
66
67
73function autoload($class)
74{
75 $class = str_replace('\\', '/', str_replace(__NAMESPACE__ . '\\', '', $class));
76 $filePath = __DIR__ . '/' . $class . '.php';
77 $filePath = str_replace('..', '', $filePath);
78
79 $resolved = realpath($filePath);
80 if ($resolved !== false && file_exists($resolved)) {
81 require_once($resolved);
82 }
83}
84
85spl_autoload_register(__NAMESPACE__ . '\autoload');
86
132{
133 public const priorityCountries = [
134 'en' => ['us','gb','ie','au','nz'],
135 'de' => ['de','at','ch'],
136 'fr' => ['fr','ch','be','lu','ca'],
137 'it' => ['it','ch'],
138 ];
142 protected $method;
143
147 protected $url;
148
152 protected $submitURL;
153
157 protected $successURL;
158
162 protected $cancelURL;
163
167 protected $label;
168
172 protected $backLabel;
173
177 protected $cancelLabel;
178
182 protected $class;
183
187 protected $validator;
188
192 protected $jsValidation;
193
197 protected $jsAutosave;
198
203
207 protected $sessionSlot;
208
212 private $currentStepId;
213
217 private $steps = [];
218
222 protected $ttl;
223
227 public $valid;
228
232 public $isAutoSaveRequest = false;
233
237 protected $internalFields = [
238 'formIsValid',
239 'formIsAutosaved',
240 'formName',
241 'formTimestamp',
242 'formStep',
243 'formFinalPost',
244 'formCsrfToken',
245 'formCaptcha',
246 ];
247
251 protected $namespaces = ['\\Depage\\HtmlForm\\Elements'];
260 public function __construct(string $name, array $parameters = [], HtmlForm|null $form = null)
261 {
262 // workaround for crash with gettext when LANG is not set in the environment
263 // gettext is used in default settings of some input elements (e,g, error messages)
264 if (empty(getenv('LANG'))) {
265 $fallback = getenv('LANGUAGE') ?: 'en_US.UTF-8';
266 putenv('LANG=' . $fallback);
267 }
268 $this->isAutoSaveRequest = isset($_POST['formAutosave']) && $_POST['formAutosave'] === "true";
269
270 parent::__construct($name, $parameters, $this);
271
272 $this->url = parse_url($this->submitURL);
273 if (!empty($this->successURL)) {
274 $this->validateRedirectUrl($this->successURL, true);
275 }
276 if (!empty($this->cancelURL)) {
277 $this->validateRedirectUrl($this->cancelURL, true);
278 }
279 if (empty($this->successURL)) {
280 $this->successURL = $this->submitURL;
281 }
282 if (empty($this->cancelURL)) {
283 $this->cancelURL = $this->submitURL;
284 }
285
286 $this->currentStepId = isset($_GET['step']) ? $_GET['step'] : 0;
287
288 $this->startSession();
289
290 $this->valid = (isset($this->sessionSlot['formIsValid'])) ? $this->sessionSlot['formIsValid'] : null;
291
292 // set CSRF Token
293 if (!isset($this->sessionSlot['formCsrfToken'])) {
294 $this->sessionSlot['formCsrfToken'] = $this->getNewCsrfToken();
295 }
296
297 if (!isset($this->sessionSlot['formFinalPost'])) {
298 $this->sessionSlot['formFinalPost'] = false;
299 }
300
301 // create a hidden input to tell forms apart
302 $this->addHidden('formName')->setValue($this->name);
303
304 // create hidden input for submitted step
305 $this->addHidden('formStep')->setValue($this->currentStepId);
306
307 // create hidden input for CSRF token
308 $this->addHidden('formCsrfToken')->setValue($this->sessionSlot['formCsrfToken']);
309
310 $this->addChildElements();
311 }
312
321 protected function setDefaults(): void
322 {
323 parent::setDefaults();
324
325 $this->defaults['label'] = 'submit';
326 $this->defaults['cancelLabel'] = '';
327 $this->defaults['backLabel'] = '';
328 $this->defaults['class'] = '';
329 $this->defaults['method'] = 'post';
330 // @todo adjust submit url for steps when used
331 $this->defaults['submitURL'] = $_SERVER['REQUEST_URI'];
332 $this->defaults['successURL'] = null;
333 $this->defaults['cancelURL'] = null;
334 $this->defaults['validator'] = null;
335 $this->defaults['ttl'] = 60 * 60; // 60 minutes
336 $this->defaults['jsValidation'] = 'blur';
337 $this->defaults['jsAutosave'] = 'false';
338 }
339
348 private function startSession(): void
349 {
350 // check if there's an open session
351 if (!session_id()) {
352 $params = session_get_cookie_params();
353 $sessionName = session_name();
354
355 session_set_cookie_params([
356 'lifetime' => $this->ttl,
357 'path' => $params['path'],
358 'domain' => $params['domain'],
359 'secure' => $params['secure'],
360 'httponly' => $params['httponly'],
361 'samesite' => 'Lax',
362 ]);
363 session_start();
364
365 // Extend the expiration time upon page load
366 if (isset($_COOKIE[$sessionName])) {
367 setcookie(
368 $sessionName,
369 $_COOKIE[$sessionName],
370 time() + $this->ttl,
371 $params['path'],
372 $params['domain'],
373 $params['secure'],
374 $params['httponly'],
375 );
376 }
377 }
378 $this->sessionSlotName = 'htmlform-' . $this->name . '-data';
379 $this->sessionSlot = & $_SESSION[$this->sessionSlotName];
380
381 $this->sessionExpiry();
382 }
391 private function sessionExpiry(): void
392 {
393 if (isset($this->ttl) && is_numeric($this->ttl)) {
394 $timestamp = time();
395
396 if (
397 isset($this->sessionSlot['formTimestamp'])
398 && ($timestamp - $this->sessionSlot['formTimestamp'] > $this->ttl)
399 ) {
400 $this->clearSession();
401 $this->sessionSlot = & $_SESSION[$this->sessionSlotName];
402 }
403
404 $this->sessionSlot['formTimestamp'] = $timestamp;
405 }
406 }
412 public function isEmpty()
413 {
414 return !isset($this->sessionSlot['formName']);
415 }
416
422 protected function getNewCsrfToken(): string
423 {
424 // Keep drawing fresh tokens until the OS CSPRNG reports the bytes are
425 // cryptographically strong. If the RNG is in degraded mode the token
426 // would be predictable and CSRF protection would be defeated.
427 do {
428 $csrfToken = base64_encode(openssl_random_pseudo_bytes(16, $cryptoStrong));
429 } while (!$cryptoStrong && strlen($csrfToken) > 0);
430
431 return $csrfToken;
432 }
433
445 protected function addElement(string $type, string $name, array $parameters): Abstracts\Element
446 {
447 $this->checkElementName($name);
448
449 $newElement = parent::addElement($type, $name, $parameters);
450
451 if ($newElement instanceof Elements\Step) {
452 $this->steps[] = $newElement;
453 }
454 if ($newElement instanceof Abstracts\Input) {
455 $this->updateInputValue($name);
456 }
457
458 return $newElement;
459 }
460
469 public function checkElementName(string $name): void
470 {
471 foreach ($this->getElements(true) as $element) {
472 if ($element->getName() === $name) {
473 throw new Exceptions\DuplicateElementNameException("Element name \"{$name}\" already in use.");
474 }
475 }
476 }
477
482 private function getCurrentElements(): array
483 {
484 $currentElements = [];
485
486 foreach ($this->elements as $element) {
487 if ($element instanceof Abstracts\Container) {
488 if (
489 !($element instanceof Elements\Step)
490 || (isset($this->steps[$this->currentStepId]) && ($element == $this->steps[$this->currentStepId]))
491 ) {
492 $currentElements = array_merge($currentElements, $element->getElements());
493 }
494 } else {
495 $currentElements[] = $element;
496 }
497 }
498
499 return $currentElements;
500 }
507 public function registerNamespace(string $namespace): void
508 {
509 $this->namespaces[] = $namespace;
510 }
511
516 public function getNamespaces(): array
517 {
518 return $this->namespaces;
519 }
520
527 private function inCurrentStep(string $name): bool
528 {
529 return in_array($this->getElement($name), $this->getCurrentElements());
530 }
541 public function setCurrentStep(int|null $step = null): void
542 {
543 if (!is_null($step)) {
544 $this->currentStepId = $step;
545 }
546 if (!is_numeric($this->currentStepId)
547 || ($this->currentStepId > count($this->steps) - 1)
548 || ($this->currentStepId < 0)
549 ) {
550 $this->currentStepId = $this->getFirstInvalidStep();
551 }
552 }
553 public function getUrl(): array
554 {
555 return $this->url;
556 }
557
562 public function getSteps(): array
563 {
564 return $this->steps;
565 }
566
571 public function getCurrentStepId(): int
572 {
573 return $this->currentStepId;
574 }
575
584 public function getFirstInvalidStep(): int
585 {
586 if (count($this->steps) > 0) {
587 foreach ($this->steps as $stepNumber => $step) {
588 if (!$step->validate()) {
589 return $stepNumber;
590 }
591 }
598 return count($this->steps) - 1;
599 } else {
600 return 0;
601 }
602 }
603
610 public function buildUrl(array $args = []): string
611 {
612 $url = isset($this->url['scheme']) ? $this->url['scheme'] . '://' : '';
613 $url .= $this->url['host'] ?? '';
614 $url .= isset($this->url['port']) ? ':' . $this->url['port'] : '';
615 $url .= $this->url['path'] ?? '';
616 $url .= $this->buildUrlQuery($args);
617
618 return $url;
619 }
620
626 public function buildUrlQuery(array $args = []): string
627 {
628 $query = '';
629 $queryParts = [];
630
631 if (isset($this->url['query']) && $this->url['query'] != "") {
632 //decoding query string
633 $query = html_entity_decode($this->url['query']);
634
635 //parsing the query into an array
636 parse_str($query, $queryParts);
637 }
638
639 foreach ($args as $name => $value) {
640 if ($value != "") {
641 $queryParts[$name] = $value;
642 } elseif (isset($queryParts[$name])) {
643 unset($queryParts[$name]);
644 }
645 }
646
647 // build the query again
648 $query = http_build_query($queryParts);
649
650 if ($query == "") {
651 return "";
652 }
653
654 return "?" . $query;
655 }
656
668 public function updateInputValue(string $name): void
669 {
670 $element = $this->getElement($name);
671
672 // handle captcha phrase
673 if ($this->getElement($name) instanceof Elements\Captcha) {
674 $element->setSessionSlot($this->sessionSlot);
675 }
676
677 // if it's a post, take the value from there and save it to the session
678 if (
679 isset($_POST['formName']) && ($_POST['formName'] === $this->name)
680 && $this->inCurrentStep($name)
681 && isset($_POST['formCsrfToken']) && $_POST['formCsrfToken'] === $this->sessionSlot['formCsrfToken']
682 ) {
683 if ($this->getElement($name) instanceof Elements\File) {
684 // handle uploaded file
685 $oldValue = isset($this->sessionSlot[$name]) ? $this->sessionSlot[$name] : null;
686 $this->sessionSlot[$name] = $element->handleUploadedFiles($oldValue);
687 } elseif (!$element->getDisabled()) {
688 // save value
689 $value = isset($_POST[$name]) ? $_POST[$name] : null;
690 $this->sessionSlot[$name] = $element->setValue($value);
691 } elseif (!isset($this->sessionSlot[$name])) {
692 // set default value for disabled elements
693 $this->sessionSlot[$name] = $element->setValue($element->getDefaultValue());
694 }
695 }
696 // if it's not a post, try to get the value from the session
697 elseif (isset($this->sessionSlot[$name])) {
698 $element->setValue($this->sessionSlot[$name]);
699 }
700 }
701
707 public function clearInputValue(string $name): void
708 {
709 $element = $this->getElement($name);
710
711 $this->sessionSlot[$name] = $element->clearValue();
712 }
713
723 public function populate(array|object $data = []): void
724 {
725 foreach ($this->getElements() as $element) {
726 $name = $element->name;
727 if (!in_array($name, $this->internalFields)) {
728 if (is_array($data) && isset($data[$name])) {
729 $value = $data[$name];
730 } elseif (is_object($data) && isset($data->$name)) {
731 $value = $data->$name;
732 }
733
734 if (isset($value)) {
735 $element->setDefaultValue($value);
736 if ($element->getDisabled() && !isset($this->sessionSlot[$name])) {
737 $this->sessionSlot[$name] = $value;
738 }
739 }
740
741 unset($value);
742 }
743 }
744 }
745
756 public function process(): void
757 {
758 $this->setCurrentStep();
759 // if there's post-data from this form
760 if (isset($_POST['formName']) && ($_POST['formName'] === $this->name)) {
761 // CSRF verification: a form submission with a missing or incorrect
762 // token is rejected here, before any onPost() business logic or
763 // session mutation (formFinalPost, value population) can run.
764 $csrfToken = isset($_POST['formCsrfToken']) ? $_POST['formCsrfToken'] : null;
765 if (!is_string($csrfToken) || $csrfToken !== $this->sessionSlot['formCsrfToken']) {
766 $this->httpResponseCode(400);
767 $this->log('HtmlForm: Request rejected because of a missing or incorrect CsrfToken');
768 return;
769 }
770
771 // save in session if submission was from last step
772 $this->sessionSlot['formFinalPost'] = count($this->steps) == 0 || $_POST['formStep'] + 1 == count($this->steps)
774
775 if (!empty($this->cancelLabel) && isset($_POST['formSubmit']) && $_POST['formSubmit'] === $this->cancelLabel) {
776 // cancel button was pressed
777 $this->clearSession();
778 $this->redirect($this->cancelURL);
779 } elseif ($this->isAutoSaveRequest) {
780 // do not redirect -> is autosave
781 $this->onPost();
782 } elseif (!empty($this->backLabel) && isset($_POST['formSubmit']) && $_POST['formSubmit'] === $this->backLabel) {
783 // back button was pressed
784 $this->onPost();
785
786 $this->sessionSlot['formFinalPost'] = false;
787 $prevStep = $this->currentStepId - 1;
788 if ($prevStep < 0) {
789 $prevStep = 0;
790 }
791 $this->redirect($this->buildUrl(['step' => ($prevStep <= 0) ? '' : $prevStep]));
792 } elseif ($this->validate()) {
793 // form was successfully submitted
794 $this->onPost();
795
796 $this->redirect($this->successURL);
797 } else {
798 // goto to next step or display first invalid step
799 $this->onPost();
800
801 $nextStep = $this->currentStepId + 1;
802 $firstInvalidStep = $this->getFirstInvalidStep();
803 if ($nextStep > $firstInvalidStep) {
804 $nextStep = $firstInvalidStep;
805 }
806 if ($nextStep > count($this->steps)) {
807 $nextStep = count($this->steps) - 1;
808 }
809 $this->redirect($this->buildUrl(['step' => ($nextStep == 0) ? '' : $nextStep]));
810 }
811 }
812 }
813
823 public function validate(): bool
824 {
825 // onValidate hook for custom required/validation rules
826 $this->valid = $this->onValidate();
827
828 $this->valid = $this->valid && $this->validateAutosave();
829
830 if ($this->valid && !is_null($this->validator)) {
831 if (is_callable($this->validator)) {
832 $this->valid = call_user_func($this->validator, $this, $this->getValues());
833 } else {
834 throw new Exceptions\ValidatorNotCallable("The validator paramater must be callable");
835 }
836 }
837 $this->valid = $this->valid && $this->sessionSlot['formFinalPost'];
838
839 // save validation-state in session
840 $this->sessionSlot['formIsValid'] = $this->valid;
841
842 return $this->valid;
843 }
844
853 protected function onPost(): bool
854 {
855 return true;
856 }
857
866 protected function onValidate(): bool
867 {
868 return true;
869 }
870
879 public function validateAutosave(): bool
880 {
881 parent::validate();
882
883 if (isset($_POST['formName'])) {
884 // Enforce CSRF for the submission itself: a missing or incorrect
885 // token here is treated as invalid (not silently accepted). This is
886 // scoped to actual form submissions only, so a direct validate()
887 // call (no POST) used to check a finalized form stays unaffected.
888 $csrfToken = isset($_POST['formCsrfToken']) ? $_POST['formCsrfToken'] : null;
889 $hasCorrectToken = is_string($csrfToken) && ($csrfToken === $this->sessionSlot['formCsrfToken']);
890 $this->valid = $this->valid && $hasCorrectToken;
891
892 if (!$hasCorrectToken) {
893 $this->httpResponseCode(400);
894 $this->log('HtmlForm: Request invalid because of a missing or incorrect CsrfToken');
895 }
896 }
897
898 $partValid = $this->valid;
899
900 // save data in session when autosaving but don't validate successfully
901 if ($this->isAutoSaveRequest
902 || (isset($this->sessionSlot['formIsAutosaved'])
903 && $this->sessionSlot['formIsAutosaved'] === true)
904 ) {
905 $this->valid = false;
906 }
907
908 // save whether form was autosaved the last time
909 $this->sessionSlot['formIsAutosaved'] = $this->isAutoSaveRequest;
910
911 return $partValid;
912 }
913
918 public function getValues(): ?array
919 {
920 if (isset($this->sessionSlot)) {
921 // remove internal attributes from values
922 return array_diff_key($this->sessionSlot, array_fill_keys($this->internalFields, ''));
923 } else {
924 return null;
925 }
926 }
927
932 public function getValuesWithLabel(): ?array
933 {
934 //get values first
935 $values = $this->getValues();
936 $valuesWithLabel = [];
937 if (isset($values)) {
938 foreach ($values as $element => $value) {
939 $elem = $this->getElement($element);
940
941 if ($elem) {
942 $valuesWithLabel[$element] = [
943 "value" => $value,
944 "label" => $elem->getLabel(),
945 ];
946 }
947 }
948
949 return $valuesWithLabel;
950 } else {
951 return null;
952 }
953 }
954
962 private function validateRedirectUrl(string $url, $allowExternal = false): void
963 {
964 if (empty($url)) {
965 return;
966 }
967
968 $parsed = parse_url($url);
969
970 if ($parsed === false) {
971 throw new \InvalidArgumentException("Invalid redirect URL.");
972 }
973
974 if (isset($parsed['scheme']) && !in_array(strtolower($parsed['scheme']), ['http', 'https'], true)) {
975 throw new \InvalidArgumentException("Redirect to non-HTTP scheme not allowed.");
976 }
977
978 if (!isset($parsed['host'])) {
979 return;
980 }
981
982 if (!$allowExternal) {
983 $allowedHosts = $_SERVER['HTTP_HOST'] ?? '';
984 $allowedHosts = array_map('trim', explode(',', $allowedHosts));
985 if (!in_array($parsed['host'], $allowedHosts, true)) {
986 throw new \InvalidArgumentException("Redirect to external host not allowed.");
987 }
988 }
989 }
997 private function validateRedirect(string $url): void
998 {
999 if (empty($url)) {
1000 throw new \InvalidArgumentException("Empty URL.");
1001 }
1002
1003 if (preg_match('/[\r\n]/', $url)) {
1004 throw new \InvalidArgumentException("Invalid URL: newline characters detected.");
1005 }
1006
1007 $parsed = parse_url($url);
1008 if ($parsed === false) {
1009 throw new \InvalidArgumentException("Invalid URL format.");
1010 }
1011
1012 if (isset($parsed['scheme'])) {
1013 $allowedSchemes = ['http', 'https'];
1014 if (!in_array(strtolower($parsed['scheme']), $allowedSchemes, true)) {
1015 throw new \InvalidArgumentException("Invalid URL scheme.");
1016 }
1017 }
1018 }
1024 public function redirect(string $url): void
1025 {
1026 $this->validateRedirect($url);
1027 $safeUrl = htmlspecialchars($url, ENT_QUOTES | FILTER_FLAG_NO_ENCODE_QUOTES, 'UTF-8');
1028 header('Location: ' . $safeUrl);
1029 exit;
1030 }
1031
1036 public function httpResponseCode(int $code): void
1037 {
1038 http_response_code($code);
1039 }
1040
1047 public function clearSession(bool $clearCsrfToken = true): void
1048 {
1049 if ($clearCsrfToken) {
1050 // clear everything
1051 $this->clearValue();
1052
1053 unset($_SESSION[$this->sessionSlotName]);
1054 unset($this->sessionSlot);
1055 } else {
1056 // clear everything except internal fields
1057 foreach ($this->getElements(false) as $element) {
1058 if (!$element->getDisabled() && !in_array($element->name, $this->internalFields)) {
1059 unset($this->sessionSlot[$element->name]);
1060 }
1061 }
1062 }
1063 }
1064
1070 public static function clearOldSessions(int $ttl = 3600, string $pattern = "/^htmlform-.*/"): void
1071 {
1072 $timestamp = time();
1073
1074 if (empty($_SESSION)) {
1075 return;
1076 }
1077 foreach ($_SESSION as $key => &$val) {
1078 if (preg_match($pattern, $key)
1079 && isset($val['formTimestamp'])
1080 && ($timestamp - $val['formTimestamp'] > $ttl)
1081 ) {
1082 unset($_SESSION[$key]);
1083 }
1084 }
1085 }
1086
1090 protected function htmlDataAttributes(): string
1091 {
1092 $this->dataAttr['jsvalidation'] = $this->jsValidation;
1093 $this->dataAttr['jsautosave'] = $this->jsAutosave === true ? "true" : $this->jsAutosave;
1094
1095 return parent::htmlDataAttributes();
1096 }
1097
1100 protected function htmlSubmitURL(): string
1101 {
1102 $step = $this->currentStepId != 0 ? $this->currentStepId : '';
1103
1104 return $this->htmlEscape($this->buildUrl(['step' => $step]));
1105 }
1106
1114 public function __toString(): string
1115 {
1116 $renderedElements = '';
1117 $submit = '';
1118 $cancel = '';
1119 $back = '';
1120 $label = $this->htmlLabel();
1121 $cancellabel = $this->htmlCancelLabel();
1122 $backlabel = $this->htmlBackLabel();
1123 $class = $this->htmlClass();
1124 $method = $this->htmlMethod();
1125 $submitURL = $this->htmlSubmitURL();
1126 $dataAttr = $this->htmlDataAttributes();
1127 $disabledAttr = $this->disabled ? " disabled=\"disabled\"" : "";
1128
1129 foreach ($this->elementsAndHtml as $element) {
1130 // leave out inactive step elements
1131 if (!($element instanceof Elements\Step)
1132 || (isset($this->steps[$this->currentStepId]) && $this->steps[$this->currentStepId] == $element)
1133 ) {
1134 $renderedElements .= $element;
1135 }
1136 }
1137
1138 if (!empty($this->cancelLabel)) {
1139 $cancel = "<p id=\"{$this->name}-cancel\" class=\"cancel\"><input type=\"submit\" name=\"formSubmit\" value=\"{$cancellabel}\"$disabledAttr></p>\n";
1140 }
1141 if (!empty($this->backLabel) && $this->currentStepId > 0) {
1142 $back = "<p id=\"{$this->name}-back\" class=\"back\"><input type=\"submit\" name=\"formSubmit\" value=\"{$backlabel}\"$disabledAttr></p>\n";
1143 }
1144 if (!empty($this->label)) {
1145 $submit = "<p id=\"{$this->name}-submit\" class=\"submit\"><input type=\"submit\" name=\"formSubmit\" value=\"{$label}\"$disabledAttr></p>\n";
1146 }
1147
1148
1149 return "<form id=\"{$this->name}\" name=\"{$this->name}\" class=\"depage-form {$class}\" method=\"{$method}\" action=\"{$submitURL}\"{$dataAttr} enctype=\"multipart/form-data\">" . "\n" .
1150 $renderedElements .
1151 $submit .
1152 $cancel .
1153 $back .
1154 "</form>";
1155 }
1156}
1157
1185
1195
1206
1216
1227
1237
1249
1260
1271
1284
1293
1304
1305/* vim:set ft=php sw=4 sts=4 fdm=marker et : */
container element base class
Definition Container.php:60
addChildElements()
Sub-element generator hook.
$form
Parent form object reference.
Definition Container.php:72
clearValue()
Deletes values of all child elements.
getElement(string $name, bool $includeFieldsets=false)
Gets subelement by name.
getElements(bool $includeFieldsets=false)
Returns containers subelements.
log(string $argument, ?string $type=null)
error & warning logger
Definition Element.php:238
$dataAttr
Extra information about the data that is saved inside the element.
Definition Element.php:79
htmlEscape(array|string $options=[])
Escapes HTML in strings and arrays of strings.
Definition Element.php:263
main interface to users
Definition HtmlForm.php:132
$valid
Form validation result/status.
Definition HtmlForm.php:227
$isAutoSaveRequest
true if form request is from autosave call
Definition HtmlForm.php:232
registerNamespace(string $namespace)
Stores element namespaces for adding.
Definition HtmlForm.php:507
clearInputValue(string $name)
clearInputValue
Definition HtmlForm.php:707
$method
HTML form method attribute.
Definition HtmlForm.php:142
getCurrentStepId()
Returns the current step id.
Definition HtmlForm.php:571
$label
Contains the submit button label of the form.
Definition HtmlForm.php:167
validate()
Validates the forms subelements.
Definition HtmlForm.php:823
httpResponseCode(int $code)
Sets the HTTP response code.
$jsAutosave
Contains the javascript autosave type of the form.
Definition HtmlForm.php:197
$class
Contains the additional class value of the form.
Definition HtmlForm.php:182
$cancelURL
Specifies where the user is redirected to, once the form-data is cancelled.
Definition HtmlForm.php:162
getSteps()
Returns an array of steps.
Definition HtmlForm.php:562
__construct(string $name, array $parameters=[], HtmlForm|null $form=null)
HtmlForm class constructor.
Definition HtmlForm.php:260
$jsValidation
Contains the javascript validation type of the form.
Definition HtmlForm.php:192
getFirstInvalidStep()
Returns first step that didn't pass validation.
Definition HtmlForm.php:584
htmlDataAttributes()
Returns dataAttr escaped as attribute string.
$namespaces
Namespace strings for addible element classes.
Definition HtmlForm.php:251
onValidate()
Validation hook.
Definition HtmlForm.php:866
validateAutosave()
If the form is autosaving the validation property is defaulted to false.
Definition HtmlForm.php:879
$ttl
Time until session expiry (seconds).
Definition HtmlForm.php:222
getValues()
Gets form-data from current PHP session.
Definition HtmlForm.php:918
static clearOldSessions(int $ttl=3600, string $pattern="/^htmlform-.*/")
clearOldSessions
__toString()
Renders form to HTML.
process()
Calls form validation and handles redirects.
Definition HtmlForm.php:756
$submitURL
HTML form action attribute.
Definition HtmlForm.php:152
redirect(string $url)
Redirects Browser to a different URL.
checkElementName(string $name)
Checks for duplicate subelement names.
Definition HtmlForm.php:469
buildUrlQuery(array $args=[])
Adding step parameter to already existing query.
Definition HtmlForm.php:626
$successURL
Specifies where the user is redirected to, once the form-data is valid.
Definition HtmlForm.php:157
updateInputValue(string $name)
Updates the value of an associated input element.
Definition HtmlForm.php:668
$sessionSlot
PHP session handle.
Definition HtmlForm.php:207
clearSession(bool $clearCsrfToken=true)
Deletes the current forms' PHP session data.
$internalFields
List of internal fieldnames that are not part of the results.
Definition HtmlForm.php:237
$validator
Contains the validator function of the form.
Definition HtmlForm.php:187
$cancelLabel
Contains the cancel button label of the form.
Definition HtmlForm.php:177
populate(array|object $data=[])
Fills subelement values.
Definition HtmlForm.php:723
setCurrentStep(int|null $step=null)
Validates step number of GET request.
Definition HtmlForm.php:541
htmlSubmitURL()
Returns form url escaped as attribute string.
getValuesWithLabel()
Gets form-data from current PHP session but also contain elemnt labels.
Definition HtmlForm.php:932
setDefaults()
Collects initial values across subclasses.
Definition HtmlForm.php:321
$sessionSlotName
Contains the name of the array in the PHP session, holding the form-data.
Definition HtmlForm.php:202
$url
url of the current page
Definition HtmlForm.php:147
$backLabel
Contains the back button label of the form.
Definition HtmlForm.php:172
getNamespaces()
Returns list of registered namespaces.
Definition HtmlForm.php:516
isEmpty()
Returns wether form has been submitted before or not.
Definition HtmlForm.php:412
buildUrl(array $args=[])
Builds URL from parts.
Definition HtmlForm.php:610
getNewCsrfToken()
Returns new XSRF token.
Definition HtmlForm.php:422
addElement(string $type, string $name, array $parameters)
Adds input or fieldset elements to htmlform.
Definition HtmlForm.php:445
Abstract element classes.
Definition Container.php:11
Classes for HTML input-elements.
Definition Address.php:10
htmlform class and autoloader
autoload($class)
PHP autoloader.
Definition HtmlForm.php:73