2010-12-14 23:25:33 +00:00
< ? php
/*
2011-01-15 13:29:43 +00:00
* This file is part of the Symfony package .
2010-12-14 23:25:33 +00:00
*
2011-03-06 11:40:06 +00:00
* ( c ) Fabien Potencier < fabien @ symfony . com >
2010-12-14 23:25:33 +00:00
*
2011-01-15 13:29:43 +00:00
* For the full copyright and license information , please view the LICENSE
* file that was distributed with this source code .
2010-12-14 23:25:33 +00:00
*/
2011-01-15 13:29:43 +00:00
namespace Symfony\Component\DependencyInjection\Compiler ;
2018-03-03 10:07:55 +00:00
use Symfony\Component\Config\Definition\BaseNode ;
2011-01-15 13:29:43 +00:00
use Symfony\Component\DependencyInjection\ContainerBuilder ;
2017-09-23 11:08:51 +01:00
use Symfony\Component\DependencyInjection\Exception\LogicException ;
2017-10-08 14:44:15 +01:00
use Symfony\Component\DependencyInjection\Exception\RuntimeException ;
2016-12-28 10:12:23 +00:00
use Symfony\Component\DependencyInjection\Extension\ConfigurationExtensionInterface ;
2017-08-16 22:15:56 +01:00
use Symfony\Component\DependencyInjection\Extension\Extension ;
2017-09-23 11:08:51 +01:00
use Symfony\Component\DependencyInjection\Extension\ExtensionInterface ;
2013-01-23 16:42:45 +00:00
use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface ;
2017-08-27 14:23:17 +01:00
use Symfony\Component\DependencyInjection\ParameterBag\EnvPlaceholderParameterBag ;
2017-09-23 11:08:51 +01:00
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface ;
2011-01-15 13:29:43 +00:00
2010-12-14 23:25:33 +00:00
/**
2014-12-21 17:00:50 +00:00
* Merges extension configs into the container builder .
2010-12-14 23:25:33 +00:00
*
2011-03-06 11:40:06 +00:00
* @ author Fabien Potencier < fabien @ symfony . com >
2010-12-14 23:25:33 +00:00
*/
class MergeExtensionConfigurationPass implements CompilerPassInterface
{
/**
2014-04-15 06:57:34 +01:00
* { @ inheritdoc }
2010-12-14 23:25:33 +00:00
*/
public function process ( ContainerBuilder $container )
{
$parameters = $container -> getParameterBag () -> all ();
$definitions = $container -> getDefinitions ();
$aliases = $container -> getAliases ();
2015-01-10 09:50:24 +00:00
$exprLangProviders = $container -> getExpressionLanguageProviders ();
2018-03-03 10:07:55 +00:00
$configAvailable = class_exists ( BaseNode :: class );
2010-12-14 23:25:33 +00:00
2012-09-20 22:20:53 +01:00
foreach ( $container -> getExtensions () as $extension ) {
if ( $extension instanceof PrependExtensionInterface ) {
$extension -> prepend ( $container );
}
}
2011-02-15 04:58:18 +00:00
foreach ( $container -> getExtensions () as $name => $extension ) {
2011-03-03 12:53:29 +00:00
if ( ! $config = $container -> getExtensionConfig ( $name )) {
// this extension was not called
continue ;
}
2017-08-28 16:40:54 +01:00
$resolvingBag = $container -> getParameterBag ();
if ( $resolvingBag instanceof EnvPlaceholderParameterBag && $extension instanceof Extension ) {
// create a dedicated bag so that we can track env vars per-extension
$resolvingBag = new MergeExtensionConfigurationParameterBag ( $resolvingBag );
2018-03-03 10:07:55 +00:00
if ( $configAvailable ) {
BaseNode :: setPlaceholderUniquePrefix ( $resolvingBag -> getEnvPlaceholderUniquePrefix ());
}
2017-08-28 16:40:54 +01:00
}
2017-08-16 22:15:56 +01:00
$config = $resolvingBag -> resolveValue ( $config );
2011-03-03 12:53:29 +00:00
2017-11-26 17:52:52 +00:00
try {
2017-11-29 13:28:14 +00:00
$tmpContainer = new MergeExtensionConfigurationContainerBuilder ( $extension , $resolvingBag );
2017-11-26 17:52:52 +00:00
$tmpContainer -> setResourceTracking ( $container -> isTrackingResources ());
$tmpContainer -> addObjectResource ( $extension );
if ( $extension instanceof ConfigurationExtensionInterface && null !== $configuration = $extension -> getConfiguration ( $config , $tmpContainer )) {
$tmpContainer -> addObjectResource ( $configuration );
}
2010-12-14 23:25:33 +00:00
2017-11-26 17:52:52 +00:00
foreach ( $exprLangProviders as $provider ) {
$tmpContainer -> addExpressionLanguageProvider ( $provider );
}
2015-01-10 09:50:24 +00:00
2017-11-26 17:52:52 +00:00
$extension -> load ( $config , $tmpContainer );
} catch ( \Exception $e ) {
if ( $resolvingBag instanceof MergeExtensionConfigurationParameterBag ) {
$container -> getParameterBag () -> mergeEnvPlaceholders ( $resolvingBag );
}
throw $e ;
2018-03-03 10:07:55 +00:00
} finally {
if ( $configAvailable ) {
BaseNode :: resetPlaceholders ();
}
2017-11-26 17:52:52 +00:00
}
2010-12-14 23:25:33 +00:00
2017-08-28 16:40:54 +01:00
if ( $resolvingBag instanceof MergeExtensionConfigurationParameterBag ) {
// don't keep track of env vars that are *overridden* when configs are merged
2017-08-29 13:43:33 +01:00
$resolvingBag -> freezeAfterProcessing ( $extension , $tmpContainer );
2017-08-16 22:15:56 +01:00
}
2011-02-15 04:58:18 +00:00
$container -> merge ( $tmpContainer );
2015-03-12 14:14:24 +00:00
$container -> getParameterBag () -> add ( $parameters );
2010-12-14 23:25:33 +00:00
}
$container -> addDefinitions ( $definitions );
$container -> addAliases ( $aliases );
}
}
2017-08-16 22:15:56 +01:00
/**
* @ internal
*/
class MergeExtensionConfigurationParameterBag extends EnvPlaceholderParameterBag
{
2017-08-28 16:40:54 +01:00
private $processedEnvPlaceholders ;
2017-08-16 22:15:56 +01:00
2017-08-28 16:40:54 +01:00
public function __construct ( parent $parameterBag )
2017-08-16 22:15:56 +01:00
{
2017-08-28 16:40:54 +01:00
parent :: __construct ( $parameterBag -> all ());
$this -> mergeEnvPlaceholders ( $parameterBag );
2017-08-18 16:51:40 +01:00
}
2017-08-29 13:43:33 +01:00
public function freezeAfterProcessing ( Extension $extension , ContainerBuilder $container )
2017-08-18 16:51:40 +01:00
{
2017-08-29 13:43:33 +01:00
if ( ! $config = $extension -> getProcessedConfigs ()) {
// Extension::processConfiguration() wasn't called, we cannot know how configs were merged
return ;
}
2019-01-16 09:39:14 +00:00
$this -> processedEnvPlaceholders = [];
2017-08-28 23:20:37 +01:00
2017-08-29 13:43:33 +01:00
// serialize config and container to catch env vars nested in object graphs
$config = serialize ( $config ) . serialize ( $container -> getDefinitions ()) . serialize ( $container -> getAliases ()) . serialize ( $container -> getParameterBag () -> all ());
2017-08-28 23:20:37 +01:00
foreach ( parent :: getEnvPlaceholders () as $env => $placeholders ) {
foreach ( $placeholders as $placeholder ) {
if ( false !== stripos ( $config , $placeholder )) {
$this -> processedEnvPlaceholders [ $env ] = $placeholders ;
break ;
}
}
}
2017-08-16 22:15:56 +01:00
}
/**
* { @ inheritdoc }
*/
public function getEnvPlaceholders ()
{
2017-08-28 16:40:54 +01:00
return null !== $this -> processedEnvPlaceholders ? $this -> processedEnvPlaceholders : parent :: getEnvPlaceholders ();
2017-08-16 22:15:56 +01:00
}
2018-03-03 10:07:55 +00:00
public function getUnusedEnvPlaceholders () : array
{
2019-01-16 18:24:45 +00:00
return null === $this -> processedEnvPlaceholders ? [] : array_diff_key ( parent :: getEnvPlaceholders (), $this -> processedEnvPlaceholders );
2018-03-03 10:07:55 +00:00
}
2017-08-16 22:15:56 +01:00
}
2017-09-23 11:08:51 +01:00
/**
* A container builder preventing using methods that wouldn ' t have any effect from extensions .
*
* @ internal
*/
class MergeExtensionConfigurationContainerBuilder extends ContainerBuilder
{
private $extensionClass ;
public function __construct ( ExtensionInterface $extension , ParameterBagInterface $parameterBag = null )
{
parent :: __construct ( $parameterBag );
2018-07-26 09:45:46 +01:00
$this -> extensionClass = \get_class ( $extension );
2017-09-23 11:08:51 +01:00
}
/**
* { @ inheritdoc }
*/
2017-09-26 07:23:35 +01:00
public function addCompilerPass ( CompilerPassInterface $pass , $type = PassConfig :: TYPE_BEFORE_OPTIMIZATION , int $priority = 0 )
2017-09-23 11:08:51 +01:00
{
2018-07-26 09:45:46 +01:00
throw new LogicException ( sprintf ( 'You cannot add compiler pass "%s" from extension "%s". Compiler passes must be registered before the container is compiled.' , \get_class ( $pass ), $this -> extensionClass ));
2017-09-23 11:08:51 +01:00
}
/**
* { @ inheritdoc }
*/
public function registerExtension ( ExtensionInterface $extension )
{
2018-07-26 09:45:46 +01:00
throw new LogicException ( sprintf ( 'You cannot register extension "%s" from "%s". Extensions must be registered before the container is compiled.' , \get_class ( $extension ), $this -> extensionClass ));
2017-09-23 11:08:51 +01:00
}
/**
* { @ inheritdoc }
*/
2017-09-26 07:23:35 +01:00
public function compile ( bool $resolveEnvPlaceholders = false )
2017-09-23 11:08:51 +01:00
{
throw new LogicException ( sprintf ( 'Cannot compile the container in extension "%s".' , $this -> extensionClass ));
}
2017-10-08 14:44:15 +01:00
/**
* { @ inheritdoc }
*/
public function resolveEnvPlaceholders ( $value , $format = null , array & $usedEnvs = null )
{
if ( true !== $format || ! \is_string ( $value )) {
return parent :: resolveEnvPlaceholders ( $value , $format , $usedEnvs );
}
$bag = $this -> getParameterBag ();
$value = $bag -> resolveValue ( $value );
2019-07-04 10:24:58 +01:00
if ( ! $bag instanceof EnvPlaceholderParameterBag ) {
return parent :: resolveEnvPlaceholders ( $value , $format , $usedEnvs );
}
2017-10-08 14:44:15 +01:00
foreach ( $bag -> getEnvPlaceholders () as $env => $placeholders ) {
if ( false === strpos ( $env , ':' )) {
continue ;
}
foreach ( $placeholders as $placeholder ) {
if ( false !== stripos ( $value , $placeholder )) {
throw new RuntimeException ( sprintf ( 'Using a cast in "env(%s)" is incompatible with resolution at compile time in "%s". The logic in the extension should be moved to a compiler pass, or an env parameter with no cast should be used instead.' , $env , $this -> extensionClass ));
}
}
}
return parent :: resolveEnvPlaceholders ( $value , $format , $usedEnvs );
}
2017-09-23 11:08:51 +01:00
}