Completed
Pull Request — master (#597)
by Greg
03:11 queued 10s
created

Config::hasDefault()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 1
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
     * @param mixed $defaultOverride
0 ignored issues
show
Bug introduced by
There is no parameter named $defaultOverride. 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...
201
     *
202
     * @return mixed
203
     */
204
    public function hasDefault($key)
205
    {
206
        return isset($this->defaults[$key]);
207
    }
208
209
    /**
210
     * Return the default value for a given configuration item.
211
     *
212
     * @param string $key
213
     * @param mixed $defaultOverride
214
     *
215
     * @return mixed
216
     */
217
    public function getDefault($key, $defaultOverride = null)
218
    {
219
        return $this->hasDefault($key) ? $this->defaults[$key] : $defaultOverride;
220
    }
221
222
    /**
223
     * Set the default value for a configuration setting. This allows us to
224
     * set defaults either before or after more specific configuration values
225
     * are loaded. Keeping defaults separate from current settings also
226
     * allows us to determine when a setting has been overridden.
227
     *
228
     * @param string $key
229
     * @param string $value
230
     */
231
    public function setDefault($key, $value)
232
    {
233
        $this->defaults[$key] = $value;
234
        return $this;
235
    }
236
237
    /**
238
     * @return bool
239
     */
240
    public function isSimulated()
241
    {
242
        return $this->get(self::SIMULATE);
243
    }
244
245
    /**
246
     * @param bool $simulated
247
     *
248
     * @return $this
249
     */
250
    public function setSimulated($simulated = true)
251
    {
252
        return $this->set(self::SIMULATE, $simulated);
253
    }
254
255
    /**
256
     * @return bool
257
     */
258
    public function isInteractive()
259
    {
260
        return $this->get(self::INTERACTIVE);
261
    }
262
263
    /**
264
     * @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...
265
     *
266
     * @return $this
267
     */
268
    public function setInteractive($interactive = true)
269
    {
270
        return $this->set(self::INTERACTIVE, $interactive);
271
    }
272
273
    /**
274
     * @return bool
275
     */
276
    public function isDecorated()
277
    {
278
        return $this->get(self::DECORATED);
279
    }
280
281
    /**
282
     * @param bool $decorated
283
     *
284
     * @return $this
285
     */
286
    public function setDecorated($decorated = true)
287
    {
288
        return $this->set(self::DECORATED, $decorated);
289
    }
290
291
    /**
292
     * @param int $interval
293
     *
294
     * @return $this
295
     */
296
    public function setProgressBarAutoDisplayInterval($interval)
297
    {
298
        return $this->set(self::PROGRESS_BAR_AUTO_DISPLAY_INTERVAL, $interval);
299
    }
300
}
301