symfony: PropertyPathMapper crashing with CollectionType when framework.property_access.throw_exception_on_invalid_index is true

Symfony version(s) affected: 3.4.*|4.* (Probably also 2.7+?)

Description
If you enable the option in the framework to throw exceptions on invalid index for the propery accessor:

framework:
    property_access:
        throw_exception_on_invalid_index: true

(Which I think should be the default behavior by the way, but this is outside the scope of this bug)

When you have a form using a CollectionType, the PropertyPathMapper will crash when trying to access invalid indexes when the ResizeFormListener adds a new field to the form. (Which happens when you add an element to your collection)

How to reproduce
On a Symfony 3.4 standard edition, enable the throw_exception_on_invalid_index option as mentionned before. Then use this code in the DefaultController

class DefaultController extends Controller
{
    /**
     * @Route("/", name="homepage")
     *
     * @param Request $request
     *
     * @return \Symfony\Component\HttpFoundation\Response
     */
    public function indexAction(Request $request)
    {
        $formBuilder = $this->createFormBuilder(['collection' => ['test']]);
        $formBuilder->add(
            'collection',
            CollectionType::class,
            [
                'allow_add' => true,
                'entry_type' => TextType::class,
            ]
        );
        $form = $formBuilder->getForm();
        $form->handleRequest($request);

        return $this->render(
            'default/index.html.twig',
            [
                'form' => $form->createView(),
            ]
        );
    }
}

And this code for the template:

{% extends 'base.html.twig' %}

{% block body %}
    {{ form_start(form) }}
    {{ form_widget(form) }}
    <input type="text" name="form[collection][1]" value="invalid index">
    <button type="submit">Submit</button>
    {{ form_end(form) }}
{% endblock %}

This adds the element manually because this a the simplest way to test this.

Submit : Crash

Cannot read index "1" while trying to traverse path "[1]". Available indices are "Array
(
[0] => 0
)
".

Possible Solutions
There are many way to tackle this problem :

  • Remove the throw_exception_on_invalid_index option if it’s not meant to be used (I’m against this because I think it should be true by default)
  • Inject a custom PropertyAccessor in the PropertyPathMapper; not form.property_accessor. (I find this solution a bit strange)
  • Make the form.property_accessor ignore any settings from the framework.property_accessor.\* configuration.
  • Catch the exception properly inside the PropertyPathMapper
  • Create a custom CollectionPropertyPathMapper only for the CollectionType that uses a dedicated property accessor that ignores the global setting.
  • Create a custom CollectionPropertyPathMapper only for the CollectionType that catches the exception properly. (I’m in favor of this one)

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Reactions: 3
  • Comments: 16 (6 by maintainers)

Commits related to this issue

Most upvoted comments

After reviewing this issue in Symfony 4.4, I discovered that a new feature was introduced in v4.3: https://github.com/symfony/symfony/issues/30536

The option throw_exception_on_invalid_property_path defaults to true and do exactly what it’s supposed to do: Throwing exceptions on invalid property paths but allowing write access to non-existing indices.

I’m closing this issue as the 3.* branch is not supported anymore so this is now irrelevant.

I will try to create a PR soon using the solution proposed by @xabbuh

I think using a different property accessor in the Form component with the option disabled could be a sensible fix.

@alamirault or anyone stumbling upon this issue, I’ll provide a PR when I have time, meanwhile you can install sidus/base-bundle for a quick fix.