Completed
Push — master ( a8c5d7...b64b96 )
by Arne
02:19
created

ConfigurationFileReader::readJsonFile()   B

Complexity

Conditions 6
Paths 5

Size

Total Lines 26

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 26
rs 8.8817
c 0
b 0
f 0
cc 6
nc 5
nop 1
1
<?php
2
3
namespace Storeman\Config;
4
5
use Psr\Log\LoggerAwareInterface;
6
use Psr\Log\LoggerAwareTrait;
7
use Psr\Log\NullLogger;
8
use Storeman\ArrayUtils;
9
use Storeman\Container;
10
use Storeman\Exception;
11
use Storeman\PathUtils;
12
use Storeman\Validation\ContainerConstraintValidatorFactory;
13
use Symfony\Component\Validator\Validation;
14
use Symfony\Component\Validator\Validator\ValidatorInterface;
15
16
class ConfigurationFileReader implements LoggerAwareInterface
17
{
18
    use LoggerAwareTrait;
19
20
21
    /**
22
     * @var Container
23
     */
24
    protected $container;
25
26
    public function __construct(Container $container = null)
27
    {
28
        $this->container = $container ?: new Container();
29
        $this->logger = new NullLogger();
30
    }
31
32
    public function getConfiguration(string $configurationFilePath, array $configurationDefaults = [], array $vaultConfigurationDefaults = []): Configuration
33
    {
34
        $this->logger->info("Reading config file from {$configurationFilePath}...");
35
36
        $array = $this->readJsonFile($configurationFilePath);
37
        $array = $this->mergeDefaults($array, $configurationDefaults, $vaultConfigurationDefaults);
38
39
        try
40
        {
41
            $configuration = new Configuration();
42
            $configuration->exchangeArray($array);
43
        }
44
        catch (\InvalidArgumentException $exception)
45
        {
46
            throw new ConfigurationException("In file {$configurationFilePath}", 0, $exception);
47
        }
48
49
        $this->validateConfiguration($configuration, $configurationFilePath);
50
51
        $this->logger->info("The following configuration has been read: " . var_export($configuration->getArrayCopy(), true));
52
53
        return $configuration;
54
    }
55
56
    protected function readJsonFile(string $configurationFilePath): array
57
    {
58
        $configurationFilePath = PathUtils::getAbsolutePath($configurationFilePath);
59
60
        if (!is_file($configurationFilePath) || !is_readable($configurationFilePath))
61
        {
62
            throw new Exception("Configuration file {$configurationFilePath} is not a readable file.");
63
        }
64
65
        if (($json = file_get_contents($configurationFilePath)) === false)
66
        {
67
            throw new Exception("Failed to read config file {$configurationFilePath}.");
68
        }
69
70
        if (($array = json_decode($json, true)) === null)
71
        {
72
            throw new Exception("Malformed configuration file: {$configurationFilePath}.");
73
        }
74
75
        if (!is_array($array))
76
        {
77
            throw new ConfigurationException("Malformed configuration file: {$configurationFilePath}");
78
        }
79
80
        return $array;
81
    }
82
83
    protected function mergeDefaults(array $config, array $configurationDefaults, array $vaultConfigurationDefaults): array
84
    {
85
        if (!array_key_exists('vaults', $config) || !is_array($config['vaults']))
86
        {
87
            throw new ConfigurationException("Missing vault configurations");
88
        }
89
90
        $config = ArrayUtils::merge($configurationDefaults, $config);
91
        $config['vaults'] = array_map(function($vaultConfig) use ($vaultConfigurationDefaults) {
92
93
            if (!is_array($vaultConfig))
94
            {
95
                throw new ConfigurationException();
96
            }
97
98
            return ArrayUtils::merge($vaultConfigurationDefaults, $vaultConfig);
99
100
        }, $config['vaults']);
101
102
        return $config;
103
    }
104
105
    protected function validateConfiguration(Configuration $configuration, string $configurationFilePath)
106
    {
107
        $constraintViolations = $this->getValidator()->validate($configuration);
108
        if ($constraintViolations->count())
109
        {
110
            $violation = $constraintViolations->get(0);
111
112
            throw new ConfigurationException("{$configurationFilePath}: {$violation->getPropertyPath()} - {$violation->getMessage()}");
113
        }
114
    }
115
116
    protected function getValidator(): ValidatorInterface
117
    {
118
        return Validation::createValidatorBuilder()
119
            ->setConstraintValidatorFactory(new ContainerConstraintValidatorFactory($this->container))
120
            ->addMethodMapping('loadValidatorMetadata')
121
            ->getValidator();
122
    }
123
}
124