1313
1414use Symfony \Component \DependencyInjection \ContainerBuilder ;
1515use Symfony \Component \DependencyInjection \Extension \ConfigurationExtensionInterface ;
16+ use Symfony \Component \DependencyInjection \Extension \Extension ;
1617use Symfony \Component \DependencyInjection \Extension \PrependExtensionInterface ;
18+ use Symfony \Component \DependencyInjection \Parameterbag \EnvPlaceholderParameterBag ;
1719
1820/**
1921 * Merges extension configs into the container builder.
@@ -43,7 +45,10 @@ public function process(ContainerBuilder $container)
4345 // this extension was not called
4446 continue ;
4547 }
46- $ config = $ container ->getParameterBag ()->resolveValue ($ config );
48+ // EnvPlaceholderParameterBag tracks env vars when calling resolveValue().
49+ // Clone so that tracking is done in a dedicated bag.
50+ $ resolvingBag = clone $ container ->getParameterBag ();
51+ $ config = $ resolvingBag ->resolveValue ($ config );
4752
4853 $ tmpContainer = new ContainerBuilder ($ container ->getParameterBag ());
4954 $ tmpContainer ->setResourceTracking ($ container ->isTrackingResources ());
@@ -58,6 +63,15 @@ public function process(ContainerBuilder $container)
5863
5964 $ extension ->load ($ config , $ tmpContainer );
6065
66+ if ($ resolvingBag instanceof EnvPlaceholderParameterBag) {
67+ // $resolvingBag keeps track of env vars encoutered *before* merging configs
68+ if ($ extension instanceof Extension) {
69+ // but we don't want to keep track of env vars that are *overridden* when configs are merged
70+ $ resolvingBag = new MergeExtensionConfigurationParameterBag ($ extension , $ resolvingBag );
71+ }
72+ $ container ->getParameterBag ()->mergeEnvPlaceholders ($ resolvingBag );
73+ }
74+
6175 $ container ->merge ($ tmpContainer );
6276 $ container ->getParameterBag ()->add ($ parameters );
6377 }
@@ -66,3 +80,58 @@ public function process(ContainerBuilder $container)
6680 $ container ->addAliases ($ aliases );
6781 }
6882}
83+
84+ /**
85+ * @internal
86+ */
87+ class MergeExtensionConfigurationParameterBag extends EnvPlaceholderParameterBag
88+ {
89+ private $ beforeProcessingEnvPlaceholders ;
90+
91+ public function __construct (Extension $ extension , parent $ resolvingBag )
92+ {
93+ $ this ->beforeProcessingEnvPlaceholders = $ resolvingBag ->getEnvPlaceholders ();
94+ $ config = $ this ->resolveEnvPlaceholders ($ extension ->getProcessedConfigs ());
95+ parent ::__construct ($ this ->resolveEnvReferences ($ config ));
96+ }
97+
98+ /**
99+ * {@inheritdoc}
100+ */
101+ public function getEnvPlaceholders ()
102+ {
103+ // contains the list of env vars that are still used after configs have been merged
104+ $ envPlaceholders = parent ::getEnvPlaceholders ();
105+
106+ foreach ($ envPlaceholders as $ env => $ placeholders ) {
107+ if (isset ($ this ->beforeProcessingEnvPlaceholders [$ env ])) {
108+ // for still-used env vars, keep track of their before-processing placeholders
109+ $ envPlaceholders [$ env ] += $ this ->beforeProcessingEnvPlaceholders [$ env ];
110+ }
111+ }
112+
113+ return $ envPlaceholders ;
114+ }
115+
116+ /**
117+ * Replaces-back env placeholders to their original "%env(FOO)%" version.
118+ */
119+ private function resolveEnvPlaceholders ($ value )
120+ {
121+ if (is_array ($ value )) {
122+ foreach ($ value as $ k => $ v ) {
123+ $ value [$ this ->resolveEnvPlaceholders ($ k )] = $ this ->resolveEnvPlaceholders ($ v );
124+ }
125+ } elseif (is_string ($ value )) {
126+ foreach ($ this ->beforeProcessingEnvPlaceholders as $ env => $ placeholders ) {
127+ foreach ($ placeholders as $ placeholder ) {
128+ if (false !== stripos ($ value , $ placeholder )) {
129+ $ value = str_ireplace ($ placeholder , "%env( $ env)% " , $ value );
130+ }
131+ }
132+ }
133+ }
134+
135+ return $ value ;
136+ }
137+ }
0 commit comments