73 $class = str_replace(
'\\',
'/', str_replace(__NAMESPACE__ .
'\\',
'', $class));
74 $file = __DIR__ .
'/' . $class .
'.php';
76 if (file_exists($file)) {
81spl_autoload_register(__NAMESPACE__ .
'\autoload');
122 'en' => [
'us',
'gb',
'ie',
'au',
'nz'],
123 'de' => [
'de',
'at',
'ch'],
124 'fr' => [
'fr',
'ch',
'be',
'lu',
'ca'],
200 private $currentStepId;
205 private $steps = array();
250 $this->isAutoSaveRequest = isset($_POST[
'formAutosave']) && $_POST[
'formAutosave'] ===
"true";
252 $this->url = parse_url($_SERVER[
'REQUEST_URI']);
254 parent::__construct(
$name, $parameters, $this);
256 $this->url = parse_url($this->submitURL);
257 if (empty($this->successURL)) {
260 if (empty($this->cancelURL)) {
264 $this->currentStepId = isset($_GET[
'step']) ? $_GET[
'step'] : 0;
266 $this->startSession();
268 $this->valid = (isset($this->sessionSlot[
'formIsValid'])) ? $this->sessionSlot[
'formIsValid'] :
null;
271 if (!isset($this->sessionSlot[
'formCsrfToken'])) {
275 if (!isset($this->sessionSlot[
'formFinalPost'])) {
276 $this->sessionSlot[
'formFinalPost'] =
false;
280 $this->addHidden(
'formName')->setValue($this->name);
283 $this->addHidden(
'formStep')->setValue($this->currentStepId);
286 $this->addHidden(
'formCsrfToken')->setValue($this->sessionSlot[
'formCsrfToken']);
301 parent::setDefaults();
303 $this->defaults[
'label'] =
'submit';
304 $this->defaults[
'cancelLabel'] =
null;
305 $this->defaults[
'backLabel'] =
null;
306 $this->defaults[
'class'] =
'';
307 $this->defaults[
'method'] =
'post';
309 $this->defaults[
'submitURL'] = $_SERVER[
'REQUEST_URI'];
310 $this->defaults[
'successURL'] =
null;
311 $this->defaults[
'cancelURL'] =
null;
312 $this->defaults[
'validator'] =
null;
313 $this->defaults[
'ttl'] = 60 * 60;
314 $this->defaults[
'jsValidation'] =
'blur';
315 $this->defaults[
'jsAutosave'] =
'false';
326 private function startSession()
330 $params = session_get_cookie_params();
331 $sessionName = session_name();
333 session_set_cookie_params(
343 if (isset($_COOKIE[$sessionName])) {
346 $_COOKIE[$sessionName],
355 $this->sessionSlotName =
'htmlform-' . $this->name .
'-data';
358 $this->sessionExpiry();
368 private function sessionExpiry()
370 if (isset($this->ttl) && is_numeric($this->ttl)) {
374 isset($this->sessionSlot[
'formTimestamp'])
375 && ($timestamp - $this->sessionSlot[
'formTimestamp'] > $this->ttl)
381 $this->sessionSlot[
'formTimestamp'] = $timestamp;
391 return !isset($this->sessionSlot[
'formName']);
401 return base64_encode(openssl_random_pseudo_bytes(16));
419 $newElement = parent::addElement($type,
$name, $parameters);
421 if ($newElement instanceof Elements\Step) { $this->steps[] = $newElement; }
438 if ($element->getName() ===
$name) {
448 private function getCurrentElements()
450 $currentElements = array();
452 foreach ($this->elements as $element) {
453 if ($element instanceof Abstracts\Container) {
455 !($element instanceof Elements\Step)
456 || (isset($this->steps[$this->currentStepId]) && ($element == $this->steps[$this->currentStepId]))
458 $currentElements = array_merge($currentElements, $element->getElements());
461 $currentElements[] = $element;
465 return $currentElements;
475 $this->namespaces[] = $namespace;
493 private function inCurrentStep(
$name)
495 return in_array($this->
getElement(
$name), $this->getCurrentElements());
509 if (!is_null($step)) {
510 $this->currentStepId = $step;
512 if (!is_numeric($this->currentStepId)
513 || ($this->currentStepId > count($this->steps) - 1)
514 || ($this->currentStepId < 0)
535 return $this->currentStepId;
548 if (count($this->steps) > 0) {
549 foreach ($this->steps as $stepNumber => $step) {
550 if (!$step->validate()) {
560 return count($this->steps) - 1;
573 $queryParts = array();
575 if (isset($this->url[
'query']) && $this->url[
'query'] !=
"") {
577 $query = html_entity_decode($this->url[
'query']);
580 parse_str($query, $queryParts);
583 foreach ($args as
$name => $value) {
585 $queryParts[
$name] = $value;
586 } elseif (isset($queryParts[
$name])) {
587 unset($queryParts[
$name]);
592 $query = http_build_query($queryParts);
595 $query =
"?" . $query;
618 $element->setSessionSlot($this->sessionSlot);
623 isset($_POST[
'formName']) && ($_POST[
'formName'] === $this->name)
624 && $this->inCurrentStep(
$name)
625 && isset($_POST[
'formCsrfToken']) && $_POST[
'formCsrfToken'] === $this->sessionSlot[
'formCsrfToken']
629 $oldValue = isset($this->sessionSlot[
$name]) ? $this->sessionSlot[
$name] :
null;
630 $this->sessionSlot[
$name] = $element->handleUploadedFiles($oldValue);
631 }
else if (!$element->getDisabled()) {
633 $value = isset($_POST[
$name]) ? $_POST[
$name] :
null;
634 $this->sessionSlot[
$name] = $element->setValue($value);
635 }
else if (!isset($this->sessionSlot[
$name])) {
637 $this->sessionSlot[
$name] = $element->setValue($element->getDefaultValue());
641 else if (isset($this->sessionSlot[
$name])) {
642 $element->setValue($this->sessionSlot[
$name]);
655 $this->sessionSlot[
$name] = $element->clearValue();
670 $name = $element->name;
671 if (!in_array(
$name, $this->internalFields)) {
672 if (is_array($data) && isset($data[
$name])) {
673 $value = $data[
$name];
674 }
else if (is_object($data) && isset($data->$name)) {
675 $value = $data->$name;
679 $element->setDefaultValue($value);
680 if ($element->getDisabled() && !isset($this->sessionSlot[
$name])) {
681 $this->sessionSlot[
$name] = $value;
704 if (isset($_POST[
'formName']) && ($_POST[
'formName'] === $this->name)) {
706 $this->sessionSlot[
'formFinalPost'] = count($this->steps) == 0 || $_POST[
'formStep'] + 1 == count($this->steps)
709 if (!empty($this->cancelLabel) && isset($_POST[
'formSubmit']) && $_POST[
'formSubmit'] === $this->cancelLabel) {
713 } elseif ($this->isAutoSaveRequest) {
716 } elseif (!empty($this->backLabel) && isset($_POST[
'formSubmit']) && $_POST[
'formSubmit'] === $this->backLabel) {
720 $this->sessionSlot[
'formFinalPost'] =
false;
721 $prevStep = $this->currentStepId - 1;
725 $urlStepParameter = ($prevStep <= 0) ? $this->
buildUrlQuery(array(
'step' =>
'')) : $this->
buildUrlQuery(array(
'step' => $prevStep));
726 $this->
redirect($this->url[
'path'] . $urlStepParameter);
736 $nextStep = $this->currentStepId + 1;
738 if ($nextStep > $firstInvalidStep) {
739 $nextStep = $firstInvalidStep;
741 if ($nextStep > count($this->steps)) {
742 $nextStep = count($this->steps) - 1;
744 $urlStepParameter = ($nextStep == 0) ? $this->
buildUrlQuery(array(
'step' =>
'')) : $this->
buildUrlQuery(array(
'step' => $nextStep));
745 $this->
redirect($this->url[
'path'] . $urlStepParameter);
766 if ($this->valid && !is_null($this->validator)) {
767 if (is_callable($this->validator)) {
768 $this->valid = call_user_func($this->validator, $this, $this->
getValues());
770 throw new exceptions\validatorNotCallable(
"The validator paramater must be callable");
773 $this->valid = $this->valid && $this->sessionSlot[
'formFinalPost'];
819 if (isset($_POST[
'formCsrfToken'])) {
820 $hasCorrectToken = $_POST[
'formCsrfToken'] === $this->sessionSlot[
'formCsrfToken'];
821 $this->valid = $this->valid && $hasCorrectToken;
823 if (!$hasCorrectToken) {
824 http_response_code(400);
825 $this->
log(
"HtmlForm: Requst invalid because of incorrect CsrfToken");
832 if ($this->isAutoSaveRequest
833 || (isset($this->sessionSlot[
'formIsAutosaved'])
834 && $this->sessionSlot[
'formIsAutosaved'] ===
true)
836 $this->valid =
false;
851 if (isset($this->sessionSlot)) {
853 return array_diff_key($this->sessionSlot, array_fill_keys($this->internalFields,
''));
867 $valuesWithLabel = array();
868 if (isset($values)) {
869 foreach ($values as $element => $value) {
872 if ($elem) $valuesWithLabel[$element] = array(
874 "label" => $elem->getLabel()
878 return $valuesWithLabel;
890 header(
'Location: ' .
$url);
891 die(
"Tried to redirect you to <a href=\"$url\">$url</a>");
902 if ($clearCsrfToken) {
906 unset($_SESSION[$this->sessionSlotName]);
907 unset($this->sessionSlot);
911 if (!$element->getDisabled() && !in_array($element->name, $this->internalFields)) {
912 unset($this->sessionSlot[$element->name]);
927 if (empty($_SESSION)) {
930 foreach ($_SESSION as $key => &$val) {
931 if (preg_match($pattern, $key)
932 && isset($val[
'formTimestamp'])
933 && ($timestamp - $val[
'formTimestamp'] >
$ttl)
935 unset($_SESSION[$key]);
946 $this->dataAttr[
'jsautosave'] = $this->jsAutosave ===
true ?
"true" :
$this->jsAutosave;
948 return parent::htmlDataAttributes();
955 $scheme = isset($this->form->url[
'scheme']) ? $this->form->url[
'scheme'] .
'://' :
'';
956 $host = isset($this->form->url[
'host']) ? $this->form->url[
'host'] :
'';
957 $port = isset($this->form->url[
'port']) ?
':' . $this->form->url[
'port'] :
'';
958 $path = isset($this->form->url[
'path']) ? $this->form->url[
'path'] :
'';
959 $baseUrl =
"$scheme$host$port$path";
960 $step = $this->currentStepId != 0 ? $this->currentStepId :
'';
962 return $this->
htmlEscape($baseUrl . $this->form->buildUrlQuery([
'step' => $step]));
974 $renderedElements =
'';
978 $label = $this->htmlLabel();
979 $cancellabel = $this->htmlCancelLabel();
980 $backlabel = $this->htmlBackLabel();
981 $class = $this->htmlClass();
985 $disabledAttr = $this->disabled ?
" disabled=\"disabled\"" :
"";
987 foreach ($this->elementsAndHtml as $element) {
989 if (!($element instanceof elements\step)
990 || (isset($this->steps[$this->currentStepId]) && $this->steps[$this->currentStepId] == $element)
992 $renderedElements .= $element;
996 if (!is_null($this->cancelLabel)) {
997 $cancel =
"<p id=\"{$this->name}-cancel\" class=\"cancel\"><input type=\"submit\" name=\"formSubmit\" value=\"{$cancellabel}\"$disabledAttr></p>\n";
999 if (!is_null($this->backLabel) && $this->currentStepId > 0) {
1000 $back =
"<p id=\"{$this->name}-back\" class=\"back\"><input type=\"submit\" name=\"formSubmit\" value=\"{$backlabel}\"$disabledAttr></p>\n";
1002 if (!empty($this->label)) {
1003 $submit =
"<p id=\"{$this->name}-submit\" class=\"submit\"><input type=\"submit\" name=\"formSubmit\" value=\"{$label}\"$disabledAttr></p>\n";
1007 return "<form id=\"{$this->name}\" name=\"{$this->name}\" class=\"depage-form {$class}\" method=\"{$method}\" action=\"{$submitURL}\"{$dataAttr} enctype=\"multipart/form-data\">" .
"\n" .
container element base class
getElements($includeFieldsets=false)
Returns containers subelements.
addChildElements()
Sub-element generator hook.
$form
Parent form object reference.
clearValue()
Deletes values of all child elements.
getElement($name, $includeFieldsets=false)
Gets subelement by name.
log($argument, $type=null)
error & warning logger
$dataAttr
Extra information about the data that is saved inside the element.
htmlEscape($options=array())
Escapes HTML in strings and arrays of strings.
thrown when there are duplicate element names
$valid
Form validation result/status.
$isAutoSaveRequest
true if form request is from autosave call
addElement($type, $name, $parameters)
Adds input or fieldset elements to htmlform.
$method
HTML form method attribute.
getCurrentStepId()
Returns the current step id.
$label
Contains the submit button label of the form.
validate()
Validates the forms subelements.
setCurrentStep($step=null)
Validates step number of GET request.
$jsAutosave
Contains the javascript autosave type of the form.
$class
Contains the additional class value of the form.
$cancelURL
Specifies where the user is redirected to, once the form-data is cancelled.
updateInputValue($name)
Updates the value of an associated input element.
__construct($name, $parameters=array(), $form=null)
HtmlForm class constructor.
getSteps()
Returns an array of steps.
$jsValidation
Contains the javascript validation type of the form.
getFirstInvalidStep()
Returns first step that didn't pass validation.
checkElementName($name)
Checks for duplicate subelement names.
htmlDataAttributes()
Returns dataAttr escaped as attribute string.
clearInputValue($name)
clearInputValue
$namespaces
Namespace strings for addible element classes.
onValidate()
Validation hook.
validateAutosave()
If the form is autosaving the validation property is defaulted to false.
$ttl
Time until session expiry (seconds)
getValues()
Gets form-data from current PHP session.
__toString()
Renders form to HTML.
populate($data=array())
Fills subelement values.
process()
Calls form validation and handles redirects.
$submitURL
HTML form action attribute.
$successURL
Specifies where the user is redirected to, once the form-data is valid.
$sessionSlot
PHP session handle.
$internalFields
List of internal fieldnames that are not part of the results.
$validator
Contains the validator function of the form.
registerNamespace($namespace)
Stores element namespaces for adding.
$cancelLabel
Contains the cancel button label of the form.
htmlSubmitURL()
Returns dataAttr escaped as attribute string.
getValuesWithLabel()
Gets form-data from current PHP session but also contain elemnt labels.
setDefaults()
Collects initial values across subclasses.
static clearOldSessions($ttl=3600, $pattern="/^htmlform-.*/")
clearOldSessions
$sessionSlotName
Contains the name of the array in the PHP session, holding the form-data.
redirect($url)
Redirects Browser to a different URL.
$url
url of the current page
buildUrlQuery($args=array())
Adding step parameter to already existing query.
$backLabel
Contains the back button label of the form.
getNamespaces()
Returns list of registered namespaces.
isEmpty()
Returns wether form has been submitted before or not.
getNewCsrfToken()
Returns new XSRF token.
clearSession($clearCsrfToken=true)
Deletes the current forms' PHP session data.
htmlform class and autoloader
autoload($class)
PHP autoloader.