Commit ee6ddbe8 authored by alexpott's avatar alexpott

Issue #2332389 by tim.plunkett: Finish adding methods to FormStateInterface.

parent fe85f9be
......@@ -806,12 +806,9 @@ function install_tasks_to_display($install_state) {
function install_get_form($form_id, array &$install_state) {
// Ensure the form will not redirect, since install_run_tasks() uses a custom
// redirection logic.
$form_state = new FormState(array(
'build_info' => array(
'args' => array(&$install_state),
),
'no_redirect' => TRUE,
));
$form_state = (new FormState())
->addBuildInfo('args', [&$install_state])
->disableRedirect();
$form_builder = \Drupal::formBuilder();
if ($install_state['interactive']) {
$form = $form_builder->buildForm($form_id, $form_state);
......@@ -827,7 +824,7 @@ function install_get_form($form_id, array &$install_state) {
// values taken from the installation state.
$install_form_id = $form_builder->getFormId($form_id, $form_state);
if (!empty($install_state['forms'][$install_form_id])) {
$form_state->set('values', $install_state['forms'][$install_form_id]);
$form_state->setValues($install_state['forms'][$install_form_id]);
}
$form_builder->submitForm($form_id, $form_state);
......
......@@ -356,9 +356,8 @@ public function validateConfigurationForm(array &$form, FormStateInterface $form
foreach ($this->getVisibilityConditions() as $condition_id => $condition) {
// Allow the condition to validate the form.
$condition_values = new FormState(array(
'values' => $form_state->getValue(array('visibility', $condition_id)),
));
$condition_values = (new FormState())
->setValues($form_state->getValue(['visibility', $condition_id]));
$condition->validateConfigurationForm($form, $condition_values);
// Update the original form values.
$form_state->setValue(array('visibility', $condition_id), $condition_values['values']);
......@@ -389,9 +388,8 @@ public function submitConfigurationForm(array &$form, FormStateInterface $form_s
$this->configuration['cache'] = $form_state->getValue('cache');
foreach ($this->getVisibilityConditions() as $condition_id => $condition) {
// Allow the condition to submit the form.
$condition_values = new FormState(array(
'values' => $form_state->getValue(array('visibility', $condition_id)),
));
$condition_values = (new FormState())
->setValues($form_state->getValue(['visibility', $condition_id]));
$condition->submitConfigurationForm($form, $condition_values);
// Update the original form values.
$form_state->setValue(array('visibility', $condition_id), $condition_values['values']);
......
......@@ -49,11 +49,7 @@ public function getForm(EntityInterface $entity, $operation = 'default', array $
$form_object = $this->entityManager->getFormObject($entity->getEntityTypeId(), $operation);
$form_object->setEntity($entity);
$form_state = new FormState($form_state_additions);
$form_state['build_info']['callback_object'] = $form_object;
$form_state['build_info']['base_form_id'] = $form_object->getBaseFormID();
$form_state['build_info'] += array('args' => array());
$form_state = (new FormState())->setFormState($form_state_additions);
return $this->formBuilder->buildForm($form_object, $form_state);
}
......
......@@ -10,7 +10,7 @@
/**
* Provides an interface for a Form that has a base form ID.
*
* This will become the $form_state['build_info']['base_form_id'] used to
* This will become the $form_state->getBaseInfo()['base_form_id'] used to
* generate the name of hook_form_BASE_FORM_ID_alter().
*/
interface BaseFormIdInterface extends FormInterface {
......
......@@ -157,7 +157,7 @@ public function getFormId($form_arg, FormStateInterface &$form_state) {
}
// Add the $form_arg as the callback object and determine the form ID.
$form_state->addBuildInfo('callback_object', $form_arg);
$form_state->setFormObject($form_arg);
if ($form_arg instanceof BaseFormIdInterface) {
$form_state->addBuildInfo('base_form_id', $form_arg->getBaseFormID());
}
......@@ -188,7 +188,7 @@ public function buildForm($form_id, FormStateInterface &$form_state) {
$input = $form_state->getUserInput();
if (!isset($input)) {
$request = $this->requestStack->getCurrentRequest();
$input = $form_state['method'] == 'get' ? $request->query->all() : $request->request->all();
$input = $form_state->isMethodType('get') ? $request->query->all() : $request->request->all();
$form_state->setUserInput($input);
}
......@@ -227,7 +227,7 @@ public function buildForm($form_id, FormStateInterface &$form_state) {
// self::setCache() removes uncacheable $form_state keys (see properties
// in \Drupal\Core\Form\FormState) in order for multi-step forms to work
// properly. This means that form processing logic for single-step forms
// using $form_state['cache'] may depend on data stored in those keys
// using $form_state->isCached() may depend on data stored in those keys
// during self::retrieveForm()/self::prepareForm(), but form processing
// should not depend on whether the form is cached or not, so $form_state
// is adjusted to match what it would be after a
......@@ -239,7 +239,9 @@ public function buildForm($form_id, FormStateInterface &$form_state) {
// - temporary: Any assigned data is expected to survives within the same
// page request.
if ($check_cache) {
$cache_form_state = $form_state->getCacheableArray(array('always_process', 'temporary'));
$cache_form_state = $form_state->getCacheableArray();
$cache_form_state['always_process'] = $form_state->getAlwaysProcess();
$cache_form_state['temporary'] = $form_state->getTemporary();
$form_state = $form_state_before_retrieval;
$form_state->setFormState($cache_form_state);
}
......@@ -280,6 +282,8 @@ public function buildForm($form_id, FormStateInterface &$form_state) {
*/
public function rebuildForm($form_id, FormStateInterface &$form_state, $old_form = NULL) {
$form = $this->retrieveForm($form_id, $form_state);
// All rebuilt forms will be cached.
$form_state->setCached();
// If only parts of the form will be returned to the browser (e.g., Ajax or
// RIA clients), re-use the old #build_id to not require client-side code to
......@@ -288,7 +292,8 @@ public function rebuildForm($form_id, FormStateInterface &$form_state, $old_form
// build's data in the form cache; also allowing the user to go back to an
// earlier build, make changes, and re-submit.
// @see self::prepareForm()
if (isset($old_form['#build_id']) && !empty($form_state['rebuild_info']['copy']['#build_id'])) {
$rebuild_info = $form_state->getRebuildInfo();
if (isset($old_form['#build_id']) && !empty($rebuild_info['copy']['#build_id'])) {
$form['#build_id'] = $old_form['#build_id'];
}
else {
......@@ -298,7 +303,7 @@ public function rebuildForm($form_id, FormStateInterface &$form_state, $old_form
// #action defaults to request_uri(), but in case of Ajax and other partial
// rebuilds, the form is submitted to an alternate URL, and the original
// #action needs to be retained.
if (isset($old_form['#action']) && !empty($form_state['rebuild_info']['copy']['#action'])) {
if (isset($old_form['#action']) && !empty($rebuild_info['copy']['#action'])) {
$form['#action'] = $old_form['#action'];
}
......@@ -308,13 +313,13 @@ public function rebuildForm($form_id, FormStateInterface &$form_state, $old_form
// cached is the $form structure before it passes through
// self::doBuildForm(), so we need to do it here.
// @todo For Drupal 8, find a way to avoid this code duplication.
if (empty($form_state['no_cache'])) {
if ($form_state->isCached()) {
$this->setCache($form['#build_id'], $form, $form_state);
}
// Clear out all group associations as these might be different when
// re-rendering the form.
$form_state->set('groups', array());
$form_state->setGroups([]);
// Return a fully built form that is ready for rendering.
return $this->doBuildForm($form_id, $form, $form_state);
......@@ -338,7 +343,8 @@ public function setCache($form_build_id, $form, FormStateInterface $form_state)
* {@inheritdoc}
*/
public function submitForm($form_arg, FormStateInterface &$form_state) {
if (!isset($form_state['build_info']['args'])) {
$build_info = $form_state->getBuildInfo();
if (empty($build_info['args'])) {
$args = func_get_args();
// Remove $form and $form_state from the arguments.
unset($args[0], $args[1]);
......@@ -351,15 +357,15 @@ public function submitForm($form_arg, FormStateInterface &$form_state) {
// there).
$form_state->setUserInput($form_state->getValues());
$form_state->set('programmed', TRUE);
$form_state->setProgrammed();
$form_id = $this->getFormId($form_arg, $form_state);
$form = $this->retrieveForm($form_id, $form_state);
// Programmed forms are always submitted.
$form_state->set('submitted', TRUE);
$form_state->setSubmitted();
// Reset form validation.
$form_state->set('must_validate', TRUE);
$form_state->setValidationEnforced();
$form_state->clearErrors();
$this->prepareForm($form_id, $form, $form_state);
......@@ -376,9 +382,10 @@ public function retrieveForm($form_id, FormStateInterface &$form_state) {
// We save two copies of the incoming arguments: one for modules to use
// when mapping form ids to constructor functions, and another to pass to
// the constructor function itself.
$args = $form_state['build_info']['args'];
$build_info = $form_state->getBuildInfo();
$args = $build_info['args'];
$callback = array($form_state['build_info']['callback_object'], 'buildForm');
$callback = [$form_state->getFormObject(), 'buildForm'];
$form = array();
// Assign a default CSS class name based on $form_id.
......@@ -386,8 +393,8 @@ public function retrieveForm($form_id, FormStateInterface &$form_state) {
// form constructor function to override or remove the default class.
$form['#attributes']['class'][] = Html::getClass($form_id);
// Same for the base form ID, if any.
if (isset($form_state['build_info']['base_form_id'])) {
$form['#attributes']['class'][] = Html::getClass($form_state['build_info']['base_form_id']);
if (isset($build_info['base_form_id'])) {
$form['#attributes']['class'][] = Html::getClass($build_info['base_form_id']);
}
// We need to pass $form_state by reference in order for forms to modify it,
......@@ -410,10 +417,10 @@ public function retrieveForm($form_id, FormStateInterface &$form_state) {
* {@inheritdoc}
*/
public function processForm($form_id, &$form, FormStateInterface &$form_state) {
$form_state->set('values', array());
$form_state->setValues([]);
// With GET, these forms are always submitted if requested.
if ($form_state['method'] == 'get' && !empty($form_state['always_process'])) {
if ($form_state->isMethodType('get') && $form_state->getAlwaysProcess()) {
$input = $form_state->getUserInput();
if (!isset($input['form_build_id'])) {
$input['form_build_id'] = $form['#build_id'];
......@@ -435,7 +442,7 @@ public function processForm($form_id, &$form, FormStateInterface &$form_state) {
$form = $this->doBuildForm($form_id, $form, $form_state);
// Only process the input if we have a correct form submission.
if ($form_state['process_input']) {
if ($form_state->isProcessingInput()) {
// Form constructors may explicitly set #token to FALSE when cross site
// request forgery is irrelevant to the form, such as search forms.
if (isset($form['#token']) && $form['#token'] === FALSE) {
......@@ -447,8 +454,9 @@ public function processForm($form_id, &$form, FormStateInterface &$form_state) {
// submit button is not taken account. Therefore, check whether there is
// exactly one submit button in the form, and if so, automatically use it
// as triggering_element.
if ($form_state['programmed'] && !isset($form_state['triggering_element']) && count($form_state['buttons']) == 1) {
$form_state->set('triggering_element', reset($form_state['buttons']));
$buttons = $form_state->getButtons();
if ($form_state->isProgrammed() && !$form_state->getTriggeringElement() && count($buttons) == 1) {
$form_state->setTriggeringElement(reset($buttons));
}
$this->formValidator->validateForm($form_id, $form, $form_state);
......@@ -462,35 +470,35 @@ public function processForm($form_id, &$form, FormStateInterface &$form_state) {
Html::resetSeenIds();
}
if (!$form_state['rebuild'] && !FormState::hasAnyErrors()) {
if (!$form_state->isRebuilding() && !FormState::hasAnyErrors()) {
if ($submit_response = $this->formSubmitter->doSubmitForm($form, $form_state)) {
return $submit_response;
}
}
// Don't rebuild or cache form submissions invoked via self::submitForm().
if (!empty($form_state['programmed'])) {
if ($form_state->isProgrammed()) {
return;
}
// If $form_state['rebuild'] has been set and input has been processed
// If $form_state->isRebuilding() has been set and input has been processed
// without validation errors, we are in a multi-step workflow that is not
// yet complete. A new $form needs to be constructed based on the changes
// made to $form_state during this request. Normally, a submit handler
// sets $form_state['rebuild'] if a fully executed form requires another
// step. However, for forms that have not been fully executed (e.g., Ajax
// submissions triggered by non-buttons), there is no submit handler to
// set $form_state['rebuild']. It would not make sense to redisplay the
// identical form without an error for the user to correct, so we also
// rebuild error-free non-executed forms, regardless of
// $form_state['rebuild'].
// sets $form_state->isRebuilding() if a fully executed form requires
// another step. However, for forms that have not been fully executed
// (e.g., Ajax submissions triggered by non-buttons), there is no submit
// handler to set $form_state->isRebuilding(). It would not make sense to
// redisplay the identical form without an error for the user to correct,
// so we also rebuild error-free non-executed forms, regardless of
// $form_state->isRebuilding().
// @todo Simplify this logic; considering Ajax and non-HTML front-ends,
// along with element-level #submit properties, it makes no sense to
// have divergent form execution based on whether the triggering element
// has #executes_submit_callback set to TRUE.
if (($form_state['rebuild'] || !$form_state['executed']) && !FormState::hasAnyErrors()) {
if (($form_state->isRebuilding() || !$form_state->isExecuted()) && !FormState::hasAnyErrors()) {
// Form building functions (e.g., self::handleInputElement()) may use
// $form_state['rebuild'] to determine if they are running in the
// $form_state->isRebuilding() to determine if they are running in the
// context of a rebuild, so ensure it is set.
$form_state->setRebuild();
$form = $this->rebuildForm($form_id, $form_state, $form);
......@@ -498,13 +506,13 @@ public function processForm($form_id, &$form, FormStateInterface &$form_state) {
}
// After processing the form, the form builder or a #process callback may
// have set $form_state['cache'] to indicate that the form and form state
// shall be cached. But the form may only be cached if the 'no_cache'
// property is not set to TRUE. Only cache $form as it was prior to
// self::doBuildForm(), because self::doBuildForm() must run for each
// request to accommodate new user input. Rebuilt forms are not cached here,
// because self::rebuildForm() already takes care of that.
if (!$form_state['rebuild'] && $form_state['cache'] && empty($form_state['no_cache'])) {
// have called $form_state->setCached() to indicate that the form and form
// state shall be cached. But the form may only be cached if
// $form_state->disableCache() is not called. Only cache $form as it was
// prior to self::doBuildForm(), because self::doBuildForm() must run for
// each request to accommodate new user input. Rebuilt forms are not cached
// here, because self::rebuildForm() already takes care of that.
if (!$form_state->isRebuilding() && $form_state->isCached()) {
$this->setCache($form['#build_id'], $unprocessed_form, $form_state);
}
}
......@@ -516,10 +524,9 @@ public function prepareForm($form_id, &$form, FormStateInterface &$form_state) {
$user = $this->currentUser();
$form['#type'] = 'form';
$form_state->set('programmed', isset($form_state['programmed']) ? $form_state['programmed'] : FALSE);
// Fix the form method, if it is 'get' in $form_state, but not in $form.
if ($form_state->get('method') == 'get' && !isset($form['#method'])) {
if ($form_state->isMethodType('get') && !isset($form['#method'])) {
$form['#method'] = 'get';
}
......@@ -551,7 +558,7 @@ public function prepareForm($form_id, &$form, FormStateInterface &$form_state) {
// since tokens are session-bound and forms displayed to anonymous users are
// very likely cached, we cannot assign a token for them.
// During installation, there is no $user yet.
if ($user && $user->isAuthenticated() && !$form_state['programmed']) {
if ($user && $user->isAuthenticated() && !$form_state->isProgrammed()) {
// Form constructors may explicitly set #token to FALSE when cross site
// request forgery is irrelevant to the form, such as search forms.
if (isset($form['#token']) && $form['#token'] === FALSE) {
......@@ -592,22 +599,23 @@ public function prepareForm($form_id, &$form, FormStateInterface &$form_state) {
$form['#validate'][] = '::validateForm';
$form['#submit'][] = '::submitForm';
$build_info = $form_state->getBuildInfo();
// If no #theme has been set, automatically apply theme suggestions.
// theme_form() itself is in #theme_wrappers and not #theme. Therefore, the
// #theme function only has to care for rendering the inner form elements,
// not the form itself.
if (!isset($form['#theme'])) {
$form['#theme'] = array($form_id);
if (isset($form_state['build_info']['base_form_id'])) {
$form['#theme'][] = $form_state['build_info']['base_form_id'];
if (isset($build_info['base_form_id'])) {
$form['#theme'][] = $build_info['base_form_id'];
}
}
// Invoke hook_form_alter(), hook_form_BASE_FORM_ID_alter(), and
// hook_form_FORM_ID_alter() implementations.
$hooks = array('form');
if (isset($form_state['build_info']['base_form_id'])) {
$hooks[] = 'form_' . $form_state['build_info']['base_form_id'];
if (isset($build_info['base_form_id'])) {
$hooks[] = 'form_' . $build_info['base_form_id'];
}
$hooks[] = 'form_' . $form_id;
$this->moduleHandler->alter($hooks, $form, $form_state, $form_id);
......@@ -690,11 +698,11 @@ public function doBuildForm($form_id, &$element, FormStateInterface &$form_state
// for programmed forms coming from self::submitForm(), or if the form_id
// coming from the POST data is set and matches the current form_id.
$input = $form_state->getUserInput();
if ($form_state['programmed'] || (!empty($input) && (isset($input['form_id']) && ($input['form_id'] == $form_id)))) {
$form_state->set('process_input', TRUE);
if ($form_state->isProgrammed() || (!empty($input) && (isset($input['form_id']) && ($input['form_id'] == $form_id)))) {
$form_state->setProcessInput();
}
else {
$form_state->set('process_input', FALSE);
$form_state->setProcessInput(FALSE);
}
// All form elements should have an #array_parents property.
......@@ -791,14 +799,14 @@ public function doBuildForm($form_id, &$element, FormStateInterface &$form_state
// If there is a file element, we need to flip a flag so later the
// form encoding can be set.
if (isset($element['#type']) && $element['#type'] == 'file') {
$form_state->set('has_file_element', TRUE);
$form_state->setHasFileElement();
}
// Final tasks for the form element after self::doBuildForm() has run for
// all other elements.
if (isset($element['#type']) && $element['#type'] == 'form') {
// If there is a file element, we set the form encoding.
if (isset($form_state['has_file_element'])) {
if ($form_state->hasFileElement()) {
$element['#attributes']['enctype'] = 'multipart/form-data';
}
......@@ -808,24 +816,26 @@ public function doBuildForm($form_id, &$element, FormStateInterface &$form_state
// though the user clicked the first button. Therefore, to be as
// consistent as we can be across browsers, if no 'triggering_element' has
// been identified yet, default it to the first button.
if (!$form_state['programmed'] && !isset($form_state['triggering_element']) && !empty($form_state['buttons'])) {
$form_state->set('triggering_element', $form_state['buttons'][0]);
$buttons = $form_state->getButtons();
if (!$form_state->isProgrammed() && !$form_state->getTriggeringElement() && !empty($buttons)) {
$form_state->setTriggeringElement($buttons[0]);
}
$triggering_element = $form_state->get('triggering_element');
$triggering_element = $form_state->getTriggeringElement();
// If the triggering element specifies "button-level" validation and
// submit handlers to run instead of the default form-level ones, then add
// those to the form state.
foreach (array('validate', 'submit') as $type) {
if (isset($triggering_element['#' . $type])) {
$form_state->set($type . '_handlers', $triggering_element['#' . $type]);
}
if (isset($triggering_element['#validate'])) {
$form_state->setValidateHandlers($triggering_element['#validate']);
}
if (isset($triggering_element['#submit'])) {
$form_state->setSubmitHandlers($triggering_element['#submit']);
}
// If the triggering element executes submit handlers, then set the form
// state key that's needed for those handlers to run.
if (!empty($triggering_element['#executes_submit_callback'])) {
$form_state->set('submitted', TRUE);
$form_state->setSubmitted();
}
// Special processing if the triggering element is a button.
......@@ -899,7 +909,7 @@ protected function handleInputElement($form_id, &$element, FormStateInterface &$
// #access=FALSE on an element usually allow access for some users, so forms
// submitted with self::submitForm() may bypass access restriction and be
// treated as high-privilege users instead.
$process_input = empty($element['#disabled']) && (($form_state['programmed'] && $form_state['programmed_bypass_access_check']) || ($form_state['process_input'] && (!isset($element['#access']) || $element['#access'])));
$process_input = empty($element['#disabled']) && (($form_state->isProgrammed() && $form_state->isBypassingProgrammedAccessChecks()) || ($form_state->isProcessingInput() && (!isset($element['#access']) || $element['#access'])));
// Set the element's #value property.
if (!isset($element['#value']) && !array_key_exists('#value', $element)) {
......@@ -925,7 +935,7 @@ protected function handleInputElement($form_id, &$element, FormStateInterface &$
// use default values for the latter, if required. Programmatically
// submitted forms can submit explicit NULL values when calling
// self::submitForm() so we do not modify FormState::$input for them.
if (!$input_exists && !$form_state['rebuild'] && !$form_state['programmed']) {
if (!$input_exists && !$form_state->isRebuilding() && !$form_state->isProgrammed()) {
// Add the necessary parent keys to FormState::$input and sets the
// element's input value to NULL.
NestedArray::setValue($form_state->getUserInput(), $element['#parents'], NULL);
......@@ -967,7 +977,7 @@ protected function handleInputElement($form_id, &$element, FormStateInterface &$
if ($process_input) {
// Detect if the element triggered the submission via Ajax.
if ($this->elementTriggeredScriptedSubmission($element, $form_state)) {
$form_state->set('triggering_element', $element);
$form_state->setTriggeringElement($element);
}
// If the form was submitted by the browser rather than via Ajax, then it
......@@ -979,11 +989,11 @@ protected function handleInputElement($form_id, &$element, FormStateInterface &$
// form_state_values_clean() and for the self::doBuildForm() code that
// handles a form submission containing no button information in
// \Drupal::request()->request.
$buttons = $form_state->get('buttons');
$buttons = $form_state->getButtons();
$buttons[] = $element;
$form_state->set('buttons', $buttons);
$form_state->setButtons($buttons);
if ($this->buttonWasClicked($element, $form_state)) {
$form_state->set('triggering_element', $element);
$form_state->setTriggeringElement($element);
}
}
}
......@@ -1031,10 +1041,10 @@ protected function elementTriggeredScriptedSubmission($element, FormStateInterfa
* textfield (self::doBuildForm() has extra code for that).
*
* Because this function contains only part of the logic needed to determine
* $form_state['triggering_element'], it should not be called from anywhere
* $form_state->getTriggeringElement(), it should not be called from anywhere
* other than within the Form API. Form validation and submit handlers needing
* to know which button was clicked should get that information from
* $form_state['triggering_element'].
* $form_state->getTriggeringElement().
*/
protected function buttonWasClicked($element, FormStateInterface &$form_state) {
// First detect normal 'vanilla' button clicks. Traditionally, all standard
......
......@@ -42,7 +42,7 @@ public function getFormId($form_arg, FormStateInterface &$form_state);
* function. For example, the node_edit form requires that a node object is
* passed in here when it is called. These are available to implementations
* of hook_form_alter() and hook_form_FORM_ID_alter() as the array
* $form_state['build_info']['args'].
* $form_state->getBuildInfo()['args'].
*
* @return array
* The form array.
......@@ -79,10 +79,10 @@ public function buildForm($form_id, FormStateInterface &$form_state);
* This is the key function for making multi-step forms advance from step to
* step. It is called by self::processForm() when all user input processing,
* including calling validation and submission handlers, for the request is
* finished. If a validate or submit handler set $form_state['rebuild'] to
* TRUE, and if other conditions don't preempt a rebuild from happening, then
* this function is called to generate a new $form, the next step in the form
* workflow, to be returned for rendering.
* finished. If a validate or submit handler set $form_state->isRebuilding()
* to TRUE, and if other conditions don't preempt a rebuild from happening,
* then this function is called to generate a new $form, the next step in the
* form workflow, to be returned for rendering.
*
* Ajax form submissions are almost always multi-step workflows, so that is
* one common use-case during which form rebuilding occurs. See
......@@ -98,7 +98,7 @@ public function buildForm($form_id, FormStateInterface &$form_state);
* (optional) A previously built $form. Used to retain the #build_id and
* #action properties in Ajax callbacks and similar partial form rebuilds.
* The only properties copied from $old_form are the ones which both exist
* in $old_form and for which $form_state['rebuild_info']['copy'][PROPERTY]
* in $old_form and for which $form_state->getRebuildInfo()['copy'][PROPERTY]
* is TRUE. If $old_form is not passed, the entire $form is rebuilt freshly.
* 'rebuild_info' needs to be a separate top-level property next to
* 'build_info', since the contained data must not be cached.
......@@ -148,8 +148,8 @@ public function rebuildForm($form_id, FormStateInterface &$form_state, $old_form
* @endcode
* would be called via self::submitForm() as follows:
* @code
* $form_state->set('values', $my_form_values);
* $form_state['build_info']['args'] = array(&$object);
* $form_state->setValues($my_form_values);
* $form_state->addBuildInfo('args', [&$object]);
* drupal_form_submit('mymodule_form', $form_state);
* @endcode
* For example:
......@@ -161,7 +161,7 @@ public function rebuildForm($form_id, FormStateInterface &$form_state, $old_form
* $values['pass']['pass1'] = 'password';
* $values['pass']['pass2'] = 'password';
* $values['op'] = t('Create new account');
* $form_state->set('values', $values);
* $form_state->setValues($values);
* drupal_form_submit('user_register_form', $form_state);
* @endcode
*/
......@@ -293,8 +293,8 @@ public function prepareForm($form_id, &$form, FormStateInterface &$form_state);
* the next submission needs to be processed, a multi-step workflow is
* needed. This is most commonly implemented with a submit handler setting
* persistent data within $form_state based on *validated* values in
* $form_state->getValues() and setting $form_state['rebuild']. The form
* building functions must then be implemented to use the $form_state data
* $form_state->getValues() and checking $form_state->isRebuilding(). The
* form building functions must then be implemented to use the $form_state
* to rebuild the form with the structure appropriate for the new state.
* - Where user input must affect the rendering of the form without affecting
* its structure, the necessary conditional rendering logic should reside
......
......@@ -95,8 +95,9 @@ protected function loadCachedFormState($form_build_id, FormStateInterface $form_
// If the original form is contained in include files, load the files.
// @see \Drupal\Core\Form\FormStateInterface::loadInclude()
$form_state['build_info'] += array('files' => array());
foreach ($form_state['build_info']['files'] as $file) {
$build_info = $form_state->getBuildInfo();
$build_info += ['files' => []];
foreach ($build_info['files'] as $file) {
if (is_array($file)) {
$file += array('type' => 'inc', 'name' => $file['module']);
$this->moduleHandler->loadInclude($file['module'], $file['type'], $file['name']);
......@@ -109,9 +110,10 @@ protected function loadCachedFormState($form_build_id, FormStateInterface $form_
// for this request.
// @todo Ensure we are not storing an excessively large string list
// in: https://www.drupal.org/node/2295823
$form_state['build_info'] += array('safe_strings' => array());
SafeMarkup::setMultiple($form_state['build_info']['safe_strings']);
unset($form_state['build_info']['safe_strings']);
$build_info += ['safe_strings' => []];
SafeMarkup::setMultiple($build_info['safe_strings']);
unset($build_info['safe_strings']);
$form_state->setBuildInfo($build_info);
}
}
......
......@@ -25,13 +25,6 @@ class FormState implements FormStateInterface, \ArrayAccess {
*/
protected static $anyErrors = FALSE;
/**
* The internal storage of the form state.
*
* @var array
*/
protected $internalStorage = array();
/**
* The complete form structure.
*
......@@ -92,12 +85,12 @@ class FormState implements FormStateInterface, \ArrayAccess {
* re-submit the form). However, if 'rebuild' has been set to TRUE, then a new
* copy of the form is immediately built and sent to the browser, instead of a
* redirect. This is used for multi-step forms, such as wizards and
* confirmation forms. Normally, $form_state['rebuild'] is set by a submit
* handler, since its is usually logic within a submit handler that determines
* whether a form is done or requires another step. However, a validation
* handler may already set $form_state['rebuild'] to cause the form processing
* to bypass submit handlers and rebuild the form instead, even if there are
* no validation errors.
* confirmation forms. Normally, self::$rebuild is set by a submit handler,
* since its is usually logic within a submit handler that determines whether
* a form is done or requires another step. However, a validation handler may
* already set self::$rebuild to cause the form processing to bypass submit
* handlers and rebuild the form instead, even if there are no validation
* errors.
*
* This property is uncacheable.
*
......@@ -217,10 +210,6 @@ class FormState implements FormStateInterface, \ArrayAccess {
/**
* If TRUE and the method is GET, a form_id is not necessary.
*
* This should only be used on RESTful GET forms that do NOT write data, as
* this could lead to security issues. It is useful so that searches do not
* need to have a form_id in their query arguments to trigger the search.
*
* This property is uncacheable.
*
* @var bool
......@@ -383,6 +372,13 @@ class FormState implements FormStateInterface, \ArrayAccess {
/**
* Stores which errors should be limited during validation.
*
* An array of "sections" within which user input must be valid. If the
* element is within one of these sections, the error must be recorded.
* Otherwise, it can be suppressed. self::$limit_validation_errors can be an
* empty array, in which case all errors are suppressed. For example, a
* "Previous" button might want its submit action to be triggered even if none
* of the submitted values are valid.
*
* This property is uncacheable.
*
* @var array|null
......@@ -394,41 +390,367 @@ class FormState implements FormStateInterface, \ArrayAccess {
*
* This property is uncacheable.
*
* @var array|null
* @var array
*/
protected $validate_handlers;
protected $validate_handlers = [];
/**
* Stores the gathered submission handlers.
*
* This property is uncacheable.
*
* @var array|null
* @var array
*/
protected $submit_handlers;
protected $submit_handlers = [];
/**
* Constructs a \Drupal\Core\Form\FormState object.
*
* @param array $form_state_additions
* (optional) An associative array used to build the current state of the
* form. Use this to pass additional information to the form, such as the
* langcode. Defaults to an empty array.
* {@inheritdoc}
*/
public function __construct(array $form_state_additions = array()) {
$this->setFormState($form_state_additions);
public function setFormState(array $form_state_additions) {
foreach ($form_state_additions as $key => $value) {
if (property_exists($this, $key)) {
$this->{$key} = $value;
}
else {
$this->set($key, $value);
}
}
return $this;
}
/**
* {@inheritdoc}
*/
public function setFormState(array $form_state_additions) {
foreach ($form_state_additions as $key => $value) {
$this->set($key, $value);
}
public function setAlwaysProcess($always_process = TRUE) {
$this->always_process = (bool) $always_process;
return $this;
}
/**
* {@inheritdoc}
*/
public function getAlwaysProcess() {
return $this->always_process;
}
/**
* {@inheritdoc}
*/
public function setButtons(array $buttons) {
$this->buttons = $buttons;
return $this;
}
/**
* {@inheritdoc}
*/
public function getButtons() {
return $this->buttons;
}
/**
* {@inheritdoc}
*/
public function setCached($cache = TRUE) {
$this->cache = (bool) $cache;
return $this;
}