- *
- * @internal
- */
-class SecretEnvVarProcessor implements EnvVarProcessorInterface
-{
- private $vault;
- private $localVault;
-
- public function __construct(AbstractVault $vault, AbstractVault $localVault = null)
- {
- $this->vault = $vault;
- $this->localVault = $localVault;
- }
-
- /**
- * {@inheritdoc}
- */
- public static function getProvidedTypes()
- {
- return [
- 'secret' => 'string',
- ];
- }
-
- /**
- * {@inheritdoc}
- */
- public function getEnv($prefix, $name, \Closure $getEnv): string
- {
- if (null !== $this->localVault && null !== ($secret = $this->localVault->reveal($name)) && \array_key_exists($name, $this->vault->list())) {
- return $secret;
- }
-
- if (null !== $secret = $this->vault->reveal($name)) {
- return $secret;
- }
-
- throw new EnvNotFoundException($this->vault->getLastMessage() ?? sprintf('Secret "%s" not found or decryption key is missing.', $name));
- }
-}
diff --git a/src/Symfony/Bundle/FrameworkBundle/Secrets/SodiumVault.php b/src/Symfony/Bundle/FrameworkBundle/Secrets/SodiumVault.php
index cb6e9f527f..e6fcab5060 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Secrets/SodiumVault.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Secrets/SodiumVault.php
@@ -11,6 +11,8 @@
namespace Symfony\Bundle\FrameworkBundle\Secrets;
+use Symfony\Component\DependencyInjection\EnvVarLoaderInterface;
+
/**
* @author Tobias Schultze
+ */
+interface EnvVarLoaderInterface
+{
+ /**
+ * @return string[] Key/value pairs that can be accessed using the regular "%env()%" syntax
+ */
+ public function loadEnvVars(): array;
+}
diff --git a/src/Symfony/Component/DependencyInjection/EnvVarProcessor.php b/src/Symfony/Component/DependencyInjection/EnvVarProcessor.php
index ff528a90b1..69c62ad870 100644
--- a/src/Symfony/Component/DependencyInjection/EnvVarProcessor.php
+++ b/src/Symfony/Component/DependencyInjection/EnvVarProcessor.php
@@ -12,6 +12,7 @@
namespace Symfony\Component\DependencyInjection;
use Symfony\Component\DependencyInjection\Exception\EnvNotFoundException;
+use Symfony\Component\DependencyInjection\Exception\ParameterCircularReferenceException;
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
/**
@@ -20,10 +21,17 @@ use Symfony\Component\DependencyInjection\Exception\RuntimeException;
class EnvVarProcessor implements EnvVarProcessorInterface
{
private $container;
+ private $loaders;
+ private $loadedVars = [];
- public function __construct(ContainerInterface $container)
+ /**
+ * @param EnvVarLoaderInterface[] $loaders
+ */
+ public function __construct(ContainerInterface $container, \Traversable $loaders = null)
{
$this->container = $container;
+ $this->loaders = new \IteratorIterator($loaders ?? new \ArrayIterator());
+ $this->loaders = $this->loaders->getInnerIterator();
}
/**
@@ -127,12 +135,31 @@ class EnvVarProcessor implements EnvVarProcessorInterface
} elseif (isset($_SERVER[$name]) && 0 !== strpos($name, 'HTTP_')) {
$env = $_SERVER[$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(sprintf('Environment variable not found: "%s".', $name));
+ foreach ($this->loadedVars as $vars) {
+ if (false !== $env = ($vars[$name] ?? false)) {
+ break;
+ }
}
- if (null === $env = $this->container->getParameter("env($name)")) {
- return null;
+ try {
+ while ((false === $env || null === $env) && $this->loaders->valid()) {
+ $loader = $this->loaders->current();
+ $this->loaders->next();
+ $this->loadedVars[] = $vars = $loader->loadEnvVars();
+ $env = $vars[$name] ?? false;
+ }
+ } catch (ParameterCircularReferenceException $e) {
+ // skip loaders that need an env var that is not defined
+ }
+
+ if (false === $env || null === $env) {
+ if (!$this->container->hasParameter("env($name)")) {
+ throw new EnvNotFoundException(sprintf('Environment variable not found: "%s".', $name));
+ }
+
+ if (null === $env = $this->container->getParameter("env($name)")) {
+ return null;
+ }
}
}
diff --git a/src/Symfony/Component/DependencyInjection/LazyString.php b/src/Symfony/Component/DependencyInjection/LazyString.php
deleted file mode 100644
index 6a03c376b8..0000000000
--- a/src/Symfony/Component/DependencyInjection/LazyString.php
+++ /dev/null
@@ -1,112 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\DependencyInjection;
-
-/**
- * A string whose value is computed lazily by a callback.
- *
- * @author Nicolas Grekas
- */
-class LazyString
-{
- private $value;
-
- /**
- * @param callable $callback A callable or a [Closure, method] lazy-callable
- *
- * @return static
- */
- public static function fromCallable($callback, ...$arguments): self
- {
- if (!\is_callable($callback) && !(\is_array($callback) && isset($callback[0]) && $callback[0] instanceof \Closure && 2 >= \count($callback))) {
- throw new \TypeError(sprintf('Argument 1 passed to %s() must be a callable or a [Closure, method] lazy-callable, %s given.', __METHOD__, \gettype($callback)));
- }
-
- $lazyString = new static();
- $lazyString->value = static function () use (&$callback, &$arguments, &$value): string {
- if (null !== $arguments) {
- if (!\is_callable($callback)) {
- $callback[0] = $callback[0]();
- $callback[1] = $callback[1] ?? '__invoke';
- }
- $value = $callback(...$arguments);
- $callback = self::getPrettyName($callback);
- $arguments = null;
- }
-
- return $value ?? '';
- };
-
- return $lazyString;
- }
-
- public function __toString()
- {
- if (\is_string($this->value)) {
- return $this->value;
- }
-
- try {
- return $this->value = ($this->value)();
- } catch (\Throwable $e) {
- if (\TypeError::class === \get_class($e) && __FILE__ === $e->getFile()) {
- $type = explode(', ', $e->getMessage());
- $type = substr(array_pop($type), 0, -\strlen(' returned'));
- $r = new \ReflectionFunction($this->value);
- $callback = $r->getStaticVariables()['callback'];
-
- $e = new \TypeError(sprintf('Return value of %s() passed to %s::fromCallable() must be of the type string, %s returned.', $callback, static::class, $type));
- }
-
- if (\PHP_VERSION_ID < 70400) {
- // leverage the ErrorHandler component with graceful fallback when it's not available
- return trigger_error($e, E_USER_ERROR);
- }
-
- throw $e;
- }
- }
-
- private function __construct()
- {
- }
-
- private static function getPrettyName(callable $callback): string
- {
- if (\is_string($callback)) {
- return $callback;
- }
-
- if (\is_array($callback)) {
- $class = \is_object($callback[0]) ? \get_class($callback[0]) : $callback[0];
- $method = $callback[1];
- } elseif ($callback instanceof \Closure) {
- $r = new \ReflectionFunction($callback);
-
- if (false !== strpos($r->name, '{closure}') || !$class = $r->getClosureScopeClass()) {
- return $r->name;
- }
-
- $class = $class->name;
- $method = $r->name;
- } else {
- $class = \get_class($callback);
- $method = '__invoke';
- }
-
- if (isset($class[15]) && "\0" === $class[15] && 0 === strpos($class, "class@anonymous\x00")) {
- $class = get_parent_class($class).'@anonymous';
- }
-
- return $class.'::'.$method;
- }
-}
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ServiceLocatorTagPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ServiceLocatorTagPassTest.php
index 1de02d2577..66af69b543 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ServiceLocatorTagPassTest.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ServiceLocatorTagPassTest.php
@@ -114,6 +114,7 @@ class ServiceLocatorTagPassTest extends TestCase
->setArguments([[
'bar' => new Reference('baz'),
new Reference('bar'),
+ 16 => new Reference('baz'),
]])
->addTag('container.service_locator')
;
@@ -124,6 +125,7 @@ class ServiceLocatorTagPassTest extends TestCase
$locator = $container->get('foo');
$this->assertSame(TestDefinition1::class, \get_class($locator('bar')));
+ $this->assertSame(TestDefinition2::class, \get_class($locator(16)));
}
public function testBindingsAreCopied()
diff --git a/src/Symfony/Component/DependencyInjection/Tests/EnvVarProcessorTest.php b/src/Symfony/Component/DependencyInjection/Tests/EnvVarProcessorTest.php
index 12d852f453..9970eb474f 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/EnvVarProcessorTest.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/EnvVarProcessorTest.php
@@ -5,6 +5,7 @@ namespace Symfony\Component\DependencyInjection\Tests;
use PHPUnit\Framework\TestCase;
use Symfony\Component\DependencyInjection\Container;
use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\EnvVarLoaderInterface;
use Symfony\Component\DependencyInjection\EnvVarProcessor;
class EnvVarProcessorTest extends TestCase
@@ -517,4 +518,39 @@ CSV;
[null, null],
];
}
+
+ public function testEnvLoader()
+ {
+ $loaders = function () {
+ yield new class() implements EnvVarLoaderInterface {
+ public function loadEnvVars(): array
+ {
+ return [
+ 'FOO_ENV_LOADER' => '123',
+ ];
+ }
+ };
+
+ yield new class() implements EnvVarLoaderInterface {
+ public function loadEnvVars(): array
+ {
+ return [
+ 'FOO_ENV_LOADER' => '234',
+ 'BAR_ENV_LOADER' => '456',
+ ];
+ }
+ };
+ };
+
+ $processor = new EnvVarProcessor(new Container(), $loaders());
+
+ $result = $processor->getEnv('string', 'FOO_ENV_LOADER', function () {});
+ $this->assertSame('123', $result);
+
+ $result = $processor->getEnv('string', 'BAR_ENV_LOADER', function () {});
+ $this->assertSame('456', $result);
+
+ $result = $processor->getEnv('string', 'FOO_ENV_LOADER', function () {});
+ $this->assertSame('123', $result); // check twice
+ }
}
diff --git a/src/Symfony/Component/DependencyInjection/Tests/LazyStringTest.php b/src/Symfony/Component/DependencyInjection/Tests/LazyStringTest.php
deleted file mode 100644
index 38899bb369..0000000000
--- a/src/Symfony/Component/DependencyInjection/Tests/LazyStringTest.php
+++ /dev/null
@@ -1,72 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\DependencyInjection\Tests;
-
-use PHPUnit\Framework\TestCase;
-use Symfony\Component\DependencyInjection\LazyString;
-use Symfony\Component\ErrorHandler\ErrorHandler;
-
-class LazyStringTest extends TestCase
-{
- public function testLazyString()
- {
- $count = 0;
- $s = LazyString::fromCallable(function () use (&$count) {
- return ++$count;
- });
-
- $this->assertSame(0, $count);
- $this->assertSame('1', (string) $s);
- $this->assertSame(1, $count);
- }
-
- public function testLazyCallable()
- {
- $count = 0;
- $s = LazyString::fromCallable([function () use (&$count) {
- return new class($count) {
- private $count;
-
- public function __construct(int &$count)
- {
- $this->count = &$count;
- }
-
- public function __invoke()
- {
- return ++$this->count;
- }
- };
- }]);
-
- $this->assertSame(0, $count);
- $this->assertSame('1', (string) $s);
- $this->assertSame(1, $count);
- $this->assertSame('1', (string) $s); // ensure the value is memoized
- $this->assertSame(1, $count);
- }
-
- /**
- * @runInSeparateProcess
- */
- public function testReturnTypeError()
- {
- ErrorHandler::register();
-
- $s = LazyString::fromCallable(function () { return []; });
-
- $this->expectException(\TypeError::class);
- $this->expectExceptionMessage('Return value of '.__NAMESPACE__.'\{closure}() passed to '.LazyString::class.'::fromCallable() must be of the type string, array returned.');
-
- (string) $s;
- }
-}
diff --git a/src/Symfony/Component/DependencyInjection/composer.json b/src/Symfony/Component/DependencyInjection/composer.json
index 8c7cef09c8..b7fffed9b0 100644
--- a/src/Symfony/Component/DependencyInjection/composer.json
+++ b/src/Symfony/Component/DependencyInjection/composer.json
@@ -23,7 +23,6 @@
"require-dev": {
"symfony/yaml": "^4.4|^5.0",
"symfony/config": "^5.0",
- "symfony/error-handler": "^4.4|^5.0",
"symfony/expression-language": "^4.4|^5.0"
},
"suggest": {
diff --git a/src/Symfony/Component/VarDumper/CHANGELOG.md b/src/Symfony/Component/VarDumper/CHANGELOG.md
index a4eb962fd3..94b1c17d1d 100644
--- a/src/Symfony/Component/VarDumper/CHANGELOG.md
+++ b/src/Symfony/Component/VarDumper/CHANGELOG.md
@@ -10,6 +10,7 @@ CHANGELOG
* added the stamps of a message after it is dispatched in `TraceableMessageBus` and `MessengerDataCollector` collected data
* added `UuidCaster`
* made all casters final
+ * added support for the `NO_COLOR` env var (https://no-color.org/)
4.3.0
-----
diff --git a/src/Symfony/Component/VarDumper/Dumper/CliDumper.php b/src/Symfony/Component/VarDumper/Dumper/CliDumper.php
index 42930a3403..326ce1d863 100644
--- a/src/Symfony/Component/VarDumper/Dumper/CliDumper.php
+++ b/src/Symfony/Component/VarDumper/Dumper/CliDumper.php
@@ -578,6 +578,11 @@ class CliDumper extends AbstractDumper
return false;
}
+ // Follow https://no-color.org/
+ if (isset($_SERVER['NO_COLOR']) || false !== getenv('NO_COLOR')) {
+ return false;
+ }
+
if ('Hyper' === getenv('TERM_PROGRAM')) {
return true;
}
diff --git a/src/Symfony/Component/Workflow/MarkingStore/MethodMarkingStore.php b/src/Symfony/Component/Workflow/MarkingStore/MethodMarkingStore.php
index 5951f5f800..3ef65b315a 100644
--- a/src/Symfony/Component/Workflow/MarkingStore/MethodMarkingStore.php
+++ b/src/Symfony/Component/Workflow/MarkingStore/MethodMarkingStore.php
@@ -61,7 +61,7 @@ final class MethodMarkingStore implements MarkingStoreInterface
}
if ($this->singleState) {
- $marking = [$marking => 1];
+ $marking = [(string) $marking => 1];
}
return new Marking($marking);
diff --git a/src/Symfony/Component/Workflow/Tests/MarkingStore/MethodMarkingStoreTest.php b/src/Symfony/Component/Workflow/Tests/MarkingStore/MethodMarkingStoreTest.php
index 7b5c7ffa91..7393faa825 100644
--- a/src/Symfony/Component/Workflow/Tests/MarkingStore/MethodMarkingStoreTest.php
+++ b/src/Symfony/Component/Workflow/Tests/MarkingStore/MethodMarkingStoreTest.php
@@ -52,4 +52,35 @@ class MethodMarkingStoreTest extends TestCase
$this->assertEquals($marking, $marking2);
}
+
+ public function testGetMarkingWithValueObject()
+ {
+ $subject = new Subject($this->createValueObject('first_place'));
+
+ $markingStore = new MethodMarkingStore(true);
+
+ $marking = $markingStore->getMarking($subject);
+
+ $this->assertInstanceOf(Marking::class, $marking);
+ $this->assertCount(1, $marking->getPlaces());
+ $this->assertSame('first_place', (string) $subject->getMarking());
+ }
+
+ private function createValueObject(string $markingValue)
+ {
+ return new class($markingValue) {
+ /** @var string */
+ private $markingValue;
+
+ public function __construct(string $markingValue)
+ {
+ $this->markingValue = $markingValue;
+ }
+
+ public function __toString()
+ {
+ return $this->markingValue;
+ }
+ };
+ }
}