Skip to content
Snippets Groups Projects
Dotenv.php 7.14 KiB
Newer Older
  • Learn to ignore specific revisions
  • <?php
    
    namespace LakeDrops\Component\Composer;
    
    use Composer\IO\IOInterface;
    use Symfony\Component\Dotenv\Dotenv as SymfonyDotenv;
    
    /**
     * Manages .env files.
     */
    final class Dotenv {
    
      /**
       * Name of the plugin "owning" this instance, used as a prefix.
       *
       * @var string
       */
    
    jurgenhaas's avatar
    jurgenhaas committed
      private string $name;
    
    
      /**
       * The input-output object of the composer session.
       *
       * @var \Composer\IO\IOInterface
       */
    
    jurgenhaas's avatar
    jurgenhaas committed
      private IOInterface $io;
    
    
      /**
       * The Symfony Dotenv object.
       *
       * @var \Symfony\Component\Dotenv\Dotenv
       */
    
    jurgenhaas's avatar
    jurgenhaas committed
      private SymfonyDotenv $dotenv;
    
    jurgenhaas's avatar
    jurgenhaas committed
       * Array with all known environment variables.
       *
    
    jurgenhaas's avatar
    jurgenhaas committed
      private array $userEnv;
    
      /**
       * Dotenv constructor.
       *
       * @param string $name
       *   Name of the plugin "owning" this instance, used as a prefix.
       * @param \Composer\IO\IOInterface $io
       *   The input-output object of the composer session.
       */
      public function __construct(string $name, IOInterface $io) {
        $this->name = strtoupper($name);
        $this->io = $io;
    
        $this->dotenv = new SymfonyDotenv();
        $this->load();
      }
    
      /**
       * Receive a value from the environment with the project name as prefix.
       *
       * First of all, it prefixes the key with the plugin name and converts the
       * result into upper case. Then looking for the value with PHP's getenv and if
       * nothing found, it prompts the user for input if the session is interactive.
       * Finally, if the value is still not available, the default value is being
       * used.
       *
       * @param string $key
       *   The key of the environment variable.
       * @param string $prompt
    
       *   The text being displayed when prompting the user. Leave empty to use
       *   default value and never prompt the user.
    
       * @param string $default
       *   The default value.
       *
       * @return string
       *   The value.
       */
    
    jurgenhaas's avatar
    jurgenhaas committed
      public function receive(string $key, string $prompt, string $default = ''): string {
    
        return $this->receiveGlobal($this->name . '_' . $key, $prompt, $default);
      }
    
      /**
       * Receive a value from the environment.
       *
       * @param string $key
       *   The key of the environment variable.
       * @param string $prompt
    
       *   The text being displayed when prompting the user. Leave empty to use
       *   default value and never prompt the user.
    
       * @param string $default
       *   The default value.
       *
       * @return string
       *   The value.
       */
    
    jurgenhaas's avatar
    jurgenhaas committed
      public function receiveGlobal(string $key, string $prompt, string $default = ''): string {
    
        $key = strtoupper($key);
        $value = $_ENV[$key] ?? NULL;
        if ($value === NULL) {
    
          if (!empty($prompt) && $this->io->isInteractive()) {
    
            $value = $this->io->ask($prompt . ': ');
          }
          if ($value === NULL) {
            $value = $default;
          }
          $this->put($key, $value, TRUE);
        }
        else {
          $this->put($key, $value);
        }
    
        return $value;
      }
    
      /**
       * Set an environment variable.
       *
       * Sets the environment variable into a .env file only if there isn't an
       * environment variable present yet. The .env file will also be added to the
       * .gitignore file.
       *
       * @param string $key
       *   The name of the environment variable.
       * @param string $value
       *   The value of the environment variable.
       * @param bool $overwrite
    
    jurgenhaas's avatar
    jurgenhaas committed
       *   Whether an existing value should be overwritten.
    
    jurgenhaas's avatar
    jurgenhaas committed
      public function put(string $key, string $value, bool $overwrite = FALSE): void {
    
        $data = $this->parse();
        $key = strtoupper($key);
        if (!$overwrite && isset($data[$key])) {
          return;
        }
    
    jurgenhaas's avatar
    jurgenhaas committed
        if (isset($this->userEnv[$key]) && (string) $this->userEnv[$key] === $value) {
    
        $envContent = '';
        foreach ($data as $k => $v) {
          $envContent .= $k . '=' . $v . PHP_EOL;
        }
        file_put_contents('.env', $envContent);
        $this->load();
        Utils::gitIgnore('.env');
      }
    
      /**
       * Replace environment variables in keys and values of a given array.
       *
       * Credit: https://github.com/composer/composer/pull/5837
       *
       * @param mixed $data
       *   The keyed array in which to replace environment variables.
       *
       * @return array
       *   The same data with environment variables being replaced.
       */
      public function replaceEnvironmentVariables($data): array {
    
        foreach ($data as $key => $value) {
          unset($data[$key]);
          if (is_array($value)) {
            $data[$key] = $this->replaceEnvironmentVariables($value);
          }
          else {
            $this->findEnvironmentVariables($value, $key);
            $data[$key] = $value;
          }
        }
    
    jurgenhaas's avatar
    jurgenhaas committed
       * Identify and replace environment variables in a key pair.
    
    jurgenhaas's avatar
    jurgenhaas committed
       *   String in which to replace environment variable placeholders.
    
    jurgenhaas's avatar
    jurgenhaas committed
       *   String in which to replace environment variable placeholders.
    
       */
      private function findEnvironmentVariables(string &$item, string &$key): void {
    
    jurgenhaas's avatar
    jurgenhaas committed
        $pattern = '@\{\$env:([\w_]*)\}@i';
    
        preg_match_all($pattern, $item, $item_matches);
        preg_match_all($pattern, $key, $key_matches);
    
        if (!empty($item_matches[1])) {
          $item = $this->replaceItemEnvironmentVariables($item, $item_matches[1]);
        }
        if (!empty($key_matches[1])) {
          $key = $this->replaceItemEnvironmentVariables($key, $key_matches[1]);
        }
      }
    
      /**
    
    jurgenhaas's avatar
    jurgenhaas committed
       * Replace environment variables in a string.
    
    jurgenhaas's avatar
    jurgenhaas committed
       *   String in which to replace environment variable placeholders.
    
       * @param array $matches
    
    jurgenhaas's avatar
    jurgenhaas committed
       *   List of environment vairables to replace in the given string.
    
    jurgenhaas's avatar
    jurgenhaas committed
       *   The string with replaced environment variables.
    
       */
      private function replaceItemEnvironmentVariables(string $item, array $matches): string {
        foreach ($matches as $var) {
          $value = $_ENV[$var] ?? NULL;
          if ($value === NULL) {
            $value = '';
          }
    
    jurgenhaas's avatar
    jurgenhaas committed
          $item = str_replace('{$env:' . $var . '}', $value, $item);
    
        }
        return $item;
      }
    
      /**
       * Parse the local .env file and return the content as an array.
       *
    
    jurgenhaas's avatar
    jurgenhaas committed
       *   WHether to also include the environment variable from the user's home
       *   directory.
    
    jurgenhaas's avatar
    jurgenhaas committed
       *   List of all environment variables.
    
    jurgenhaas's avatar
    jurgenhaas committed
      private function parse(bool $include_user = FALSE): array {
    
        $userEnv = getenv("HOME") . '/.env';
        if ($include_user && file_exists($userEnv)) {
          $userContent = file_get_contents($userEnv);
          $this->userEnv = $this->dotenv->parse($userContent, $userEnv);
          $envContent .= PHP_EOL . $userContent;
    
        $envContent .= file_exists('.env') ? file_get_contents('.env') : '';
    
        return $this->dotenv->parse($envContent);
      }
    
      /**
       * Load the local .env file.
       */
      private function load(): void {
        try {
    
          foreach ($this->parse(TRUE) as $key => $value) {
    
          $userEnv = getenv("HOME") . '/.env';
          if (file_exists($userEnv) && file_exists('.env')) {
            $this->dotenv->load($userEnv, '.env');
          }
          elseif (file_exists($userEnv)) {
            $this->dotenv->load($userEnv);
          }
          elseif (file_exists('.env')) {
            $this->dotenv->load('.env');
          }
    
    jurgenhaas's avatar
    jurgenhaas committed
        catch (\Exception $ex) {