FormState.php 32 KB

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