Issue 1:
Currently, the only way to make an arrayNode a map, rather than a list, is to use useAttributeAsKey, the problem with that method is that it requires you to use an attribute within the array as the key. What if you want to use the array key as the .. array key?

For example

->arrayNode('servers')
->prototype('array')
->children()
->scalarNode('username')
->isRequired()
->end()
->scalarNode('password')
->isRequired()
->end()
->end()
->end()
->end()

with config

something:
servers:
serverA:
username: userA
password: passwordA
serverB:
username: userB
password: passwordB

comes out as

servers [
serverA => [username: userA, password: passwordA],
serverB => [username: userB, password: passwordB],
]

Great! Until.. you merge config across files. If you put serverA in a file and import it, then add serverB, it comes out like -

servers [
serverA => [username: userA, password: passwordA],
0 => [username: userB, password: passwordB],
]

The key for the nodes get lost. This issue is described on https://symfony.com/doc/current/components/config/definition.html

As of writing this, there is an inconsistency: if only one file provides the configuration in question, the keys (i.e. sf_connection and default) are not lost. But if more than one file provides the configuration, the keys are lost as described above.

that's ..not great. Especially if you're relying on referencing that key from somewhere else in your config (i.e. default_server: ..). However, since we've not indicated the arrayNode is a map, I guess this behaviour was undefined (probably odd it kept the string keys in the first place).

You could argue that if the key is important, to put it in the attributes, like

something:
servers:
-
username: userA
password: passwordA
server: serverA
-
username: userB
password: passwordB
server: serverB

with useAttributeAsKey('server'). However, this is not a very efficient way of writing configuration - it's clearly more for XML based processing, not YAML.

One way people have been getting around this is to use useAttributeAsKey with an attribute that doesn't exist, like ->useAttributeAsKey('some_field_that_doesnt_exist') which seems pretty hacky.

Suggest something like

adding ->useAsMap() which turns the array into a map internally, or

breaking arrayNode into listNode and mapNode types (with arrayNode defaulting to listNode for backward compatibility for a while).

Related

#12304

symfony/symfony-docs#4509

#17436

https://stackoverflow.com/questions/10924504/allowing-key-value-pairs-in-symfony-2-bundle-semantic-configuration

Issue 2:
When passing to 'useAttributeAsKey' function key that is not used in below config, in the test environment the side effect is that $config in [Bundle]Extension.php contains settings only for this env. When this method is not being used at all, the $config contains config for each env.

Bundle\DependencyInjection\Configuration::getConfigTreeBuilder():

$root->children()
->arrayNode('some_config')
->prototype('array')
->children()
->scalarNode('some_key')->end()
->end()
->end()
->end()
->end();

config_dev:

my_app:
some_config:
Option1:
some_key: yyyy
Option2:
some_key: xxx

config_test:

my_app:
some_config:
Option1:
some_key: aaaaa
Option2:
some_key: bbbbb

Bundle\DependencyInjection\BundleExtension::load():

....

var_dump($config['some_config'])
....

Expected result on test env:

array (size=2)
Option1 =>
some_key: aaaaa
Option2 =>
some_key: bbbbb

Actual result:

array (size=4)
Option1 =>
some_key: yyyy
Option2 =>
some_key: xxx
0 =>
some_key: aaaaa
1 =>
some_key: bbbbb

How to fix that?

Bundle\DependencyInjection\Configuration::getConfigTreeBuilder():

$root->children()
->arrayNode('some_config')
->useAttributeAsKey('some_not_existing_key_to_prevent_associative_array_in_different_env_SYMFONY_BUG')
->prototype('array')
->children()
->scalarNode('some_key')->end()
->end()
->end()
->end()
->end();

Then:

Bundle\DependencyInjection\BundleExtension::load():

....
var_dump($config['some_config'])
....

Actual result on test env after fixing:

array (size=2)
Option1 =>
some_key: aaaaa
Option2 =>
some_key: bbbbb

But I don't think it's a proper behaviour.