Skip to content
Snippets Groups Projects
Dotenv.php 5.6 KiB
Newer Older
  • Learn to ignore specific revisions
  • <?php
    
    namespace LakeDrops\Component\Composer;
    
    use Composer\IO\IOInterface;
    use Exception;
    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
       */
      private $name;
    
      /**
       * The input-output object of the composer session.
       *
       * @var \Composer\IO\IOInterface
       */
      private $io;
    
      /**
       * The Symfony Dotenv object.
       *
       * @var \Symfony\Component\Dotenv\Dotenv
       */
      private $dotenv;
    
      /**
       * 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.
       * @param string $default
       *   The default value.
       *
       * @return string
       *   The value.
       */
      public function receive(string $key, string $prompt, $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.
       * @param string $default
       *   The default value.
       *
       * @return string
       *   The value.
       */
      public function receiveGlobal(string $key, string $prompt, $default = ''): string {
        $key = strtoupper($key);
        $value = $_ENV[$key] ?? NULL;
        if ($value === NULL) {
          if ($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
       */
      public function put(string $key, string $value, $overwrite = FALSE): void {
        $data = $this->parse();
        $key = strtoupper($key);
        if (!$overwrite && isset($data[$key])) {
          return;
        }
        $data[$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 {
        array_walk_recursive($data, array($this, 'findEnvironmentVariables'));
        return $data;
      }
    
      /**
       * Identify and replace environment variables in a key pair
       *
       * @param string $item
       * @param string $key
       */
      private function findEnvironmentVariables(string &$item, string &$key): void {
        $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]);
        }
      }
    
      /**
       * Replace environment variables in a string
       *
       * @param string $item
       * @param array $matches
       *
       * @return string
       */
      private function replaceItemEnvironmentVariables(string $item, array $matches): string {
        foreach ($matches as $var) {
          $value = $_ENV[$var] ?? NULL;
          if ($value === NULL) {
            $value = '';
          }
          $item = str_replace( '{$env:' . $var . '}', $value, $item );
        }
        return $item;
      }
    
      /**
       * Parse the local .env file and return the content as an array.
       *
    
      private function parse($include_user = FALSE): array {
    
        if ($include_user && file_exists(getenv("HOME") . '/.env')) {
          $envContent .= PHP_EOL . file_get_contents( getenv("HOME") . '/.env');
        }
    
        $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) {
    
          $this->dotenv->load(getenv("HOME") . '/.env', '.env');