/
githubmirror
/
symfony
Обзор
Документация
Войти
/
githubmirror
/
symfony
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
8.2
src/Symfony/Component/DependencyInjection/Tests/EnvVarProcessorTest.php
1 043 строки
30 KB
Nicolas Grekas
CS fixes
13 апр 2026, 18:40
13 апр 2026, 18:40
bde244c
Код
Авторство
О чём код?
<?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\Attributes\DataProvider; use PHPUnit\Framework\Attributes\TestWith; use PHPUnit\Framework\TestCase; use Symfony\Component\DependencyInjection\Argument\RewindableGenerator; use Symfony\Component\DependencyInjection\Container; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\EnvVarLoaderInterface; use Symfony\Component\DependencyInjection\EnvVarProcessor; use Symfony\Component\DependencyInjection\Exception\EnvNotFoundException; use Symfony\Component\DependencyInjection\Exception\ParameterCircularReferenceException; use Symfony\Component\DependencyInjection\Exception\RuntimeException; use Symfony\Component\DependencyInjection\Tests\Fixtures\IntBackedEnum; use Symfony\Component\DependencyInjection\Tests\Fixtures\StringBackedEnum; class EnvVarProcessorTest extends TestCase { public const TEST_CONST = 'test'; #[DataProvider('validStrings')] public function testGetEnvString($value, $processed) { $container = new ContainerBuilder(); $container->setParameter('env(foo)', $value); $container->compile(); $processor = new EnvVarProcessor($container); $result = $processor->getEnv('string', 'foo', function () { $this->fail('Should not be called'); }); $this->assertSame($processed, $result); } public static function validStrings() { return [ ['hello', 'hello'], ['true', 'true'], ['false', 'false'], ['null', 'null'], ['1', '1'], ['0', '0'], ['1.1', '1.1'], ['1e1', '1e1'], ]; } #[DataProvider('validRealEnvValues')] public function testGetEnvRealEnv($value, $processed) { $_ENV['FOO'] = $value; $processor = new EnvVarProcessor(new Container()); $result = $processor->getEnv('string', 'FOO', function () { $this->fail('Should not be called'); }); $this->assertSame($processed, $result); unset($_ENV['FOO']); } public static function validRealEnvValues() { return [ ['hello', 'hello'], [true, '1'], [false, ''], [1, '1'], [0, '0'], [1.1, '1.1'], [10, '10'], ]; } public function testGetEnvRealEnvInvalid() { $_ENV['FOO'] = null; $this->expectException(EnvNotFoundException::class); $this->expectExceptionMessage('Environment variable not found: "FOO".'); $processor = new EnvVarProcessor(new Container()); $processor->getEnv('string', 'FOO', function () { $this->fail('Should not be called'); }); unset($_ENV['FOO']); } public function testGetEnvRealEnvNonScalar() { $_ENV['FOO'] = []; $this->expectException(RuntimeException::class); $this->expectExceptionMessage('Non-scalar env var "FOO" cannot be cast to "string".'); $processor = new EnvVarProcessor(new Container()); $processor->getEnv('string', 'FOO', function () { $this->fail('Should not be called'); }); unset($_ENV['FOO']); } #[DataProvider('validBools')] public function testGetEnvBool($value, $processed) { $processor = new EnvVarProcessor(new Container()); $result = $processor->getEnv('bool', 'foo', function ($name) use ($value) { $this->assertSame('foo', $name); return $value; }); $this->assertSame($processed, $result); } public function testGetEnvCachesEnv() { $_ENV['FOO'] = ''; $GLOBALS['ENV_FOO'] = 'value'; $loaders = static function () { yield new class implements EnvVarLoaderInterface { public function loadEnvVars(): array { return ['FOO' => $GLOBALS['ENV_FOO']]; } }; }; $processor = new EnvVarProcessor(new Container(), new RewindableGenerator($loaders, 1)); $noop = static function () {}; $result = $processor->getEnv('string', 'FOO', $noop); $this->assertSame('value', $result); $GLOBALS['ENV_FOO'] = 'new value'; $result = $processor->getEnv('string', 'FOO', $noop); $this->assertSame('value', $result); unset($_ENV['FOO'], $GLOBALS['ENV_FOO']); } public function testReset() { $_ENV['FOO'] = ''; $GLOBALS['ENV_FOO'] = 'value'; $loaders = static function () { yield new class implements EnvVarLoaderInterface { public function loadEnvVars(): array { return ['FOO' => $GLOBALS['ENV_FOO']]; } }; }; $processor = new EnvVarProcessor(new Container(), new RewindableGenerator($loaders, 1)); $noop = static function () {}; $result = $processor->getEnv('string', 'FOO', $noop); $this->assertSame('value', $result); $GLOBALS['ENV_FOO'] = 'new value'; $processor->reset(); $result = $processor->getEnv('string', 'FOO', $noop); $this->assertSame('new value', $result); unset($_ENV['FOO'], $GLOBALS['ENV_FOO']); } #[DataProvider('validBools')] public function testGetEnvNot($value, $processed) { $processor = new EnvVarProcessor(new Container()); $result = $processor->getEnv('not', 'foo', function ($name) use ($value) { $this->assertSame('foo', $name); return $value; }); $this->assertSame(!$processed, $result); } public static function validBools() { return [ ['true', true], ['false', false], ['null', false], ['', false], ['1', true], ['0', false], ['1.1', true], ['1e1', true], ]; } #[DataProvider('validInts')] public function testGetEnvInt($value, $processed) { $processor = new EnvVarProcessor(new Container()); $result = $processor->getEnv('int', 'foo', function ($name) use ($value) { $this->assertSame('foo', $name); return $value; }); $this->assertSame($processed, $result); } public static function validInts() { return [ ['1', 1], ['1.1', 1], ['1e1', 10], ]; } #[DataProvider('invalidInts')] public function testGetEnvIntInvalid($value) { $processor = new EnvVarProcessor(new Container()); $this->expectException(RuntimeException::class); $this->expectExceptionMessage('Non-numeric env var'); $processor->getEnv('int', 'foo', function ($name) use ($value) { $this->assertSame('foo', $name); return $value; }); } public static function invalidInts() { return [ ['foo'], ['true'], ['null'], ]; } #[DataProvider('validFloats')] public function testGetEnvFloat($value, $processed) { $processor = new EnvVarProcessor(new Container()); $result = $processor->getEnv('float', 'foo', function ($name) use ($value) { $this->assertSame('foo', $name); return $value; }); $this->assertSame($processed, $result); } public static function validFloats() { return [ ['1', 1.0], ['1.1', 1.1], ['1e1', 10.0], ]; } #[DataProvider('invalidFloats')] public function testGetEnvFloatInvalid($value) { $processor = new EnvVarProcessor(new Container()); $this->expectException(RuntimeException::class); $this->expectExceptionMessage('Non-numeric env var'); $processor->getEnv('float', 'foo', function ($name) use ($value) { $this->assertSame('foo', $name); return $value; }); } public static function invalidFloats() { return [ ['foo'], ['true'], ['null'], ]; } #[DataProvider('validConsts')] public function testGetEnvConst($value, $processed) { $processor = new EnvVarProcessor(new Container()); $result = $processor->getEnv('const', 'foo', function ($name) use ($value) { $this->assertSame('foo', $name); return $value; }); $this->assertSame($processed, $result); } public static function validConsts() { return [ ['Symfony\Component\DependencyInjection\Tests\EnvVarProcessorTest::TEST_CONST', self::TEST_CONST], ['E_ERROR', \E_ERROR], ]; } #[DataProvider('invalidConsts')] public function testGetEnvConstInvalid($value) { $processor = new EnvVarProcessor(new Container()); $this->expectException(RuntimeException::class); $this->expectExceptionMessage('undefined constant'); $processor->getEnv('const', 'foo', function ($name) use ($value) { $this->assertSame('foo', $name); return $value; }); } public static function invalidConsts() { return [ ['Symfony\Component\DependencyInjection\Tests\EnvVarProcessorTest::UNDEFINED_CONST'], ['UNDEFINED_CONST'], ]; } public function testGetEnvBase64() { $processor = new EnvVarProcessor(new Container()); $result = $processor->getEnv('base64', 'foo', function ($name) { $this->assertSame('foo', $name); return base64_encode('hello'); }); $this->assertSame('hello', $result); $result = $processor->getEnv('base64', 'foo', static fn ($name) => '/+0='); $this->assertSame("\xFF\xED", $result); $result = $processor->getEnv('base64', 'foo', static fn ($name) => '_-0='); $this->assertSame("\xFF\xED", $result); } public function testGetEnvTrim() { $processor = new EnvVarProcessor(new Container()); $result = $processor->getEnv('trim', 'foo', function ($name) { $this->assertSame('foo', $name); return " hello\n"; }); $this->assertSame('hello', $result); } #[DataProvider('validJson')] public function testGetEnvJson($value, $processed) { $processor = new EnvVarProcessor(new Container()); $result = $processor->getEnv('json', 'foo', function ($name) use ($value) { $this->assertSame('foo', $name); return $value; }); $this->assertSame($processed, $result); } public static function validJson() { return [ ['[1]', [1]], ['{"key": "value"}', ['key' => 'value']], [null, null], ]; } public function testGetEnvInvalidJson() { $processor = new EnvVarProcessor(new Container()); $this->expectException(RuntimeException::class); $this->expectExceptionMessage('Syntax error'); $processor->getEnv('json', 'foo', function ($name) { $this->assertSame('foo', $name); return 'invalid_json'; }); } #[DataProvider('otherJsonValues')] public function testGetEnvJsonOther($value) { $processor = new EnvVarProcessor(new Container()); $this->expectException(RuntimeException::class); $this->expectExceptionMessage('Invalid JSON env var'); $processor->getEnv('json', 'foo', function ($name) use ($value) { $this->assertSame('foo', $name); return json_encode($value); }); } public static function otherJsonValues() { return [ [1], [1.1], [true], [false], ['foo'], ]; } public function testGetEnvUnknown() { $processor = new EnvVarProcessor(new Container()); $this->expectException(RuntimeException::class); $this->expectExceptionMessage('Unsupported env var prefix'); $processor->getEnv('unknown', 'foo', function ($name) { $this->assertSame('foo', $name); return 'foo'; }); } public function testGetEnvKeyInvalidKey() { $processor = new EnvVarProcessor(new Container()); $this->expectException(RuntimeException::class); $this->expectExceptionMessage('Invalid env "key:foo": a key specifier should be provided.'); $processor->getEnv('key', 'foo', function ($name) { $this->fail('Should not get here'); }); } #[DataProvider('noArrayValues')] public function testGetEnvKeyNoArrayResult($value) { $processor = new EnvVarProcessor(new Container()); $this->expectException(RuntimeException::class); $this->expectExceptionMessage('Resolved value of "foo" did not result in an array value.'); $processor->getEnv('key', 'index:foo', function ($name) use ($value) { $this->assertSame('foo', $name); return $value; }); } public static function noArrayValues() { return [ [null], ['string'], [1], [true], ]; } #[DataProvider('invalidArrayValues')] public function testGetEnvKeyArrayKeyNotFound($value) { $processor = new EnvVarProcessor(new Container()); $this->expectException(EnvNotFoundException::class); $this->expectExceptionMessage('Key "index" not found in'); $processor->getEnv('key', 'index:foo', function ($name) use ($value) { $this->assertSame('foo', $name); return $value; }); } public static function invalidArrayValues() { return [ [[]], [['index2' => 'value']], [['index', 'index2']], ]; } #[DataProvider('arrayValues')] public function testGetEnvKey($value) { $processor = new EnvVarProcessor(new Container()); $this->assertSame($value['index'], $processor->getEnv('key', 'index:foo', function ($name) use ($value) { $this->assertSame('foo', $name); return $value; })); } public static function arrayValues() { return [ [['index' => 'password']], [['index' => 'true']], [['index' => false]], [['index' => '1']], [['index' => 1]], [['index' => '1.1']], [['index' => 1.1]], [['index' => []]], [['index' => ['val1', 'val2']]], ]; } public function testGetEnvKeyChained() { $processor = new EnvVarProcessor(new Container()); $this->assertSame('password', $processor->getEnv('key', 'index:file:foo', function ($name) { $this->assertSame('file:foo', $name); return [ 'index' => 'password', ]; })); } #[DataProvider('provideGetEnvEnum')] public function testGetEnvEnum(\BackedEnum $backedEnum) { $processor = new EnvVarProcessor(new Container()); $result = $processor->getEnv('enum', $backedEnum::class.':foo', function (string $name) use ($backedEnum) { $this->assertSame('foo', $name); return $backedEnum->value; }); $this->assertSame($backedEnum, $result); } public static function provideGetEnvEnum(): iterable { return [ [StringBackedEnum::Bar], [IntBackedEnum::Nine], ]; } public function testGetEnvEnumInvalidEnum() { $processor = new EnvVarProcessor(new Container()); $this->expectException(RuntimeException::class); $this->expectExceptionMessage('Invalid env "enum:foo": a "BackedEnum" class-string should be provided.'); $processor->getEnv('enum', 'foo', function () { $this->fail('Should not get here'); }); } public function testGetEnvEnumInvalidResolvedValue() { $processor = new EnvVarProcessor(new Container()); $this->expectException(RuntimeException::class); $this->expectExceptionMessage('Resolved value of "foo" did not result in a string or int value.'); $processor->getEnv('enum', StringBackedEnum::class.':foo', static fn () => null); } public function testGetEnvEnumInvalidArg() { $processor = new EnvVarProcessor(new Container()); $this->expectException(RuntimeException::class); $this->expectExceptionMessage('"bogus" is not a "BackedEnum".'); $processor->getEnv('enum', 'bogus:foo', static fn () => ''); } public function testGetEnvEnumInvalidBackedValue() { $processor = new EnvVarProcessor(new Container()); $this->expectException(RuntimeException::class); $this->expectExceptionMessage('Enum value "bogus" is not backed by "'.StringBackedEnum::class.'".'); $processor->getEnv('enum', StringBackedEnum::class.':foo', static fn () => 'bogus'); } #[DataProvider('validNullables')] public function testGetEnvNullable($value, $processed) { $processor = new EnvVarProcessor(new Container()); $result = $processor->getEnv('default', ':foo', function ($name) use ($value) { $this->assertSame('foo', $name); return $value; }); $this->assertSame($processed, $result); } public static function validNullables() { return [ ['hello', 'hello'], ['', null], ['null', 'null'], ['Null', 'Null'], ['NULL', 'NULL'], ]; } public function testRequireMissingFile() { $processor = new EnvVarProcessor(new Container()); $this->expectException(EnvNotFoundException::class); $this->expectExceptionMessage('missing-file'); $processor->getEnv('require', '/missing-file', static fn ($name) => $name); } public function testRequireFile() { $path = __DIR__.'/Fixtures/php/return_foo_string.php'; $processor = new EnvVarProcessor(new Container()); $result = $processor->getEnv('require', $path, function ($name) use ($path) { $this->assertSame($path, $name); return $path; }); $this->assertEquals('foo', $result); } #[DataProvider('validResolve')] public function testGetEnvResolve($value, $processed) { $container = new ContainerBuilder(); $container->setParameter('bar', $value); $container->compile(); $processor = new EnvVarProcessor($container); $result = $processor->getEnv('resolve', 'foo', static fn () => '%bar%'); $this->assertSame($processed, $result); } public static function validResolve() { return [ ['string', 'string'], [1, '1'], [1.1, '1.1'], [true, '1'], [false, ''], ]; } public function testGetEnvResolveNoMatch() { $processor = new EnvVarProcessor(new Container()); $result = $processor->getEnv('resolve', 'foo', static fn () => '%%'); $this->assertSame('%', $result); } #[DataProvider('notScalarResolve')] public function testGetEnvResolveNotScalar($value) { $container = new ContainerBuilder(); $container->setParameter('bar', $value); $container->compile(); $processor = new EnvVarProcessor($container); $this->expectException(RuntimeException::class); $this->expectExceptionMessage('Parameter "bar" found when resolving env var "foo" must be scalar'); $processor->getEnv('resolve', 'foo', static fn () => '%bar%'); } public static function notScalarResolve() { return [ [null], [[]], ]; } public function testGetEnvResolveNestedEnv() { $container = new ContainerBuilder(); $container->setParameter('env(BAR)', 'BAR in container'); $container->compile(); $processor = new EnvVarProcessor($container); $getEnv = $processor->getEnv(...); $result = $processor->getEnv('resolve', 'foo', static fn ($name) => 'foo' === $name ? '%env(BAR)%' : $getEnv('string', $name, static function () {})); $this->assertSame('BAR in container', $result); } public function testGetEnvResolveNestedRealEnv() { $_ENV['BAR'] = 'BAR in environment'; $container = new ContainerBuilder(); $container->setParameter('env(BAR)', 'BAR in container'); $container->compile(); $processor = new EnvVarProcessor($container); $getEnv = $processor->getEnv(...); $result = $processor->getEnv('resolve', 'foo', static fn ($name) => 'foo' === $name ? '%env(BAR)%' : $getEnv('string', $name, static function () {})); $this->assertSame('BAR in environment', $result); unset($_ENV['BAR']); } #[DataProvider('validCsv')] public function testGetEnvCsv($value, $processed) { $processor = new EnvVarProcessor(new Container()); $result = $processor->getEnv('csv', 'foo', function ($name) use ($value) { $this->assertSame('foo', $name); return $value; }); $this->assertSame($processed, $result); } public function testGetEnvShuffle() { srand(2); // to set seed for `shuffle` $this->assertSame( ['bar', 'foo'], (new EnvVarProcessor(new Container()))->getEnv('shuffle', '', static fn () => ['foo', 'bar']), ); } public function testGetEnvShuffleInvalid() { $this->expectException(RuntimeException::class); $this->expectExceptionMessage('Env var "foo" cannot be shuffled, expected array, got "string".'); (new EnvVarProcessor(new Container()))->getEnv('shuffle', 'foo', static fn () => 'bar'); } public static function validCsv() { $complex = <<<'CSV' ,"""","foo""","\""",\,foo\ CSV; return [ ['', []], [',', ['', '']], ['1', ['1']], ['1,2," 3 "', ['1', '2', ' 3 ']], ['\\,\\\\', ['\\', '\\\\']], [$complex, ['', '"', 'foo"', '\\"', '\\', 'foo\\']], [null, null], ]; } public function testEnvLoader() { $_ENV['BAZ_ENV_LOADER'] = ''; $_ENV['BUZ_ENV_LOADER'] = ''; $loaders = static function () { yield new class implements EnvVarLoaderInterface { public function loadEnvVars(): array { return [ 'FOO_ENV_LOADER' => '123', 'BAZ_ENV_LOADER' => '', 'LAZY_ENV_LOADER' => new class { public function __toString(): string { return ''; } }, ]; } }; yield new class implements EnvVarLoaderInterface { public function loadEnvVars(): array { return [ 'FOO_ENV_LOADER' => '234', 'BAR_ENV_LOADER' => '456', 'BAZ_ENV_LOADER' => '567', 'LAZY_ENV_LOADER' => new class { public function __toString(): string { return '678'; } }, ]; } }; }; $processor = new EnvVarProcessor(new Container(), new RewindableGenerator($loaders, 2)); $result = $processor->getEnv('string', 'FOO_ENV_LOADER', static function () {}); $this->assertSame('123', $result); $result = $processor->getEnv('string', 'BAR_ENV_LOADER', static function () {}); $this->assertSame('456', $result); $result = $processor->getEnv('string', 'BAZ_ENV_LOADER', static function () {}); $this->assertSame('567', $result); $result = $processor->getEnv('string', 'BUZ_ENV_LOADER', static function () {}); $this->assertSame('', $result); $result = $processor->getEnv('string', 'FOO_ENV_LOADER', static function () {}); $this->assertSame('123', $result); // check twice $result = $processor->getEnv('string', 'LAZY_ENV_LOADER', static function () {}); $this->assertSame('678', $result); unset($_ENV['BAZ_ENV_LOADER']); unset($_ENV['BUZ_ENV_LOADER']); } public function testCircularEnvLoader() { $container = new ContainerBuilder(); $container->setParameter('env(FOO_CONTAINER)', 'foo'); $container->compile(); $index = 0; $loaders = static function () use (&$index) { if (0 === $index++) { throw new ParameterCircularReferenceException(['FOO_CONTAINER']); } yield new class implements EnvVarLoaderInterface { public function loadEnvVars(): array { return [ 'FOO_ENV_LOADER' => '123', ]; } }; }; $processor = new EnvVarProcessor($container, new RewindableGenerator($loaders, 1)); $result = $processor->getEnv('string', 'FOO_CONTAINER', static function () {}); $this->assertSame('foo', $result); $result = $processor->getEnv('string', 'FOO_ENV_LOADER', static function () {}); $this->assertSame('123', $result); $result = $processor->getEnv('default', ':BAR_CONTAINER', function ($name) use ($processor) { $this->assertSame('BAR_CONTAINER', $name); return $processor->getEnv('string', $name, static function () {}); }); $this->assertNull($result); $this->assertSame(2, $index); } public function testGetEnvInvalidPrefixWithDefault() { $processor = new EnvVarProcessor(new Container()); $this->expectException(RuntimeException::class); $this->expectExceptionMessage('Unsupported env var prefix'); $processor->getEnv('unknown', 'default::FAKE', function ($name) { $this->assertSame('default::FAKE', $name); return null; }); } #[DataProvider('provideGetEnvUrlPath')] public function testGetEnvUrlPath(?string $expected, string $url) { $this->assertSame($expected, (new EnvVarProcessor(new Container()))->getEnv('url', 'foo', static fn (): string => $url)['path']); } public static function provideGetEnvUrlPath() { return [ ['', 'https://symfony.com'], ['', 'https://symfony.com/'], ['/', 'https://symfony.com//'], ['blog', 'https://symfony.com/blog'], ['blog/', 'https://symfony.com/blog/'], ['blog//', 'https://symfony.com/blog//'], ]; } #[TestWith(['http://foo.com\\bar'])] #[TestWith(['\\\\foo.com/bar'])] #[TestWith(["a\rb"])] #[TestWith(["a\nb"])] #[TestWith(["a\tb"])] #[TestWith(["\u0000foo"])] #[TestWith(["foo\u0000"])] #[TestWith([' foo'])] #[TestWith(['foo '])] #[TestWith([':'])] public function testGetEnvBadUrl(string $url) { $this->expectException(RuntimeException::class); (new EnvVarProcessor(new Container()))->getEnv('url', 'foo', static fn (): string => $url); } #[TestWith(['', 'string'])] #[TestWith([null, ''])] #[TestWith([false, 'bool'])] #[TestWith([true, 'not'])] #[TestWith([0, 'int'])] #[TestWith([0.0, 'float'])] public function testGetEnvCastsNullBehavior($expected, string $prefix) { $processor = new EnvVarProcessor(new Container()); $this->assertSame($expected, $processor->getEnv($prefix, 'default::FOO', static fn () => $processor->getEnv('default', ':FOO', static fn () => null))); } public function testGetEnvWithEmptyStringPrefixCastsToString() { $processor = new EnvVarProcessor(new Container()); unset($_ENV['FOO']); $_ENV['FOO'] = 4; try { $this->assertSame('4', $processor->getEnv('', 'FOO', function () { $this->fail('Should not be called'); })); } finally { unset($_ENV['FOO']); } } #[DataProvider('provideGetEnvDefined')] public function testGetEnvDefined(bool $expected, callable $callback) { $this->assertSame($expected, (new EnvVarProcessor(new Container()))->getEnv('defined', 'NO_SOMETHING', $callback)); } public function testGetEnvUrlencode() { $processor = new EnvVarProcessor(new Container()); $result = $processor->getEnv('urlencode', 'URLENCODETEST', static fn () => 'foo: Data123!@-_ + bar: Not the same content as Data123!@-_ +'); $this->assertSame('foo%3A%20Data123%21%40-_%20%2B%20bar%3A%20Not%20the%20same%20content%20as%20Data123%21%40-_%20%2B', $result); } public static function provideGetEnvDefined(): iterable { yield 'Defined' => [true, static fn () => 'foo']; yield 'Falsy but defined' => [true, static fn () => '0']; yield 'Empty string' => [false, static fn () => '']; yield 'Null' => [false, static fn () => null]; yield 'Env var not defined' => [false, static fn () => throw new EnvNotFoundException()]; } #[DataProvider('provideQueryStringScenarios')] public function testQueryStringEnvVarProcessor($envValue, $expectedResult) { $processor = new EnvVarProcessor(new Container()); $result = $processor->getEnv('query_string', 'MY_VAR', static fn () => $envValue); $this->assertSame($expectedResult, $result); } public static function provideQueryStringScenarios(): iterable { yield 'url_without_query' => ['https://example.com', []]; yield 'url_with_empty_query' => ['https://example.com?', []]; yield 'url_with_query' => ['https://example.com?foo=bar&baz=123', ['foo' => 'bar', 'baz' => '123']]; yield 'raw_query_string' => ['foo=bar&test=1', ['foo' => 'bar', 'test' => '1']]; } }