From c86f6ecbed237e16645a264f68f5a92b94d88406 Mon Sep 17 00:00:00 2001
From: jurgenhaas <juergen.haas@lakedrops.com>
Date: Fri, 25 Dec 2020 20:52:06 +0100
Subject: [PATCH] docker/l3d#58 Move project settings out of composer.json

---
 BaseHandler.php |   3 +-
 Config.php      |   3 +-
 Dotenv.php      | 213 ++++++++++++++++++++++++++++++++++++++++++++++++
 composer.json   |   2 +-
 4 files changed, 216 insertions(+), 5 deletions(-)
 create mode 100644 Dotenv.php

diff --git a/BaseHandler.php b/BaseHandler.php
index 89cce82..102aa60 100644
--- a/BaseHandler.php
+++ b/BaseHandler.php
@@ -6,7 +6,6 @@ use Composer\Composer;
 use Composer\IO\IOInterface;
 use Composer\Package\PackageInterface;
 use Composer\Script\Event;
-use LakeDrops\Component\Dotenv\Dotenv;
 use Symfony\Component\Console\Input\InputInterface;
 
 /**
@@ -57,7 +56,7 @@ abstract class BaseHandler implements BaseHandlerInterface {
   protected $drupalCorePackage;
 
   /**
-   * @var \LakeDrops\Component\Dotenv\Dotenv
+   * @var \LakeDrops\Component\Composer\Dotenv
    */
   protected $env;
 
diff --git a/Config.php b/Config.php
index 66c8627..a3978e6 100644
--- a/Config.php
+++ b/Config.php
@@ -2,7 +2,6 @@
 
 namespace LakeDrops\Component\Composer;
 
-use LakeDrops\Component\Dotenv\Dotenv;
 use Symfony\Component\Yaml\Yaml;
 use Twig\Error\LoaderError;
 use Twig\Error\RuntimeError;
@@ -37,7 +36,7 @@ final class Config {
    *
    * @param string $component
    * @param array $default_values
-   * @param \LakeDrops\Component\Dotenv\Dotenv $env
+   * @param \LakeDrops\Component\Composer\Dotenv $env
    */
   public function __construct(string $component, array $default_values, Dotenv $env) {
     $this->component = $component;
diff --git a/Dotenv.php b/Dotenv.php
new file mode 100644
index 0000000..a1a364c
--- /dev/null
+++ b/Dotenv.php
@@ -0,0 +1,213 @@
+<?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.
+   *
+   * @return array
+   */
+  private function parse(): array {
+    $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() as $key => $value) {
+        unset($_SERVER[$key]);
+      }
+      $this->dotenv->load('.env');
+    }
+    catch (Exception $ex) {
+      // Ignore.
+    }
+  }
+
+}
diff --git a/composer.json b/composer.json
index 93ef369..3b4ab1c 100644
--- a/composer.json
+++ b/composer.json
@@ -26,7 +26,7 @@
     "require": {
         "php": ">=7.2",
         "ext-json": "*",
-        "lakedrops/dotenv": "^1.5||dev-master",
+        "symfony/dotenv": "^3.4||^4.4||^5.0",
         "symfony/yaml": "^3.4||^4.4||^5.0",
         "twig/twig": "^1.44"
     },
-- 
GitLab