diff --git a/src/Core/Environment.php b/src/Core/Environment.php index ccae510e6..84bf2cfe7 100644 --- a/src/Core/Environment.php +++ b/src/Core/Environment.php @@ -177,7 +177,10 @@ class Environment } /** - * Get value of environment variable + * Get value of environment variable. + * If the value is false, you should check Environment::hasEnv() to see + * if the value is an actual environment variable value or if the variable + * simply hasn't been set. * * @param string $name * @return mixed Value of the environment variable, or false if not set @@ -223,6 +226,17 @@ class Environment static::$env[$name] = $value; } + /** + * Check if an environment variable is set + */ + public static function hasEnv(string $name): bool + { + return array_key_exists($name, static::$env) + || is_array($_ENV) && array_key_exists($name, $_ENV) + || is_array($_SERVER) && array_key_exists($name, $_SERVER) + || getenv($name) !== false; + } + /** * Returns true if this script is being run from the command line rather than the web server * diff --git a/tests/php/Core/EnvironmentTest.php b/tests/php/Core/EnvironmentTest.php index cf92c8787..e5875fd41 100644 --- a/tests/php/Core/EnvironmentTest.php +++ b/tests/php/Core/EnvironmentTest.php @@ -2,6 +2,7 @@ namespace SilverStripe\Core\Tests; +use ReflectionProperty; use SilverStripe\Core\Environment; use SilverStripe\Dev\SapphireTest; @@ -66,4 +67,87 @@ class EnvironmentTest extends SapphireTest $this->assertEquals('fail', $vars['test']); $this->assertEquals('global', $GLOBALS['test']); } + + public function provideHasEnv() + { + $setAsOptions = [ + '.env', + '_ENV', + '_SERVER', + 'putenv', + ]; + $valueOptions = [ + true, + false, + null, + 0, + 1, + 1.75, + '', + '0', + 'some-value', + ]; + $scenarios = []; + foreach ($setAsOptions as $setAs) { + foreach ($valueOptions as $value) { + $scenarios[] = [ + 'setAs' => $setAs, + 'value' => $value, + 'expected' => true, + ]; + } + } + $scenarios[] = [ + 'setAs' => null, + 'value' => null, + 'expected' => false, + ]; + return $scenarios; + } + + /** + * @dataProvider provideHasEnv + */ + public function testHasEnv(?string $setAs, $value, bool $expected) + { + $name = '_ENVTEST_HAS_ENV'; + + // Set the value + switch ($setAs) { + case '.env': + Environment::setEnv($name, $value); + break; + case '_ENV': + $_ENV[$name] = $value; + break; + case '_SERVER': + $_SERVER[$name] = $value; + break; + case 'putenv': + if ($value === null) { + $val = 'null'; + } elseif (is_bool($value)) { + $val = $value ? 'true' : 'false'; + } else { + $val = (string)$value; + } + putenv("$name=$val"); + break; + default: + // null is no-op, to validate not setting it works as expected. + if ($setAs !== null) { + $this->fail("setAs value $setAs isn't taken into account correctly for this test."); + } + } + + $this->assertSame($expected, Environment::hasEnv($name)); + + // unset the value + $reflectionEnv = new ReflectionProperty(Environment::class, 'env'); + $reflectionEnv->setAccessible(true); + $reflectionEnv->setValue(array_diff($reflectionEnv->getValue(), [$name => $value])); + unset($_ENV[$name]); + unset($_SERVER[$name]); + putenv("$name"); + } }