src/Configuration/Loader.php
<?php
declare(strict_types=1);
namespace Churn\Configuration;
use Churn\Configuration\Validator\CachePath;
use Churn\Configuration\Validator\CommitsSince;
use Churn\Configuration\Validator\DirectoriesToScan;
use Churn\Configuration\Validator\FileExtensions;
use Churn\Configuration\Validator\FilesToIgnore;
use Churn\Configuration\Validator\FilesToShow;
use Churn\Configuration\Validator\Hooks;
use Churn\Configuration\Validator\MaxScoreThreshold;
use Churn\Configuration\Validator\MinScoreToShow;
use Churn\Configuration\Validator\ParallelJobs;
use Churn\Configuration\Validator\Vcs;
use InvalidArgumentException;
use Symfony\Component\Yaml\Exception\ParseException;
use Symfony\Component\Yaml\Yaml;
/**
* @internal
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
final class Loader
{
/**
* @param string $confPath Path of the configuration file to load.
* @param boolean $isDefaultValue Indicates whether $confPath contains the default value.
* @throws InvalidArgumentException If the configuration file cannot be loaded.
*/
public static function fromPath(string $confPath, bool $isDefaultValue): Config
{
$originalConfPath = $confPath;
$confPath = self::normalizePath($isDefaultValue ? '.' : $confPath);
if (false !== $confPath && \is_readable($confPath)) {
$config = new EditableConfig($confPath);
$config->setUnrecognizedKeys(self::validate($config, self::loadYaml($confPath)));
return $config;
}
if ($isDefaultValue) {
return new ReadOnlyConfig();
}
throw new InvalidArgumentException('The configuration file can not be read at ' . $originalConfPath);
}
/**
* @param string $confPath Path to normalize.
* @return string|false
*/
private static function normalizePath(string $confPath)
{
if (!\is_dir($confPath)) {
return \realpath($confPath);
}
$confPath = \rtrim($confPath, '/\\') . '/churn.yml';
$realConfPath = \realpath($confPath);
if (false !== $realConfPath) {
return $realConfPath;
}
$confPath .= '.dist';
return \realpath($confPath);
}
/**
* @param string $confPath Path of the yaml file to load.
* @return array<mixed>
* @throws InvalidArgumentException If the configuration file is invalid.
*/
private static function loadYaml(string $confPath): array
{
try {
$content = Yaml::parse((string) \file_get_contents($confPath)) ?? [];
} catch (ParseException $e) {
$content = null;
}
if (!\is_array($content)) {
throw new InvalidArgumentException('The content of the configuration file is invalid');
}
return $content;
}
/**
* @param EditableConfig $config The configuration object.
* @param array<mixed> $configuration The array containing the configuration values.
* @return array<int|string>
*/
private static function validate(EditableConfig $config, array $configuration): array
{
$validators = [new CachePath(), new CommitsSince(), new DirectoriesToScan(), new FileExtensions(),
new FilesToIgnore(), new FilesToShow(), new Hooks(), new MaxScoreThreshold(), new MinScoreToShow(),
new ParallelJobs(), new Vcs()];
foreach ($validators as $validator) {
$validator->validate($config, $configuration);
unset($configuration[$validator->getKey()]);
}
return \array_keys($configuration);
}
}