digipolisgent/robo-digipolis-general

View on GitHub
src/ReadProperties.php

Summary

Maintainability
A
3 hrs
Test Coverage
A
100%
<?php

namespace DigipolisGent\Robo\Task\General;

use Ckr\Util\ArrayMerger;
use Robo\Result;
use Robo\Task\BaseTask;
use Symfony\Component\Finder\Finder;
use Symfony\Component\Yaml\Yaml;

/**
 * Parses default.properties.yml and properties.yml files in to config. Config
 * is available for classes implementing \Robo\Contract\ConfigAwareInterface and
 * using \Robo\Common\ConfigAwareTrait.
 *
 * ``` php
 * $this->taskReadProperties()
 *     ->in(getcwd())
 *     ->followSymlinks()
 *     ->run();
 * $this->getConfig()->get('my.config.setting');
 * ```
 *
 */
class ReadProperties extends BaseTask
{

    /**
     * The finder object to scan for files.
     *
     * @var \Symfony\Component\Finder\Finder
     */
    protected $finder;

    /**
     * The directories to search in.
     *
     * @var array
     */
    protected $dirs;

    /**
     * Searches files and directories which match defined rules.
     *
     * @param string|array $dirs
     *   A directory path or an array of directories in which to search for
     *   config files.
     *
     * @throws \InvalidArgumentException
     *   If one of the directories does not exist.
     */
    public function __construct($dirs = [])
    {
        $this->finder = new Finder();
        $this->dirs = $dirs;
    }

    /**
     * Sets the finder.
     *
     * @param \Symfony\Component\Finder\Finder $finder
     *
     * @return $this
     *
     * @codeCoverageIgnore
     */
    public function finder(Finder $finder)
    {
        $this->finder = $finder;

        return $this;
    }

    /**
     * Searches files and directories which match defined rules.
     *
     * @param string|array $dirs
     *   A directory path or an array of directories in which to search for
     *   config files.
     *
     * @return $this
     *
     * @throws \InvalidArgumentException
     *   If one of the directories does not exist.
     *
     * @codeCoverageIgnore
     */
    public function in($dirs)
    {
        $this->dirs = $dirs;

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function run()
    {
        try {
            $this->finder->in($this->dirs)->files();
            $defaults = clone $this->finder;
            $packageOverrides = clone $this->finder;
            $projectConfig = [];

            // Get the property overrides for this project.
            $root = $this->getConfig()->get('digipolis.root.project', false);
            if ($root && file_exists($root . '/properties.yml')) {
                $this->printTaskInfo(
                    sprintf(
                        'Parsing config from %s.',
                        $root . '/properties.yml'
                    )
                );
                $contents = file_get_contents($root . '/properties.yml');
                $this->printTaskDebug(sprintf('Parsing %s', $contents));
                $projectConfig = Yaml::parse($contents);
            }

            $expander = class_exists('\\Grasmash\\YamlExpander\\Expander') ?
                '\\Grasmash\\YamlExpander\\Expander::expandArrayProperties' :
                [new \Grasmash\Expander\Expander(), 'expandArrayProperties'];
            $parsedConfig = call_user_func($expander,
                ArrayMerger::doMerge(
                    ArrayMerger::doMerge(
                        // Get the default properties.
                        $this->parseConfigFiles($defaults->name('default.properties.yml')),
                        // Get the property overrides for robo packages.
                        $this->parseConfigFiles($packageOverrides->name('properties.yml')),
                        ArrayMerger::FLAG_PREVENT_DOUBLE_VALUE_WHEN_APPENDING_NUMERIC_KEYS
                    ),
                    // Add the project overrides last.
                    $projectConfig,
                    ArrayMerger::FLAG_PREVENT_DOUBLE_VALUE_WHEN_APPENDING_NUMERIC_KEYS
                )
            );

            $this->printTaskDebug(sprintf('Resulted config: %s', print_r($parsedConfig, true)));

            // Save the settings to config.
            $config = $this->getConfig();
            foreach ($parsedConfig as $key => $value) {
                $config->set($key, $value);
            }
        } catch (\Exception $exception) {
            return Result::fromException($this, $exception);
        }
        return Result::success($this, 'Parsed all config.');
    }

    /**
     * Helper function to parse config files.
     *
     * @param \Symfony\Component\Finder\Finder $files
     *   The files to parse.
     *
     * @return array
     *   The parsed config.
     */
    protected function parseConfigFiles(Finder $files)
    {
        $configs = [];
        foreach ($files as $file) {
            // Check if this is part of a Robo package.
            $path = $file->getRealPath();
            if (!file_exists(dirname($path) . '/RoboFile.php')) {
                continue;
            }
            $this->printTaskInfo(sprintf('Parsing config from %s.', $path));
            $contents = file_get_contents($path);
            $this->printTaskDebug(sprintf('Parsing %s', $contents));
            $configs[] = Yaml::parse($contents);
        }

        if (!$configs) {
          return [];
        }

        // Sort by priority, higher priority (lower number) takes precedence.
        usort($configs, function ($a, $b) {
            return ($a['_priority'] ?? 0 ) - ($b['_priority'] ?? 0);
        });

        $result = array_shift($configs);
        foreach ($configs as $config) {
            $result = ArrayMerger::doMerge(
                $config,
                $result,
                ArrayMerger::FLAG_PREVENT_DOUBLE_VALUE_WHEN_APPENDING_NUMERIC_KEYS
            );
        }

        return $result;
    }
}