bug #10232 [Form] Fix "Array was modified outside object" in ResizeFormListener. (Chekote)

This PR was submitted for the 2.4 branch but it was merged into the 2.3 branch instead (closes #10232).

Discussion
----------

[Form] Fix "Array was modified outside object" in ResizeFormListener.

| Q             | A
| ------------- | ---
| Bug fix?      | yes
| New feature?  | no
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets |
| License       | MIT
| Doc PR        |

The onSubmit() method of the ResizeFormListener class is assuming the data is an array, and calling unset directly inside a foreach. This works fine in most scenarios, but if data is an instance of IteratorAggregate, it breaks with the following error:

Symfony\Component\Form\Extension\Core\EventListener\ResizeFormListener::onSubmit(): ArrayIterator::next(): Array was modified outside object and internal position is no longer valid in ./vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/EventListener/ResizeFormListener.php line 142

This is because the foreach loop is using an Iterator in the background, but the ResizeFormListener has unset the underlying data directly, causing the Iterator and data to be out of sync. When the data is an instance of IteratorAggregate, the loop should use the iterator directly and not rely on foreach.

The onSubmit method has been updated accordingly.

Commits
-------

4116b6e Fix "Array was modified outside object" in ResizeFormListener.
This commit is contained in:
Fabien Potencier 2014-02-11 15:26:59 +01:00
commit 57a4942812
1 changed files with 15 additions and 3 deletions

View File

@ -139,9 +139,21 @@ class ResizeFormListener implements EventSubscriberInterface
// The data mapper only adds, but does not remove items, so do this
// here
if ($this->allowDelete) {
foreach ($data as $name => $child) {
if (!$form->has($name)) {
unset($data[$name]);
if ($data instanceof \IteratorAggregate) {
$iter = $data->getIterator();
while ($iter->valid()) {
$name = $iter->key();
if ($form->has($name)) {
$iter->next();
} else {
$iter->offsetUnset($name);
}
}
} else {
foreach ($data as $name => $child) {
if (!$form->has($name)) {
unset($data[$name]);
}
}
}
}