vendor/symfony/dependency-injection/EnvVarProcessor.php line 65

Open in your IDE?
  1. <?php
  2. /*
  3.  * This file is part of the Symfony package.
  4.  *
  5.  * (c) Fabien Potencier <fabien@symfony.com>
  6.  *
  7.  * For the full copyright and license information, please view the LICENSE
  8.  * file that was distributed with this source code.
  9.  */
  10. namespace Symfony\Component\DependencyInjection;
  11. use Symfony\Component\DependencyInjection\Exception\EnvNotFoundException;
  12. use Symfony\Component\DependencyInjection\Exception\ParameterCircularReferenceException;
  13. use Symfony\Component\DependencyInjection\Exception\RuntimeException;
  14. /**
  15.  * @author Nicolas Grekas <p@tchwork.com>
  16.  */
  17. class EnvVarProcessor implements EnvVarProcessorInterface
  18. {
  19.     private $container;
  20.     private \Traversable $loaders;
  21.     private array $loadedVars = [];
  22.     /**
  23.      * @param EnvVarLoaderInterface[] $loaders
  24.      */
  25.     public function __construct(ContainerInterface $container\Traversable $loaders null)
  26.     {
  27.         $this->container $container;
  28.         $this->loaders $loaders ?? new \ArrayIterator();
  29.     }
  30.     /**
  31.      * {@inheritdoc}
  32.      */
  33.     public static function getProvidedTypes(): array
  34.     {
  35.         return [
  36.             'base64' => 'string',
  37.             'bool' => 'bool',
  38.             'not' => 'bool',
  39.             'const' => 'bool|int|float|string|array',
  40.             'csv' => 'array',
  41.             'file' => 'string',
  42.             'float' => 'float',
  43.             'int' => 'int',
  44.             'json' => 'array',
  45.             'key' => 'bool|int|float|string|array',
  46.             'url' => 'array',
  47.             'query_string' => 'array',
  48.             'resolve' => 'string',
  49.             'default' => 'bool|int|float|string|array',
  50.             'string' => 'string',
  51.             'trim' => 'string',
  52.             'require' => 'bool|int|float|string|array',
  53.         ];
  54.     }
  55.     /**
  56.      * {@inheritdoc}
  57.      */
  58.     public function getEnv(string $prefixstring $name\Closure $getEnv): mixed
  59.     {
  60.         $i strpos($name':');
  61.         if ('key' === $prefix) {
  62.             if (false === $i) {
  63.                 throw new RuntimeException(sprintf('Invalid env "key:%s": a key specifier should be provided.'$name));
  64.             }
  65.             $next substr($name$i 1);
  66.             $key substr($name0$i);
  67.             $array $getEnv($next);
  68.             if (!\is_array($array)) {
  69.                 throw new RuntimeException(sprintf('Resolved value of "%s" did not result in an array value.'$next));
  70.             }
  71.             if (!isset($array[$key]) && !\array_key_exists($key$array)) {
  72.                 throw new EnvNotFoundException(sprintf('Key "%s" not found in %s (resolved from "%s").'$keyjson_encode($array), $next));
  73.             }
  74.             return $array[$key];
  75.         }
  76.         if ('default' === $prefix) {
  77.             if (false === $i) {
  78.                 throw new RuntimeException(sprintf('Invalid env "default:%s": a fallback parameter should be provided.'$name));
  79.             }
  80.             $next substr($name$i 1);
  81.             $default substr($name0$i);
  82.             if ('' !== $default && !$this->container->hasParameter($default)) {
  83.                 throw new RuntimeException(sprintf('Invalid env fallback in "default:%s": parameter "%s" not found.'$name$default));
  84.             }
  85.             try {
  86.                 $env $getEnv($next);
  87.                 if ('' !== $env && null !== $env) {
  88.                     return $env;
  89.                 }
  90.             } catch (EnvNotFoundException $e) {
  91.                 // no-op
  92.             }
  93.             return '' === $default null $this->container->getParameter($default);
  94.         }
  95.         if ('file' === $prefix || 'require' === $prefix) {
  96.             if (!is_scalar($file $getEnv($name))) {
  97.                 throw new RuntimeException(sprintf('Invalid file name: env var "%s" is non-scalar.'$name));
  98.             }
  99.             if (!is_file($file)) {
  100.                 throw new EnvNotFoundException(sprintf('File "%s" not found (resolved from "%s").'$file$name));
  101.             }
  102.             if ('file' === $prefix) {
  103.                 return file_get_contents($file);
  104.             } else {
  105.                 return require $file;
  106.             }
  107.         }
  108.         if (false !== $i || 'string' !== $prefix) {
  109.             $env $getEnv($name);
  110.         } elseif (isset($_ENV[$name])) {
  111.             $env $_ENV[$name];
  112.         } elseif (isset($_SERVER[$name]) && !str_starts_with($name'HTTP_')) {
  113.             $env $_SERVER[$name];
  114.         } elseif (false === ($env getenv($name)) || null === $env) { // null is a possible value because of thread safety issues
  115.             foreach ($this->loadedVars as $vars) {
  116.                 if (false !== $env = ($vars[$name] ?? false)) {
  117.                     break;
  118.                 }
  119.             }
  120.             if (false === $env || null === $env) {
  121.                 $loaders $this->loaders;
  122.                 $this->loaders = new \ArrayIterator();
  123.                 try {
  124.                     $i 0;
  125.                     $ended true;
  126.                     $count $loaders instanceof \Countable $loaders->count() : 0;
  127.                     foreach ($loaders as $loader) {
  128.                         if (\count($this->loadedVars) > $i++) {
  129.                             continue;
  130.                         }
  131.                         $this->loadedVars[] = $vars $loader->loadEnvVars();
  132.                         if (false !== $env $vars[$name] ?? false) {
  133.                             $ended false;
  134.                             break;
  135.                         }
  136.                     }
  137.                     if ($ended || $count === $i) {
  138.                         $loaders $this->loaders;
  139.                     }
  140.                 } catch (ParameterCircularReferenceException $e) {
  141.                     // skip loaders that need an env var that is not defined
  142.                 } finally {
  143.                     $this->loaders $loaders;
  144.                 }
  145.             }
  146.             if (false === $env || null === $env) {
  147.                 if (!$this->container->hasParameter("env($name)")) {
  148.                     throw new EnvNotFoundException(sprintf('Environment variable not found: "%s".'$name));
  149.                 }
  150.                 $env $this->container->getParameter("env($name)");
  151.             }
  152.         }
  153.         if (null === $env) {
  154.             if (!isset($this->getProvidedTypes()[$prefix])) {
  155.                 throw new RuntimeException(sprintf('Unsupported env var prefix "%s".'$prefix));
  156.             }
  157.             return null;
  158.         }
  159.         if (!is_scalar($env)) {
  160.             throw new RuntimeException(sprintf('Non-scalar env var "%s" cannot be cast to "%s".'$name$prefix));
  161.         }
  162.         if ('string' === $prefix) {
  163.             return (string) $env;
  164.         }
  165.         if (\in_array($prefix, ['bool''not'], true)) {
  166.             $env = (bool) (filter_var($env\FILTER_VALIDATE_BOOLEAN) ?: filter_var($env\FILTER_VALIDATE_INT) ?: filter_var($env\FILTER_VALIDATE_FLOAT));
  167.             return 'not' === $prefix ? !$env $env;
  168.         }
  169.         if ('int' === $prefix) {
  170.             if (false === $env filter_var($env\FILTER_VALIDATE_INT) ?: filter_var($env\FILTER_VALIDATE_FLOAT)) {
  171.                 throw new RuntimeException(sprintf('Non-numeric env var "%s" cannot be cast to int.'$name));
  172.             }
  173.             return (int) $env;
  174.         }
  175.         if ('float' === $prefix) {
  176.             if (false === $env filter_var($env\FILTER_VALIDATE_FLOAT)) {
  177.                 throw new RuntimeException(sprintf('Non-numeric env var "%s" cannot be cast to float.'$name));
  178.             }
  179.             return (float) $env;
  180.         }
  181.         if ('const' === $prefix) {
  182.             if (!\defined($env)) {
  183.                 throw new RuntimeException(sprintf('Env var "%s" maps to undefined constant "%s".'$name$env));
  184.             }
  185.             return \constant($env);
  186.         }
  187.         if ('base64' === $prefix) {
  188.             return base64_decode(strtr($env'-_''+/'));
  189.         }
  190.         if ('json' === $prefix) {
  191.             $env json_decode($envtrue);
  192.             if (\JSON_ERROR_NONE !== json_last_error()) {
  193.                 throw new RuntimeException(sprintf('Invalid JSON in env var "%s": '$name).json_last_error_msg());
  194.             }
  195.             if (null !== $env && !\is_array($env)) {
  196.                 throw new RuntimeException(sprintf('Invalid JSON env var "%s": array or null expected, "%s" given.'$nameget_debug_type($env)));
  197.             }
  198.             return $env;
  199.         }
  200.         if ('url' === $prefix) {
  201.             $parsedEnv parse_url($env);
  202.             if (false === $parsedEnv) {
  203.                 throw new RuntimeException(sprintf('Invalid URL in env var "%s".'$name));
  204.             }
  205.             if (!isset($parsedEnv['scheme'], $parsedEnv['host'])) {
  206.                 throw new RuntimeException(sprintf('Invalid URL env var "%s": schema and host expected, "%s" given.'$name$env));
  207.             }
  208.             $parsedEnv += [
  209.                 'port' => null,
  210.                 'user' => null,
  211.                 'pass' => null,
  212.                 'path' => null,
  213.                 'query' => null,
  214.                 'fragment' => null,
  215.             ];
  216.             // remove the '/' separator
  217.             $parsedEnv['path'] = '/' === ($parsedEnv['path'] ?? '/') ? '' substr($parsedEnv['path'], 1);
  218.             return $parsedEnv;
  219.         }
  220.         if ('query_string' === $prefix) {
  221.             $queryString parse_url($env\PHP_URL_QUERY) ?: $env;
  222.             parse_str($queryString$result);
  223.             return $result;
  224.         }
  225.         if ('resolve' === $prefix) {
  226.             return preg_replace_callback('/%%|%([^%\s]+)%/', function ($match) use ($name$getEnv) {
  227.                 if (!isset($match[1])) {
  228.                     return '%';
  229.                 }
  230.                 if (str_starts_with($match[1], 'env(') && str_ends_with($match[1], ')') && 'env()' !== $match[1]) {
  231.                     $value $getEnv(substr($match[1], 4, -1));
  232.                 } else {
  233.                     $value $this->container->getParameter($match[1]);
  234.                 }
  235.                 if (!is_scalar($value)) {
  236.                     throw new RuntimeException(sprintf('Parameter "%s" found when resolving env var "%s" must be scalar, "%s" given.'$match[1], $nameget_debug_type($value)));
  237.                 }
  238.                 return $value;
  239.             }, $env);
  240.         }
  241.         if ('csv' === $prefix) {
  242.             return str_getcsv($env',''"''');
  243.         }
  244.         if ('trim' === $prefix) {
  245.             return trim($env);
  246.         }
  247.         throw new RuntimeException(sprintf('Unsupported env var prefix "%s" for env name "%s".'$prefix$name));
  248.     }
  249. }