Merge branch '3.4'
* 3.4: Do not display short exception trace for built-in console exceptions Deprecated the web_profiler.position option [DI] rename ResolveDefinitionTemplatesPass to ResolveChildDefinitionsPass Throw a meaningful exception when an undefined user provider is used inside a firewall [DI] Allow processing env vars
This commit is contained in:
commit
8357f2ae4a
@ -52,6 +52,9 @@ DependencyInjection
|
||||
|
||||
* Case insensitivity of parameter names is deprecated and will be removed in 4.0.
|
||||
|
||||
* The `ResolveDefinitionTemplatesPass` class is deprecated and will be removed in 4.0.
|
||||
Use the `ResolveChildDefinitionsPass` class instead.
|
||||
|
||||
Debug
|
||||
-----
|
||||
|
||||
|
@ -159,6 +159,9 @@ DependencyInjection
|
||||
* The `DefinitionDecorator` class has been removed. Use the `ChildDefinition`
|
||||
class instead.
|
||||
|
||||
* The `ResolveDefinitionTemplatesPass` class has been removed.
|
||||
Use the `ResolveChildDefinitionsPass` class instead.
|
||||
|
||||
* Using unsupported configuration keys in YAML configuration files raises an
|
||||
exception.
|
||||
|
||||
|
@ -73,6 +73,8 @@ CHANGELOG
|
||||
`EventDispatcherDebugCommand`, `RouterDebugCommand`, `RouterMatchCommand`,
|
||||
`TranslationDebugCommand`, `TranslationUpdateCommand`, `XliffLintCommand`
|
||||
and `YamlLintCommand` classes have been marked as final
|
||||
* Deprecated the `web_profiler.position` config option (in Symfony 4.0 the toolbar
|
||||
will always be displayed at the bottom).
|
||||
|
||||
3.3.0
|
||||
-----
|
||||
|
@ -36,6 +36,7 @@ use Symfony\Component\DependencyInjection\ChildDefinition;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\DependencyInjection\Definition;
|
||||
use Symfony\Component\DependencyInjection\EnvVarProcessorInterface;
|
||||
use Symfony\Component\DependencyInjection\Exception\LogicException;
|
||||
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
@ -263,6 +264,8 @@ class FrameworkExtension extends Extension
|
||||
->addTag('console.command');
|
||||
$container->registerForAutoconfiguration(ResourceCheckerInterface::class)
|
||||
->addTag('config_cache.resource_checker');
|
||||
$container->registerForAutoconfiguration(EnvVarProcessorInterface::class)
|
||||
->addTag('container.env_var_processor');
|
||||
$container->registerForAutoconfiguration(ServiceSubscriberInterface::class)
|
||||
->addTag('container.service_subscriber');
|
||||
$container->registerForAutoconfiguration(ArgumentValueResolverInterface::class)
|
||||
|
@ -310,6 +310,9 @@ class SecurityExtension extends Extension
|
||||
// Provider id (take the first registered provider if none defined)
|
||||
if (isset($firewall['provider'])) {
|
||||
$defaultProvider = $this->getUserProviderId($firewall['provider']);
|
||||
if (!in_array($defaultProvider, $providerIds, true)) {
|
||||
throw new InvalidConfigurationException(sprintf('Invalid firewall "%s": user provider "%s" not found.', $id, $firewall['provider']));
|
||||
}
|
||||
} else {
|
||||
$defaultProvider = reset($providerIds);
|
||||
}
|
||||
@ -400,7 +403,7 @@ class SecurityExtension extends Extension
|
||||
$configuredEntryPoint = isset($firewall['entry_point']) ? $firewall['entry_point'] : null;
|
||||
|
||||
// Authentication listeners
|
||||
list($authListeners, $defaultEntryPoint) = $this->createAuthenticationListeners($container, $id, $firewall, $authenticationProviders, $defaultProvider, $configuredEntryPoint);
|
||||
list($authListeners, $defaultEntryPoint) = $this->createAuthenticationListeners($container, $id, $firewall, $authenticationProviders, $defaultProvider, $providerIds, $configuredEntryPoint);
|
||||
|
||||
$config->replaceArgument(7, $configuredEntryPoint ?: $defaultEntryPoint);
|
||||
|
||||
@ -455,7 +458,7 @@ class SecurityExtension extends Extension
|
||||
return $this->contextListeners[$contextKey] = $listenerId;
|
||||
}
|
||||
|
||||
private function createAuthenticationListeners($container, $id, $firewall, &$authenticationProviders, $defaultProvider, $defaultEntryPoint)
|
||||
private function createAuthenticationListeners($container, $id, $firewall, &$authenticationProviders, $defaultProvider, array $providerIds, $defaultEntryPoint)
|
||||
{
|
||||
$listeners = array();
|
||||
$hasListeners = false;
|
||||
@ -465,7 +468,14 @@ class SecurityExtension extends Extension
|
||||
$key = str_replace('-', '_', $factory->getKey());
|
||||
|
||||
if (isset($firewall[$key])) {
|
||||
$userProvider = isset($firewall[$key]['provider']) ? $this->getUserProviderId($firewall[$key]['provider']) : $defaultProvider;
|
||||
if (isset($firewall[$key]['provider'])) {
|
||||
if (!in_array($firewall[$key]['provider'], $providerIds, true)) {
|
||||
throw new InvalidConfigurationException(sprintf('Invalid firewall "%s": user provider "%s" not found.', $id, $firewall[$key]['provider']));
|
||||
}
|
||||
$userProvider = $this->getUserProviderId($firewall[$key]['provider']);
|
||||
} else {
|
||||
$userProvider = $defaultProvider;
|
||||
}
|
||||
|
||||
list($provider, $listenerId, $defaultEntryPoint) = $factory->create($container, $id, $firewall[$key], $userProvider, $defaultEntryPoint);
|
||||
|
||||
|
@ -387,6 +387,24 @@ abstract class CompleteConfigurationTest extends TestCase
|
||||
$container = $this->getContainer('access_decision_manager_service_and_strategy');
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException
|
||||
* @expectedExceptionMessage Invalid firewall "main": user provider "undefined" not found.
|
||||
*/
|
||||
public function testFirewallUndefinedUserProvider()
|
||||
{
|
||||
$this->getContainer('firewall_undefined_provider');
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException
|
||||
* @expectedExceptionMessage Invalid firewall "main": user provider "undefined" not found.
|
||||
*/
|
||||
public function testFirewallListenerUndefinedProvider()
|
||||
{
|
||||
$this->getContainer('listener_undefined_provider');
|
||||
}
|
||||
|
||||
protected function getContainer($file)
|
||||
{
|
||||
$file = $file.'.'.$this->getFileExtension();
|
||||
|
@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
$container->loadFromExtension('security', array(
|
||||
'providers' => array(
|
||||
'default' => array(
|
||||
'memory' => array(
|
||||
'users' => array('foo' => array('password' => 'foo', 'roles' => 'ROLE_USER')),
|
||||
),
|
||||
),
|
||||
),
|
||||
'firewalls' => array(
|
||||
'main' => array(
|
||||
'provider' => 'undefined',
|
||||
'form_login' => true,
|
||||
),
|
||||
),
|
||||
));
|
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
$container->loadFromExtension('security', array(
|
||||
'providers' => array(
|
||||
'default' => array(
|
||||
'memory' => array(
|
||||
'users' => array('foo' => array('password' => 'foo', 'roles' => 'ROLE_USER')),
|
||||
),
|
||||
),
|
||||
),
|
||||
'firewalls' => array(
|
||||
'main' => array(
|
||||
'form_login' => array('provider' => 'undefined'),
|
||||
),
|
||||
),
|
||||
));
|
@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<container xmlns="http://symfony.com/schema/dic/services"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:sec="http://symfony.com/schema/dic/security"
|
||||
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
|
||||
|
||||
<sec:config>
|
||||
<sec:providers>
|
||||
<sec:provider name="default" id="foo" />
|
||||
</sec:providers>
|
||||
|
||||
<sec:firewalls>
|
||||
<sec:firewall name="main" provider="undefined">
|
||||
<sec:form_login />
|
||||
</sec:firewall>
|
||||
</sec:firewalls>
|
||||
</sec:config>
|
||||
|
||||
</container>
|
@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<container xmlns="http://symfony.com/schema/dic/services"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:sec="http://symfony.com/schema/dic/security"
|
||||
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
|
||||
|
||||
<sec:config>
|
||||
<sec:providers>
|
||||
<sec:provider name="default" id="foo" />
|
||||
</sec:providers>
|
||||
|
||||
<sec:firewalls>
|
||||
<sec:firewall name="main">
|
||||
<sec:form_login provider="undefined" />
|
||||
</sec:firewall>
|
||||
</sec:firewalls>
|
||||
</sec:config>
|
||||
|
||||
</container>
|
@ -0,0 +1,10 @@
|
||||
security:
|
||||
providers:
|
||||
default:
|
||||
memory:
|
||||
users: { foo: { password: foo, roles: ROLE_USER } }
|
||||
|
||||
firewalls:
|
||||
main:
|
||||
provider: undefined
|
||||
form_login: true
|
@ -0,0 +1,10 @@
|
||||
security:
|
||||
providers:
|
||||
default:
|
||||
memory:
|
||||
users: { foo: { password: foo, roles: ROLE_USER } }
|
||||
|
||||
firewalls:
|
||||
main:
|
||||
form_login:
|
||||
provider: undefined
|
@ -39,6 +39,7 @@ class Configuration implements ConfigurationInterface
|
||||
->booleanNode('toolbar')->defaultFalse()->end()
|
||||
->scalarNode('position')
|
||||
->defaultValue('bottom')
|
||||
->setDeprecated('The "web_profiler.position" configuration key has been deprecated in Symfony 3.4 and it will be removed in 4.0.')
|
||||
->validate()
|
||||
->ifNotInArray(array('bottom', 'top'))
|
||||
->thenInvalid('The CSS position %s is not supported')
|
||||
|
@ -749,7 +749,9 @@ class Application
|
||||
}
|
||||
|
||||
$messages = array();
|
||||
if (!$e instanceof ExceptionInterface || OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) {
|
||||
$messages[] = sprintf('<comment>%s</comment>', OutputFormatter::escape(sprintf('In %s line %s:', basename($e->getFile()) ?: 'n/a', $e->getLine() ?: 'n/a')));
|
||||
}
|
||||
$messages[] = $emptyLine = sprintf('<error>%s</error>', str_repeat(' ', $len));
|
||||
if ('' === $message || OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) {
|
||||
$messages[] = sprintf('<error>%s%s</error>', $title, str_repeat(' ', max(0, $len - Helper::strlen($title))));
|
||||
|
@ -1,5 +1,4 @@
|
||||
|
||||
In Application.php line 607:
|
||||
|
||||
Command "foo" is not defined.
|
||||
|
||||
|
@ -1,5 +1,4 @@
|
||||
|
||||
In ArrayInput.php line 178:
|
||||
|
||||
The "--foo" option does not exist.
|
||||
|
||||
|
@ -1,5 +1,4 @@
|
||||
|
||||
In Application.php line 607:
|
||||
|
||||
Command "foo" is not define
|
||||
d.
|
||||
|
@ -26,11 +26,13 @@ CHANGELOG
|
||||
3.4.0
|
||||
-----
|
||||
|
||||
* added `EnvVarProcessorInterface` and corresponding "container.env_var_processor" tag for processing env vars
|
||||
* added support for ignore-on-uninitialized references
|
||||
* deprecated service auto-registration while autowiring
|
||||
* deprecated the ability to check for the initialization of a private service with the `Container::initialized()` method
|
||||
* deprecated support for top-level anonymous services in XML
|
||||
* deprecated case insensitivity of parameter names
|
||||
* deprecated the `ResolveDefinitionTemplatesPass` class in favor of `ResolveChildDefinitionsPass`
|
||||
|
||||
3.3.0
|
||||
-----
|
||||
|
@ -43,12 +43,13 @@ class PassConfig
|
||||
100 => array(
|
||||
new ResolveClassPass(),
|
||||
new ResolveInstanceofConditionalsPass(),
|
||||
new RegisterEnvVarProcessorsPass(),
|
||||
),
|
||||
);
|
||||
|
||||
$this->optimizationPasses = array(array(
|
||||
new ExtensionCompilerPass(),
|
||||
new ResolveDefinitionTemplatesPass(),
|
||||
new ResolveChildDefinitionsPass(),
|
||||
new ServiceLocatorTagPass(),
|
||||
new DecoratorServicePass(),
|
||||
new ResolveParameterPlaceHoldersPass(false),
|
||||
|
@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\DependencyInjection\Compiler;
|
||||
|
||||
use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\EnvVarProcessorInterface;
|
||||
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\DependencyInjection\ParameterBag\EnvPlaceholderParameterBag;
|
||||
use Symfony\Component\DependencyInjection\ServiceLocator;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
|
||||
/**
|
||||
* Creates the container.env_var_processors_locator service.
|
||||
*
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
class RegisterEnvVarProcessorsPass implements CompilerPassInterface
|
||||
{
|
||||
private static $allowedTypes = array('array', 'bool', 'float', 'int', 'string');
|
||||
|
||||
public function process(ContainerBuilder $container)
|
||||
{
|
||||
$bag = $container->getParameterBag();
|
||||
$types = array();
|
||||
$processors = array();
|
||||
foreach ($container->findTaggedServiceIds('container.env_var_processor') as $id => $tags) {
|
||||
foreach ($tags as $attr) {
|
||||
if (!$r = $container->getReflectionClass($class = $container->getDefinition($id)->getClass())) {
|
||||
throw new InvalidArgumentException(sprintf('Class "%s" used for service "%s" cannot be found.', $class, $id));
|
||||
} elseif (!$r->isSubclassOf(EnvVarProcessorInterface::class)) {
|
||||
throw new InvalidArgumentException(sprintf('Service "%s" must implement interface "%s".', $id, EnvVarProcessorInterface::class));
|
||||
}
|
||||
foreach ($class::getProvidedTypes() as $prefix => $type) {
|
||||
$processors[$prefix] = new ServiceClosureArgument(new Reference($id));
|
||||
$types[$prefix] = self::validateProvidedTypes($type, $class);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($processors) {
|
||||
if ($bag instanceof EnvPlaceholderParameterBag) {
|
||||
$bag->setProvidedTypes($types);
|
||||
}
|
||||
$container->register('container.env_var_processors_locator', ServiceLocator::class)
|
||||
->setArguments(array($processors))
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
private static function validateProvidedTypes($types, $class)
|
||||
{
|
||||
$types = explode('|', $types);
|
||||
|
||||
foreach ($types as $type) {
|
||||
if (!in_array($type, self::$allowedTypes)) {
|
||||
throw new InvalidArgumentException(sprintf('Invalid type "%s" returned by "%s::getProvidedTypes()", expected one of "%s".', $type, $class, implode('", "', self::$allowedTypes)));
|
||||
}
|
||||
}
|
||||
|
||||
return $types;
|
||||
}
|
||||
}
|
@ -23,7 +23,7 @@ use Symfony\Component\DependencyInjection\Exception\RuntimeException;
|
||||
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
class ResolveDefinitionTemplatesPass extends AbstractRecursivePass
|
||||
class ResolveChildDefinitionsPass extends AbstractRecursivePass
|
||||
{
|
||||
protected function processValue($value, $isRoot = false)
|
||||
{
|
@ -13,6 +13,7 @@ namespace Symfony\Component\DependencyInjection;
|
||||
|
||||
use Symfony\Component\DependencyInjection\Exception\EnvNotFoundException;
|
||||
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\DependencyInjection\Exception\ParameterCircularReferenceException;
|
||||
use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
|
||||
use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException;
|
||||
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
|
||||
@ -48,9 +49,11 @@ class Container implements ResettableContainerInterface
|
||||
protected $methodMap = array();
|
||||
protected $aliases = array();
|
||||
protected $loading = array();
|
||||
protected $resolving = array();
|
||||
|
||||
private $envCache = array();
|
||||
private $compiled = false;
|
||||
private $getEnv;
|
||||
|
||||
/**
|
||||
* @param ParameterBagInterface $parameterBag A ParameterBagInterface instance
|
||||
@ -336,23 +339,37 @@ class Container implements ResettableContainerInterface
|
||||
*/
|
||||
protected function getEnv($name)
|
||||
{
|
||||
if (isset($this->resolving[$envName = "env($name)"])) {
|
||||
throw new ParameterCircularReferenceException(array_keys($this->resolving));
|
||||
}
|
||||
if (isset($this->envCache[$name]) || array_key_exists($name, $this->envCache)) {
|
||||
return $this->envCache[$name];
|
||||
}
|
||||
if (isset($_SERVER[$name]) && 0 !== strpos($name, 'HTTP_')) {
|
||||
return $this->envCache[$name] = $_SERVER[$name];
|
||||
if (!$this->has($id = 'container.env_var_processors_locator')) {
|
||||
$this->set($id, new ServiceLocator(array()));
|
||||
}
|
||||
if (isset($_ENV[$name])) {
|
||||
return $this->envCache[$name] = $_ENV[$name];
|
||||
}
|
||||
if (false !== ($env = getenv($name)) && null !== $env) { // null is a possible value because of thread safety issues
|
||||
return $this->envCache[$name] = $env;
|
||||
}
|
||||
if (!$this->hasParameter("env($name)")) {
|
||||
throw new EnvNotFoundException($name);
|
||||
if (!$this->getEnv) {
|
||||
$this->getEnv = new \ReflectionMethod($this, __FUNCTION__);
|
||||
$this->getEnv->setAccessible(true);
|
||||
$this->getEnv = $this->getEnv->getClosure($this);
|
||||
}
|
||||
$processors = $this->get($id);
|
||||
|
||||
return $this->envCache[$name] = $this->getParameter("env($name)");
|
||||
if (false !== $i = strpos($name, ':')) {
|
||||
$prefix = substr($name, 0, $i);
|
||||
$localName = substr($name, 1 + $i);
|
||||
} else {
|
||||
$prefix = 'string';
|
||||
$localName = $name;
|
||||
}
|
||||
$processor = $processors->has($prefix) ? $processors->get($prefix) : new EnvVarProcessor($this);
|
||||
|
||||
$this->resolving[$envName] = true;
|
||||
try {
|
||||
return $this->envCache[$name] = $processor->getEnv($prefix, $localName, $this->getEnv);
|
||||
} finally {
|
||||
unset($this->resolving[$envName]);
|
||||
}
|
||||
}
|
||||
|
||||
private function __clone()
|
||||
|
@ -1396,20 +1396,26 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
|
||||
protected function getEnv($name)
|
||||
{
|
||||
$value = parent::getEnv($name);
|
||||
$bag = $this->getParameterBag();
|
||||
|
||||
if (!is_string($value) || !$this->getParameterBag() instanceof EnvPlaceholderParameterBag) {
|
||||
if (!is_string($value) || !$bag instanceof EnvPlaceholderParameterBag) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
foreach ($this->getParameterBag()->getEnvPlaceholders() as $env => $placeholders) {
|
||||
foreach ($bag->getEnvPlaceholders() as $env => $placeholders) {
|
||||
if (isset($placeholders[$value])) {
|
||||
$bag = new ParameterBag($this->getParameterBag()->all());
|
||||
$bag = new ParameterBag($bag->all());
|
||||
|
||||
return $bag->unescapeValue($bag->get("env($name)"));
|
||||
}
|
||||
}
|
||||
|
||||
return $value;
|
||||
$this->resolving["env($name)"] = true;
|
||||
try {
|
||||
return $bag->unescapeValue($this->resolveEnvPlaceholders($bag->escapeValue($value), true));
|
||||
} finally {
|
||||
unset($this->resolving["env($name)"]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1047,7 +1047,7 @@ EOF;
|
||||
$export = $this->exportParameters(array($value));
|
||||
$export = explode('0 => ', substr(rtrim($export, " )\n"), 7, -1), 2);
|
||||
|
||||
if (preg_match("/\\\$this->(?:getEnv\('\w++'\)|targetDirs\[\d++\])/", $export[1])) {
|
||||
if (preg_match("/\\\$this->(?:getEnv\('(?:\w++:)*+\w++'\)|targetDirs\[\d++\])/", $export[1])) {
|
||||
$dynamicPhp[$key] = sprintf('%scase %s: $value = %s; break;', $export[0], $this->export($key), $export[1]);
|
||||
} else {
|
||||
$php[] = sprintf('%s%s => %s,', $export[0], $this->export($key), $export[1]);
|
||||
@ -1590,7 +1590,7 @@ EOF;
|
||||
return $dumpedValue;
|
||||
}
|
||||
|
||||
if (!preg_match("/\\\$this->(?:getEnv\('\w++'\)|targetDirs\[\d++\])/", $dumpedValue)) {
|
||||
if (!preg_match("/\\\$this->(?:getEnv\('(?:\w++:)*+\w++'\)|targetDirs\[\d++\])/", $dumpedValue)) {
|
||||
return sprintf("\$this->parameters['%s']", $name);
|
||||
}
|
||||
}
|
||||
@ -1790,13 +1790,16 @@ EOF;
|
||||
{
|
||||
$export = var_export($value, true);
|
||||
|
||||
if ("'" === $export[0] && $export !== $resolvedExport = $this->container->resolveEnvPlaceholders($export, "'.\$this->getEnv('%s').'")) {
|
||||
if ("'" === $export[0] && $export !== $resolvedExport = $this->container->resolveEnvPlaceholders($export, "'.\$this->getEnv('string:%s').'")) {
|
||||
$export = $resolvedExport;
|
||||
if ("'" === $export[1]) {
|
||||
$export = substr($export, 3);
|
||||
}
|
||||
if (".''" === substr($export, -3)) {
|
||||
$export = substr($export, 0, -3);
|
||||
if ("'" === $export[1]) {
|
||||
$export = substr_replace($export, '', 18, 7);
|
||||
}
|
||||
}
|
||||
if ("'" === $export[1]) {
|
||||
$export = substr($export, 3);
|
||||
}
|
||||
}
|
||||
|
||||
|
160
src/Symfony/Component/DependencyInjection/EnvVarProcessor.php
Normal file
160
src/Symfony/Component/DependencyInjection/EnvVarProcessor.php
Normal file
@ -0,0 +1,160 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\DependencyInjection;
|
||||
|
||||
use Symfony\Component\Config\Util\XmlUtils;
|
||||
use Symfony\Component\DependencyInjection\Exception\EnvNotFoundException;
|
||||
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
|
||||
|
||||
class EnvVarProcessor implements EnvVarProcessorInterface
|
||||
{
|
||||
private $container;
|
||||
|
||||
public function __construct(ContainerInterface $container)
|
||||
{
|
||||
$this->container = $container;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getProvidedTypes()
|
||||
{
|
||||
return array(
|
||||
'base64' => 'string',
|
||||
'bool' => 'bool',
|
||||
'const' => 'bool|int|float|string|array',
|
||||
'file' => 'string',
|
||||
'float' => 'float',
|
||||
'int' => 'int',
|
||||
'json' => 'array',
|
||||
'resolve' => 'string',
|
||||
'string' => 'string',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getEnv($prefix, $name, \Closure $getEnv)
|
||||
{
|
||||
$i = strpos($name, ':');
|
||||
|
||||
if ('file' === $prefix) {
|
||||
if (!is_scalar($file = $getEnv($name))) {
|
||||
throw new RuntimeException(sprintf('Invalid file name: env var "%s" is non-scalar.', $name));
|
||||
}
|
||||
if (!file_exists($file)) {
|
||||
throw new RuntimeException(sprintf('Env "file:%s" not found: %s does not exist.', $name, $file));
|
||||
}
|
||||
|
||||
return file_get_contents($file);
|
||||
}
|
||||
|
||||
if (false !== $i || 'string' !== $prefix) {
|
||||
if (null === $env = $getEnv($name)) {
|
||||
return;
|
||||
}
|
||||
} elseif (isset($_SERVER[$name]) && 0 !== strpos($name, 'HTTP_')) {
|
||||
$env = $_SERVER[$name];
|
||||
} elseif (isset($_ENV[$name])) {
|
||||
$env = $_ENV[$name];
|
||||
} elseif (false === ($env = getenv($name)) || null === $env) { // null is a possible value because of thread safety issues
|
||||
if (!$this->container->hasParameter("env($name)")) {
|
||||
throw new EnvNotFoundException($name);
|
||||
}
|
||||
|
||||
if (null === $env = $this->container->getParameter("env($name)")) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_scalar($env)) {
|
||||
throw new RuntimeException(sprintf('Non-scalar env var "%s" cannot be cast to %s.', $name, $prefix));
|
||||
}
|
||||
|
||||
if ('string' === $prefix) {
|
||||
return (string) $env;
|
||||
}
|
||||
|
||||
if ('bool' === $prefix) {
|
||||
return (bool) self::phpize($env);
|
||||
}
|
||||
|
||||
if ('int' === $prefix) {
|
||||
if (!is_numeric($env = self::phpize($env))) {
|
||||
throw new RuntimeException(sprintf('Non-numeric env var "%s" cannot be cast to int.', $name));
|
||||
}
|
||||
|
||||
return (int) $env;
|
||||
}
|
||||
|
||||
if ('float' === $prefix) {
|
||||
if (!is_numeric($env = self::phpize($env))) {
|
||||
throw new RuntimeException(sprintf('Non-numeric env var "%s" cannot be cast to float.', $name));
|
||||
}
|
||||
|
||||
return (float) $env;
|
||||
}
|
||||
|
||||
if ('const' === $prefix) {
|
||||
if (!defined($env)) {
|
||||
throw new RuntimeException(sprintf('Env var "%s" maps to undefined constant "%s".', $name, $env));
|
||||
}
|
||||
|
||||
return constant($name);
|
||||
}
|
||||
|
||||
if ('base64' === $prefix) {
|
||||
return base64_decode($env);
|
||||
}
|
||||
|
||||
if ('json' === $prefix) {
|
||||
$env = json_decode($env, true, JSON_BIGINT_AS_STRING);
|
||||
|
||||
if (JSON_ERROR_NONE !== json_last_error()) {
|
||||
throw new RuntimeException(sprintf('Invalid JSON in env var "%s": '.json_last_error_msg(), $name));
|
||||
}
|
||||
|
||||
if (!is_array($env)) {
|
||||
throw new RuntimeException(sprintf('Invalid JSON env var "%s": array expected, %s given.', $name, gettype($env)));
|
||||
}
|
||||
|
||||
return $env;
|
||||
}
|
||||
|
||||
if ('resolve' === $prefix) {
|
||||
return preg_replace_callback('/%%|%([^%\s]+)%/', function ($match) use ($name) {
|
||||
if (!isset($match[1])) {
|
||||
return '%';
|
||||
}
|
||||
$value = $this->container->getParameter($match[1]);
|
||||
if (!is_scalar($value)) {
|
||||
throw new RuntimeException(sprintf('Parameter "%s" found when resolving env var "%s" must be scalar, "%s" given.', $match[1], $name, gettype($value)));
|
||||
}
|
||||
|
||||
return $value;
|
||||
}, $env);
|
||||
}
|
||||
|
||||
throw new RuntimeException(sprintf('Unsupported env var prefix "%s".', $prefix));
|
||||
}
|
||||
|
||||
private static function phpize($value)
|
||||
{
|
||||
if (!class_exists(XmlUtils::class)) {
|
||||
throw new RuntimeException('The Symfony Config component is required to cast env vars to "bool", "int" or "float".');
|
||||
}
|
||||
|
||||
return XmlUtils::phpize($value);
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\DependencyInjection;
|
||||
|
||||
/**
|
||||
* The EnvVarProcessorInterface is implemented by objects that manage environment-like variables.
|
||||
*
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
interface EnvVarProcessorInterface
|
||||
{
|
||||
/**
|
||||
* Returns the value of the given variable as managed by the current instance.
|
||||
*
|
||||
* @param string $prefix The namespace of the variable
|
||||
* @param string $name The name of the variable within the namespace
|
||||
* @param \Closure $getEnv A closure that allows fetching more env vars
|
||||
*
|
||||
* @return mixed
|
||||
*
|
||||
* @throws RuntimeException on error
|
||||
*/
|
||||
public function getEnv($prefix, $name, \Closure $getEnv);
|
||||
|
||||
/**
|
||||
* @return string[] The PHP-types managed by getEnv(), keyed by prefixes
|
||||
*/
|
||||
public static function getProvidedTypes();
|
||||
}
|
@ -20,6 +20,7 @@ use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
|
||||
class EnvPlaceholderParameterBag extends ParameterBag
|
||||
{
|
||||
private $envPlaceholders = array();
|
||||
private $providedTypes = array();
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
@ -34,7 +35,7 @@ class EnvPlaceholderParameterBag extends ParameterBag
|
||||
return $placeholder; // return first result
|
||||
}
|
||||
}
|
||||
if (preg_match('/\W/', $env)) {
|
||||
if (!preg_match('/^(?:\w++:)*+\w++$/', $env)) {
|
||||
throw new InvalidArgumentException(sprintf('Invalid %s name: only "word" characters are allowed.', $name));
|
||||
}
|
||||
|
||||
@ -80,6 +81,24 @@ class EnvPlaceholderParameterBag extends ParameterBag
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps env prefixes to their corresponding PHP types.
|
||||
*/
|
||||
public function setProvidedTypes(array $providedTypes)
|
||||
{
|
||||
$this->providedTypes = $providedTypes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the PHP types corresponding to env() parameter prefixes.
|
||||
*
|
||||
* @return string[][]
|
||||
*/
|
||||
public function getProvidedTypes()
|
||||
{
|
||||
return $this->providedTypes;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\DependencyInjection\Tests\Compiler;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\DependencyInjection\Compiler\RegisterEnvVarProcessorsPass;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\EnvVarProcessorInterface;
|
||||
|
||||
class RegisterEnvVarProcessorsPassTest extends TestCase
|
||||
{
|
||||
public function testSimpleProcessor()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
$container->register('foo', SimpleProcessor::class)->addTag('container.env_var_processor');
|
||||
|
||||
(new RegisterEnvVarProcessorsPass())->process($container);
|
||||
|
||||
$this->assertTrue($container->has('container.env_var_processors_locator'));
|
||||
$this->assertInstanceof(SimpleProcessor::class, $container->get('container.env_var_processors_locator')->get('foo'));
|
||||
|
||||
$this->assertSame(array('foo' => array('string')), $container->getParameterBag()->getProvidedTypes());
|
||||
}
|
||||
|
||||
public function testNoProcessor()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
|
||||
(new RegisterEnvVarProcessorsPass())->process($container);
|
||||
|
||||
$this->assertFalse($container->has('container.env_var_processors_locator'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException
|
||||
* @expectedExceptionMessage Invalid type "foo" returned by "Symfony\Component\DependencyInjection\Tests\Compiler\BadProcessor::getProvidedTypes()", expected one of "array", "bool", "float", "int", "string".
|
||||
*/
|
||||
public function testBadProcessor()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
$container->register('foo', BadProcessor::class)->addTag('container.env_var_processor');
|
||||
|
||||
(new RegisterEnvVarProcessorsPass())->process($container);
|
||||
}
|
||||
}
|
||||
|
||||
class SimpleProcessor implements EnvVarProcessorInterface
|
||||
{
|
||||
public function getEnv($prefix, $name, \Closure $getEnv)
|
||||
{
|
||||
return $getEnv($name);
|
||||
}
|
||||
|
||||
public static function getProvidedTypes()
|
||||
{
|
||||
return array('foo' => 'string');
|
||||
}
|
||||
}
|
||||
|
||||
class BadProcessor extends SimpleProcessor
|
||||
{
|
||||
public static function getProvidedTypes()
|
||||
{
|
||||
return array('foo' => 'string|foo');
|
||||
}
|
||||
}
|
@ -13,10 +13,10 @@ namespace Symfony\Component\DependencyInjection\Tests\Compiler;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\DependencyInjection\ChildDefinition;
|
||||
use Symfony\Component\DependencyInjection\Compiler\ResolveDefinitionTemplatesPass;
|
||||
use Symfony\Component\DependencyInjection\Compiler\ResolveChildDefinitionsPass;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
|
||||
class ResolveDefinitionTemplatesPassTest extends TestCase
|
||||
class ResolveChildDefinitionsPassTest extends TestCase
|
||||
{
|
||||
public function testProcess()
|
||||
{
|
||||
@ -372,7 +372,7 @@ class ResolveDefinitionTemplatesPassTest extends TestCase
|
||||
|
||||
protected function process(ContainerBuilder $container)
|
||||
{
|
||||
$pass = new ResolveDefinitionTemplatesPass();
|
||||
$pass = new ResolveChildDefinitionsPass();
|
||||
$pass->process($container);
|
||||
}
|
||||
}
|
@ -14,7 +14,7 @@ namespace Symfony\Component\DependencyInjection\Tests\Compiler;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\DependencyInjection\ChildDefinition;
|
||||
use Symfony\Component\DependencyInjection\Compiler\ResolveInstanceofConditionalsPass;
|
||||
use Symfony\Component\DependencyInjection\Compiler\ResolveDefinitionTemplatesPass;
|
||||
use Symfony\Component\DependencyInjection\Compiler\ResolveChildDefinitionsPass;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
|
||||
class ResolveInstanceofConditionalsPassTest extends TestCase
|
||||
@ -57,7 +57,7 @@ class ResolveInstanceofConditionalsPassTest extends TestCase
|
||||
$container->setDefinition('child', $def);
|
||||
|
||||
(new ResolveInstanceofConditionalsPass())->process($container);
|
||||
(new ResolveDefinitionTemplatesPass())->process($container);
|
||||
(new ResolveChildDefinitionsPass())->process($container);
|
||||
|
||||
$expected = array(
|
||||
array('foo', array('bar')),
|
||||
@ -95,7 +95,7 @@ class ResolveInstanceofConditionalsPassTest extends TestCase
|
||||
));
|
||||
|
||||
(new ResolveInstanceofConditionalsPass())->process($container);
|
||||
(new ResolveDefinitionTemplatesPass())->process($container);
|
||||
(new ResolveChildDefinitionsPass())->process($container);
|
||||
|
||||
$def = $container->getDefinition('foo');
|
||||
$this->assertTrue($def->isAutowired());
|
||||
@ -119,7 +119,7 @@ class ResolveInstanceofConditionalsPassTest extends TestCase
|
||||
->setFactory('autoconfigured_factory');
|
||||
|
||||
(new ResolveInstanceofConditionalsPass())->process($container);
|
||||
(new ResolveDefinitionTemplatesPass())->process($container);
|
||||
(new ResolveChildDefinitionsPass())->process($container);
|
||||
|
||||
$def = $container->getDefinition('normal_service');
|
||||
// autowired thanks to the autoconfigured instanceof
|
||||
@ -147,7 +147,7 @@ class ResolveInstanceofConditionalsPassTest extends TestCase
|
||||
;
|
||||
|
||||
(new ResolveInstanceofConditionalsPass())->process($container);
|
||||
(new ResolveDefinitionTemplatesPass())->process($container);
|
||||
(new ResolveChildDefinitionsPass())->process($container);
|
||||
|
||||
$def = $container->getDefinition('normal_service');
|
||||
$this->assertSame(array('duplicated_tag' => array(array(), array('and_attributes' => 1))), $def->getTags());
|
||||
@ -165,7 +165,7 @@ class ResolveInstanceofConditionalsPassTest extends TestCase
|
||||
->setAutowired(true);
|
||||
|
||||
(new ResolveInstanceofConditionalsPass())->process($container);
|
||||
(new ResolveDefinitionTemplatesPass())->process($container);
|
||||
(new ResolveChildDefinitionsPass())->process($container);
|
||||
|
||||
$def = $container->getDefinition('normal_service');
|
||||
$this->assertFalse($def->isAutowired());
|
||||
|
@ -665,6 +665,70 @@ class ContainerBuilderTest extends TestCase
|
||||
$container->compile(true);
|
||||
}
|
||||
|
||||
public function testDynamicEnv()
|
||||
{
|
||||
putenv('DUMMY_FOO=some%foo%');
|
||||
putenv('DUMMY_BAR=%bar%');
|
||||
|
||||
$container = new ContainerBuilder();
|
||||
$container->setParameter('foo', 'Foo%env(resolve:DUMMY_BAR)%');
|
||||
$container->setParameter('bar', 'Bar');
|
||||
$container->setParameter('baz', '%env(resolve:DUMMY_FOO)%');
|
||||
|
||||
$container->compile(true);
|
||||
putenv('DUMMY_FOO');
|
||||
putenv('DUMMY_BAR');
|
||||
|
||||
$this->assertSame('someFooBar', $container->getParameter('baz'));
|
||||
}
|
||||
|
||||
public function testCastEnv()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
$container->setParameter('env(FAKE)', '123');
|
||||
|
||||
$container->register('foo', 'stdClass')->setProperties(array(
|
||||
'fake' => '%env(int:FAKE)%',
|
||||
));
|
||||
|
||||
$container->compile(true);
|
||||
|
||||
$this->assertSame(123, $container->get('foo')->fake);
|
||||
}
|
||||
|
||||
public function testEnvAreNullable()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
$container->setParameter('env(FAKE)', null);
|
||||
|
||||
$container->register('foo', 'stdClass')->setProperties(array(
|
||||
'fake' => '%env(int:FAKE)%',
|
||||
));
|
||||
|
||||
$container->compile(true);
|
||||
|
||||
$this->assertNull($container->get('foo')->fake);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\DependencyInjection\Exception\ParameterCircularReferenceException
|
||||
* @expectedExceptionMessage Circular reference detected for parameter "env(resolve:DUMMY_ENV_VAR)" ("env(resolve:DUMMY_ENV_VAR)" > "env(resolve:DUMMY_ENV_VAR)").
|
||||
*/
|
||||
public function testCircularDynamicEnv()
|
||||
{
|
||||
putenv('DUMMY_ENV_VAR=some%foo%');
|
||||
|
||||
$container = new ContainerBuilder();
|
||||
$container->setParameter('foo', '%bar%');
|
||||
$container->setParameter('bar', '%env(resolve:DUMMY_ENV_VAR)%');
|
||||
|
||||
try {
|
||||
$container->compile(true);
|
||||
} finally {
|
||||
putenv('DUMMY_ENV_VAR');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \LogicException
|
||||
*/
|
||||
|
@ -21,6 +21,7 @@ use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface as SymfonyContainerInterface;
|
||||
use Symfony\Component\DependencyInjection\Dumper\PhpDumper;
|
||||
use Symfony\Component\DependencyInjection\EnvVarProcessorInterface;
|
||||
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
use Symfony\Component\DependencyInjection\Tests\Fixtures\StubbedTranslator;
|
||||
@ -318,13 +319,82 @@ class PhpDumperTest extends TestCase
|
||||
|
||||
public function testEnvParameter()
|
||||
{
|
||||
$rand = mt_rand();
|
||||
putenv('Baz='.$rand);
|
||||
$container = new ContainerBuilder();
|
||||
$loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml'));
|
||||
$loader->load('services26.yml');
|
||||
$container->setParameter('env(json_file)', self::$fixturesPath.'/array.json');
|
||||
$container->compile();
|
||||
$dumper = new PhpDumper($container);
|
||||
|
||||
$this->assertStringEqualsFile(self::$fixturesPath.'/php/services26.php', $dumper->dump(), '->dump() dumps inline definitions which reference service_container');
|
||||
$this->assertStringEqualsFile(self::$fixturesPath.'/php/services26.php', $dumper->dump(array('class' => 'Symfony_DI_PhpDumper_Test_EnvParameters', 'file' => self::$fixturesPath.'/php/services26.php')));
|
||||
|
||||
require self::$fixturesPath.'/php/services26.php';
|
||||
$container = new \Symfony_DI_PhpDumper_Test_EnvParameters();
|
||||
$this->assertSame($rand, $container->getParameter('baz'));
|
||||
$this->assertSame(array(123, 'abc'), $container->getParameter('json'));
|
||||
$this->assertSame('sqlite:///foo/bar/var/data.db', $container->getParameter('db_dsn'));
|
||||
putenv('Baz');
|
||||
}
|
||||
|
||||
public function testResolvedBase64EnvParameters()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
$container->setParameter('env(foo)', base64_encode('world'));
|
||||
$container->setParameter('hello', '%env(base64:foo)%');
|
||||
$container->compile(true);
|
||||
|
||||
$expected = array(
|
||||
'env(foo)' => 'd29ybGQ=',
|
||||
'hello' => 'world',
|
||||
);
|
||||
$this->assertSame($expected, $container->getParameterBag()->all());
|
||||
}
|
||||
|
||||
public function testDumpedBase64EnvParameters()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
$container->setParameter('env(foo)', base64_encode('world'));
|
||||
$container->setParameter('hello', '%env(base64:foo)%');
|
||||
$container->compile();
|
||||
|
||||
$dumper = new PhpDumper($container);
|
||||
$dumper->dump();
|
||||
|
||||
$this->assertStringEqualsFile(self::$fixturesPath.'/php/services_base64_env.php', $dumper->dump(array('class' => 'Symfony_DI_PhpDumper_Test_Base64Parameters')));
|
||||
|
||||
require self::$fixturesPath.'/php/services_base64_env.php';
|
||||
$container = new \Symfony_DI_PhpDumper_Test_Base64Parameters();
|
||||
$this->assertSame('world', $container->getParameter('hello'));
|
||||
}
|
||||
|
||||
public function testCustomEnvParameters()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
$container->setParameter('env(foo)', str_rot13('world'));
|
||||
$container->setParameter('hello', '%env(rot13:foo)%');
|
||||
$container->register(Rot13EnvVarProcessor::class)->addTag('container.env_var_processor');
|
||||
$container->compile();
|
||||
|
||||
$dumper = new PhpDumper($container);
|
||||
$dumper->dump();
|
||||
|
||||
$this->assertStringEqualsFile(self::$fixturesPath.'/php/services_rot13_env.php', $dumper->dump(array('class' => 'Symfony_DI_PhpDumper_Test_Rot13Parameters')));
|
||||
|
||||
require self::$fixturesPath.'/php/services_rot13_env.php';
|
||||
$container = new \Symfony_DI_PhpDumper_Test_Rot13Parameters();
|
||||
$this->assertSame('world', $container->getParameter('hello'));
|
||||
}
|
||||
|
||||
public function testFileEnvProcessor()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
$container->setParameter('env(foo)', __FILE__);
|
||||
$container->setParameter('random', '%env(file:foo)%');
|
||||
$container->compile(true);
|
||||
|
||||
$this->assertStringEqualsFile(__FILE__, $container->getParameter('random'));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -340,6 +410,31 @@ class PhpDumperTest extends TestCase
|
||||
$dumper->dump();
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\DependencyInjection\Exception\ParameterCircularReferenceException
|
||||
* @expectedExceptionMessage Circular reference detected for parameter "env(resolve:DUMMY_ENV_VAR)" ("env(resolve:DUMMY_ENV_VAR)" > "env(resolve:DUMMY_ENV_VAR)").
|
||||
*/
|
||||
public function testCircularDynamicEnv()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
$container->setParameter('foo', '%bar%');
|
||||
$container->setParameter('bar', '%env(resolve:DUMMY_ENV_VAR)%');
|
||||
$container->compile();
|
||||
|
||||
$dumper = new PhpDumper($container);
|
||||
$dump = $dumper->dump(array('class' => $class = __FUNCTION__));
|
||||
|
||||
eval('?>'.$dump);
|
||||
$container = new $class();
|
||||
|
||||
putenv('DUMMY_ENV_VAR=%foo%');
|
||||
try {
|
||||
$container->getParameter('bar');
|
||||
} finally {
|
||||
putenv('DUMMY_ENV_VAR');
|
||||
}
|
||||
}
|
||||
|
||||
public function testInlinedDefinitionReferencingServiceContainer()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
@ -703,3 +798,16 @@ class PhpDumperTest extends TestCase
|
||||
$this->assertSame('foo', $container->getParameter('BAR'));
|
||||
}
|
||||
}
|
||||
|
||||
class Rot13EnvVarProcessor implements EnvVarProcessorInterface
|
||||
{
|
||||
public function getEnv($prefix, $name, \Closure $getEnv)
|
||||
{
|
||||
return str_rot13($getEnv($name));
|
||||
}
|
||||
|
||||
public static function getProvidedTypes()
|
||||
{
|
||||
return array('rot13' => 'string');
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1 @@
|
||||
[123, "abc"]
|
@ -9,14 +9,14 @@ use Symfony\Component\DependencyInjection\Exception\RuntimeException;
|
||||
use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag;
|
||||
|
||||
/**
|
||||
* ProjectServiceContainer.
|
||||
* Symfony_DI_PhpDumper_Test_EnvParameters.
|
||||
*
|
||||
* This class has been auto-generated
|
||||
* by the Symfony Dependency Injection Component.
|
||||
*
|
||||
* @final since Symfony 3.3
|
||||
*/
|
||||
class ProjectServiceContainer extends Container
|
||||
class Symfony_DI_PhpDumper_Test_EnvParameters extends Container
|
||||
{
|
||||
private $parameters;
|
||||
private $targetDirs = array();
|
||||
@ -27,6 +27,10 @@ class ProjectServiceContainer extends Container
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$dir = __DIR__;
|
||||
for ($i = 1; $i <= 5; ++$i) {
|
||||
$this->targetDirs[$i] = $dir = dirname($dir);
|
||||
}
|
||||
$this->parameters = $this->getDefaultParameters();
|
||||
|
||||
$this->services = $this->privates = array();
|
||||
@ -71,7 +75,7 @@ class ProjectServiceContainer extends Container
|
||||
{
|
||||
$class = $this->getEnv('FOO');
|
||||
|
||||
return $this->services['test'] = new $class($this->getEnv('Bar'), 'foo'.$this->getEnv('FOO').'baz');
|
||||
return $this->services['test'] = new $class($this->getEnv('Bar'), 'foo'.$this->getEnv('string:FOO').'baz', $this->getEnv('int:Baz'));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -127,6 +131,10 @@ class ProjectServiceContainer extends Container
|
||||
|
||||
private $loadedDynamicParameters = array(
|
||||
'bar' => false,
|
||||
'baz' => false,
|
||||
'json' => false,
|
||||
'db_dsn' => false,
|
||||
'env(json_file)' => false,
|
||||
);
|
||||
private $dynamicParameters = array();
|
||||
|
||||
@ -143,6 +151,10 @@ class ProjectServiceContainer extends Container
|
||||
{
|
||||
switch ($name) {
|
||||
case 'bar': $value = $this->getEnv('FOO'); break;
|
||||
case 'baz': $value = $this->getEnv('int:Baz'); break;
|
||||
case 'json': $value = $this->getEnv('json:file:json_file'); break;
|
||||
case 'db_dsn': $value = $this->getEnv('resolve:DB'); break;
|
||||
case 'env(json_file)': $value = ($this->targetDirs[1].'/array.json'); break;
|
||||
default: throw new InvalidArgumentException(sprintf('The dynamic parameter "%s" must be defined.', $name));
|
||||
}
|
||||
$this->loadedDynamicParameters[$name] = true;
|
||||
@ -158,7 +170,9 @@ class ProjectServiceContainer extends Container
|
||||
protected function getDefaultParameters()
|
||||
{
|
||||
return array(
|
||||
'project_dir' => '/foo/bar',
|
||||
'env(FOO)' => 'foo',
|
||||
'env(DB)' => 'sqlite://%project_dir%/var/data.db',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,149 @@
|
||||
<?php
|
||||
|
||||
use Symfony\Component\DependencyInjection\Argument\RewindableGenerator;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\DependencyInjection\Container;
|
||||
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\DependencyInjection\Exception\LogicException;
|
||||
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
|
||||
use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag;
|
||||
|
||||
/**
|
||||
* Symfony_DI_PhpDumper_Test_Base64Parameters.
|
||||
*
|
||||
* This class has been auto-generated
|
||||
* by the Symfony Dependency Injection Component.
|
||||
*
|
||||
* @final since Symfony 3.3
|
||||
*/
|
||||
class Symfony_DI_PhpDumper_Test_Base64Parameters extends Container
|
||||
{
|
||||
private $parameters;
|
||||
private $targetDirs = array();
|
||||
private $privates = array();
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->parameters = $this->getDefaultParameters();
|
||||
|
||||
$this->services = $this->privates = array();
|
||||
|
||||
$this->aliases = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function reset()
|
||||
{
|
||||
$this->privates = array();
|
||||
parent::reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function compile()
|
||||
{
|
||||
throw new LogicException('You cannot compile a dumped container that was already compiled.');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isCompiled()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getParameter($name)
|
||||
{
|
||||
$name = (string) $name;
|
||||
|
||||
if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters))) {
|
||||
throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name));
|
||||
}
|
||||
if (isset($this->loadedDynamicParameters[$name])) {
|
||||
return $this->loadedDynamicParameters[$name] ? $this->dynamicParameters[$name] : $this->getDynamicParameter($name);
|
||||
}
|
||||
|
||||
return $this->parameters[$name];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function hasParameter($name)
|
||||
{
|
||||
$name = (string) $name;
|
||||
|
||||
return isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setParameter($name, $value)
|
||||
{
|
||||
throw new LogicException('Impossible to call set() on a frozen ParameterBag.');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getParameterBag()
|
||||
{
|
||||
if (null === $this->parameterBag) {
|
||||
$parameters = $this->parameters;
|
||||
foreach ($this->loadedDynamicParameters as $name => $loaded) {
|
||||
$parameters[$name] = $loaded ? $this->dynamicParameters[$name] : $this->getDynamicParameter($name);
|
||||
}
|
||||
$this->parameterBag = new FrozenParameterBag($parameters);
|
||||
}
|
||||
|
||||
return $this->parameterBag;
|
||||
}
|
||||
|
||||
private $loadedDynamicParameters = array(
|
||||
'hello' => false,
|
||||
);
|
||||
private $dynamicParameters = array();
|
||||
|
||||
/**
|
||||
* Computes a dynamic parameter.
|
||||
*
|
||||
* @param string The name of the dynamic parameter to load
|
||||
*
|
||||
* @return mixed The value of the dynamic parameter
|
||||
*
|
||||
* @throws InvalidArgumentException When the dynamic parameter does not exist
|
||||
*/
|
||||
private function getDynamicParameter($name)
|
||||
{
|
||||
switch ($name) {
|
||||
case 'hello': $value = $this->getEnv('base64:foo'); break;
|
||||
default: throw new InvalidArgumentException(sprintf('The dynamic parameter "%s" must be defined.', $name));
|
||||
}
|
||||
$this->loadedDynamicParameters[$name] = true;
|
||||
|
||||
return $this->dynamicParameters[$name] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the default parameters.
|
||||
*
|
||||
* @return array An array of the default parameters
|
||||
*/
|
||||
protected function getDefaultParameters()
|
||||
{
|
||||
return array(
|
||||
'env(foo)' => 'd29ybGQ=',
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,175 @@
|
||||
<?php
|
||||
|
||||
use Symfony\Component\DependencyInjection\Argument\RewindableGenerator;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\DependencyInjection\Container;
|
||||
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\DependencyInjection\Exception\LogicException;
|
||||
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
|
||||
use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag;
|
||||
|
||||
/**
|
||||
* Symfony_DI_PhpDumper_Test_Rot13Parameters.
|
||||
*
|
||||
* This class has been auto-generated
|
||||
* by the Symfony Dependency Injection Component.
|
||||
*
|
||||
* @final since Symfony 3.3
|
||||
*/
|
||||
class Symfony_DI_PhpDumper_Test_Rot13Parameters extends Container
|
||||
{
|
||||
private $parameters;
|
||||
private $targetDirs = array();
|
||||
private $privates = array();
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->parameters = $this->getDefaultParameters();
|
||||
|
||||
$this->services = $this->privates = array();
|
||||
$this->methodMap = array(
|
||||
'Symfony\\Component\\DependencyInjection\\Tests\\Dumper\\Rot13EnvVarProcessor' => 'getRot13EnvVarProcessorService',
|
||||
'container.env_var_processors_locator' => 'getContainer_EnvVarProcessorsLocatorService',
|
||||
);
|
||||
|
||||
$this->aliases = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function reset()
|
||||
{
|
||||
$this->privates = array();
|
||||
parent::reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function compile()
|
||||
{
|
||||
throw new LogicException('You cannot compile a dumped container that was already compiled.');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isCompiled()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the public 'Symfony\Component\DependencyInjection\Tests\Dumper\Rot13EnvVarProcessor' shared service.
|
||||
*
|
||||
* @return \Symfony\Component\DependencyInjection\Tests\Dumper\Rot13EnvVarProcessor
|
||||
*/
|
||||
protected function getRot13EnvVarProcessorService()
|
||||
{
|
||||
return $this->services['Symfony\Component\DependencyInjection\Tests\Dumper\Rot13EnvVarProcessor'] = new \Symfony\Component\DependencyInjection\Tests\Dumper\Rot13EnvVarProcessor();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the public 'container.env_var_processors_locator' shared service.
|
||||
*
|
||||
* @return \Symfony\Component\DependencyInjection\ServiceLocator
|
||||
*/
|
||||
protected function getContainer_EnvVarProcessorsLocatorService()
|
||||
{
|
||||
return $this->services['container.env_var_processors_locator'] = new \Symfony\Component\DependencyInjection\ServiceLocator(array('rot13' => function () {
|
||||
return ($this->services['Symfony\Component\DependencyInjection\Tests\Dumper\Rot13EnvVarProcessor'] ?? $this->services['Symfony\Component\DependencyInjection\Tests\Dumper\Rot13EnvVarProcessor'] = new \Symfony\Component\DependencyInjection\Tests\Dumper\Rot13EnvVarProcessor());
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getParameter($name)
|
||||
{
|
||||
$name = (string) $name;
|
||||
|
||||
if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters))) {
|
||||
throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name));
|
||||
}
|
||||
if (isset($this->loadedDynamicParameters[$name])) {
|
||||
return $this->loadedDynamicParameters[$name] ? $this->dynamicParameters[$name] : $this->getDynamicParameter($name);
|
||||
}
|
||||
|
||||
return $this->parameters[$name];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function hasParameter($name)
|
||||
{
|
||||
$name = (string) $name;
|
||||
|
||||
return isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setParameter($name, $value)
|
||||
{
|
||||
throw new LogicException('Impossible to call set() on a frozen ParameterBag.');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getParameterBag()
|
||||
{
|
||||
if (null === $this->parameterBag) {
|
||||
$parameters = $this->parameters;
|
||||
foreach ($this->loadedDynamicParameters as $name => $loaded) {
|
||||
$parameters[$name] = $loaded ? $this->dynamicParameters[$name] : $this->getDynamicParameter($name);
|
||||
}
|
||||
$this->parameterBag = new FrozenParameterBag($parameters);
|
||||
}
|
||||
|
||||
return $this->parameterBag;
|
||||
}
|
||||
|
||||
private $loadedDynamicParameters = array(
|
||||
'hello' => false,
|
||||
);
|
||||
private $dynamicParameters = array();
|
||||
|
||||
/**
|
||||
* Computes a dynamic parameter.
|
||||
*
|
||||
* @param string The name of the dynamic parameter to load
|
||||
*
|
||||
* @return mixed The value of the dynamic parameter
|
||||
*
|
||||
* @throws InvalidArgumentException When the dynamic parameter does not exist
|
||||
*/
|
||||
private function getDynamicParameter($name)
|
||||
{
|
||||
switch ($name) {
|
||||
case 'hello': $value = $this->getEnv('rot13:foo'); break;
|
||||
default: throw new InvalidArgumentException(sprintf('The dynamic parameter "%s" must be defined.', $name));
|
||||
}
|
||||
$this->loadedDynamicParameters[$name] = true;
|
||||
|
||||
return $this->dynamicParameters[$name] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the default parameters.
|
||||
*
|
||||
* @return array An array of the default parameters
|
||||
*/
|
||||
protected function getDefaultParameters()
|
||||
{
|
||||
return array(
|
||||
'env(foo)' => 'jbeyq',
|
||||
);
|
||||
}
|
||||
}
|
@ -1,6 +1,11 @@
|
||||
parameters:
|
||||
project_dir: '/foo/bar'
|
||||
env(FOO): foo
|
||||
env(DB): 'sqlite://%%project_dir%%/var/data.db'
|
||||
bar: '%env(FOO)%'
|
||||
baz: '%env(int:Baz)%'
|
||||
json: '%env(json:file:json_file)%'
|
||||
db_dsn: '%env(resolve:DB)%'
|
||||
|
||||
services:
|
||||
test:
|
||||
@ -8,3 +13,4 @@ services:
|
||||
arguments:
|
||||
- '%env(Bar)%'
|
||||
- 'foo%bar%baz'
|
||||
- '%baz%'
|
||||
|
Reference in New Issue
Block a user