Merge branch '4.4'
* 4.4: [DI] fix locators with numeric keys Add support for NO_COLOR env var [DI][FrameworkBundle] add EnvVarLoaderInterface - remove SecretEnvVarProcessor Fix error when we use VO for the marking property [DI] Remove LazyString from 4.4, before adding back to the String component
This commit is contained in:
commit
0cd837bf1a
@ -345,6 +345,11 @@ class DeprecationErrorHandler
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Follow https://no-color.org/
|
||||||
|
if (isset($_SERVER['NO_COLOR']) || false !== getenv('NO_COLOR')) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if ('Hyper' === getenv('TERM_PROGRAM')) {
|
if ('Hyper' === getenv('TERM_PROGRAM')) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -48,6 +48,7 @@ use Symfony\Component\DependencyInjection\Compiler\ServiceLocatorTagPass;
|
|||||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||||
use Symfony\Component\DependencyInjection\Definition;
|
use Symfony\Component\DependencyInjection\Definition;
|
||||||
|
use Symfony\Component\DependencyInjection\EnvVarLoaderInterface;
|
||||||
use Symfony\Component\DependencyInjection\EnvVarProcessorInterface;
|
use Symfony\Component\DependencyInjection\EnvVarProcessorInterface;
|
||||||
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
|
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
|
||||||
use Symfony\Component\DependencyInjection\Exception\LogicException;
|
use Symfony\Component\DependencyInjection\Exception\LogicException;
|
||||||
@ -378,6 +379,8 @@ class FrameworkExtension extends Extension
|
|||||||
->addTag('console.command');
|
->addTag('console.command');
|
||||||
$container->registerForAutoconfiguration(ResourceCheckerInterface::class)
|
$container->registerForAutoconfiguration(ResourceCheckerInterface::class)
|
||||||
->addTag('config_cache.resource_checker');
|
->addTag('config_cache.resource_checker');
|
||||||
|
$container->registerForAutoconfiguration(EnvVarLoaderInterface::class)
|
||||||
|
->addTag('container.env_var_loader');
|
||||||
$container->registerForAutoconfiguration(EnvVarProcessorInterface::class)
|
$container->registerForAutoconfiguration(EnvVarProcessorInterface::class)
|
||||||
->addTag('container.env_var_processor');
|
->addTag('container.env_var_processor');
|
||||||
$container->registerForAutoconfiguration(ServiceLocator::class)
|
$container->registerForAutoconfiguration(ServiceLocator::class)
|
||||||
@ -1348,9 +1351,13 @@ class FrameworkExtension extends Extension
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($config['decryption_env_var']) {
|
if ($config['decryption_env_var']) {
|
||||||
$container->getDefinition('secrets.decryption_key')->replaceArgument(1, $config['decryption_env_var']);
|
if (!preg_match('/^(?:\w*+:)*+\w++$/', $config['decryption_env_var'])) {
|
||||||
|
throw new InvalidArgumentException(sprintf('Invalid value "%s" set as "decryption_env_var": only "word" characters are allowed.', $config['decryption_env_var']));
|
||||||
|
}
|
||||||
|
|
||||||
|
$container->getDefinition('secrets.vault')->replaceArgument(1, "%env({$config['decryption_env_var']})%");
|
||||||
} else {
|
} else {
|
||||||
$container->removeDefinition('secrets.decryption_key');
|
$container->getDefinition('secrets.vault')->replaceArgument(1, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,36 +6,13 @@
|
|||||||
|
|
||||||
<services>
|
<services>
|
||||||
<service id="secrets.vault" class="Symfony\Bundle\FrameworkBundle\Secrets\SodiumVault">
|
<service id="secrets.vault" class="Symfony\Bundle\FrameworkBundle\Secrets\SodiumVault">
|
||||||
|
<tag name="container.env_var_loader" />
|
||||||
<argument>%kernel.project_dir%/config/secrets/%kernel.environment%</argument>
|
<argument>%kernel.project_dir%/config/secrets/%kernel.environment%</argument>
|
||||||
<argument type="service" id="secrets.decryption_key" on-invalid="ignore" />
|
<argument>%env(base64:default::SYMFONY_DECRYPTION_SECRET)%</argument>
|
||||||
</service>
|
|
||||||
|
|
||||||
<!--
|
|
||||||
LazyString::fromCallable() is used as a wrapper to lazily read the SYMFONY_DECRYPTION_SECRET var from the env.
|
|
||||||
By overriding this service and using the same strategy, the decryption key can be fetched lazily from any other service if needed.
|
|
||||||
-->
|
|
||||||
<service id="secrets.decryption_key" class="Symfony\Component\DependencyInjection\LazyString">
|
|
||||||
<factory class="Symfony\Component\DependencyInjection\LazyString" method="fromCallable" />
|
|
||||||
<argument type="service">
|
|
||||||
<service class="Closure">
|
|
||||||
<factory class="Closure" method="fromCallable" />
|
|
||||||
<argument type="collection">
|
|
||||||
<argument type="service" id="service_container" />
|
|
||||||
<argument>getEnv</argument>
|
|
||||||
</argument>
|
|
||||||
</service>
|
|
||||||
</argument>
|
|
||||||
<argument>base64:default::SYMFONY_DECRYPTION_SECRET</argument>
|
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
<service id="secrets.local_vault" class="Symfony\Bundle\FrameworkBundle\Secrets\DotenvVault">
|
<service id="secrets.local_vault" class="Symfony\Bundle\FrameworkBundle\Secrets\DotenvVault">
|
||||||
<argument>%kernel.project_dir%/.env.local</argument>
|
<argument>%kernel.project_dir%/.env.local</argument>
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
<service id="secrets.env_var_processor" class="Symfony\Bundle\FrameworkBundle\Secrets\SecretEnvVarProcessor">
|
|
||||||
<argument type="service" id="secrets.vault" />
|
|
||||||
<argument type="service" id="secrets.local_vault" on-invalid="ignore" />
|
|
||||||
<tag name="container.env_var_processor" />
|
|
||||||
</service>
|
|
||||||
</services>
|
</services>
|
||||||
</container>
|
</container>
|
||||||
|
@ -117,6 +117,12 @@
|
|||||||
<tag name="kernel.event_subscriber" />
|
<tag name="kernel.event_subscriber" />
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
|
<service id="container.env_var_processor" class="Symfony\Component\DependencyInjection\EnvVarProcessor">
|
||||||
|
<tag name="container.env_var_processor" />
|
||||||
|
<argument type="service" id="service_container" />
|
||||||
|
<argument type="tagged_iterator" tag="container.env_var_loader" />
|
||||||
|
</service>
|
||||||
|
|
||||||
<service id="slugger" class="Symfony\Component\String\Slugger\AsciiSlugger">
|
<service id="slugger" class="Symfony\Component\String\Slugger\AsciiSlugger">
|
||||||
<argument>%kernel.default_locale%</argument>
|
<argument>%kernel.default_locale%</argument>
|
||||||
<tag name="kernel.locale_aware" />
|
<tag name="kernel.locale_aware" />
|
||||||
|
@ -54,7 +54,7 @@ class DotenvVault extends AbstractVault
|
|||||||
{
|
{
|
||||||
$this->lastMessage = null;
|
$this->lastMessage = null;
|
||||||
$this->validateName($name);
|
$this->validateName($name);
|
||||||
$v = \is_string($_SERVER[$name] ?? null) ? $_SERVER[$name] : ($_ENV[$name] ?? null);
|
$v = \is_string($_SERVER[$name] ?? null) && 0 !== strpos($name, 'HTTP_') ? $_SERVER[$name] : ($_ENV[$name] ?? null);
|
||||||
|
|
||||||
if (null === $v) {
|
if (null === $v) {
|
||||||
$this->lastMessage = sprintf('Secret "%s" not found in "%s".', $name, $this->getPrettyPath($this->dotenvFile));
|
$this->lastMessage = sprintf('Secret "%s" not found in "%s".', $name, $this->getPrettyPath($this->dotenvFile));
|
||||||
|
@ -1,59 +0,0 @@
|
|||||||
<?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\Bundle\FrameworkBundle\Secrets;
|
|
||||||
|
|
||||||
use Symfony\Component\DependencyInjection\EnvVarProcessorInterface;
|
|
||||||
use Symfony\Component\DependencyInjection\Exception\EnvNotFoundException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Tobias Schultze <http://tobion.de>
|
|
||||||
* @author Nicolas Grekas <p@tchwork.com>
|
|
||||||
*
|
|
||||||
* @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));
|
|
||||||
}
|
|
||||||
}
|
|
@ -11,6 +11,8 @@
|
|||||||
|
|
||||||
namespace Symfony\Bundle\FrameworkBundle\Secrets;
|
namespace Symfony\Bundle\FrameworkBundle\Secrets;
|
||||||
|
|
||||||
|
use Symfony\Component\DependencyInjection\EnvVarLoaderInterface;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Tobias Schultze <http://tobion.de>
|
* @author Tobias Schultze <http://tobion.de>
|
||||||
* @author Jérémy Derussé <jeremy@derusse.com>
|
* @author Jérémy Derussé <jeremy@derusse.com>
|
||||||
@ -18,7 +20,7 @@ namespace Symfony\Bundle\FrameworkBundle\Secrets;
|
|||||||
*
|
*
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
class SodiumVault extends AbstractVault
|
class SodiumVault extends AbstractVault implements EnvVarLoaderInterface
|
||||||
{
|
{
|
||||||
private $encryptionKey;
|
private $encryptionKey;
|
||||||
private $decryptionKey;
|
private $decryptionKey;
|
||||||
@ -56,8 +58,8 @@ class SodiumVault extends AbstractVault
|
|||||||
// ignore failures to load keys
|
// ignore failures to load keys
|
||||||
}
|
}
|
||||||
|
|
||||||
if ('' !== $this->decryptionKey && !file_exists($this->pathPrefix.'sodium.encrypt.public')) {
|
if ('' !== $this->decryptionKey && !file_exists($this->pathPrefix.'encrypt.public.php')) {
|
||||||
$this->export('sodium.encrypt.public', $this->encryptionKey);
|
$this->export('encrypt.public', $this->encryptionKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$override && null !== $this->encryptionKey) {
|
if (!$override && null !== $this->encryptionKey) {
|
||||||
@ -69,10 +71,10 @@ class SodiumVault extends AbstractVault
|
|||||||
$this->decryptionKey = sodium_crypto_box_keypair();
|
$this->decryptionKey = sodium_crypto_box_keypair();
|
||||||
$this->encryptionKey = sodium_crypto_box_publickey($this->decryptionKey);
|
$this->encryptionKey = sodium_crypto_box_publickey($this->decryptionKey);
|
||||||
|
|
||||||
$this->export('sodium.encrypt.public', $this->encryptionKey);
|
$this->export('encrypt.public', $this->encryptionKey);
|
||||||
$this->export('sodium.decrypt.private', $this->decryptionKey);
|
$this->export('decrypt.private', $this->decryptionKey);
|
||||||
|
|
||||||
$this->lastMessage = sprintf('Sodium keys have been generated at "%s*.{public,private}".', $this->getPrettyPath($this->pathPrefix));
|
$this->lastMessage = sprintf('Sodium keys have been generated at "%s*.public/private.php".', $this->getPrettyPath($this->pathPrefix));
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -82,12 +84,12 @@ class SodiumVault extends AbstractVault
|
|||||||
$this->lastMessage = null;
|
$this->lastMessage = null;
|
||||||
$this->validateName($name);
|
$this->validateName($name);
|
||||||
$this->loadKeys();
|
$this->loadKeys();
|
||||||
$this->export($name.'.'.substr_replace(md5($name), '.sodium', -26), sodium_crypto_box_seal($value, $this->encryptionKey ?? sodium_crypto_box_publickey($this->decryptionKey)));
|
$this->export($name.'.'.substr(md5($name), 0, 6), sodium_crypto_box_seal($value, $this->encryptionKey ?? sodium_crypto_box_publickey($this->decryptionKey)));
|
||||||
|
|
||||||
$list = $this->list();
|
$list = $this->list();
|
||||||
$list[$name] = null;
|
$list[$name] = null;
|
||||||
uksort($list, 'strnatcmp');
|
uksort($list, 'strnatcmp');
|
||||||
file_put_contents($this->pathPrefix.'sodium.list', sprintf("<?php\n\nreturn %s;\n", var_export($list, true), LOCK_EX));
|
file_put_contents($this->pathPrefix.'list.php', sprintf("<?php\n\nreturn %s;\n", var_export($list, true), LOCK_EX));
|
||||||
|
|
||||||
$this->lastMessage = sprintf('Secret "%s" encrypted in "%s"; you can commit it.', $name, $this->getPrettyPath(\dirname($this->pathPrefix).\DIRECTORY_SEPARATOR));
|
$this->lastMessage = sprintf('Secret "%s" encrypted in "%s"; you can commit it.', $name, $this->getPrettyPath(\dirname($this->pathPrefix).\DIRECTORY_SEPARATOR));
|
||||||
}
|
}
|
||||||
@ -97,7 +99,7 @@ class SodiumVault extends AbstractVault
|
|||||||
$this->lastMessage = null;
|
$this->lastMessage = null;
|
||||||
$this->validateName($name);
|
$this->validateName($name);
|
||||||
|
|
||||||
if (!file_exists($file = $this->pathPrefix.$name.'.'.substr_replace(md5($name), '.sodium', -26))) {
|
if (!file_exists($file = $this->pathPrefix.$name.'.'.substr_replace(md5($name), '.php', -26))) {
|
||||||
$this->lastMessage = sprintf('Secret "%s" not found in "%s".', $name, $this->getPrettyPath(\dirname($this->pathPrefix).\DIRECTORY_SEPARATOR));
|
$this->lastMessage = sprintf('Secret "%s" not found in "%s".', $name, $this->getPrettyPath(\dirname($this->pathPrefix).\DIRECTORY_SEPARATOR));
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
@ -131,7 +133,7 @@ class SodiumVault extends AbstractVault
|
|||||||
$this->lastMessage = null;
|
$this->lastMessage = null;
|
||||||
$this->validateName($name);
|
$this->validateName($name);
|
||||||
|
|
||||||
if (!file_exists($file = $this->pathPrefix.$name.'.'.substr_replace(md5($name), '.sodium', -26))) {
|
if (!file_exists($file = $this->pathPrefix.$name.'.'.substr_replace(md5($name), '.php', -26))) {
|
||||||
$this->lastMessage = sprintf('Secret "%s" not found in "%s".', $name, $this->getPrettyPath(\dirname($this->pathPrefix).\DIRECTORY_SEPARATOR));
|
$this->lastMessage = sprintf('Secret "%s" not found in "%s".', $name, $this->getPrettyPath(\dirname($this->pathPrefix).\DIRECTORY_SEPARATOR));
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@ -139,7 +141,7 @@ class SodiumVault extends AbstractVault
|
|||||||
|
|
||||||
$list = $this->list();
|
$list = $this->list();
|
||||||
unset($list[$name]);
|
unset($list[$name]);
|
||||||
file_put_contents($this->pathPrefix.'sodium.list', sprintf("<?php\n\nreturn %s;\n", var_export($list, true), LOCK_EX));
|
file_put_contents($this->pathPrefix.'list.php', sprintf("<?php\n\nreturn %s;\n", var_export($list, true), LOCK_EX));
|
||||||
|
|
||||||
$this->lastMessage = sprintf('Secret "%s" removed from "%s".', $name, $this->getPrettyPath(\dirname($this->pathPrefix).\DIRECTORY_SEPARATOR));
|
$this->lastMessage = sprintf('Secret "%s" removed from "%s".', $name, $this->getPrettyPath(\dirname($this->pathPrefix).\DIRECTORY_SEPARATOR));
|
||||||
|
|
||||||
@ -150,7 +152,7 @@ class SodiumVault extends AbstractVault
|
|||||||
{
|
{
|
||||||
$this->lastMessage = null;
|
$this->lastMessage = null;
|
||||||
|
|
||||||
if (!file_exists($file = $this->pathPrefix.'sodium.list')) {
|
if (!file_exists($file = $this->pathPrefix.'list.php')) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -167,6 +169,11 @@ class SodiumVault extends AbstractVault
|
|||||||
return $secrets;
|
return $secrets;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function loadEnvVars(): array
|
||||||
|
{
|
||||||
|
return $this->list(true);
|
||||||
|
}
|
||||||
|
|
||||||
private function loadKeys(): void
|
private function loadKeys(): void
|
||||||
{
|
{
|
||||||
if (!\function_exists('sodium_crypto_box_seal')) {
|
if (!\function_exists('sodium_crypto_box_seal')) {
|
||||||
@ -177,12 +184,12 @@ class SodiumVault extends AbstractVault
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (file_exists($this->pathPrefix.'sodium.decrypt.private')) {
|
if (file_exists($this->pathPrefix.'decrypt.private.php')) {
|
||||||
$this->decryptionKey = (string) include $this->pathPrefix.'sodium.decrypt.private';
|
$this->decryptionKey = (string) include $this->pathPrefix.'decrypt.private.php';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (file_exists($this->pathPrefix.'sodium.encrypt.public')) {
|
if (file_exists($this->pathPrefix.'encrypt.public.php')) {
|
||||||
$this->encryptionKey = (string) include $this->pathPrefix.'sodium.encrypt.public';
|
$this->encryptionKey = (string) include $this->pathPrefix.'encrypt.public.php';
|
||||||
} elseif ('' !== $this->decryptionKey) {
|
} elseif ('' !== $this->decryptionKey) {
|
||||||
$this->encryptionKey = sodium_crypto_box_publickey($this->decryptionKey);
|
$this->encryptionKey = sodium_crypto_box_publickey($this->decryptionKey);
|
||||||
} else {
|
} else {
|
||||||
@ -196,7 +203,7 @@ class SodiumVault extends AbstractVault
|
|||||||
$data = str_replace('%', '\x', rawurlencode($data));
|
$data = str_replace('%', '\x', rawurlencode($data));
|
||||||
$data = sprintf("<?php // %s on %s\n\nreturn \"%s\";\n", $name, date('r'), $data);
|
$data = sprintf("<?php // %s on %s\n\nreturn \"%s\";\n", $name, date('r'), $data);
|
||||||
|
|
||||||
if (false === file_put_contents($this->pathPrefix.$file, $data, LOCK_EX)) {
|
if (false === file_put_contents($this->pathPrefix.$file.'.php', $data, LOCK_EX)) {
|
||||||
$e = error_get_last();
|
$e = error_get_last();
|
||||||
throw new \ErrorException($e['message'] ?? 'Failed to write secrets data.', 0, $e['type'] ?? E_USER_WARNING);
|
throw new \ErrorException($e['message'] ?? 'Failed to write secrets data.', 0, $e['type'] ?? E_USER_WARNING);
|
||||||
}
|
}
|
||||||
|
@ -1,9 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
$container->setParameter('env(REDIS_URL)', 'redis://paas.com');
|
|
||||||
|
|
||||||
$container->loadFromExtension('framework', [
|
|
||||||
'cache' => [
|
|
||||||
'default_redis_provider' => '%env(REDIS_URL)%',
|
|
||||||
],
|
|
||||||
]);
|
|
@ -1,17 +0,0 @@
|
|||||||
<?xml version="1.0" ?>
|
|
||||||
<container xmlns="http://symfony.com/schema/dic/services"
|
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
||||||
xmlns:framework="http://symfony.com/schema/dic/symfony"
|
|
||||||
xsi:schemaLocation="http://symfony.com/schema/dic/services https://symfony.com/schema/dic/services/services-1.0.xsd
|
|
||||||
http://symfony.com/schema/dic/symfony https://symfony.com/schema/dic/symfony/symfony-1.0.xsd">
|
|
||||||
|
|
||||||
<parameters>
|
|
||||||
<parameter key="env(REDIS_URL)">redis://paas.com</parameter>
|
|
||||||
</parameters>
|
|
||||||
|
|
||||||
<framework:config>
|
|
||||||
<framework:cache>
|
|
||||||
<framework:default-redis-provider>%env(REDIS_URL)%</framework:default-redis-provider>
|
|
||||||
</framework:cache>
|
|
||||||
</framework:config>
|
|
||||||
</container>
|
|
@ -1,6 +0,0 @@
|
|||||||
parameters:
|
|
||||||
env(REDIS_URL): redis://paas.com
|
|
||||||
|
|
||||||
framework:
|
|
||||||
cache:
|
|
||||||
default_redis_provider: "%env(REDIS_URL)%"
|
|
@ -34,7 +34,7 @@ use Symfony\Component\DependencyInjection\ContainerBuilder;
|
|||||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||||
use Symfony\Component\DependencyInjection\Definition;
|
use Symfony\Component\DependencyInjection\Definition;
|
||||||
use Symfony\Component\DependencyInjection\Loader\ClosureLoader;
|
use Symfony\Component\DependencyInjection\Loader\ClosureLoader;
|
||||||
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
|
use Symfony\Component\DependencyInjection\ParameterBag\EnvPlaceholderParameterBag;
|
||||||
use Symfony\Component\DependencyInjection\Reference;
|
use Symfony\Component\DependencyInjection\Reference;
|
||||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||||
use Symfony\Component\HttpClient\ScopingHttpClient;
|
use Symfony\Component\HttpClient\ScopingHttpClient;
|
||||||
@ -1202,20 +1202,6 @@ abstract class FrameworkExtensionTest extends TestCase
|
|||||||
$this->assertSame($redisUrl, $url);
|
$this->assertSame($redisUrl, $url);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testCacheDefaultRedisProviderWithEnvVar()
|
|
||||||
{
|
|
||||||
$container = $this->createContainerFromFile('cache_env_var');
|
|
||||||
|
|
||||||
$redisUrl = 'redis://paas.com';
|
|
||||||
$providerId = '.cache_connection.'.ContainerBuilder::hash($redisUrl);
|
|
||||||
|
|
||||||
$this->assertTrue($container->hasDefinition($providerId));
|
|
||||||
|
|
||||||
$url = $container->getDefinition($providerId)->getArgument(0);
|
|
||||||
|
|
||||||
$this->assertSame($redisUrl, $url);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testCachePoolServices()
|
public function testCachePoolServices()
|
||||||
{
|
{
|
||||||
$container = $this->createContainerFromFile('cache', [], true, false);
|
$container = $this->createContainerFromFile('cache', [], true, false);
|
||||||
@ -1383,7 +1369,7 @@ abstract class FrameworkExtensionTest extends TestCase
|
|||||||
|
|
||||||
protected function createContainer(array $data = [])
|
protected function createContainer(array $data = [])
|
||||||
{
|
{
|
||||||
return new ContainerBuilder(new ParameterBag(array_merge([
|
return new ContainerBuilder(new EnvPlaceholderParameterBag(array_merge([
|
||||||
'kernel.bundles' => ['FrameworkBundle' => 'Symfony\\Bundle\\FrameworkBundle\\FrameworkBundle'],
|
'kernel.bundles' => ['FrameworkBundle' => 'Symfony\\Bundle\\FrameworkBundle\\FrameworkBundle'],
|
||||||
'kernel.bundles_metadata' => ['FrameworkBundle' => ['namespace' => 'Symfony\\Bundle\\FrameworkBundle', 'path' => __DIR__.'/../..']],
|
'kernel.bundles_metadata' => ['FrameworkBundle' => ['namespace' => 'Symfony\\Bundle\\FrameworkBundle', 'path' => __DIR__.'/../..']],
|
||||||
'kernel.cache_dir' => __DIR__,
|
'kernel.cache_dir' => __DIR__,
|
||||||
|
@ -26,19 +26,19 @@ class SodiumVaultTest extends TestCase
|
|||||||
$vault = new SodiumVault($this->secretsDir);
|
$vault = new SodiumVault($this->secretsDir);
|
||||||
|
|
||||||
$this->assertTrue($vault->generateKeys());
|
$this->assertTrue($vault->generateKeys());
|
||||||
$this->assertFileExists($this->secretsDir.'/test.sodium.encrypt.public');
|
$this->assertFileExists($this->secretsDir.'/test.encrypt.public.php');
|
||||||
$this->assertFileExists($this->secretsDir.'/test.sodium.decrypt.private');
|
$this->assertFileExists($this->secretsDir.'/test.decrypt.private.php');
|
||||||
|
|
||||||
$encKey = file_get_contents($this->secretsDir.'/test.sodium.encrypt.public');
|
$encKey = file_get_contents($this->secretsDir.'/test.encrypt.public.php');
|
||||||
$decKey = file_get_contents($this->secretsDir.'/test.sodium.decrypt.private');
|
$decKey = file_get_contents($this->secretsDir.'/test.decrypt.private.php');
|
||||||
|
|
||||||
$this->assertFalse($vault->generateKeys());
|
$this->assertFalse($vault->generateKeys());
|
||||||
$this->assertStringEqualsFile($this->secretsDir.'/test.sodium.encrypt.public', $encKey);
|
$this->assertStringEqualsFile($this->secretsDir.'/test.encrypt.public.php', $encKey);
|
||||||
$this->assertStringEqualsFile($this->secretsDir.'/test.sodium.decrypt.private', $decKey);
|
$this->assertStringEqualsFile($this->secretsDir.'/test.decrypt.private.php', $decKey);
|
||||||
|
|
||||||
$this->assertTrue($vault->generateKeys(true));
|
$this->assertTrue($vault->generateKeys(true));
|
||||||
$this->assertStringNotEqualsFile($this->secretsDir.'/test.sodium.encrypt.public', $encKey);
|
$this->assertStringNotEqualsFile($this->secretsDir.'/test.encrypt.public.php', $encKey);
|
||||||
$this->assertStringNotEqualsFile($this->secretsDir.'/test.sodium.decrypt.private', $decKey);
|
$this->assertStringNotEqualsFile($this->secretsDir.'/test.decrypt.private.php', $decKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testEncryptAndDecrypt()
|
public function testEncryptAndDecrypt()
|
||||||
|
@ -29,6 +29,7 @@ CHANGELOG
|
|||||||
* deprecated returning `null` from `Command::execute()`, return `0` instead
|
* deprecated returning `null` from `Command::execute()`, return `0` instead
|
||||||
* Deprecated the `Application::renderException()` and `Application::doRenderException()` methods,
|
* Deprecated the `Application::renderException()` and `Application::doRenderException()` methods,
|
||||||
use `renderThrowable()` and `doRenderThrowable()` instead.
|
use `renderThrowable()` and `doRenderThrowable()` instead.
|
||||||
|
* added support for the `NO_COLOR` env var (https://no-color.org/)
|
||||||
|
|
||||||
4.3.0
|
4.3.0
|
||||||
-----
|
-----
|
||||||
|
@ -97,6 +97,11 @@ class StreamOutput extends Output
|
|||||||
*/
|
*/
|
||||||
protected function hasColorSupport()
|
protected function hasColorSupport()
|
||||||
{
|
{
|
||||||
|
// Follow https://no-color.org/
|
||||||
|
if (isset($_SERVER['NO_COLOR']) || false !== getenv('NO_COLOR')) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if ('Hyper' === getenv('TERM_PROGRAM')) {
|
if ('Hyper' === getenv('TERM_PROGRAM')) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,6 @@ CHANGELOG
|
|||||||
* made singly-implemented interfaces detection be scoped by file
|
* made singly-implemented interfaces detection be scoped by file
|
||||||
* added ability to define a static priority method for tagged service
|
* added ability to define a static priority method for tagged service
|
||||||
* added support for improved syntax to define method calls in Yaml
|
* added support for improved syntax to define method calls in Yaml
|
||||||
* added `LazyString` for lazy computation of string values injected into services
|
|
||||||
* made the `%env(base64:...)%` processor able to decode base64url
|
* made the `%env(base64:...)%` processor able to decode base64url
|
||||||
* added ability to choose behavior of decorations on non existent decorated services
|
* added ability to choose behavior of decorations on non existent decorated services
|
||||||
|
|
||||||
|
@ -188,7 +188,7 @@ final class CheckTypeDeclarationsPass extends AbstractRecursivePass
|
|||||||
$checkFunction = sprintf('is_%s', $parameter->getType()->getName());
|
$checkFunction = sprintf('is_%s', $parameter->getType()->getName());
|
||||||
|
|
||||||
if (!$parameter->getType()->isBuiltin() || !$checkFunction($value)) {
|
if (!$parameter->getType()->isBuiltin() || !$checkFunction($value)) {
|
||||||
throw new InvalidParameterTypeException($this->currentId, \gettype($value), $parameter);
|
throw new InvalidParameterTypeException($this->currentId, \is_object($value) ? \get_class($value) : \gettype($value), $parameter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -52,6 +52,8 @@ final class ServiceLocatorTagPass extends AbstractRecursivePass
|
|||||||
throw new InvalidArgumentException(sprintf('Invalid definition for service "%s": an array of references is expected as first argument when the "container.service_locator" tag is set.', $this->currentId));
|
throw new InvalidArgumentException(sprintf('Invalid definition for service "%s": an array of references is expected as first argument when the "container.service_locator" tag is set.', $this->currentId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$i = 0;
|
||||||
|
|
||||||
foreach ($arguments[0] as $k => $v) {
|
foreach ($arguments[0] as $k => $v) {
|
||||||
if ($v instanceof ServiceClosureArgument) {
|
if ($v instanceof ServiceClosureArgument) {
|
||||||
continue;
|
continue;
|
||||||
@ -60,10 +62,13 @@ final class ServiceLocatorTagPass extends AbstractRecursivePass
|
|||||||
throw new InvalidArgumentException(sprintf('Invalid definition for service "%s": an array of references is expected as first argument when the "container.service_locator" tag is set, "%s" found for key "%s".', $this->currentId, \is_object($v) ? \get_class($v) : \gettype($v), $k));
|
throw new InvalidArgumentException(sprintf('Invalid definition for service "%s": an array of references is expected as first argument when the "container.service_locator" tag is set, "%s" found for key "%s".', $this->currentId, \is_object($v) ? \get_class($v) : \gettype($v), $k));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (\is_int($k)) {
|
if ($i === $k) {
|
||||||
unset($arguments[0][$k]);
|
unset($arguments[0][$k]);
|
||||||
|
|
||||||
$k = (string) $v;
|
$k = (string) $v;
|
||||||
|
++$i;
|
||||||
|
} elseif (\is_int($k)) {
|
||||||
|
$i = null;
|
||||||
}
|
}
|
||||||
$arguments[0][$k] = new ServiceClosureArgument($v);
|
$arguments[0][$k] = new ServiceClosureArgument($v);
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,25 @@
|
|||||||
|
<?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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* EnvVarLoaderInterface objects return key/value pairs that are added to the list of available env vars.
|
||||||
|
*
|
||||||
|
* @author Nicolas Grekas <p@tchwork.com>
|
||||||
|
*/
|
||||||
|
interface EnvVarLoaderInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @return string[] Key/value pairs that can be accessed using the regular "%env()%" syntax
|
||||||
|
*/
|
||||||
|
public function loadEnvVars(): array;
|
||||||
|
}
|
@ -12,6 +12,7 @@
|
|||||||
namespace Symfony\Component\DependencyInjection;
|
namespace Symfony\Component\DependencyInjection;
|
||||||
|
|
||||||
use Symfony\Component\DependencyInjection\Exception\EnvNotFoundException;
|
use Symfony\Component\DependencyInjection\Exception\EnvNotFoundException;
|
||||||
|
use Symfony\Component\DependencyInjection\Exception\ParameterCircularReferenceException;
|
||||||
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
|
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -20,10 +21,17 @@ use Symfony\Component\DependencyInjection\Exception\RuntimeException;
|
|||||||
class EnvVarProcessor implements EnvVarProcessorInterface
|
class EnvVarProcessor implements EnvVarProcessorInterface
|
||||||
{
|
{
|
||||||
private $container;
|
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->container = $container;
|
||||||
|
$this->loaders = new \IteratorIterator($loaders ?? new \ArrayIterator());
|
||||||
|
$this->loaders = $this->loaders->getInnerIterator();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -127,6 +135,24 @@ class EnvVarProcessor implements EnvVarProcessorInterface
|
|||||||
} elseif (isset($_SERVER[$name]) && 0 !== strpos($name, 'HTTP_')) {
|
} elseif (isset($_SERVER[$name]) && 0 !== strpos($name, 'HTTP_')) {
|
||||||
$env = $_SERVER[$name];
|
$env = $_SERVER[$name];
|
||||||
} elseif (false === ($env = getenv($name)) || null === $env) { // null is a possible value because of thread safety issues
|
} elseif (false === ($env = getenv($name)) || null === $env) { // null is a possible value because of thread safety issues
|
||||||
|
foreach ($this->loadedVars as $vars) {
|
||||||
|
if (false !== $env = ($vars[$name] ?? false)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)")) {
|
if (!$this->container->hasParameter("env($name)")) {
|
||||||
throw new EnvNotFoundException(sprintf('Environment variable not found: "%s".', $name));
|
throw new EnvNotFoundException(sprintf('Environment variable not found: "%s".', $name));
|
||||||
}
|
}
|
||||||
@ -135,6 +161,7 @@ class EnvVarProcessor implements EnvVarProcessorInterface
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!is_scalar($env)) {
|
if (!is_scalar($env)) {
|
||||||
throw new RuntimeException(sprintf('Non-scalar env var "%s" cannot be cast to %s.', $name, $prefix));
|
throw new RuntimeException(sprintf('Non-scalar env var "%s" cannot be cast to %s.', $name, $prefix));
|
||||||
|
@ -1,112 +0,0 @@
|
|||||||
<?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;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A string whose value is computed lazily by a callback.
|
|
||||||
*
|
|
||||||
* @author Nicolas Grekas <p@tchwork.com>
|
|
||||||
*/
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
@ -114,6 +114,7 @@ class ServiceLocatorTagPassTest extends TestCase
|
|||||||
->setArguments([[
|
->setArguments([[
|
||||||
'bar' => new Reference('baz'),
|
'bar' => new Reference('baz'),
|
||||||
new Reference('bar'),
|
new Reference('bar'),
|
||||||
|
16 => new Reference('baz'),
|
||||||
]])
|
]])
|
||||||
->addTag('container.service_locator')
|
->addTag('container.service_locator')
|
||||||
;
|
;
|
||||||
@ -124,6 +125,7 @@ class ServiceLocatorTagPassTest extends TestCase
|
|||||||
$locator = $container->get('foo');
|
$locator = $container->get('foo');
|
||||||
|
|
||||||
$this->assertSame(TestDefinition1::class, \get_class($locator('bar')));
|
$this->assertSame(TestDefinition1::class, \get_class($locator('bar')));
|
||||||
|
$this->assertSame(TestDefinition2::class, \get_class($locator(16)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testBindingsAreCopied()
|
public function testBindingsAreCopied()
|
||||||
|
@ -5,6 +5,7 @@ namespace Symfony\Component\DependencyInjection\Tests;
|
|||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
use Symfony\Component\DependencyInjection\Container;
|
use Symfony\Component\DependencyInjection\Container;
|
||||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||||
|
use Symfony\Component\DependencyInjection\EnvVarLoaderInterface;
|
||||||
use Symfony\Component\DependencyInjection\EnvVarProcessor;
|
use Symfony\Component\DependencyInjection\EnvVarProcessor;
|
||||||
|
|
||||||
class EnvVarProcessorTest extends TestCase
|
class EnvVarProcessorTest extends TestCase
|
||||||
@ -517,4 +518,39 @@ CSV;
|
|||||||
[null, null],
|
[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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,72 +0,0 @@
|
|||||||
<?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;
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
@ -23,7 +23,6 @@
|
|||||||
"require-dev": {
|
"require-dev": {
|
||||||
"symfony/yaml": "^4.4|^5.0",
|
"symfony/yaml": "^4.4|^5.0",
|
||||||
"symfony/config": "^5.0",
|
"symfony/config": "^5.0",
|
||||||
"symfony/error-handler": "^4.4|^5.0",
|
|
||||||
"symfony/expression-language": "^4.4|^5.0"
|
"symfony/expression-language": "^4.4|^5.0"
|
||||||
},
|
},
|
||||||
"suggest": {
|
"suggest": {
|
||||||
|
@ -10,6 +10,7 @@ CHANGELOG
|
|||||||
* added the stamps of a message after it is dispatched in `TraceableMessageBus` and `MessengerDataCollector` collected data
|
* added the stamps of a message after it is dispatched in `TraceableMessageBus` and `MessengerDataCollector` collected data
|
||||||
* added `UuidCaster`
|
* added `UuidCaster`
|
||||||
* made all casters final
|
* made all casters final
|
||||||
|
* added support for the `NO_COLOR` env var (https://no-color.org/)
|
||||||
|
|
||||||
4.3.0
|
4.3.0
|
||||||
-----
|
-----
|
||||||
|
@ -578,6 +578,11 @@ class CliDumper extends AbstractDumper
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Follow https://no-color.org/
|
||||||
|
if (isset($_SERVER['NO_COLOR']) || false !== getenv('NO_COLOR')) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if ('Hyper' === getenv('TERM_PROGRAM')) {
|
if ('Hyper' === getenv('TERM_PROGRAM')) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -61,7 +61,7 @@ final class MethodMarkingStore implements MarkingStoreInterface
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($this->singleState) {
|
if ($this->singleState) {
|
||||||
$marking = [$marking => 1];
|
$marking = [(string) $marking => 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Marking($marking);
|
return new Marking($marking);
|
||||||
|
@ -52,4 +52,35 @@ class MethodMarkingStoreTest extends TestCase
|
|||||||
|
|
||||||
$this->assertEquals($marking, $marking2);
|
$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;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user