Loading core/lib/Drupal/Core/Form/FormBuilder.php +15 −9 Original line number Diff line number Diff line Loading @@ -105,13 +105,6 @@ class FormBuilder implements FormBuilderInterface { */ protected $forms; /** * An array of validated forms. * * @var array */ protected $validatedForms = array(); /** * An array of options used for recursive flattening. * Loading Loading @@ -301,6 +294,7 @@ public function getFormStateDefaults() { 'files' => array(), ), 'temporary' => array(), 'validation_complete' => FALSE, 'submitted' => FALSE, 'executed' => FALSE, 'programmed' => FALSE, Loading Loading @@ -431,6 +425,7 @@ protected function getUncacheableKeys() { 'input', 'method', 'submit_handlers', 'validation_complete', 'submitted', 'executed', 'validate_handlers', Loading Loading @@ -806,7 +801,14 @@ public function prepareForm($form_id, &$form, &$form_state) { * {@inheritdoc} */ public function validateForm($form_id, &$form, &$form_state) { if (isset($this->validatedForms[$form_id]) && empty($form_state['must_validate'])) { // If this form is flagged to always validate, ensure that previous runs of // validation are ignored. if (!empty($form_state['must_validate'])) { $form_state['validation_complete'] = FALSE; } // If this form has completed validation, do not validate again. if (!empty($form_state['validation_complete'])) { return; } Loading Loading @@ -888,7 +890,7 @@ protected function finalizeValidation($form_id, &$form, &$form_state) { // After validation, loop through and assign each element its errors. $this->setElementErrorsFromFormState($form, $form_state); // Mark this form as validated. $this->validatedForms[$form_id] = TRUE; $form_state['validation_complete'] = TRUE; } /** Loading Loading @@ -1198,6 +1200,10 @@ public function executeHandlers($type, &$form, &$form_state) { * {@inheritdoc} */ public function setErrorByName($name, array &$form_state, $message = '') { if (!empty($form_state['validation_complete'])) { throw new \LogicException('Form errors cannot be set after form validation has finished.'); } if (!isset($form_state['errors'][$name])) { $record = TRUE; if (isset($form_state['limit_validation_errors'])) { Loading core/tests/Drupal/Tests/Core/Form/FormBuilderTest.php +71 −0 Original line number Diff line number Diff line Loading @@ -17,6 +17,8 @@ /** * Tests the form builder. * * @coversDefaultClass \Drupal\Core\Form\FormBuilder * * @group Drupal * @group Form */ Loading Loading @@ -218,6 +220,34 @@ public function testHandleRedirectWithResponse() { $this->assertSame($response, $form_state['response']); } /** * Tests that form errors during submission throw an exception. * * @covers ::setErrorByName * * @expectedException \LogicException * @expectedExceptionMessage Form errors cannot be set after form validation has finished. */ public function testFormErrorsDuringSubmission() { $form_id = 'test_form_id'; $expected_form = $form_id(); $form_arg = $this->getMockForm($form_id, $expected_form); $form_builder = $this->formBuilder; $form_arg->expects($this->any()) ->method('submitForm') ->will($this->returnCallback(function ($form, &$form_state) use ($form_builder) { $form_builder->setErrorByName('test', $form_state, 'Hello'); })); $form_state = array(); $this->formBuilder->getFormId($form_arg, $form_state); $form_state['values'] = array(); $form_state['input']['form_id'] = $form_id; $this->simulateFormSubmission($form_id, $form_arg, $form_state, FALSE); } /** * Tests the redirectForm() method when a redirect is expected. * Loading Loading @@ -527,6 +557,47 @@ public function testSubmitForm() { $this->assertNotEmpty($errors['options']); } /** * Tests the 'must_validate' $form_state flag. * * @covers ::validateForm */ public function testMustValidate() { $form_id = 'test_form_id'; $expected_form = $form_id(); $form_arg = $this->getMock('Drupal\Core\Form\FormInterface'); $form_arg->expects($this->any()) ->method('getFormId') ->will($this->returnValue($form_id)); $form_arg->expects($this->any()) ->method('buildForm') ->will($this->returnValue($expected_form)); $form_builder = $this->formBuilder; $form_arg->expects($this->exactly(2)) ->method('validateForm') ->will($this->returnCallback(function (&$form, &$form_state) use ($form_builder) { $form_builder->setErrorByName('test', $form_state, 'foo'); })); $form_state = array(); // This submission will trigger validation. $this->simulateFormSubmission($form_id, $form_arg, $form_state); $errors = $this->formBuilder->getErrors($form_state); $this->assertNotEmpty($errors['test']); // This submission will not re-trigger validation. $this->simulateFormSubmission($form_id, $form_arg, $form_state); $errors = $this->formBuilder->getErrors($form_state); $this->assertNotEmpty($errors['test']); // The must_validate flag will re-trigger validation. $form_state['must_validate'] = TRUE; $this->simulateFormSubmission($form_id, $form_arg, $form_state); $errors = $this->formBuilder->getErrors($form_state); $this->assertNotEmpty($errors['test']); } /** * Tests the flattenOptions() method. * Loading core/tests/Drupal/Tests/Core/Form/FormValidationTest.php +0 −31 Original line number Diff line number Diff line Loading @@ -26,37 +26,6 @@ public static function getInfo() { ); } public function testNoDuplicateErrorsForIdenticalForm() { $form_id = 'test_form_id'; $expected_form = $form_id(); $expected_form['test']['#required'] = TRUE; // Mock a form object that will be built three times. $form_arg = $this->getMockForm($form_id, $expected_form, 3); // The first form will have errors. $form_state = array(); $this->formBuilder->getFormId($form_arg, $form_state); $this->simulateFormSubmission($form_id, $form_arg, $form_state); $errors = $this->formBuilder->getErrors($form_state); $this->assertNotEmpty($errors['test']); // The second form will not have errors. $form_state = array(); $this->simulateFormSubmission($form_id, $form_arg, $form_state); $errors = $this->formBuilder->getErrors($form_state); $this->assertEmpty($errors); // Reset the form builder. $this->setupFormBuilder(); // On a new request, the first form will have errors again. $form_state = array(); $this->simulateFormSubmission($form_id, $form_arg, $form_state); $errors = $this->formBuilder->getErrors($form_state); $this->assertNotEmpty($errors['test']); } public function testUniqueHtmlId() { $form_id = 'test_form_id'; $expected_form = $form_id(); Loading Loading
core/lib/Drupal/Core/Form/FormBuilder.php +15 −9 Original line number Diff line number Diff line Loading @@ -105,13 +105,6 @@ class FormBuilder implements FormBuilderInterface { */ protected $forms; /** * An array of validated forms. * * @var array */ protected $validatedForms = array(); /** * An array of options used for recursive flattening. * Loading Loading @@ -301,6 +294,7 @@ public function getFormStateDefaults() { 'files' => array(), ), 'temporary' => array(), 'validation_complete' => FALSE, 'submitted' => FALSE, 'executed' => FALSE, 'programmed' => FALSE, Loading Loading @@ -431,6 +425,7 @@ protected function getUncacheableKeys() { 'input', 'method', 'submit_handlers', 'validation_complete', 'submitted', 'executed', 'validate_handlers', Loading Loading @@ -806,7 +801,14 @@ public function prepareForm($form_id, &$form, &$form_state) { * {@inheritdoc} */ public function validateForm($form_id, &$form, &$form_state) { if (isset($this->validatedForms[$form_id]) && empty($form_state['must_validate'])) { // If this form is flagged to always validate, ensure that previous runs of // validation are ignored. if (!empty($form_state['must_validate'])) { $form_state['validation_complete'] = FALSE; } // If this form has completed validation, do not validate again. if (!empty($form_state['validation_complete'])) { return; } Loading Loading @@ -888,7 +890,7 @@ protected function finalizeValidation($form_id, &$form, &$form_state) { // After validation, loop through and assign each element its errors. $this->setElementErrorsFromFormState($form, $form_state); // Mark this form as validated. $this->validatedForms[$form_id] = TRUE; $form_state['validation_complete'] = TRUE; } /** Loading Loading @@ -1198,6 +1200,10 @@ public function executeHandlers($type, &$form, &$form_state) { * {@inheritdoc} */ public function setErrorByName($name, array &$form_state, $message = '') { if (!empty($form_state['validation_complete'])) { throw new \LogicException('Form errors cannot be set after form validation has finished.'); } if (!isset($form_state['errors'][$name])) { $record = TRUE; if (isset($form_state['limit_validation_errors'])) { Loading
core/tests/Drupal/Tests/Core/Form/FormBuilderTest.php +71 −0 Original line number Diff line number Diff line Loading @@ -17,6 +17,8 @@ /** * Tests the form builder. * * @coversDefaultClass \Drupal\Core\Form\FormBuilder * * @group Drupal * @group Form */ Loading Loading @@ -218,6 +220,34 @@ public function testHandleRedirectWithResponse() { $this->assertSame($response, $form_state['response']); } /** * Tests that form errors during submission throw an exception. * * @covers ::setErrorByName * * @expectedException \LogicException * @expectedExceptionMessage Form errors cannot be set after form validation has finished. */ public function testFormErrorsDuringSubmission() { $form_id = 'test_form_id'; $expected_form = $form_id(); $form_arg = $this->getMockForm($form_id, $expected_form); $form_builder = $this->formBuilder; $form_arg->expects($this->any()) ->method('submitForm') ->will($this->returnCallback(function ($form, &$form_state) use ($form_builder) { $form_builder->setErrorByName('test', $form_state, 'Hello'); })); $form_state = array(); $this->formBuilder->getFormId($form_arg, $form_state); $form_state['values'] = array(); $form_state['input']['form_id'] = $form_id; $this->simulateFormSubmission($form_id, $form_arg, $form_state, FALSE); } /** * Tests the redirectForm() method when a redirect is expected. * Loading Loading @@ -527,6 +557,47 @@ public function testSubmitForm() { $this->assertNotEmpty($errors['options']); } /** * Tests the 'must_validate' $form_state flag. * * @covers ::validateForm */ public function testMustValidate() { $form_id = 'test_form_id'; $expected_form = $form_id(); $form_arg = $this->getMock('Drupal\Core\Form\FormInterface'); $form_arg->expects($this->any()) ->method('getFormId') ->will($this->returnValue($form_id)); $form_arg->expects($this->any()) ->method('buildForm') ->will($this->returnValue($expected_form)); $form_builder = $this->formBuilder; $form_arg->expects($this->exactly(2)) ->method('validateForm') ->will($this->returnCallback(function (&$form, &$form_state) use ($form_builder) { $form_builder->setErrorByName('test', $form_state, 'foo'); })); $form_state = array(); // This submission will trigger validation. $this->simulateFormSubmission($form_id, $form_arg, $form_state); $errors = $this->formBuilder->getErrors($form_state); $this->assertNotEmpty($errors['test']); // This submission will not re-trigger validation. $this->simulateFormSubmission($form_id, $form_arg, $form_state); $errors = $this->formBuilder->getErrors($form_state); $this->assertNotEmpty($errors['test']); // The must_validate flag will re-trigger validation. $form_state['must_validate'] = TRUE; $this->simulateFormSubmission($form_id, $form_arg, $form_state); $errors = $this->formBuilder->getErrors($form_state); $this->assertNotEmpty($errors['test']); } /** * Tests the flattenOptions() method. * Loading
core/tests/Drupal/Tests/Core/Form/FormValidationTest.php +0 −31 Original line number Diff line number Diff line Loading @@ -26,37 +26,6 @@ public static function getInfo() { ); } public function testNoDuplicateErrorsForIdenticalForm() { $form_id = 'test_form_id'; $expected_form = $form_id(); $expected_form['test']['#required'] = TRUE; // Mock a form object that will be built three times. $form_arg = $this->getMockForm($form_id, $expected_form, 3); // The first form will have errors. $form_state = array(); $this->formBuilder->getFormId($form_arg, $form_state); $this->simulateFormSubmission($form_id, $form_arg, $form_state); $errors = $this->formBuilder->getErrors($form_state); $this->assertNotEmpty($errors['test']); // The second form will not have errors. $form_state = array(); $this->simulateFormSubmission($form_id, $form_arg, $form_state); $errors = $this->formBuilder->getErrors($form_state); $this->assertEmpty($errors); // Reset the form builder. $this->setupFormBuilder(); // On a new request, the first form will have errors again. $form_state = array(); $this->simulateFormSubmission($form_id, $form_arg, $form_state); $errors = $this->formBuilder->getErrors($form_state); $this->assertNotEmpty($errors['test']); } public function testUniqueHtmlId() { $form_id = 'test_form_id'; $expected_form = $form_id(); Loading