|
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 DECORATED = 'decorated'; |
|
12
|
|
|
|
|
13
|
|
|
/** |
|
14
|
|
|
* @var Data |
|
15
|
|
|
*/ |
|
16
|
|
|
protected $config; |
|
17
|
|
|
|
|
18
|
|
|
/** |
|
19
|
|
|
* @var array |
|
20
|
|
|
*/ |
|
21
|
|
|
protected $defaults; |
|
22
|
|
|
|
|
23
|
|
|
/** |
|
24
|
|
|
* Create a new configuration object, and initialize it with |
|
25
|
|
|
* the provided nested array containing configuration data. |
|
26
|
|
|
*/ |
|
27
|
|
|
public function __construct(array $data = null) |
|
28
|
|
|
{ |
|
29
|
|
|
$this->config = new Data($data); |
|
30
|
|
|
$this->defaults = $this->getGlobalOptionDefaultValues(); |
|
31
|
|
|
} |
|
32
|
|
|
|
|
33
|
|
|
/** |
|
34
|
|
|
* Determine if a non-default config value exists. |
|
35
|
|
|
*/ |
|
36
|
|
|
public function has($key) |
|
37
|
|
|
{ |
|
38
|
|
|
return ($this->config->has($key)); |
|
39
|
|
|
} |
|
40
|
|
|
|
|
41
|
|
|
/** |
|
42
|
|
|
* Fetch a configuration value |
|
43
|
|
|
* |
|
44
|
|
|
* @param string $key Which config item to look up |
|
45
|
|
|
* @param string|null $defaultOverride Override usual default value with a different default. Deprecated; provide defaults to the config processor instead. |
|
46
|
|
|
* |
|
47
|
|
|
* @return mixed |
|
48
|
|
|
*/ |
|
49
|
|
|
public function get($key, $defaultOverride = null) |
|
50
|
|
|
{ |
|
51
|
|
|
if ($this->has($key)) { |
|
52
|
|
|
return $this->config->get($key); |
|
53
|
|
|
} |
|
54
|
|
|
return $this->getDefault($key, $defaultOverride); |
|
55
|
|
|
} |
|
56
|
|
|
|
|
57
|
|
|
/** |
|
58
|
|
|
* Set a config value |
|
59
|
|
|
* |
|
60
|
|
|
* @param string $key |
|
61
|
|
|
* @param mixed $value |
|
62
|
|
|
* |
|
63
|
|
|
* @return $this |
|
64
|
|
|
*/ |
|
65
|
|
|
public function set($key, $value) |
|
66
|
|
|
{ |
|
67
|
|
|
$this->config->set($key, $value); |
|
68
|
|
|
return $this; |
|
69
|
|
|
} |
|
70
|
|
|
|
|
71
|
|
|
/** |
|
72
|
|
|
* Import configuration from the provided nexted array, replacing whatever |
|
73
|
|
|
* was here previously. No processing is done on the provided data. |
|
74
|
|
|
* |
|
75
|
|
|
* @param array $data |
|
76
|
|
|
* @return Config |
|
77
|
|
|
*/ |
|
78
|
|
|
public function import($data) |
|
79
|
|
|
{ |
|
80
|
|
|
if (!empty($data)) { |
|
81
|
|
|
$this->config->import($data, true); |
|
82
|
|
|
} |
|
83
|
|
|
return $this; |
|
84
|
|
|
} |
|
85
|
|
|
|
|
86
|
|
|
/** |
|
87
|
|
|
* Extend this configuration by merging the provided nested array. |
|
88
|
|
|
* This will also do some simple processing on the data. |
|
89
|
|
|
* |
|
90
|
|
|
* @param array|ConfigLoaderInterface $data |
|
91
|
|
|
*/ |
|
92
|
|
|
public function extend($data) |
|
93
|
|
|
{ |
|
94
|
|
|
if (empty($data)) { |
|
95
|
|
|
return; |
|
96
|
|
|
} |
|
97
|
|
|
$processor = new ConfigProcessor(); |
|
98
|
|
|
$processor->add($this->config->export()); |
|
99
|
|
|
$processor->add($data); |
|
|
|
|
|
|
100
|
|
|
return $this->import($processor->export()); |
|
101
|
|
|
} |
|
102
|
|
|
|
|
103
|
|
|
/** |
|
104
|
|
|
* Export all configuration as a nested array. |
|
105
|
|
|
*/ |
|
106
|
|
|
public function export() |
|
107
|
|
|
{ |
|
108
|
|
|
return $this->config->export(); |
|
109
|
|
|
} |
|
110
|
|
|
|
|
111
|
|
|
/** |
|
112
|
|
|
* Given an object that contains configuration methods, inject any |
|
113
|
|
|
* configuration found in the configuration file. |
|
114
|
|
|
* |
|
115
|
|
|
* The proper use for this method is to call setter methods of the |
|
116
|
|
|
* provided object. Using configuration to call methods that do work |
|
117
|
|
|
* is an abuse of this mechanism. |
|
118
|
|
|
* |
|
119
|
|
|
* TODO: We could use reflection to test to see if the return type |
|
120
|
|
|
* of the provided object is a reference to the object itself. All |
|
121
|
|
|
* setter methods should do this. This test is insufficient to guarentee |
|
122
|
|
|
* that the method is valid, but it would be a good start. |
|
123
|
|
|
*/ |
|
124
|
|
|
public function applyConfiguration($object, $configurationKey) |
|
125
|
|
|
{ |
|
126
|
|
|
if ($this->has($configurationKey)) { |
|
127
|
|
|
$settings = $this->get($configurationKey); |
|
128
|
|
|
foreach ($settings as $setterMethod => $args) { |
|
129
|
|
|
// TODO: Should it be possible to make $args a nested array |
|
130
|
|
|
// to make this code call the setter method multiple times? |
|
131
|
|
|
call_user_func_array([$object, $setterMethod], (array)$args); |
|
132
|
|
|
} |
|
133
|
|
|
} |
|
134
|
|
|
} |
|
135
|
|
|
|
|
136
|
|
|
/** |
|
137
|
|
|
* Return an associative array containing all of the global configuration |
|
138
|
|
|
* options and their default values. |
|
139
|
|
|
* |
|
140
|
|
|
* @return array |
|
141
|
|
|
*/ |
|
142
|
|
|
public function getGlobalOptionDefaultValues() |
|
143
|
|
|
{ |
|
144
|
|
|
$globalOptions = |
|
145
|
|
|
[ |
|
146
|
|
|
self::PROGRESS_BAR_AUTO_DISPLAY_INTERVAL => self::DEFAULT_PROGRESS_DELAY, |
|
147
|
|
|
self::SIMULATE => false, |
|
148
|
|
|
]; |
|
149
|
|
|
|
|
150
|
|
|
return $globalOptions; |
|
151
|
|
|
} |
|
152
|
|
|
|
|
153
|
|
|
/** |
|
154
|
|
|
* Return the default value for a given configuration item. |
|
155
|
|
|
* |
|
156
|
|
|
* @param string $key |
|
157
|
|
|
* @param mixed $defaultOverride |
|
158
|
|
|
* |
|
159
|
|
|
* @return mixed |
|
160
|
|
|
*/ |
|
161
|
|
|
public function getDefault($key, $defaultOverride = null) |
|
162
|
|
|
{ |
|
163
|
|
|
return isset($this->defaults[$key]) ? $this->defaults[$key] : $defaultOverride; |
|
164
|
|
|
} |
|
165
|
|
|
|
|
166
|
|
|
/** |
|
167
|
|
|
* Set the default value for a configuration setting. This allows us to |
|
168
|
|
|
* set defaults either before or after more specific configuration values |
|
169
|
|
|
* are loaded. Keeping defaults separate from current settings also |
|
170
|
|
|
* allows us to determine when a setting has been overridden. |
|
171
|
|
|
* |
|
172
|
|
|
* @param string $key |
|
173
|
|
|
* @param string $value |
|
174
|
|
|
*/ |
|
175
|
|
|
public function setDefault($key, $value) |
|
176
|
|
|
{ |
|
177
|
|
|
$this->defaults[$key] = $value; |
|
178
|
|
|
return $this; |
|
179
|
|
|
} |
|
180
|
|
|
|
|
181
|
|
|
/** |
|
182
|
|
|
* @return bool |
|
183
|
|
|
*/ |
|
184
|
|
|
public function isSimulated() |
|
185
|
|
|
{ |
|
186
|
|
|
return $this->get(self::SIMULATE); |
|
187
|
|
|
} |
|
188
|
|
|
|
|
189
|
|
|
/** |
|
190
|
|
|
* @param bool $simulated |
|
191
|
|
|
* |
|
192
|
|
|
* @return $this |
|
193
|
|
|
*/ |
|
194
|
|
|
public function setSimulated($simulated = true) |
|
195
|
|
|
{ |
|
196
|
|
|
return $this->set(self::SIMULATE, $simulated); |
|
197
|
|
|
} |
|
198
|
|
|
|
|
199
|
|
|
/** |
|
200
|
|
|
* @return bool |
|
201
|
|
|
*/ |
|
202
|
|
|
public function isDecorated() |
|
203
|
|
|
{ |
|
204
|
|
|
return $this->get(self::DECORATED); |
|
205
|
|
|
} |
|
206
|
|
|
|
|
207
|
|
|
/** |
|
208
|
|
|
* @param bool $decorated |
|
209
|
|
|
* |
|
210
|
|
|
* @return $this |
|
211
|
|
|
*/ |
|
212
|
|
|
public function setDecorated($decorated = true) |
|
213
|
|
|
{ |
|
214
|
|
|
return $this->set(self::DECORATED, $decorated); |
|
215
|
|
|
} |
|
216
|
|
|
|
|
217
|
|
|
/** |
|
218
|
|
|
* @param int $interval |
|
219
|
|
|
* |
|
220
|
|
|
* @return $this |
|
221
|
|
|
*/ |
|
222
|
|
|
public function setProgressBarAutoDisplayInterval($interval) |
|
223
|
|
|
{ |
|
224
|
|
|
return $this->set(self::PROGRESS_BAR_AUTO_DISPLAY_INTERVAL, $interval); |
|
225
|
|
|
} |
|
226
|
|
|
} |
|
227
|
|
|
|
This check looks at variables that have been passed in as parameters and are passed out again to other methods.
If the outgoing method call has stricter type requirements than the method itself, an issue is raised.
An additional type check may prevent trouble.