Skip to content

Conversation

KevinVanSonsbeek
Copy link
Contributor

@KevinVanSonsbeek KevinVanSonsbeek commented Aug 26, 2025

Q A
Branch? 6.4
Bug fix? yes
New feature? no
Deprecations? no
Issues Fix #61136
License MIT

As mentioned in #61136 the auto generated php configs assumes ArrayNode's are by default lists.
The option to purposefully not normalize keys however indicates this will not always be the case.

The workflow component docs for example show defining metadata with a key value array

 $pullRequest->place()
        ->name('review')
        ->metadata(['description' => 'Human review']);

But actually using said implementation will result in phpstan reporting the following error:

 Parameter #1 $value of method Symfony\Config\Framework\Workflows\WorkflowsConfig\PlaceConfig::metadata() expects list|Symfony\Component\Config\Loader\ParamConfigurator, non-empty-array<string, mixed> given.

(This is present on a few more configs than just the Workflow components configs)

@carsonbot carsonbot added this to the 6.4 milestone Aug 26, 2025
@carsonbot carsonbot changed the title Bugfixphp configs incorrectly using list param types Bugfixphp configs incorrectly using list param types Aug 26, 2025
@KevinVanSonsbeek KevinVanSonsbeek changed the title Bugfixphp configs incorrectly using list param types Bugfix #61136: Generated php configs incorrectly using list param types Aug 26, 2025
@carsonbot carsonbot changed the title Bugfix #61136: Generated php configs incorrectly using list param types [Config] Bugfix #61136: Generated php configs incorrectly using list param types Aug 27, 2025
@nicolas-grekas nicolas-grekas changed the title [Config] Bugfix #61136: Generated php configs incorrectly using list param types [Config] Generated php configs incorrectly using list param types Aug 27, 2025
nicolas-grekas
nicolas-grekas previously approved these changes Aug 27, 2025
@stof
Copy link
Member

stof commented Aug 27, 2025

Being a list or a map is not indicated by the normalizeKeys flag. It is based on useAttributeAsKey, which is required anyway to be able to define keys in XML files.
This is important when multiple configurations are merged together, because the merging behavior (as a list or as a map) is based on this useAttributeAsKey setting.

The confusion is coming from the fact that when no merging is needed (because a single configuration provides this setting), the configured array is returned as is without processing it (and so keys are preserved even if they are not meant to be.

So -1 for this PR.

@KevinVanSonsbeek
Copy link
Contributor Author

Being a list or a map is not indicated by the normalizeKeys flag. It is based on useAttributeAsKey, which is required anyway to be able to define keys in XML files. This is important when multiple configurations are merged together, because the merging behavior (as a list or as a map) is based on this useAttributeAsKey setting.

The confusion is coming from the fact that when no merging is needed (because a single configuration provides this setting), the configured array is returned as is without processing it (and so keys are preserved even if they are not meant to be.

So -1 for this PR.

I understand that the normalizeKeys might not be the best option.
But the problem i encountered with the useAttributeAsKey is that it requires the name of the key. While configs like the metadata for the workflow component do not have any hard defined key. As you can fill the metadata with anything you want.

While looking at it normalizeKeys atleast felt like a somewhat more clear indication it should most likely not be a list but a map instead. (Allthough i do admit this might be a flawed assumption)

Would an option be to possibly implement a new option for the ArrayNode along the lines of a "preserveKeys" you can set. Where if true, we default to the array instead of list?

@xabbuh
Copy link
Member

xabbuh commented Aug 27, 2025

IIRC there was an idea in the past to introduce new mapNode()/listNode() methods in addition to (or as a replacement for) the arrayNode() method. I guess that would be the best way forward.

@stof
Copy link
Member

stof commented Aug 27, 2025

@KevinVanSonsbeek useAttributeAsKey does not require the key (that would defeat the purpose of defining a map). It requires the name of the XML attribute in which the key will be stored (PHP and Yaml files will use native keys).

regarding normalizeKeys, this was added later because we did not thought about this need for maps initially. There was some discussion in the past about deprecating defining maps (based on useAttributeAsKey) without disabling key normalization (and then making that automatic in the following major version, and then deprecating+removing normalizeKeys in the next next major version) but that work was never started.

Would an option be to possibly implement a new option for the ArrayNode along the lines of a "preserveKeys" you can set. Where if true, we default to the array instead of list?

useAttributeAsKey is exactly the feature needed to support maps (and supporting them in a way compatible with XML config files where you have to store the key in an attribute because XSD schemas don't allow using the tag name itself to store the key as tag names have to be known)

@KevinVanSonsbeek
Copy link
Contributor Author

@KevinVanSonsbeek useAttributeAsKey does not require the key (that would defeat the purpose of defining a map). It requires the name of the XML attribute in which the key will be stored (PHP and Yaml files will use native keys).

regarding normalizeKeys, this was added later because we did not thought about this need for maps initially. There was some discussion in the past about deprecating defining maps (based on useAttributeAsKey) without disabling key normalization (and then making that automatic in the following major version, and then deprecating+removing normalizeKeys in the next next major version) but that work was never started.

Would an option be to possibly implement a new option for the ArrayNode along the lines of a "preserveKeys" you can set. Where if true, we default to the array instead of list?

useAttributeAsKey is exactly the feature needed to support maps (and supporting them in a way compatible with XML config files where you have to store the key in an attribute because XSD schemas don't allow using the tag name itself to store the key as tag names have to be known)

Ooh, it seems i completely misunderstood the implementation in that case. I'll have a look later when i have some more time, and turn this PR into a draft for the meanwhile 👍🏼

@KevinVanSonsbeek KevinVanSonsbeek marked this pull request as draft August 27, 2025 11:18
@nicolas-grekas nicolas-grekas dismissed their stale review August 27, 2025 13:29

later comments proved me wrong

@KevinVanSonsbeek KevinVanSonsbeek force-pushed the bugfix/61136-php-configs-incorrectly-using-list-param-types branch from 18e89bc to ad7af98 Compare August 27, 2025 18:58
@@ -506,6 +506,7 @@ private function addWorkflowSection(ArrayNodeDefinition $rootNode): void
->end()
->arrayNode('metadata')
->normalizeKeys(false)
->useAttributeAsKey('key')
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@stof Adding the useAttributeAsKey would allow us to set specific metadata by key when using the config builder.
But we'd lose out on the ability to set all metadata in 1 call if we already created an instance of the parent node builder.

This is due to the generated config changing from

    /**
     * @param ParamConfigurator|list<ParamConfigurator|mixed> $value
     *
     * @return $this
     */
    public function metadata(ParamConfigurator|array $value): static
    {
        $this->_usedProperties['metadata'] = true;
        $this->metadata = $value;
    
        return $this;
    }

To

    /**
     * @return $this
     */
    public function metadata(string $key, mixed $value): static
    {
        $this->_usedProperties['metadata'] = true;
        $this->metadata[$key] = $value;

        return $this;
    }

Would a breaking change like this be a problem for a bugfix? (cc @nicolas-grekas )

As a sidenote, a update to the workflow docs would also be required in the case we would want to perform these changes.
As it shows some examples for the php configs setting the metadata as an array. And we would lose this option.

    $pullRequest->place()
        ->name('review')
        ->metadata(['description' => 'Human review']);
    $pullRequest->place()->name('merged');
    $pullRequest->place()
        ->name('closed')
        ->metadata(['bg_color' => 'DeepSkyBlue',]);

The XML config in general seems to be broken when parsing though. The example showed on the doc page shows nodes, that should actually be attributes.

But having a combination of place nodes, with and without metadata seems to actually break when using XML config, due to the callbacks used in the beforeNormalization possibly not accounting properly for the differences in data structure of the passed $places var, depending on the type of config used :(

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small example of the data contained in the $places argument of the normalization callback, when using the example configs from the docs:

PHP
^ array:6 [
  0 => array:1 [
    "name" => "start"
  ]
  1 => array:1 [
    "name" => "coding"
  ]
  2 => array:1 [
    "name" => "test"
  ]
  3 => array:2 [
    "name" => "review"
    "metadata" => array:1 [
      "description" => "Human review"
    ]
  ]
  4 => array:1 [
    "name" => "merged"
  ]
  5 => array:2 [
    "name" => "closed"
    "metadata" => array:1 [
      "bg_color" => "DeepSkyBlue"
    ]
  ]
]
^ array:6 [
 
YAML
^ array:6 [
  "start" => null
  "coding" => null
  "test" => null
  "review" => array:1 [
    "metadata" => array:1 [
      "description" => "Human review"
    ]
  ]
  "merged" => null
  "closed" => array:1 [
    "metadata" => array:1 [
      "bg_color" => "DeepSkyBlue"
    ]
  ]
]
 
# XML
^ array:6 [
  0 => "start"
  1 => "coding"
  2 => "test"
  3 => array:2 [
    "name" => "review"
    "metadata" => array:2 [
      "description" => "Human review"
      "other_value" => "Some other value"
    ]
  ]
  4 => "merged"
  5 => array:2 [
    "name" => "closed"
    "metadata" => array:1 [
      "bg_color" => "DeepSkyBlue"
    ]
  ]
]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants