Skip to content

Commit e6edf5d

Browse files
Merge pull request #34 from matthiasnoback/igneus-fix-infinite-loop-on-unedited-field
Igneus fix infinite loop on unedited field
2 parents 288356a + 4cb4738 commit e6edf5d

File tree

8 files changed

+126
-13
lines changed

8 files changed

+126
-13
lines changed

.travis.yml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,9 @@ language: php
22

33
matrix:
44
include:
5-
- php: 7.0
6-
env: SYMFONY_VERSION=3.0.*
75
- php: 7.0
86
env: SYMFONY_VERSION=3.4.*
9-
- php: 7.1
7+
- php: 7.2
108
env: SYMFONY_VERSION=4.0.*
119

1210
before_install:

composer.json

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,18 +24,18 @@
2424
},
2525
"require": {
2626
"php": ">=7.0",
27-
"symfony/form": "~3.0|~4.0",
28-
"symfony/console": "~3.0|~4.0",
29-
"symfony/translation": "~3.0|~4.0"
27+
"symfony/form": "~3.4|~4.0",
28+
"symfony/console": "~3.4|~4.0",
29+
"symfony/translation": "~3.4|~4.0"
3030
},
3131
"require-dev": {
3232
"beberlei/assert": "~2.1",
3333
"behat/behat": "~3.0",
3434
"friendsofphp/php-cs-fixer": "^1.10|^2.2",
35-
"symfony/finder": "~3.0|~4.0",
36-
"symfony/framework-bundle": "~3.0|~4.0",
37-
"symfony/validator": "~3.0|~4.0",
38-
"symfony/yaml": "~3.0|~4.0",
39-
"symfony/security": "~3.0|~4.0"
35+
"symfony/finder": "~3.4|~4.0",
36+
"symfony/framework-bundle": "~3.4|~4.0",
37+
"symfony/validator": "~3.4|~4.0",
38+
"symfony/yaml": "~3.4|~4.0",
39+
"symfony/security": "~3.4|~4.0"
4040
}
4141
}

features/interactive.feature

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,3 +207,19 @@ Feature: It is possible to interactively fill in a form from the CLI
207207
[price] => 10.95
208208
)
209209
"""
210+
211+
Scenario: Secret required field
212+
When I run the command "form:secret_required_field" and I provide as input
213+
"""
214+
Jelmer[enter]
215+
"""
216+
Then the command was not successful
217+
And the output should contain
218+
"""
219+
Invalid data provided: ERROR: This value should not be blank.
220+
"""
221+
And the output should contain
222+
"""
223+
[RuntimeException]
224+
Errors out of the form's scope - do you have validation constraints on properties not used in the form? (Violations on unused fields: data.fieldNotUsedInTheForm)
225+
"""

src/Console/Helper/FormHelper.php

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@
77
use Symfony\Component\Console\Helper\Helper;
88
use Symfony\Component\Console\Input\InputInterface;
99
use Symfony\Component\Console\Output\OutputInterface;
10+
use Symfony\Component\Form\FormError;
11+
use Symfony\Component\Form\FormErrorIterator;
1012
use Symfony\Component\Form\FormInterface;
13+
use Symfony\Component\Validator\ConstraintViolationInterface;
1114

1215
class FormHelper extends Helper
1316
{
@@ -69,7 +72,16 @@ public function interactUsingForm(
6972
$data = $form->getData();
7073

7174
if (!$form->isValid()) {
72-
$output->write(sprintf('Invalid data provided: %s', $form->getErrors(true, false)));
75+
$formErrors = $form->getErrors(true, false);
76+
$output->write(sprintf('Invalid data provided: %s', $formErrors));
77+
if ($this->noErrorsCanBeFixed($formErrors)) {
78+
$violationPaths = $this->constraintViolationPaths($formErrors);
79+
$hint = (count($violationPaths) > 0 ? ' (Violations on unused fields: '.implode(', ', $violationPaths).')' : '');
80+
throw new \RuntimeException(
81+
'Errors out of the form\'s scope - do you have validation constraints on properties not used in the form?'
82+
. $hint
83+
);
84+
}
7385
array_map(
7486
function (FormInterface $formField) use (&$validFormFields) {
7587
if ($formField->isValid()) {
@@ -83,4 +95,33 @@ function (FormInterface $formField) use (&$validFormFields) {
8395

8496
return $data;
8597
}
98+
99+
/**
100+
* @param FormErrorIterator $errors
101+
* @return bool
102+
*/
103+
protected function noErrorsCanBeFixed(FormErrorIterator $errors)
104+
{
105+
// none of the remaining errors is related to a value of a form field
106+
return $errors->count() > 0 &&
107+
0 === count(array_filter(iterator_to_array($errors), function ($error) {
108+
return $error instanceof FormErrorIterator;
109+
}));
110+
}
111+
112+
protected function constraintViolationPaths(FormErrorIterator $errors)
113+
{
114+
$paths = [];
115+
foreach ($errors as $error) {
116+
if (!$error instanceof FormError) {
117+
continue;
118+
}
119+
$cause = $error->getCause();
120+
if (!$cause instanceof ConstraintViolationInterface) {
121+
continue;
122+
}
123+
$paths[] = $cause->getPropertyPath();
124+
}
125+
return $paths;
126+
}
86127
}

test/Command/PrintFormDataCommand.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ protected function execute(InputInterface $input, OutputInterface $output)
1818
}
1919

2020
return $data;
21-
}, $formData);
21+
}, (array)$formData);
2222

2323
$output->write(print_r($printData, true));
2424
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php
2+
3+
namespace Matthias\SymfonyConsoleForm\Tests\Form\Data;
4+
5+
use Symfony\Component\Validator\Constraints as Assert;
6+
7+
class SecretRequiredField
8+
{
9+
/**
10+
* @Assert\NotBlank()
11+
*/
12+
public $name;
13+
14+
/**
15+
* @Assert\NotBlank()
16+
*/
17+
public $fieldNotUsedInTheForm;
18+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?php
2+
3+
namespace Matthias\SymfonyConsoleForm\Tests\Form;
4+
5+
use Matthias\SymfonyConsoleForm\Tests\Form\Data\SecretRequiredField;
6+
use Symfony\Component\Form\AbstractType;
7+
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
8+
use Symfony\Component\Form\Extension\Core\Type\TextType;
9+
use Symfony\Component\Form\FormBuilderInterface;
10+
use Symfony\Component\OptionsResolver\OptionsResolver;
11+
use Symfony\Component\Validator\Constraints\Length;
12+
13+
class SecretRequiredFieldType extends AbstractType
14+
{
15+
public function buildForm(FormBuilderInterface $builder, array $options)
16+
{
17+
$builder->add(
18+
'name',
19+
TextType::class,
20+
[
21+
'label' => 'Your name',
22+
]
23+
);
24+
}
25+
26+
public function configureOptions(OptionsResolver $resolver)
27+
{
28+
$resolver->setDefaults([
29+
'data_class' => SecretRequiredField::class,
30+
]);
31+
}
32+
}

test/config.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,14 @@ services:
9090
tags:
9191
- { name: console.command }
9292

93+
secret_required_field_command:
94+
class: Matthias\SymfonyConsoleForm\Tests\Command\PrintFormDataCommand
95+
arguments:
96+
- Matthias\SymfonyConsoleForm\Tests\Form\SecretRequiredFieldType
97+
- secret_required_field
98+
tags:
99+
- { name: console.command }
100+
93101
framework:
94102
form:
95103
csrf_protection: true

0 commit comments

Comments
 (0)