Passed
Push — master ( 26d549...0e9514 )
by Alain
02:38
created

Config::resolveOptions()   B

Complexity

Conditions 4
Paths 6

Size

Total Lines 22
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 4

Importance

Changes 6
Bugs 0 Features 1
Metric Value
c 6
b 0
f 1
dl 0
loc 22
ccs 11
cts 11
cp 1
rs 8.9197
cc 4
eloc 13
nc 6
nop 1
crap 4
1
<?php
2
/**
3
 * Bright Nucleus Config Component.
4
 *
5
 * @package   BrightNucleus\Config
6
 * @author    Alain Schlesser <[email protected]>
7
 * @license   MIT
8
 * @link      http://www.brightnucleus.com/
9
 * @copyright 2016 Alain Schlesser, Bright Nucleus
10
 */
11
12
namespace BrightNucleus\Config;
13
14
use Assert;
15
use BrightNucleus\Config\ConfigSchemaInterface as Schema;
16
use BrightNucleus\Config\ConfigValidatorInterface as Validator;
17
use BrightNucleus\Config\Exception\FailedToInstantiateParentException;
18
use BrightNucleus\Config\Exception\FailedToLoadConfigException;
19
use BrightNucleus\Config\Exception\FailedToResolveConfigException;
20
use BrightNucleus\Config\Exception\InvalidConfigException;
21
use BrightNucleus\Config\Exception\InvalidConfigurationSourceException;
22
use Exception;
23
use Symfony\Component\OptionsResolver\OptionsResolver;
24
25
/**
26
 * Generic implementation of a Config object.
27
 *
28
 * @since   0.1.0
29
 *
30
 * @package BrightNucleus\Config
31
 * @author  Alain Schlesser <[email protected]>
32
 */
33
class Config extends AbstractConfig
34
{
35
36
    /**
37
     * The schema of the Config file.
38
     *
39
     * @var Schema
40
     */
41
    protected $schema;
42
43
    /**
44
     * The Validator class that gets asked to do the validation of the config.
45
     *
46
     * @since 0.1.0
47
     *
48
     * @var Validator
49
     */
50
    protected $validator;
51
52
    /**
53
     * Instantiate the Config object.
54
     *
55
     * It accepts either an array with the configuration settings, or a
56
     * filename pointing to a PHP file it can include.
57
     *
58
     * @since 0.1.0
59
     * @since 0.1.6 Accepts a delimiter to parse configuration keys.
60
     *
61
     * @param array|string         $config    Array with settings or filename for the
62
     *                                        settings file.
63
     * @param Schema|null          $schema    Optional. Config that contains default
64
     *                                        values that can get overwritten.
65
     * @param Validator|null       $validator Optional. Validator class that does the
66
     *                                        actual validation.
67
     * @param string[]|string|null $delimiter A string or array of strings that are used as delimiters to parse
68
     *                                        configuration keys. Defaults to "\", "/" & ".".
69
     *
70
     * @throws InvalidConfigurationSourceException If the config source is not a string or array.
71
     * @throws FailedToInstantiateParentException  If the parent class could not be instantiated.
72
     * @throws FailedToLoadConfigException         If loading of the config source failed.
73
     * @throws FailedToResolveConfigException      If the config file could not be resolved.
74
     * @throws InvalidConfigException              If the config file is not valid.
75
     */
76 9
    public function __construct(
77
        $config,
78
        Schema $schema = null,
79
        Validator $validator = null,
80
        $delimiter = null
81
    ) {
82 9
        $this->schema    = $schema;
83 9
        $this->validator = $validator;
84
85
        // Make sure $config is either a string or array.
86 9
        if (! (is_string($config) || is_array($config))) {
87 1
            throw new InvalidConfigurationSourceException(
88
                sprintf(
89 1
                    _('Invalid configuration source: %1$s'),
90 1
                    print_r($config, true)
91
                )
92
            );
93
        }
94
95 8
        if (is_string($config)) {
96 6
            $config = Loader::load($config);
97
        }
98
99
        // Run the $config through the OptionsResolver.
100 6
        Assert\that($config)->isArray();
101 6
        $config = $this->resolveOptions($config);
102
103
        // Instantiate the parent class.
104
        try {
105 6
            parent::__construct($config, $delimiter);
106
        } catch (Exception $exception) {
107
            throw new FailedToInstantiateParentException(
108
                sprintf(
109
                    _('Could not instantiate the configuration through its parent. Reason: %1$s'),
110
                    $exception->getMessage()
111
                )
112
            );
113
        }
114
115
        // Finally, validate the resulting config.
116 6
        if (! $this->isValid()) {
117 1
            throw new InvalidConfigException(
118
                sprintf(
119 1
                    _('ConfigInterface file is not valid: %1$s'),
120 1
                    print_r($config, true)
121
                )
122
            );
123
        }
124 6
    }
125
126
    /**
127
     * Validate the Config file.
128
     *
129
     * @since  0.1.0
130
     *
131
     * @return boolean
132
     */
133 2
    public function isValid()
134
    {
135 2
        if ($this->validator) {
136 1
            return $this->validator->isValid($this);
137
        }
138
139 2
        return true;
140
    }
141
142
    /**
143
     * Process the passed-in defaults and merge them with the new values, while
144
     * checking that all required options are set.
145
     *
146
     * @since 0.1.0
147
     *
148
     * @param array $config Configuration settings to resolve.
149
     *
150
     * @return array Resolved configuration settings.
151
     * @throws FailedToResolveConfigException If there are errors while resolving the options.
152
     */
153 4
    protected function resolveOptions($config)
154
    {
155 4
        if (! $this->schema) {
156 4
            return $config;
157
        }
158
159
        try {
160 2
            $resolver = new OptionsResolver();
161 2
            if ($this->configureOptions($resolver)) {
162 2
                $config = $resolver->resolve($config);
163
            }
164 1
        } catch (Exception $exception) {
165 1
            throw new FailedToResolveConfigException(
166
                sprintf(
167 1
                    _('Error while resolving config options: %1$s'),
168 1
                    $exception->getMessage()
169
                )
170
            );
171
        }
172
173 1
        return $config;
174
    }
175
176
    /**
177
     * Configure the possible and required options for the Config.
178
     *
179
     * This should return a bool to let the resolve_options() know whether the
180
     * actual resolving needs to be done or not.
181
     *
182
     * @since 0.1.0
183
     *
184
     * @param OptionsResolver $resolver Reference to the OptionsResolver
185
     *                                  instance.
186
     *
187
     * @return bool Whether to do the resolving.
188
     * @throws FailedToResolveConfigException If there are errors while processing.
189
     */
190 2
    protected function configureOptions(OptionsResolver $resolver)
191
    {
192 2
        $defined  = $this->schema->getDefinedOptions();
193 2
        $defaults = $this->schema->getDefaultOptions();
194 2
        $required = $this->schema->getRequiredOptions();
195
196 2
        if (! $defined && ! $defaults && ! $required) {
197
            return false;
198
        }
199
200
        try {
201 2
            if ($defined) {
202 2
                $resolver->setDefined($defined);
203
            }
204 2
            if ($defaults) {
205 2
                $resolver->setDefaults($defaults);
206
            }
207 2
            if ($required) {
208 2
                $resolver->setRequired($required);
209
            }
210
        } catch (Exception $exception) {
211
            throw new FailedToResolveConfigException(
212
                sprintf(
213
                    _('Error while processing config options: %1$s'),
214
                    $exception->getMessage()
215
                )
216
            );
217
        }
218
219 2
        return true;
220
    }
221
}
222