1<?php
2
3namespace Drupal\Core\Form;
4
5use Drupal\Component\Utility\NestedArray;
6use Drupal\Core\Url;
7use Symfony\Component\HttpFoundation\Response;
8
9/**
10 * Stores information about the state of a form.
11 */
12class FormState implements FormStateInterface {
13
14  use FormStateValuesTrait;
15
16  /**
17   * Tracks if any errors have been set on any form.
18   *
19   * @var bool
20   */
21  protected static $anyErrors = FALSE;
22
23  /**
24   * The complete form structure.
25   *
26   * #process, #after_build, #element_validate, and other handlers being invoked
27   * on a form element may use this reference to access other information in the
28   * form the element is contained in.
29   *
30   * @see self::getCompleteForm()
31   *
32   * This property is uncacheable.
33   *
34   * @var array
35   */
36  protected $complete_form;
37
38  /**
39   * An associative array of information stored by Form API that is necessary to
40   * build and rebuild the form from cache when the original context may no
41   * longer be available:
42   *   - callback: The actual callback to be used to retrieve the form array.
43   *     Can be any callable. If none is provided $form_id is used as the name
44   *     of a function to call instead.
45   *   - args: A list of arguments to pass to the form constructor.
46   *   - files: An optional array defining include files that need to be loaded
47   *     for building the form. Each array entry may be the path to a file or
48   *     another array containing values for the parameters 'type', 'module' and
49   *     'name' as needed by module_load_include(). The files listed here are
50   *     automatically loaded by \Drupal::formBuilder()->getCache(). By default
51   *     the current menu router item's 'file' definition is added, if any. Use
52   *     self::loadInclude() to add include files from a form constructor.
53   *   - form_id: Identification of the primary form being constructed and
54   *     processed.
55   *   - base_form_id: Identification for a base form, as declared in the form
56   *     class's \Drupal\Core\Form\BaseFormIdInterface::getBaseFormId() method.
57   *   - immutable: If this flag is set to TRUE, a new form build id is
58   *     generated when the form is loaded from the cache. If it is subsequently
59   *     saved to the cache again, it will have another cache id and therefore
60   *     the original form and form-state will remain unaltered. This is
61   *     important when page caching is enabled in order to prevent form state
62   *     from leaking between anonymous users.
63   *
64   * @var array
65   */
66  protected $build_info = [
67    'args' => [],
68    'files' => [],
69  ];
70
71  /**
72   * Similar to self::$build_info, but pertaining to
73   * \Drupal\Core\Form\FormBuilderInterface::rebuildForm().
74   *
75   * This property is uncacheable.
76   *
77   * @var array
78   */
79  protected $rebuild_info = [];
80
81  /**
82   * Normally, after the entire form processing is completed and submit handlers
83   * have run, a form is considered to be done and
84   * \Drupal\Core\Form\FormSubmitterInterface::redirectForm() will redirect the
85   * user to a new page using a GET request (so a browser refresh does not
86   * re-submit the form). However, if 'rebuild' has been set to TRUE, then a new
87   * copy of the form is immediately built and sent to the browser, instead of a
88   * redirect. This is used for multi-step forms, such as wizards and
89   * confirmation forms. Normally, self::$rebuild is set by a submit handler,
90   * since it is usually logic within a submit handler that determines whether a
91   * form is done or requires another step. However, a validation handler may
92   * already set self::$rebuild to cause the form processing to bypass submit
93   * handlers and rebuild the form instead, even if there are no validation
94   * errors.
95   *
96   * This property is uncacheable.
97   *
98   * @see self::setRebuild()
99   *
100   * @var bool
101   */
102  protected $rebuild = FALSE;
103
104  /**
105   * If set to TRUE the form will skip calling form element value callbacks,
106   * except for a select list of callbacks provided by Drupal core that are
107   * known to be safe.
108   *
109   * This property is uncacheable.
110   *
111   * @see self::setInvalidToken()
112   *
113   * @var bool
114   */
115  protected $invalidToken = FALSE;
116
117  /**
118   * Used when a form needs to return some kind of a
119   * \Symfony\Component\HttpFoundation\Response object, e.g., a
120   * \Symfony\Component\HttpFoundation\BinaryFileResponse when triggering a
121   * file download. If you use self::setRedirect() or self::setRedirectUrl(),
122   * it will be used to build a
123   * \Symfony\Component\HttpFoundation\RedirectResponse and will populate this
124   * key.
125   *
126   * @var \Symfony\Component\HttpFoundation\Response|null
127   */
128  protected $response;
129
130  /**
131   * Used to redirect the form on submission.
132   *
133   * @see self::getRedirect()
134   *
135   * This property is uncacheable.
136   *
137   * @var \Drupal\Core\Url|\Symfony\Component\HttpFoundation\RedirectResponse|null
138   */
139  protected $redirect;
140
141  /**
142   * If set to TRUE the form will NOT perform a redirect, even if
143   * self::$redirect is set.
144   *
145   * This property is uncacheable.
146   *
147   * @var bool
148   */
149  protected $no_redirect;
150
151  /**
152   * The HTTP form method to use for finding the input for this form.
153   *
154   * May be 'POST' or 'GET'. Defaults to 'POST'. Note that 'GET' method forms do
155   * not use form ids so are always considered to be submitted, which can have
156   * unexpected effects. The 'GET' method should only be used on forms that do
157   * not change data, as that is exclusively the domain of 'POST.'
158   *
159   * This property is uncacheable.
160   *
161   * @var string
162   */
163  protected $method = 'POST';
164
165  /**
166   * The HTTP method used by the request building or processing this form.
167   *
168   * May be any valid HTTP method. Defaults to 'GET', because even though
169   * $method is 'POST' for most forms, the form's initial build is usually
170   * performed as part of a GET request.
171   *
172   * This property is uncacheable.
173   *
174   * @var string
175   */
176  protected $requestMethod = 'GET';
177
178  /**
179   * If set to TRUE the original, unprocessed form structure will be cached,
180   * which allows the entire form to be rebuilt from cache. A typical form
181   * workflow involves two page requests; first, a form is built and rendered
182   * for the user to fill in. Then, the user fills the form in and submits it,
183   * triggering a second page request in which the form must be built and
184   * processed. By default, $form and $form_state are built from scratch during
185   * each of these page requests. Often, it is necessary or desired to persist
186   * the $form and $form_state variables from the initial page request to the
187   * one that processes the submission. 'cache' can be set to TRUE to do this.
188   * A prominent example is an Ajax-enabled form, in which
189   * \Drupal\Core\Render\Element\RenderElement::processAjaxForm()
190   * enables form caching for all forms that include an element with the #ajax
191   * property. (The Ajax handler has no way to build the form itself, so must
192   * rely on the cached version.) Note that the persistence of $form and
193   * $form_state happens automatically for (multi-step) forms having the
194   * self::$rebuild flag set, regardless of the value for self::$cache.
195   *
196   * @var bool
197   */
198  protected $cache = FALSE;
199
200  /**
201   * If set to TRUE the form will NOT be cached, even if 'cache' is set.
202   *
203   * @var bool
204   */
205  protected $no_cache;
206
207  /**
208   * An associative array of values submitted to the form.
209   *
210   * The validation functions and submit functions use this array for nearly all
211   * their decision making. (Note that #tree determines whether the values are a
212   * flat array or an array whose structure parallels the $form array. See
213   * \Drupal\Core\Render\Element\FormElement for more information.)
214   *
215   * This property is uncacheable.
216   *
217   * @var array
218   */
219  protected $values = [];
220
221  /**
222   * An associative array of form value keys to be removed by cleanValues().
223   *
224   * Any values that are temporary but must still be displayed as values in
225   * the rendered form should be added to this array using addCleanValueKey().
226   * Initialized with internal Form API values.
227   *
228   * This property is uncacheable.
229   *
230   * @var array
231   */
232  protected $cleanValueKeys = [
233    'form_id',
234    'form_token',
235    'form_build_id',
236    'op',
237  ];
238
239  /**
240   * The array of values as they were submitted by the user.
241   *
242   * These are raw and unvalidated, so should not be used without a thorough
243   * understanding of security implications. In almost all cases, code should
244   * use the data in the 'values' array exclusively. The most common use of this
245   * key is for multi-step forms that need to clear some of the user input when
246   * setting 'rebuild'. The values correspond to \Drupal::request()->request or
247   * \Drupal::request()->query, depending on the 'method' chosen.
248   *
249   * This property is uncacheable.
250   *
251   * @var array|null
252   *   The submitted user input array, or NULL if no input was submitted yet.
253   */
254  protected $input;
255
256  /**
257   * If TRUE and the method is GET, a form_id is not necessary.
258   *
259   * This property is uncacheable.
260   *
261   * @var bool
262   */
263  protected $always_process;
264
265  /**
266   * Ordinarily, a form is only validated once, but there are times when a form
267   * is resubmitted internally and should be validated again. Setting this to
268   * TRUE will force that to happen. This is most likely to occur during Ajax
269   * operations.
270   *
271   * This property is uncacheable.
272   *
273   * @var bool
274   */
275  protected $must_validate;
276
277  /**
278   * If TRUE, the form was submitted programmatically, usually invoked via
279   * \Drupal\Core\Form\FormBuilderInterface::submitForm(). Defaults to FALSE.
280   *
281   * @var bool
282   */
283  protected $programmed = FALSE;
284
285  /**
286   * If TRUE, programmatic form submissions are processed without taking #access
287   * into account. Set this to FALSE when submitting a form programmatically
288   * with values that may have been input by the user executing the current
289   * request; this will cause #access to be respected as it would on a normal
290   * form submission. Defaults to TRUE.
291   *
292   * @var bool
293   */
294  protected $programmed_bypass_access_check = TRUE;
295
296  /**
297   * TRUE signifies correct form submission. This is always TRUE for programmed
298   * forms coming from \Drupal\Core\Form\FormBuilderInterface::submitForm() (see
299   * 'programmed' key), or if the form_id coming from the
300   * \Drupal::request()->request data is set and matches the current form_id.
301   *
302   * @var bool
303   */
304  protected $process_input;
305
306  /**
307   * If TRUE, the form has been submitted. Defaults to FALSE.
308   *
309   * This property is uncacheable.
310   *
311   * @var bool
312   */
313  protected $submitted = FALSE;
314
315  /**
316   * If TRUE, the form was submitted and has been processed and executed.
317   *
318   * This property is uncacheable.
319   *
320   * @var bool
321   */
322  protected $executed = FALSE;
323
324  /**
325   * The form element that triggered submission, which may or may not be a
326   * button (in the case of Ajax forms). This key is often used to distinguish
327   * between various buttons in a submit handler, and is also used in Ajax
328   * handlers.
329   *
330   * This property is uncacheable.
331   *
332   * @var array|null
333   */
334  protected $triggering_element;
335
336  /**
337   * If TRUE, there is a file element and Form API will set the appropriate
338   * 'enctype' HTML attribute on the form.
339   *
340   * @var bool
341   */
342  protected $has_file_element;
343
344  /**
345   * Contains references to details elements to render them within vertical tabs.
346   *
347   * This property is uncacheable.
348   *
349   * @var array
350   */
351  protected $groups = [];
352
353  /**
354   * This is not a special key, and no specific support is provided for it in
355   * the Form API. By tradition it was the location where application-specific
356   * data was stored for communication between the submit, validation, and form
357   * builder functions, especially in a multi-step-style form. Form
358   * implementations may use any key(s) within $form_state (other than the keys
359   * listed here and other reserved ones used by Form API internals) for this
360   * kind of storage. The recommended way to ensure that the chosen key doesn't
361   * conflict with ones used by the Form API or other modules is to use the
362   * module name as the key name or a prefix for the key name. For example, the
363   * entity form classes use $this->entity in entity forms, or
364   * $form_state->getFormObject()->getEntity() outside the controller, to store
365   * information about the entity being edited, and this information stays
366   * available across successive clicks of the "Preview" button (if available)
367   * as well as when the "Save" button is finally clicked.
368   *
369   * @var array
370   */
371  protected $storage = [];
372
373  /**
374   * A list containing copies of all submit and button elements in the form.
375   *
376   * This property is uncacheable.
377   *
378   * @var array
379   */
380  protected $buttons = [];
381
382  /**
383   * Holds temporary data accessible during the current page request only.
384   *
385   * All $form_state properties that are not reserved keys (see
386   * other properties marked as uncacheable) persist throughout a multistep form
387   * sequence. Form API provides this key for modules to communicate information
388   * across form-related functions during a single page request. It may be used
389   * to temporarily save data that does not need to or should not be cached
390   * during the whole form workflow; e.g., data that needs to be accessed during
391   * the current form build process only. There is no use-case for this
392   * functionality in Drupal core.
393   *
394   * This property is uncacheable.
395   *
396   * @var array
397   */
398  protected $temporary = [];
399
400  /**
401   * Tracks if the form has finished validation.
402   *
403   * This property is uncacheable.
404   *
405   * @var bool
406   */
407  protected $validation_complete = FALSE;
408
409  /**
410   * Contains errors for this form.
411   *
412   * This property is uncacheable.
413   *
414   * @var array
415   */
416  protected $errors = [];
417
418  /**
419   * Stores which errors should be limited during validation.
420   *
421   * An array of "sections" within which user input must be valid. If the
422   * element is within one of these sections, the error must be recorded.
423   * Otherwise, it can be suppressed. self::$limit_validation_errors can be an
424   * empty array, in which case all errors are suppressed. For example, a
425   * "Previous" button might want its submit action to be triggered even if none
426   * of the submitted values are valid.
427   *
428   * This property is uncacheable.
429   *
430   * @var array|null
431   */
432  protected $limit_validation_errors;
433
434  /**
435   * Stores the gathered validation handlers.
436   *
437   * This property is uncacheable.
438   *
439   * @var array
440   */
441  protected $validate_handlers = [];
442
443  /**
444   * Stores the gathered submission handlers.
445   *
446   * This property is uncacheable.
447   *
448   * @var array
449   */
450  protected $submit_handlers = [];
451
452  /**
453   * {@inheritdoc}
454   */
455  public function setFormState(array $form_state_additions) {
456    foreach ($form_state_additions as $key => $value) {
457      if (property_exists($this, $key)) {
458        $this->{$key} = $value;
459      }
460      else {
461        $this->set($key, $value);
462      }
463    }
464    return $this;
465  }
466
467  /**
468   * {@inheritdoc}
469   */
470  public function setAlwaysProcess($always_process = TRUE) {
471    $this->always_process = (bool) $always_process;
472    return $this;
473  }
474
475  /**
476   * {@inheritdoc}
477   */
478  public function getAlwaysProcess() {
479    return $this->always_process;
480  }
481
482  /**
483   * {@inheritdoc}
484   */
485  public function setButtons(array $buttons) {
486    $this->buttons = $buttons;
487    return $this;
488  }
489
490  /**
491   * {@inheritdoc}
492   */
493  public function getButtons() {
494    return $this->buttons;
495  }
496
497  /**
498   * {@inheritdoc}
499   */
500  public function setCached($cache = TRUE) {
501    // Persisting $form_state is a side-effect disallowed during a "safe" HTTP
502    // method.
503    if ($cache && $this->isRequestMethodSafe()) {
504      throw new \LogicException(sprintf('Form state caching on %s requests is not allowed.', $this->requestMethod));
505    }
506
507    $this->cache = (bool) $cache;
508    return $this;
509  }
510
511  /**
512   * {@inheritdoc}
513   */
514  public function isCached() {
515    return empty($this->no_cache) && $this->cache;
516  }
517
518  /**
519   * {@inheritdoc}
520   */
521  public function disableCache() {
522    $this->no_cache = TRUE;
523    return $this;
524  }
525
526  /**
527   * {@inheritdoc}
528   */
529  public function setExecuted() {
530    $this->executed = TRUE;
531    return $this;
532  }
533
534  /**
535   * {@inheritdoc}
536   */
537  public function isExecuted() {
538    return $this->executed;
539  }
540
541  /**
542   * {@inheritdoc}
543   */
544  public function setGroups(array $groups) {
545    $this->groups = $groups;
546    return $this;
547  }
548
549  /**
550   * {@inheritdoc}
551   */
552  public function &getGroups() {
553    return $this->groups;
554  }
555
556  /**
557   * {@inheritdoc}
558   */
559  public function setHasFileElement($has_file_element = TRUE) {
560    $this->has_file_element = (bool) $has_file_element;
561    return $this;
562  }
563
564  /**
565   * {@inheritdoc}
566   */
567  public function hasFileElement() {
568    return $this->has_file_element;
569  }
570
571  /**
572   * {@inheritdoc}
573   */
574  public function setLimitValidationErrors($limit_validation_errors) {
575    $this->limit_validation_errors = $limit_validation_errors;
576    return $this;
577  }
578
579  /**
580   * {@inheritdoc}
581   */
582  public function getLimitValidationErrors() {
583    return $this->limit_validation_errors;
584  }
585
586  /**
587   * {@inheritdoc}
588   */
589  public function setMethod($method) {
590    $this->method = strtoupper($method);
591    return $this;
592  }
593
594  /**
595   * {@inheritdoc}
596   */
597  public function isMethodType($method_type) {
598    return $this->method === strtoupper($method_type);
599  }
600
601  /**
602   * {@inheritdoc}
603   */
604  public function setRequestMethod($method) {
605    $this->requestMethod = strtoupper($method);
606    return $this;
607  }
608
609  /**
610   * Checks whether the request method is a "safe" HTTP method.
611   *
612   * Link below defines GET and HEAD as "safe" methods, meaning they SHOULD NOT
613   * have side-effects, such as persisting $form_state changes.
614   *
615   * @return bool
616   *
617   * @see \Symfony\Component\HttpFoundation\Request::isMethodSafe()
618   * @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.1.1
619   */
620  protected function isRequestMethodSafe() {
621    return in_array($this->requestMethod, ['GET', 'HEAD']);
622  }
623
624  /**
625   * {@inheritdoc}
626   */
627  public function setValidationEnforced($must_validate = TRUE) {
628    $this->must_validate = (bool) $must_validate;
629    return $this;
630  }
631
632  /**
633   * {@inheritdoc}
634   */
635  public function isValidationEnforced() {
636    return $this->must_validate;
637  }
638
639  /**
640   * {@inheritdoc}
641   */
642  public function disableRedirect($no_redirect = TRUE) {
643    $this->no_redirect = (bool) $no_redirect;
644    return $this;
645  }
646
647  /**
648   * {@inheritdoc}
649   */
650  public function isRedirectDisabled() {
651    return $this->no_redirect;
652  }
653
654  /**
655   * {@inheritdoc}
656   */
657  public function setProcessInput($process_input = TRUE) {
658    $this->process_input = (bool) $process_input;
659    return $this;
660  }
661
662  /**
663   * {@inheritdoc}
664   */
665  public function isProcessingInput() {
666    return $this->process_input;
667  }
668
669  /**
670   * {@inheritdoc}
671   */
672  public function setProgrammed($programmed = TRUE) {
673    $this->programmed = (bool) $programmed;
674    return $this;
675  }
676
677  /**
678   * {@inheritdoc}
679   */
680  public function isProgrammed() {
681    return $this->programmed;
682  }
683
684  /**
685   * {@inheritdoc}
686   */
687  public function setProgrammedBypassAccessCheck($programmed_bypass_access_check = TRUE) {
688    $this->programmed_bypass_access_check = (bool) $programmed_bypass_access_check;
689    return $this;
690  }
691
692  /**
693   * {@inheritdoc}
694   */
695  public function isBypassingProgrammedAccessChecks() {
696    return $this->programmed_bypass_access_check;
697  }
698
699  /**
700   * {@inheritdoc}
701   */
702  public function setRebuildInfo(array $rebuild_info) {
703    $this->rebuild_info = $rebuild_info;
704    return $this;
705  }
706
707  /**
708   * {@inheritdoc}
709   */
710  public function getRebuildInfo() {
711    return $this->rebuild_info;
712  }
713
714  /**
715   * {@inheritdoc}
716   */
717  public function addRebuildInfo($property, $value) {
718    $rebuild_info = $this->getRebuildInfo();
719    $rebuild_info[$property] = $value;
720    $this->setRebuildInfo($rebuild_info);
721    return $this;
722  }
723
724  /**
725   * {@inheritdoc}
726   */
727  public function setStorage(array $storage) {
728    $this->storage = $storage;
729    return $this;
730  }
731
732  /**
733   * {@inheritdoc}
734   */
735  public function &getStorage() {
736    return $this->storage;
737  }
738
739  /**
740   * {@inheritdoc}
741   */
742  public function setSubmitHandlers(array $submit_handlers) {
743    $this->submit_handlers = $submit_handlers;
744    return $this;
745  }
746
747  /**
748   * {@inheritdoc}
749   */
750  public function getSubmitHandlers() {
751    return $this->submit_handlers;
752  }
753
754  /**
755   * {@inheritdoc}
756   */
757  public function setSubmitted() {
758    $this->submitted = TRUE;
759    return $this;
760  }
761
762  /**
763   * {@inheritdoc}
764   */
765  public function isSubmitted() {
766    return $this->submitted;
767  }
768
769  /**
770   * {@inheritdoc}
771   */
772  public function setTemporary(array $temporary) {
773    $this->temporary = $temporary;
774    return $this;
775  }
776
777  /**
778   * {@inheritdoc}
779   */
780  public function getTemporary() {
781    return $this->temporary;
782  }
783
784  /**
785   * {@inheritdoc}
786   */
787  public function &getTemporaryValue($key) {
788    $value = &NestedArray::getValue($this->temporary, (array) $key);
789    return $value;
790  }
791
792  /**
793   * {@inheritdoc}
794   */
795  public function setTemporaryValue($key, $value) {
796    NestedArray::setValue($this->temporary, (array) $key, $value, TRUE);
797    return $this;
798  }
799
800  /**
801   * {@inheritdoc}
802   */
803  public function hasTemporaryValue($key) {
804    $exists = NULL;
805    NestedArray::getValue($this->temporary, (array) $key, $exists);
806    return $exists;
807  }
808
809  /**
810   * {@inheritdoc}
811   */
812  public function setTriggeringElement($triggering_element) {
813    $this->triggering_element = $triggering_element;
814    return $this;
815  }
816
817  /**
818   * {@inheritdoc}
819   */
820  public function &getTriggeringElement() {
821    return $this->triggering_element;
822  }
823
824  /**
825   * {@inheritdoc}
826   */
827  public function setValidateHandlers(array $validate_handlers) {
828    $this->validate_handlers = $validate_handlers;
829    return $this;
830  }
831
832  /**
833   * {@inheritdoc}
834   */
835  public function getValidateHandlers() {
836    return $this->validate_handlers;
837  }
838
839  /**
840   * {@inheritdoc}
841   */
842  public function setValidationComplete($validation_complete = TRUE) {
843    $this->validation_complete = (bool) $validation_complete;
844    return $this;
845  }
846
847  /**
848   * {@inheritdoc}
849   */
850  public function isValidationComplete() {
851    return $this->validation_complete;
852  }
853
854  /**
855   * {@inheritdoc}
856   */
857  public function loadInclude($module, $type, $name = NULL) {
858    if (!isset($name)) {
859      $name = $module;
860    }
861    $build_info = $this->getBuildInfo();
862    if (!isset($build_info['files']["$module:$name.$type"])) {
863      // Only add successfully included files to the form state.
864      if ($result = $this->moduleLoadInclude($module, $type, $name)) {
865        $build_info['files']["$module:$name.$type"] = [
866          'type' => $type,
867          'module' => $module,
868          'name' => $name,
869        ];
870        $this->setBuildInfo($build_info);
871        return $result;
872      }
873    }
874    return FALSE;
875  }
876
877  /**
878   * {@inheritdoc}
879   */
880  public function getCacheableArray() {
881    return [
882      'build_info' => $this->getBuildInfo(),
883      'response' => $this->getResponse(),
884      'programmed' => $this->isProgrammed(),
885      'programmed_bypass_access_check' => $this->isBypassingProgrammedAccessChecks(),
886      'process_input' => $this->isProcessingInput(),
887      'has_file_element' => $this->hasFileElement(),
888      'storage' => $this->getStorage(),
889      // Use the properties directly, since self::isCached() combines them and
890      // cannot be relied upon.
891      'cache' => $this->cache,
892      'no_cache' => $this->no_cache,
893    ];
894  }
895
896  /**
897   * {@inheritdoc}
898   */
899  public function setCompleteForm(array &$complete_form) {
900    $this->complete_form = &$complete_form;
901    return $this;
902  }
903
904  /**
905   * {@inheritdoc}
906   */
907  public function &getCompleteForm() {
908    return $this->complete_form;
909  }
910
911  /**
912   * {@inheritdoc}
913   */
914  public function &get($property) {
915    $value = &NestedArray::getValue($this->storage, (array) $property);
916    return $value;
917  }
918
919  /**
920   * {@inheritdoc}
921   */
922  public function set($property, $value) {
923    NestedArray::setValue($this->storage, (array) $property, $value, TRUE);
924    return $this;
925  }
926
927  /**
928   * {@inheritdoc}
929   */
930  public function has($property) {
931    $exists = NULL;
932    NestedArray::getValue($this->storage, (array) $property, $exists);
933    return $exists;
934  }
935
936  /**
937   * {@inheritdoc}
938   */
939  public function setBuildInfo(array $build_info) {
940    $this->build_info = $build_info;
941    return $this;
942  }
943
944  /**
945   * {@inheritdoc}
946   */
947  public function getBuildInfo() {
948    return $this->build_info;
949  }
950
951  /**
952   * {@inheritdoc}
953   */
954  public function addBuildInfo($property, $value) {
955    $build_info = $this->getBuildInfo();
956    $build_info[$property] = $value;
957    $this->setBuildInfo($build_info);
958    return $this;
959  }
960
961  /**
962   * {@inheritdoc}
963   */
964  public function &getUserInput() {
965    return $this->input;
966  }
967
968  /**
969   * {@inheritdoc}
970   */
971  public function setUserInput(array $user_input) {
972    $this->input = $user_input;
973    return $this;
974  }
975
976  /**
977   * {@inheritdoc}
978   */
979  public function &getValues() {
980    return $this->values;
981  }
982
983  /**
984   * {@inheritdoc}
985   */
986  public function setResponse(Response $response) {
987    $this->response = $response;
988    return $this;
989  }
990
991  /**
992   * {@inheritdoc}
993   */
994  public function getResponse() {
995    return $this->response;
996  }
997
998  /**
999   * {@inheritdoc}
1000   */
1001  public function setRedirect($route_name, array $route_parameters = [], array $options = []) {
1002    $url = new Url($route_name, $route_parameters, $options);
1003    return $this->setRedirectUrl($url);
1004  }
1005
1006  /**
1007   * {@inheritdoc}
1008   */
1009  public function setRedirectUrl(Url $url) {
1010    $this->redirect = $url;
1011    return $this;
1012  }
1013
1014  /**
1015   * {@inheritdoc}
1016   */
1017  public function getRedirect() {
1018    // Skip redirection for form submissions invoked via
1019    // \Drupal\Core\Form\FormBuilderInterface::submitForm().
1020    if ($this->isProgrammed()) {
1021      return FALSE;
1022    }
1023    // Skip redirection if rebuild is activated.
1024    if ($this->isRebuilding()) {
1025      return FALSE;
1026    }
1027    // Skip redirection if it was explicitly disallowed.
1028    if ($this->isRedirectDisabled()) {
1029      return FALSE;
1030    }
1031
1032    return $this->redirect;
1033  }
1034
1035  /**
1036   * Sets the global status of errors.
1037   *
1038   * @param bool $errors
1039   *   TRUE if any form has any errors, FALSE otherwise.
1040   */
1041  protected static function setAnyErrors($errors = TRUE) {
1042    static::$anyErrors = $errors;
1043  }
1044
1045  /**
1046   * {@inheritdoc}
1047   */
1048  public static function hasAnyErrors() {
1049    return static::$anyErrors;
1050  }
1051
1052  /**
1053   * {@inheritdoc}
1054   */
1055  public function setErrorByName($name, $message = '') {
1056    if ($this->isValidationComplete()) {
1057      throw new \LogicException('Form errors cannot be set after form validation has finished.');
1058    }
1059
1060    $errors = $this->getErrors();
1061    if (!isset($errors[$name])) {
1062      $record = TRUE;
1063      $limit_validation_errors = $this->getLimitValidationErrors();
1064      if ($limit_validation_errors !== NULL) {
1065        $record = FALSE;
1066        foreach ($limit_validation_errors as $section) {
1067          // Exploding by '][' reconstructs the element's #parents. If the
1068          // reconstructed #parents begin with the same keys as the specified
1069          // section, then the element's values are within the part of
1070          // $form_state->getValues() that the clicked button requires to be
1071          // valid, so errors for this element must be recorded. As the exploded
1072          // array will all be strings, we need to cast every value of the
1073          // section array to string.
1074          if (array_slice(explode('][', $name), 0, count($section)) === array_map('strval', $section)) {
1075            $record = TRUE;
1076            break;
1077          }
1078        }
1079      }
1080      if ($record) {
1081        $errors[$name] = $message;
1082        $this->errors = $errors;
1083        static::setAnyErrors();
1084      }
1085    }
1086
1087    return $this;
1088  }
1089
1090  /**
1091   * {@inheritdoc}
1092   */
1093  public function setError(array &$element, $message = '') {
1094    $this->setErrorByName(implode('][', $element['#parents']), $message);
1095    return $this;
1096  }
1097
1098  /**
1099   * {@inheritdoc}
1100   */
1101  public function clearErrors() {
1102    $this->errors = [];
1103    static::setAnyErrors(FALSE);
1104  }
1105
1106  /**
1107   * {@inheritdoc}
1108   */
1109  public function getError(array $element) {
1110    if ($errors = $this->getErrors()) {
1111      $parents = [];
1112      foreach ($element['#parents'] as $parent) {
1113        $parents[] = $parent;
1114        $key = implode('][', $parents);
1115        if (isset($errors[$key])) {
1116          return $errors[$key];
1117        }
1118      }
1119    }
1120  }
1121
1122  /**
1123   * {@inheritdoc}
1124   */
1125  public function getErrors() {
1126    return $this->errors;
1127  }
1128
1129  /**
1130   * {@inheritdoc}
1131   */
1132  public function setRebuild($rebuild = TRUE) {
1133    $this->rebuild = $rebuild;
1134    return $this;
1135  }
1136
1137  /**
1138   * {@inheritdoc}
1139   */
1140  public function isRebuilding() {
1141    return $this->rebuild;
1142  }
1143
1144  /**
1145   * {@inheritdoc}
1146   */
1147  public function prepareCallback($callback) {
1148    if (is_string($callback) && substr($callback, 0, 2) == '::') {
1149      $callback = [$this->getFormObject(), substr($callback, 2)];
1150    }
1151    return $callback;
1152  }
1153
1154  /**
1155   * {@inheritdoc}
1156   */
1157  public function setFormObject(FormInterface $form_object) {
1158    $this->addBuildInfo('callback_object', $form_object);
1159    return $this;
1160  }
1161
1162  /**
1163   * {@inheritdoc}
1164   */
1165  public function getFormObject() {
1166    return $this->getBuildInfo()['callback_object'];
1167  }
1168
1169  /**
1170   * {@inheritdoc}
1171   */
1172  public function getCleanValueKeys() {
1173    return $this->cleanValueKeys;
1174  }
1175
1176  /**
1177   * {@inheritdoc}
1178   */
1179  public function setCleanValueKeys(array $cleanValueKeys) {
1180    $this->cleanValueKeys = $cleanValueKeys;
1181    return $this;
1182  }
1183
1184  /**
1185   * {@inheritdoc}
1186   */
1187  public function addCleanValueKey($cleanValueKey) {
1188    $keys = $this->getCleanValueKeys();
1189    $this->setCleanValueKeys(array_merge((array) $keys, [$cleanValueKey]));
1190    return $this;
1191  }
1192
1193  /**
1194   * {@inheritdoc}
1195   */
1196  public function cleanValues() {
1197    foreach ($this->getCleanValueKeys() as $value) {
1198      $this->unsetValue($value);
1199    }
1200
1201    // Remove button values.
1202    // \Drupal::formBuilder()->doBuildForm() collects all button elements in a
1203    // form. We remove the button value separately for each button element.
1204    foreach ($this->getButtons() as $button) {
1205      // Remove this button's value from the submitted form values by finding
1206      // the value corresponding to this button.
1207      // We iterate over the #parents of this button and move a reference to
1208      // each parent in self::getValues(). For example, if #parents is:
1209      //   array('foo', 'bar', 'baz')
1210      // then the corresponding self::getValues() part will look like this:
1211      // array(
1212      //   'foo' => array(
1213      //     'bar' => array(
1214      //       'baz' => 'button_value',
1215      //     ),
1216      //   ),
1217      // )
1218      // We start by (re)moving 'baz' to $last_parent, so we are able unset it
1219      // at the end of the iteration. Initially, $values will contain a
1220      // reference to self::getValues(), but in the iteration we move the
1221      // reference to self::getValue('foo'), and finally to
1222      // self::getValue(array('foo', 'bar')), which is the level where we
1223      // can unset 'baz' (that is stored in $last_parent).
1224      $parents = $button['#parents'];
1225      $last_parent = array_pop($parents);
1226      $key_exists = NULL;
1227      $values = &NestedArray::getValue($this->getValues(), $parents, $key_exists);
1228      if ($key_exists && is_array($values)) {
1229        unset($values[$last_parent]);
1230      }
1231    }
1232    return $this;
1233  }
1234
1235  /**
1236   * {@inheritdoc}
1237   */
1238  public function setInvalidToken($invalid_token) {
1239    $this->invalidToken = (bool) $invalid_token;
1240    return $this;
1241  }
1242
1243  /**
1244   * {@inheritdoc}
1245   */
1246  public function hasInvalidToken() {
1247    return $this->invalidToken;
1248  }
1249
1250  /**
1251   * Wraps ModuleHandler::loadInclude().
1252   */
1253  protected function moduleLoadInclude($module, $type, $name = NULL) {
1254    return \Drupal::moduleHandler()->loadInclude($module, $type, $name);
1255  }
1256
1257}
1258