From 6a779d07d0d7d2cd036b6c3b54d3d30d322cb145 Mon Sep 17 00:00:00 2001 From: Steve Boyd Date: Thu, 12 May 2022 17:12:14 +1200 Subject: [PATCH] ENH Allow multiple backtick variables in a single value --- .../03_Environment_Management.md | 7 +++-- src/Core/Injector/Injector.php | 31 ++++++++++++++----- tests/php/Core/Injector/InjectorTest.php | 27 ++++++++++++++++ 3 files changed, 55 insertions(+), 10 deletions(-) diff --git a/docs/en/00_Getting_Started/03_Environment_Management.md b/docs/en/00_Getting_Started/03_Environment_Management.md index 869ed26e0..aa87a727d 100644 --- a/docs/en/00_Getting_Started/03_Environment_Management.md +++ b/docs/en/00_Getting_Started/03_Environment_Management.md @@ -58,13 +58,16 @@ Environment::setEnv('API_KEY', 'AABBCCDDEEFF012345'); ### Using environment variables in config -To use environment variables in `.yaml` configs you can reference them using backticks. +To use environment variables in `.yaml` configs you can reference them using backticks. You can have multiple +environment variables within a single value, though the overall value must start and end with backticks. ```yaml SilverStripe\Core\Injector\Injector: MyServiceClass: properties: - MyProperty: '`ENV_VAR_HERE`' + MyProperty: '`ENV_VAR_ONE`' + MultiValueProperty: '`ENV_VAR_ONE`:`ENV_VAR_TWO`' + ThisWillNotSubstitute: 'lorem `REGULAR_TEXT` ipsum' ``` [info] diff --git a/src/Core/Injector/Injector.php b/src/Core/Injector/Injector.php index 98da89a2c..f5b4b87c8 100644 --- a/src/Core/Injector/Injector.php +++ b/src/Core/Injector/Injector.php @@ -524,16 +524,31 @@ class Injector implements ContainerInterface } // Evaluate constants surrounded by back ticks - if (preg_match('/^`(?[^`]+)`$/', $value ?? '', $matches)) { - $envValue = Environment::getEnv($matches['name']); - if ($envValue !== false) { - $value = $envValue; - } elseif (defined($matches['name'] ?? '')) { - $value = constant($matches['name'] ?? ''); - } else { - $value = null; + $hasBacticks = false; + $allMissing = true; + // $value must start and end with backticks, though there can be multiple + // things being subsituted within $value e.g. "`VAR_ONE`:`VAR_TWO`:`VAR_THREE`" + if (preg_match('/^`.+`$/', $value ?? '')) { + $hasBacticks = true; + preg_match_all('/`(?[^`]+)`/', $value, $matches); + foreach ($matches['name'] as $name) { + $envValue = Environment::getEnv($name); + $val = ''; + if ($envValue !== false) { + $val = $envValue; + } elseif (defined($name)) { + $val = constant($name); + } + $value = str_replace("`$name`", $val, $value); + if ($val) { + $allMissing = false; + } } } + // silverstripe sometimes explictly expects a null value rather than just an empty string + if ($hasBacticks && $allMissing && $value === '') { + return null; + } return $value; } diff --git a/tests/php/Core/Injector/InjectorTest.php b/tests/php/Core/Injector/InjectorTest.php index e9b8b6db4..3b8cd1850 100644 --- a/tests/php/Core/Injector/InjectorTest.php +++ b/tests/php/Core/Injector/InjectorTest.php @@ -3,6 +3,7 @@ namespace SilverStripe\Core\Tests\Injector; use SilverStripe\Core\Config\Config; +use SilverStripe\Core\Environment; use SilverStripe\Core\Injector\Factory; use SilverStripe\Core\Injector\Injector; use SilverStripe\Core\Injector\InjectorNotFoundException; @@ -815,6 +816,32 @@ class InjectorTest extends SapphireTest $this->assertInstanceOf(OtherTestObject::class, $item->property->property); } + /** + * @dataProvider provideConvertServicePropertyBackTicks + */ + public function testConvertServicePropertyBackTicks($value, $expected) + { + Environment::setEnv('INJECTOR_TEST_CSP_A', 'ABC'); + Environment::setEnv('INJECTOR_TEST_CSP_B', 'DEF'); + Environment::setEnv('INJECTOR_TEST_CSP_C', 'GHI'); + $actual = Injector::inst()->convertServiceProperty($value); + $this->assertSame($expected, $actual); + } + + public function provideConvertServicePropertyBackTicks() + { + return [ + ['`INJECTOR_TEST_CSP_A`', 'ABC'], + ['`INJECTOR_TEST_CSP_A`:`INJECTOR_TEST_CSP_B`', 'ABC:DEF'], + ['`INJECTOR_TEST_CSP_A` some text `INJECTOR_TEST_CSP_B`', 'ABC some text DEF'], + ['`INJECTOR_TEST_CSP_A``INJECTOR_TEST_CSP_B`', 'ABCDEF'], + ['`INJECTOR_TEST_CSP_A`:`INJECTOR_TEST_CSP_B``INJECTOR_TEST_CSP_C`', 'ABC:DEFGHI'], + ['`INJECTOR_TEST_CSP_A`:`INJECTOR_TEST_CSP_X`', 'ABC:'], + ['`INJECTOR_TEST_CSP_X`', null], + ['lorem `INJECTOR_TEST_CSP_A` ipsum', 'lorem `INJECTOR_TEST_CSP_A` ipsum'], + ]; + } + public function testNamedServices() { $injector = new Injector();