Completed
Push — master ( a5df1d...34e0af )
by Greg
02:47
created

Config::applyConfiguration()   B

Complexity

Conditions 5
Paths 6

Size

Total Lines 15
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 15
rs 8.8571
c 0
b 0
f 0
cc 5
eloc 8
nc 6
nop 5
1
<?php
2
namespace Robo\Config;
3
4
use Dflydev\DotAccessData\Data;
5
6
class Config
7
{
8
    const PROGRESS_BAR_AUTO_DISPLAY_INTERVAL = 'progress-delay';
9
    const DEFAULT_PROGRESS_DELAY = 2;
10
    const SIMULATE = 'simulate';
11
    const INTERACTIVE = 'interactive';
12
    const DECORATED = 'decorated';
13
14
    /**
15
     * @var Data
16
     */
17
    protected $config;
18
19
    /**
20
     * @var array
21
     */
22
    protected $defaults;
23
24
    /**
25
     * Create a new configuration object, and initialize it with
26
     * the provided nested array containing configuration data.
27
     */
28
    public function __construct(array $data = null)
29
    {
30
        $this->config = new Data($data);
31
        $this->defaults = $this->getGlobalOptionDefaultValues();
32
    }
33
34
    /**
35
     * Determine if a non-default config value exists.
36
     */
37
    public function has($key)
38
    {
39
        return ($this->config->has($key));
40
    }
41
42
    /**
43
     * Fetch a configuration value
44
     *
45
     * @param string $key Which config item to look up
46
     * @param string|null $defaultOverride Override usual default value with a different default. Deprecated; provide defaults to the config processor instead.
47
     *
48
     * @return mixed
49
     */
50
    public function get($key, $defaultOverride = null)
51
    {
52
        if ($this->has($key)) {
53
            return $this->config->get($key);
54
        }
55
        return $this->getDefault($key, $defaultOverride);
56
    }
57
58
    /**
59
     * Fetch an option value from a given key, or, if that specific key does
60
     * not contain a value, then consult various fallback options until a
61
     * value is found.
62
     *
63
     * Given the following inputs:
64
     *   - $prefix  = "command."
65
     *   - $group   = "foo.bar.baz"
66
     *   - $postfix = ".options."
67
     * This method will then consider, in order:
68
     *   - command.foo.bar.baz.options
69
     *   - command.foo.bar.options
70
     *   - command.foo.options
71
     * If any of these contain an option for "$key", then return its value.
72
     */
73
    public function getWithFallback($key, $group, $prefix = '', $postfix = '.')
74
    {
75
        $configKey = "{$prefix}{$group}${postfix}{$key}";
76
        if ($this->has($configKey)) {
77
            return $this->get($configKey);
78
        }
79
        if ($this->hasDefault($configKey)) {
80
            return $this->getDefault($configKey);
81
        }
82
        $moreGeneralGroupname = preg_replace('#\.[^.]*$#', '', $group);
83
        if ($moreGeneralGroupname != $group) {
84
            return $this->getWithFallback($key, $moreGeneralGroupname, $prefix, $postfix);
85
        }
86
        return null;
87
    }
88
89
    /**
90
     * Works like 'getWithFallback', but merges results from all applicable
91
     * groups. Settings from most specific group take precedence.
92
     */
93
    public function getWithMerge($key, $group, $prefix = '', $postfix = '.')
94
    {
95
        $configKey = "{$prefix}{$group}${postfix}{$key}";
96
        $result = [];
97
        if ($this->has($configKey)) {
98
            $result = $this->get($configKey);
99
        } elseif ($this->hasDefault($configKey)) {
100
            $result = $this->getDefault($configKey);
101
        }
102
        if (!is_array($result)) {
103
            throw new \UnexpectedValueException($configKey . ' must be a list of settings to apply.');
104
        }
105
        $moreGeneralGroupname = preg_replace('#\.[^.]*$#', '', $group);
106
        if ($moreGeneralGroupname != $group) {
107
            $result += $this->getWithMerge($key, $moreGeneralGroupname, $prefix, $postfix);
108
        }
109
        return $result;
110
    }
111
112
    /**
113
     * Set a config value
114
     *
115
     * @param string $key
116
     * @param mixed $value
117
     *
118
     * @return $this
119
     */
120
    public function set($key, $value)
121
    {
122
        $this->config->set($key, $value);
123
        return $this;
124
    }
125
126
    /**
127
     * Import configuration from the provided nexted array, replacing whatever
128
     * was here previously. No processing is done on the provided data.
129
     *
130
     * @param array $data
131
     * @return Config
132
     */
133
    public function import($data)
134
    {
135
        $this->config = new Data($data);
136
        if (!empty($data)) {
137
            $this->config->import($data, true);
138
        }
139
        return $this;
140
    }
141
142
    /**
143
     * Export all configuration as a nested array.
144
     */
145
    public function export()
146
    {
147
        return $this->config->export();
148
    }
149
150
    /**
151
     * Given an object that contains configuration methods, inject any
152
     * configuration found in the configuration file.
153
     *
154
     * The proper use for this method is to call setter methods of the
155
     * provided object. Using configuration to call methods that do work
156
     * is an abuse of this mechanism.
157
     *
158
     * TODO: We could use reflection to test to see if the return type
159
     * of the provided object is a reference to the object itself. All
160
     * setter methods should do this. This test is insufficient to guarentee
161
     * that the method is valid, but it would catch almost every misuse.
162
     */
163
    public function applyConfiguration($object, $configurationKey, $group = '', $prefix = '', $postfix = '')
164
    {
165
        if (!empty($group) && empty($postfix)) {
166
            $postfix = '.';
167
        }
168
        $settings = $this->getWithMerge($configurationKey, $group, $prefix, $postfix);
169
        foreach ($settings as $setterMethod => $args) {
170
            // TODO: Should it be possible to make $args a nested array
171
            // to make this code call the setter method multiple times?
172
            $fn = [$object, $setterMethod];
173
            if (is_callable($fn)) {
174
                call_user_func_array($fn, (array)$args);
175
            }
176
        }
177
    }
178
179
    /**
180
     * Return an associative array containing all of the global configuration
181
     * options and their default values.
182
     *
183
     * @return array
184
     */
185
    public function getGlobalOptionDefaultValues()
186
    {
187
        $globalOptions =
188
        [
189
            self::PROGRESS_BAR_AUTO_DISPLAY_INTERVAL => self::DEFAULT_PROGRESS_DELAY,
190
            self::SIMULATE => false,
191
        ];
192
193
        return $globalOptions;
194
    }
195
196
    /**
197
     * Return the default value for a given configuration item.
198
     *
199
     * @param string $key
200
     *
201
     * @return mixed
202
     */
203
    public function hasDefault($key)
204
    {
205
        return isset($this->defaults[$key]);
206
    }
207
208
    /**
209
     * Return the default value for a given configuration item.
210
     *
211
     * @param string $key
212
     * @param mixed $defaultOverride
213
     *
214
     * @return mixed
215
     */
216
    public function getDefault($key, $defaultOverride = null)
217
    {
218
        return $this->hasDefault($key) ? $this->defaults[$key] : $defaultOverride;
219
    }
220
221
    /**
222
     * Set the default value for a configuration setting. This allows us to
223
     * set defaults either before or after more specific configuration values
224
     * are loaded. Keeping defaults separate from current settings also
225
     * allows us to determine when a setting has been overridden.
226
     *
227
     * @param string $key
228
     * @param string $value
229
     */
230
    public function setDefault($key, $value)
231
    {
232
        $this->defaults[$key] = $value;
233
        return $this;
234
    }
235
236
    /**
237
     * @return bool
238
     */
239
    public function isSimulated()
240
    {
241
        return $this->get(self::SIMULATE);
242
    }
243
244
    /**
245
     * @param bool $simulated
246
     *
247
     * @return $this
248
     */
249
    public function setSimulated($simulated = true)
250
    {
251
        return $this->set(self::SIMULATE, $simulated);
252
    }
253
254
    /**
255
     * @return bool
256
     */
257
    public function isInteractive()
258
    {
259
        return $this->get(self::INTERACTIVE);
260
    }
261
262
    /**
263
     * @param bool $simulated
0 ignored issues
show
Bug introduced by
There is no parameter named $simulated. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
264
     *
265
     * @return $this
266
     */
267
    public function setInteractive($interactive = true)
268
    {
269
        return $this->set(self::INTERACTIVE, $interactive);
270
    }
271
272
    /**
273
     * @return bool
274
     */
275
    public function isDecorated()
276
    {
277
        return $this->get(self::DECORATED);
278
    }
279
280
    /**
281
     * @param bool $decorated
282
     *
283
     * @return $this
284
     */
285
    public function setDecorated($decorated = true)
286
    {
287
        return $this->set(self::DECORATED, $decorated);
288
    }
289
290
    /**
291
     * @param int $interval
292
     *
293
     * @return $this
294
     */
295
    public function setProgressBarAutoDisplayInterval($interval)
296
    {
297
        return $this->set(self::PROGRESS_BAR_AUTO_DISPLAY_INTERVAL, $interval);
298
    }
299
}
300