Skip to content
Snippets Groups Projects
Config.php 6.99 KiB
Newer Older
  • Learn to ignore specific revisions
  • <?php
    
    namespace LakeDrops\Component\Composer;
    
    
    use Composer\Json\JsonFile;
    
    jurgenhaas's avatar
    jurgenhaas committed
    use Seld\JsonLint\ParsingException;
    
    use Symfony\Component\Yaml\Yaml;
    
    jurgenhaas's avatar
    jurgenhaas committed
    use Twig\Environment;
    
    use Twig\Error\LoaderError;
    use Twig\Error\RuntimeError;
    use Twig\Error\SyntaxError;
    
    
    /**
     * Manages project config.
     */
    final class Config {
    
    
    jurgenhaas's avatar
    jurgenhaas committed
       * All config values.
       *
    
    jurgenhaas's avatar
    jurgenhaas committed
      private static array $values;
    
    jurgenhaas's avatar
    jurgenhaas committed
       * Custom config values.
       *
    
    jurgenhaas's avatar
    jurgenhaas committed
      private static array $customValues;
    
    jurgenhaas's avatar
    jurgenhaas committed
       * Config values to overwrite.
       *
    
    jurgenhaas's avatar
    jurgenhaas committed
      private static array $overwriteValues;
    
    jurgenhaas's avatar
    jurgenhaas committed
       * Use specific config values.
       *
    
    jurgenhaas's avatar
    jurgenhaas committed
      private static array $userValues;
    
    jurgenhaas's avatar
    jurgenhaas committed
       * The component for which this config object holds the values.
       *
    
    jurgenhaas's avatar
    jurgenhaas committed
      private string $component;
    
    jurgenhaas's avatar
    jurgenhaas committed
       * The Twig array loaded.
       *
    
    jurgenhaas's avatar
    jurgenhaas committed
      private ArrayLoader $twigLoader;
    
    jurgenhaas's avatar
    jurgenhaas committed
       * The Twig environment.
       *
    
    jurgenhaas's avatar
    jurgenhaas committed
      private Environment $twig;
    
    jurgenhaas's avatar
    jurgenhaas committed
       * The .env service.
       *
    
       * @var \LakeDrops\Component\Composer\Dotenv
       */
    
    jurgenhaas's avatar
    jurgenhaas committed
      private Dotenv $env;
    
    jurgenhaas's avatar
    jurgenhaas committed
       * The filename of the config file.
       *
    
    jurgenhaas's avatar
    jurgenhaas committed
      private string $configFile;
    
    jurgenhaas's avatar
    jurgenhaas committed
       * The filename of user specific config file.
       *
    
    jurgenhaas's avatar
    jurgenhaas committed
      private string $userConfigFile;
    
      /**
       * Config constructor.
       *
       * @param string $component
    
    jurgenhaas's avatar
    jurgenhaas committed
       *   The component for which this config object holds the values.
    
       * @param array $default_values
    
    jurgenhaas's avatar
    jurgenhaas committed
       *   The default configuration from the component.
    
       * @param \LakeDrops\Component\Composer\Dotenv $env
    
    jurgenhaas's avatar
    jurgenhaas committed
       *   The .env service.
    
      public function __construct(string $component, array $default_values, Dotenv $env) {
    
        $this->component = $component;
    
    jurgenhaas's avatar
    jurgenhaas committed
        $this->twigLoader = new ArrayLoader([]);
        $this->twig = new Environment($this->twigLoader);
    
        $this->configFile = getcwd() . '/.lakedrops.yml';
    
        $this->userConfigFile = getcwd() . '/.lakedrops.user.yml';
    
        $this->merge($default_values, self::$customValues[$this->component] ?? []);
    
        $this->merge([], self::$overwriteValues[$this->component] ?? [], FALSE);
    
        $this->merge([], self::$userValues[$this->component] ?? [], FALSE);
    
        $this->migrateFromComposerJson();
    
      }
    
      /**
       * Read project configuration if not already happened.
       */
      private function init(): void {
    
        if (!isset(self::$values)) {
    
          if (!file_exists($this->configFile)) {
    
            self::$customValues = [];
    
            self::$customValues = Yaml::parseFile($this->configFile);
    
    jurgenhaas's avatar
    jurgenhaas committed
            // Apply overwrite by stage declarations.
            $stage = getenv('PROJECT_BRANCH');
            if (isset(self::$customValues['stage_overwrites'][$stage])) {
    
              self::$overwriteValues = self::$customValues['stage_overwrites'][$stage];
    
    jurgenhaas's avatar
    jurgenhaas committed
            }
    
          if (!file_exists($this->userConfigFile)) {
            self::$userValues = [];
          }
          else {
            self::$userValues = Yaml::parseFile($this->userConfigFile);
          }
    
      /**
       * Migrate custom settings from composer.json if required.
       */
    
      private function migrateFromComposerJson(): void {
    
        $filename = getcwd() . '/composer.json';
        $jsonFile = new JsonFile($filename);
    
    jurgenhaas's avatar
    jurgenhaas committed
        try {
          $content = $jsonFile->read();
        }
        catch (ParsingException $e) {
          $content = [];
        }
    
        if (isset($content['extra'][$this->component])) {
          $this->merge([], $content['extra'][$this->component]);
          $this->save();
          unset($content['extra'][$this->component]);
          try {
            $jsonFile->write($content);
    
    jurgenhaas's avatar
    jurgenhaas committed
          }
          catch (\Exception $ex) {
            // Ignored, if composer.json is read-only there is a general problem.
    
    jurgenhaas's avatar
    jurgenhaas committed
       * Helper function to merge and optionally store config values.
       *
    
       * @param array $defaultValues
    
    jurgenhaas's avatar
    jurgenhaas committed
       *   Array with default values.
    
       * @param array $customValues
    
    jurgenhaas's avatar
    jurgenhaas committed
       *   Array with custom value to be merged on top of default.
    
    jurgenhaas's avatar
    jurgenhaas committed
       *   TRUE, if the resulting array should be stored back to the config file.
    
    jurgenhaas's avatar
    jurgenhaas committed
      private function merge(array $defaultValues, array $customValues, bool $store = TRUE): void {
    
        $default = self::$values[$this->component] ?? [];
        $custom = self::$customValues[$this->component] ?? [];
        if (!empty($defaultValues)) {
          $default = NestedArray::mergeDeep($default, $defaultValues);
        }
        if (!empty($customValues)) {
          $default = NestedArray::mergeDeep($default, $customValues);
    
          if ($store) {
            $custom = NestedArray::mergeDeep($custom, $customValues);
          }
    
        }
        self::$values[$this->component] = $this->env->replaceEnvironmentVariables($default);
        self::$customValues[$this->component] = $custom;
    
      }
    
      /**
       * Save the current settings.
       */
      private function save(): void {
    
        file_put_contents($this->configFile, Yaml::dump(self::$customValues, 9, 2));
    
    jurgenhaas's avatar
    jurgenhaas committed
       * Read a value from config array.
       *
    
    jurgenhaas's avatar
    jurgenhaas committed
       *   The array holding all config values.
    
    jurgenhaas's avatar
    jurgenhaas committed
       *   List of keys of how we should dive deep into the value array.
    
    jurgenhaas's avatar
    jurgenhaas committed
       *   The found value or NULL, if it doesn't exist.
    
      private function readValueFromArray(array $values, array $keys) {
    
        $key = array_shift($keys);
        if (!isset($values[$key])) {
          return NULL;
        }
        if (empty($keys)) {
          return $values[$key];
        }
        return $this->readValueFromArray($values[$key], $keys);
      }
    
      /**
    
    jurgenhaas's avatar
    jurgenhaas committed
       * Read value from config.
       *
    
       * @param string|array $keys
    
    jurgenhaas's avatar
    jurgenhaas committed
       *   List of keys of how we should dive deep into the value array. If a top
       *   level value should be read, the key can be provided as a string instead
       *   of an array.
    
    jurgenhaas's avatar
    jurgenhaas committed
       *   The found value or NULL, if it doesn't exist.
    
      public function readValue($keys) {
        if (is_string($keys)) {
          $keys = [$keys];
        }
        return $this->readValueFromArray(self::$values[$this->component], $keys);
    
    jurgenhaas's avatar
    jurgenhaas committed
       * Set a config value.
       *
    
    jurgenhaas's avatar
    jurgenhaas committed
       *   The top level key of the value in the config array.
    
    jurgenhaas's avatar
    jurgenhaas committed
       *   The value to be stored.
    
    jurgenhaas's avatar
    jurgenhaas committed
       *   Whether the new value should be stored in the config file.
    
    jurgenhaas's avatar
    jurgenhaas committed
      public function setValue(string $key, $value, bool $store = TRUE): void {
    
        $this->merge([], [$key => $value], $store);
    
    jurgenhaas's avatar
    jurgenhaas committed
       * Render a Twig template by using config values.
       *
    
       * @param string $filename
    
    jurgenhaas's avatar
    jurgenhaas committed
       *   The filename of the Twig template.
    
       * @param string $content
    
    jurgenhaas's avatar
    jurgenhaas committed
       *   The content to be rendered.
    
    jurgenhaas's avatar
    jurgenhaas committed
       *   The rendered string.
    
       */
      public function render(string $filename, string $content): string {
    
    jurgenhaas's avatar
    jurgenhaas committed
        $this->twigLoader->setTemplate($filename, $content);
    
        try {
          return $this->twig->render($filename, self::$values[$this->component]);
        }
    
    jurgenhaas's avatar
    jurgenhaas committed
        catch (LoaderError | RuntimeError | SyntaxError $e) {