/** * {@inheritdoc} */ public function buildForm(array $form, FormStateInterface $form_state) { if ($form_state->isRebuilding()) { $form_state->setUserInput(array()); } // Initialize $storage = $form_state->getStorage(); if (empty($storage)) { $user_input = $form_state->getUserInput(); if (empty($user_input)) { $_SESSION['constructions'] = 0; } // Put the initial thing into the storage $storage = ['thing' => ['title' => 'none', 'value' => '']]; $form_state->setStorage($storage); } // Count how often the form is constructed. $_SESSION['constructions']++; drupal_set_message("Form constructions: " . $_SESSION['constructions']); $form['title'] = array('#type' => 'textfield', '#title' => 'Title', '#default_value' => $storage['thing']['title'], '#required' => TRUE); $form['value'] = array('#type' => 'textfield', '#title' => 'Value', '#default_value' => $storage['thing']['value'], '#element_validate' => array('::elementValidateValueCached')); $form['continue_button'] = array('#type' => 'button', '#value' => 'Reset'); $form['continue_submit'] = array('#type' => 'submit', '#value' => 'Continue submit', '#submit' => array('::continueSubmitForm')); $form['submit'] = array('#type' => 'submit', '#value' => 'Save'); if (\Drupal::request()->get('cache')) { // Manually activate caching, so we can test that the storage keeps working // when it's enabled. $form_state->setCached(); } if ($this->getRequest()->get('immutable')) { $form_state->addBuildInfo('immutable', TRUE); } return $form; }
/** * @covers ::addBuildInfo */ public function testAddBuildInfo() { $property = 'FOO'; $value = 'BAR'; $this->decoratedFormState->addBuildInfo($property, $value)->shouldBeCalled(); $this->assertSame($this->formStateDecoratorBase, $this->formStateDecoratorBase->addBuildInfo($property, $value)); }
/** * {@inheritdoc} */ public function buildForm(array $form, FormStateInterface $form_state) { if ($form_state->isRebuilding()) { $form_state->setUserInput(array()); } // Initialize $storage = $form_state->getStorage(); if (empty($storage)) { $user_input = $form_state->getUserInput(); if (empty($user_input)) { $_SESSION['constructions'] = 0; } // Put the initial thing into the storage $storage = ['thing' => ['title' => 'none', 'value' => '']]; $form_state->setStorage($storage); } // Count how often the form is constructed. $_SESSION['constructions']++; drupal_set_message("Form constructions: " . $_SESSION['constructions']); $form['title'] = array('#type' => 'textfield', '#title' => 'Title', '#default_value' => $storage['thing']['title'], '#required' => TRUE); $form['value'] = array('#type' => 'textfield', '#title' => 'Value', '#default_value' => $storage['thing']['value'], '#element_validate' => array('::elementValidateValueCached')); $form['continue_button'] = array('#type' => 'button', '#value' => 'Reset'); $form['continue_submit'] = array('#type' => 'submit', '#value' => 'Continue submit', '#submit' => array('::continueSubmitForm')); $form['submit'] = array('#type' => 'submit', '#value' => 'Save'); // @todo Remove this in https://www.drupal.org/node/2524408, because form // cache immutability is no longer necessary, because we no longer cache // forms during safe HTTP methods. In the meantime, because // Drupal\system\Tests\Form still has test coverage for a poisoned form // cache following a GET request, trick $form_state into caching the form // to keep that test working until we either remove it or change it in // that issue. if ($this->getRequest()->get('immutable')) { $form_state->addBuildInfo('immutable', TRUE); if ($this->getRequest()->get('cache') && $this->getRequest()->isMethodSafe()) { $form_state->setRequestMethod('FAKE'); $form_state->setCached(); } } return $form; }
/** * {@inheritdoc} */ public function setCache($form_build_id, $form, FormStateInterface $form_state) { // 6 hours cache life time for forms should be plenty. $expire = 21600; // Ensure that the form build_id embedded in the form structure is the same // as the one passed in as a parameter. This is an additional safety measure // to prevent legacy code operating directly with // \Drupal::formBuilder()->getCache() and \Drupal::formBuilder()->setCache() // from accidentally overwriting immutable form state. if (isset($form['#build_id']) && $form['#build_id'] != $form_build_id) { $this->logger->error('Form build-id mismatch detected while attempting to store a form in the cache.'); return; } // Cache form structure. if (isset($form)) { if ($this->currentUser->isAuthenticated()) { $form['#cache_token'] = $this->csrfToken->get(); } unset($form['#build_id_old']); $this->keyValueExpirableFactory->get('form')->setWithExpire($form_build_id, $form, $expire); } // Cache form state. if ($this->configFactory->get('system.performance')->get('cache.page.use_internal') && $this->isPageCacheable()) { $form_state->addBuildInfo('immutable', TRUE); } // 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->addBuildInfo('safe_strings', SafeMarkup::getAll()); if ($data = $form_state->getCacheableArray()) { $this->keyValueExpirableFactory->get('form_state')->setWithExpire($form_build_id, $data, $expire); } }
/** * {@inheritdoc} */ public function retrieveForm($form_id, FormStateInterface &$form_state) { // Record the $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 // the constructor function itself. $build_info = $form_state->getBuildInfo(); $args = $build_info['args']; $callback = [$form_state->getFormObject(), 'buildForm']; $form = array(); // Assign a default CSS class name based on $form_id. // This happens here and not in self::prepareForm() in order to allow the // 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($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, // since call_user_func_array() requires that referenced variables are // passed explicitly. $args = array_merge(array($form, &$form_state), $args); $form = call_user_func_array($callback, $args); // If the form returns a response, skip subsequent page construction by // throwing an exception. // @see Drupal\Core\EventSubscriber\EnforcedFormResponseSubscriber // // @todo Exceptions should not be used for code flow control. However, the // Form API currently allows any form builder functions to return a // response. // @see https://www.drupal.org/node/2363189 if ($form instanceof Response) { throw new EnforcedResponseException($form); } $form['#form_id'] = $form_id; return $form; }
/** * {@inheritdoc} */ public function addBuildInfo($property, $value) { $this->mainFormState->addBuildInfo($property, $value); return $this; }
/** * {@inheritdoc} */ public function buildForm(array $form, FormStateInterface $form_state, ViewExecutable $view = NULL, $output = []) { if (!($step = $form_state->get('step'))) { $step = 'views_form_views_form'; $form_state->set('step', $step); } $form_state->set(['step_controller', 'views_form_views_form'], 'Drupal\\views\\Form\\ViewsFormMainForm'); // Add the base form ID. $form_state->addBuildInfo('base_form_id', $this->getBaseFormId()); $form = array(); $query = $this->requestStack->getCurrentRequest()->query->all(); $query = UrlHelper::filterQueryParameters($query, array(), ''); $options = array('query' => $query); $form['#action'] = $view->hasUrl() ? $view->getUrl()->setOptions($options)->toString() : Url::fromRoute('<current>')->setOptions($options)->toString(); // Tell the preprocessor whether it should hide the header, footer, pager, // etc. $form['show_view_elements'] = array('#type' => 'value', '#value' => $step == 'views_form_views_form' ? TRUE : FALSE); $form_object = $this->getFormObject($form_state); $form += $form_object->buildForm($form, $form_state, $view, $output); return $form; }
/** * {@inheritdoc} */ public function retrieveForm($form_id, FormStateInterface &$form_state) { // Record the $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 // the constructor function itself. $args = $form_state['build_info']['args']; $callback = array($form_state['build_info']['callback_object'], 'buildForm'); $form = array(); // Assign a default CSS class name based on $form_id. // This happens here and not in self::prepareForm() in order to allow the // form constructor function to override or remove the default class. $form['#attributes']['class'][] = $this->drupalHtmlClass($form_id); // Same for the base form ID, if any. if (isset($form_state['build_info']['base_form_id'])) { $form['#attributes']['class'][] = $this->drupalHtmlClass($form_state['build_info']['base_form_id']); } // We need to pass $form_state by reference in order for forms to modify it, // since call_user_func_array() requires that referenced variables are // passed explicitly. $args = array_merge(array($form, &$form_state), $args); $form = call_user_func_array($callback, $args); // If the form returns some kind of response, deliver it. if ($form instanceof Response) { $this->sendResponse($form); exit; } $form['#form_id'] = $form_id; return $form; }