diff --git a/core/lib/Drupal/Core/Form/FormBuilder.php b/core/lib/Drupal/Core/Form/FormBuilder.php index afe1077..62b27ba 100644 --- a/core/lib/Drupal/Core/Form/FormBuilder.php +++ b/core/lib/Drupal/Core/Form/FormBuilder.php @@ -146,9 +146,9 @@ public function getFormId($form_arg, FormStateInterface &$form_state) { } // Add the $form_arg as the callback object and determine the form ID. - $form_state['build_info']['callback_object'] = $form_arg; + $form_state->addBuildInfo('callback_object', $form_arg); if ($form_arg instanceof BaseFormIdInterface) { - $form_state['build_info']['base_form_id'] = $form_arg->getBaseFormID(); + $form_state->addBuildInfo('base_form_id', $form_arg->getBaseFormID()); } return $form_arg->getFormId(); } @@ -162,7 +162,7 @@ public function getForm($form_arg) { $args = func_get_args(); // Remove $form_arg from the arguments. unset($args[0]); - $form_state['build_info']['args'] = array_values($args); + $form_state->addBuildInfo('args', array_values($args)); return $this->buildForm($form_arg, $form_state); } @@ -176,7 +176,7 @@ public function buildForm($form_id, FormStateInterface &$form_state) { if (!isset($form_state['input'])) { $request = $this->requestStack->getCurrentRequest(); - $form_state['input'] = $form_state['method'] == 'get' ? $request->query->all() : $request->request->all(); + $form_state->set('input', $form_state['method'] == 'get' ? $request->query->all() : $request->request->all()); } if (isset($_SESSION['batch_form_state'])) { @@ -301,7 +301,7 @@ public function rebuildForm($form_id, FormStateInterface &$form_state, $old_form // Clear out all group associations as these might be different when // re-rendering the form. - $form_state['groups'] = array(); + $form_state->set('groups', array()); // Return a fully built form that is ready for rendering. return $this->doBuildForm($form_id, $form, $form_state); @@ -362,7 +362,7 @@ public function setCache($form_build_id, $form, FormStateInterface $form_state) // Store the known list of safe strings for form re-use. // @todo Ensure we are not storing an excessively large string list in: // https://www.drupal.org/node/2295823 - $form_state['build_info']['safe_strings'] = SafeMarkup::getAll(); + $form_state->addBuildInfo('safe_strings', SafeMarkup::getAll()); if ($data = $form_state->getCacheableArray()) { $this->keyValueExpirableFactory->get('form_state')->setWithExpire($form_build_id, $data, $expire); @@ -377,24 +377,24 @@ public function submitForm($form_arg, FormStateInterface &$form_state) { $args = func_get_args(); // Remove $form and $form_state from the arguments. unset($args[0], $args[1]); - $form_state['build_info']['args'] = array_values($args); + $form_state->addBuildInfo('args', array_values($args)); } // Populate $form_state['input'] with the submitted values before retrieving // the form, to be consistent with what self::buildForm() does for // non-programmatic submissions (form builder functions may expect it to be // there). - $form_state['input'] = $form_state['values']; + $form_state->set('input', $form_state->get('values')); - $form_state['programmed'] = TRUE; + $form_state->set('programmed', TRUE); $form_id = $this->getFormId($form_arg, $form_state); $form = $this->retrieveForm($form_id, $form_state); // Programmed forms are always submitted. - $form_state['submitted'] = TRUE; + $form_state->set('submitted', TRUE); // Reset form validation. - $form_state['must_validate'] = TRUE; + $form_state->set('must_validate', TRUE); $this->clearErrors($form_state); $this->prepareForm($form_id, $form, $form_state); @@ -406,7 +406,7 @@ public function submitForm($form_arg, FormStateInterface &$form_state) { */ public function retrieveForm($form_id, FormStateInterface &$form_state) { // Record the $form_id. - $form_state['build_info']['form_id'] = $form_id; + $form_state->addBuildInfo('form_id', $form_id); // 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 @@ -445,19 +445,21 @@ public function retrieveForm($form_id, FormStateInterface &$form_state) { * {@inheritdoc} */ public function processForm($form_id, &$form, FormStateInterface &$form_state) { - $form_state['values'] = array(); + $form_state->set('values', array()); // With GET, these forms are always submitted if requested. if ($form_state['method'] == 'get' && !empty($form_state['always_process'])) { - if (!isset($form_state['input']['form_build_id'])) { - $form_state['input']['form_build_id'] = $form['#build_id']; + $input = $form_state->get('input'); + if (!isset($input['form_build_id'])) { + $input['form_build_id'] = $form['#build_id']; } - if (!isset($form_state['input']['form_id'])) { - $form_state['input']['form_id'] = $form_id; + if (!isset($input['form_id'])) { + $input['form_id'] = $form_id; } - if (!isset($form_state['input']['form_token']) && isset($form['#token'])) { - $form_state['input']['form_token'] = $this->csrfToken->get($form['#token']); + if (!isset($input['form_token']) && isset($form['#token'])) { + $input['form_token'] = $this->csrfToken->get($form['#token']); } + $form_state->set('input', $input); } // self::doBuildForm() finishes building the form by calling element @@ -481,7 +483,7 @@ public function processForm($form_id, &$form, FormStateInterface &$form_state) { // 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['triggering_element'] = reset($form_state['buttons']); + $form_state->set('triggering_element', reset($form_state['buttons'])); } $this->formValidator->validateForm($form_id, $form, $form_state); @@ -525,7 +527,7 @@ public function processForm($form_id, &$form, FormStateInterface &$form_state) { // Form building functions (e.g., self::handleInputElement()) may use // $form_state['rebuild'] to determine if they are running in the // context of a rebuild, so ensure it is set. - $form_state['rebuild'] = TRUE; + $form_state->setRebuild(); $form = $this->rebuildForm($form_id, $form_state, $form); } } @@ -549,10 +551,10 @@ public function prepareForm($form_id, &$form, FormStateInterface &$form_state) { $user = $this->currentUser(); $form['#type'] = 'form'; - $form_state['programmed'] = isset($form_state['programmed']) ? $form_state['programmed'] : FALSE; + $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['method'] == 'get' && !isset($form['#method'])) { + if ($form_state->get('method') == 'get' && !isset($form['#method'])) { $form['#method'] = 'get'; } @@ -763,10 +765,10 @@ 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. if ($form_state['programmed'] || (!empty($form_state['input']) && (isset($form_state['input']['form_id']) && ($form_state['input']['form_id'] == $form_id)))) { - $form_state['process_input'] = TRUE; + $form_state->set('process_input', TRUE); } else { - $form_state['process_input'] = FALSE; + $form_state->set('process_input', FALSE); } // All form elements should have an #array_parents property. @@ -862,7 +864,7 @@ 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['has_file_element'] = TRUE; + $form_state->set('has_file_element', TRUE); } // Final tasks for the form element after self::doBuildForm() has run for @@ -880,26 +882,27 @@ public function doBuildForm($form_id, &$element, FormStateInterface &$form_state // 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['triggering_element'] = $form_state['buttons'][0]; + $form_state->set('triggering_element', $form_state['buttons'][0]); } + $triggering_element = $form_state->get('triggering_element'); // 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($form_state['triggering_element']['#' . $type])) { - $form_state[$type . '_handlers'] = $form_state['triggering_element']['#' . $type]; + if (isset($triggering_element['#' . $type])) { + $form_state->set($type . '_handlers', $triggering_element['#' . $type]); } } // If the triggering element executes submit handlers, then set the form // state key that's needed for those handlers to run. - if (!empty($form_state['triggering_element']['#executes_submit_callback'])) { - $form_state['submitted'] = TRUE; + if (!empty($triggering_element['#executes_submit_callback'])) { + $form_state->set('submitted', TRUE); } // Special processing if the triggering element is a button. - if (!empty($form_state['triggering_element']['#is_button'])) { + if (!empty($triggering_element['#is_button'])) { // Because there are several ways in which the triggering element could // have been determined (including from input variables set by // JavaScript or fallback behavior implemented for IE), and because @@ -909,7 +912,7 @@ public function doBuildForm($form_id, &$element, FormStateInterface &$form_state // $form_state['values'][BUTTON_NAME] being set. But it's common for // forms to have several buttons named 'op' and switch on // $form_state['values']['op'] during submit handler execution. - $form_state['values'][$form_state['triggering_element']['#name']] = $form_state['triggering_element']['#value']; + $form_state->addValue($triggering_element['#name'], $triggering_element['#value']); } } return $element; @@ -1032,7 +1035,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['triggering_element'] = $element; + $form_state->set('triggering_element', $element); } // If the form was submitted by the browser rather than via Ajax, then it @@ -1044,16 +1047,18 @@ 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. - $form_state['buttons'][] = $element; + $buttons = $form_state->get('buttons'); + $buttons[] = $element; + $form_state->set('buttons', $buttons); if ($this->buttonWasClicked($element, $form_state)) { - $form_state['triggering_element'] = $element; + $form_state->set('triggering_element', $element); } } } // Set the element's value in $form_state['values'], but only, if its key // does not exist yet (a #value_callback may have already populated it). - if (!NestedArray::keyExists($form_state['values'], $element['#parents'])) { + if (!NestedArray::keyExists($form_state->getValues(), $element['#parents'])) { $this->setValue($element, $element['#value'], $form_state); } } @@ -1122,7 +1127,8 @@ protected function buttonWasClicked($element, FormStateInterface &$form_state) { * {@inheritdoc} */ public function setValue($element, $value, FormStateInterface &$form_state) { - NestedArray::setValue($form_state['values'], $element['#parents'], $value, TRUE); + $values = $form_state->getValues(); + NestedArray::setValue($values, $element['#parents'], $value, TRUE); } /** diff --git a/core/lib/Drupal/Core/Form/FormState.php b/core/lib/Drupal/Core/Form/FormState.php index 6b21377..177840b 100644 --- a/core/lib/Drupal/Core/Form/FormState.php +++ b/core/lib/Drupal/Core/Form/FormState.php @@ -99,6 +99,8 @@ class FormState implements FormStateInterface, \ArrayAccess { * * This property is uncacheable. * + * @see self::setRebuild() + * * @var bool */ protected $rebuild = FALSE; @@ -500,12 +502,7 @@ public function offsetUnset($offset) { * {@inheritdoc} */ public function setIfNotExists($property, $value) { - if (property_exists($this, $property)) { - if ($this->{$property} === NULL) { - $this->set($property, $value); - } - } - else if (!array_key_exists($property, $this->internalStorage)) { + if (!$this->has($property)) { $this->set($property, $value); } return $this; @@ -542,6 +539,44 @@ public function set($property, $value) { /** * {@inheritdoc} */ + public function has($property) { + if (property_exists($this, $property)) { + return $this->{$property} !== NULL; + } + + return array_key_exists($property, $this->internalStorage); + } + + /** + * {@inheritdoc} + */ + public function addBuildInfo($property, $value) { + $build_info = $this->get('build_info'); + $build_info[$property] = $value; + $this->set('build_info', $build_info); + return $this; + } + + /** + * {@inheritdoc} + */ + public function getValues() { + return $this->values; + } + + /** + * {@inheritdoc} + */ + public function addValue($property, $value) { + $values = $this->getValues(); + $values[$property] = $value; + $this->set('values', $values); + return $this; + } + + /** + * {@inheritdoc} + */ public function setRedirect(Url $url) { $this->set('redirect_route', $url); return $this; @@ -553,34 +588,34 @@ public function setRedirect(Url $url) { public function getRedirect() { // Skip redirection for form submissions invoked via // \Drupal\Core\Form\FormBuilderInterface::submitForm(). - if (!empty($this['programmed'])) { + if ($this->get('programmed')) { return FALSE; } // Skip redirection if rebuild is activated. - if (!empty($this['rebuild'])) { + if ($this->get('rebuild')) { return FALSE; } // Skip redirection if it was explicitly disallowed. - if (!empty($this['no_redirect'])) { + if ($this->get('no_redirect')) { return FALSE; } // Check for a route-based redirection. - if (isset($this['redirect_route'])) { + if ($redirect_route = $this->get('redirect_route')) { // @todo Remove once all redirects are converted to Url. - if (!($this['redirect_route'] instanceof Url)) { - $this['redirect_route'] += array( + if (!($redirect_route instanceof Url)) { + $redirect_route += array( 'route_parameters' => array(), 'options' => array(), ); - $this['redirect_route'] = new Url($this['redirect_route']['route_name'], $this['redirect_route']['route_parameters'], $this['redirect_route']['options']); + $redirect_route = new Url($redirect_route['route_name'], $redirect_route['route_parameters'], $redirect_route['options']); } - $this['redirect_route']->setAbsolute(); - return $this['redirect_route']; + $redirect_route->setAbsolute(); + return $redirect_route; } - return $this['redirect']; + return $this->get('redirect'); } /** @@ -604,13 +639,15 @@ public static function hasAnyErrors() { * {@inheritdoc} */ public function setErrorByName($name, $message = '') { - if (!empty($this['validation_complete'])) { + if ($this->get('validation_complete')) { throw new \LogicException('Form errors cannot be set after form validation has finished.'); } - if (!isset($this['errors'][$name])) { + $errors = $this->getErrors(); + if (!isset($errors[$name])) { $record = TRUE; - if (isset($this['limit_validation_errors'])) { + $limit_validation_errors = $this->get('limit_validation_errors'); + if ($limit_validation_errors !== NULL) { // #limit_validation_errors is 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. @@ -619,7 +656,7 @@ public function setErrorByName($name, $message = '') { // its submit action to be triggered even if none of the submitted // values are valid. $record = FALSE; - foreach ($this['limit_validation_errors'] as $section) { + foreach ($limit_validation_errors as $section) { // Exploding by '][' reconstructs the element's #parents. If the // reconstructed #parents begin with the same keys as the specified // section, then the element's values are within the part of @@ -634,7 +671,8 @@ public function setErrorByName($name, $message = '') { } } if ($record) { - $this['errors'][$name] = $message; + $errors[$name] = $message; + $this->set('errors', $errors); static::setAnyErrors(); if ($message) { $this->drupalSetMessage($message, 'error'); @@ -685,6 +723,14 @@ public function getErrors() { } /** + * {@inheritdoc} + */ + public function setRebuild($rebuild = TRUE) { + $this->set('rebuild', $rebuild); + return $this; + } + + /** * Wraps drupal_set_message(). * * @return array|null diff --git a/core/lib/Drupal/Core/Form/FormStateInterface.php b/core/lib/Drupal/Core/Form/FormStateInterface.php index 44fe619..81ca873 100644 --- a/core/lib/Drupal/Core/Form/FormStateInterface.php +++ b/core/lib/Drupal/Core/Form/FormStateInterface.php @@ -129,6 +129,37 @@ public function &get($property); public function set($property, $value); /** + * @param string $property + * The property to use for the value. + */ + public function has($property); + + /** + * Adds a value to the build info. + * + * @param string $property + * The property to use for the value. + * @param mixed $value + * The value to set. + * + * @return $this + */ + public function addBuildInfo($property, $value); + + /** + * Returns the submitted and sanitized form values. + * + * @return array + * An associative array of values submitted to the form. + */ + public function getValues(); + + /** + * {@inheritdoc} + */ + public function addValue($property, $value); + + /** * Determines if any forms have any errors. * * @return bool @@ -269,4 +300,14 @@ public function getErrors(); */ public function getError($element); + /** + * Sets the form to be rebuilt after processing. + * + * @param bool $rebuild + * (optional) Whether the form should be rebuilt or not. Defaults to TRUE. + * + * @return $this + */ + public function setRebuild($rebuild = TRUE); + }