Completed
Pull Request — master (#1953)
by
unknown
02:04
created

Config::addDefinition()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 10
rs 9.9332
c 0
b 0
f 0
cc 2
nc 2
nop 1
1
<?php
2
3
namespace luya;
4
5
use luya\helpers\ArrayHelper;
6
7
/**
8
 * Configuration array Helper.
9
 *
10
 * The {{luya\Config}} allows you to create the configuration for different hosts and difference between web and console config.
11
 *
12
 * ```php
13
 * $config = new Config('myapp', dirname(__DIR__), [
14
 *     'siteTitle' => 'My LUYA Project',
15
 *     'defaultRoute' => 'cms',
16
 *     // other application level configurations
17
 * ]);
18
 *
19
 * // define global components which works either for console or web runtime
20
 *
21
 * $config->component('mail', [
22
 *     'host' => 'xyz',
23
 *     'from' => '[email protected]',
24
 * ]);
25
 *
26
 * $config->component('db', [
27
 *     'class' => 'yii\db\Connection',
28
 *     'dsn' => 'mysql:host=localhost;dbname=prod_db',
29
 *     'username' => 'foo',
30
 *     'password' => 'bar',
31
 * ]);
32
 *
33
 * // define components which are only for web or console runtime:
34
 *
35
 * $config->webComponent('request', [
36
 *     'cookieValidationKey' => 'xyz',
37
 * ]);
38
 *
39
 * // which is equals to, but the above is better to read and structure in the config file
40
 *
41
 * $config->component('request', [
42
 *     'cookieValidationKey' => 'xyz',
43
 * ])->webRuntime();
44
 *
45
 * // adding modules
46
 *
47
 * $config->module('admin', [
48
 *     'class' => 'luya\admin\Module',
49
 *     'secureLogin' => true,
50
 * ]);
51
 *
52
 * $config->module('cms', 'luya\cms\frontend\Module'); // which is equals to $config->module('cms', ['class' => 'luya\cms\frontend\Module']);
53
 *
54
 * // export and generate the config for a given enviroment or environment independent.
55
 *
56
 * return $config->toArray(); // returns the config not taking care of enviroment variables like prod, env
57
 *
58
 * return $config->toArray(Config::ENV_PROD);
59
 * ```
60
 *
61
 * Switching between envs can be usefull if certain configurations should only apply on a certain environment. Therefore you can add `env()` behind componenets, applications and modules.
62
 *
63
 * ```php
64
 * $config->component('db', [
65
 *     'class' => 'yii\db\Connection',
66
 *     'dsn' => 'mysql:host=localhost;dbname=prod_db',
67
 *     'username' => 'foo',
68
 *     'password' => 'bar',
69
 * ])->env(Config::ENV_LOCAL);
70
 *
71
 * $config->component('db', [
72
 *     'class' => 'yii\db\Connection',
73
 *     'dsn' => 'mysql:host=localhost;dbname=prod_db',
74
 *     'username' => 'foo',
75
 *     'password' => 'bar',
76
 * ])->env(Config::ENV_DEV);
77
 *
78
 * $config->component('db', [
79
 *     'class' => 'yii\db\Connection',
80
 *     'dsn' => 'mysql:host=localhost;dbname=prod_db',
81
 *     'username' => 'foo',
82
 *     'password' => 'bar',
83
 * ])->env(Config::ENV_PROD);
84
 *
85
 * return $config->toArray(Config::ENV_PROD); // would only return the prod env db component
86
 * ```
87
 *
88
 * @author Basil Suter <[email protected]>
89
 * @since 1.0.21
90
 */
91
class Config
92
{
93
    const ENV_ALL = 'all';
94
95
    const ENV_PROD = 'prod';
96
    
97
    const ENV_PREP = 'prep';
98
    
99
    const ENV_DEV = 'dev';
100
    
101
    const ENV_LOCAL = 'local';
102
103
    const RUNTIME_ALL = 0;
104
105
    const RUNTIME_CONSOLE = 1;
106
107
    const RUNTIME_WEB = 2;
108
    
109
    /**
110
     * Constructor
111
     *
112
     * @param string $id
113
     * @param string $basePath
114
     * @param array $applicationConfig
115
     */
116
    public function __construct($id, $basePath, array $applicationConfig = [])
117
    {
118
        $applicationConfig['id'] = $id;
119
        $applicationConfig['basePath'] = $basePath;
120
        $this->application($applicationConfig);
121
    }
122
    
123
    private $_env = null;
124
    
125
    /**
126
     * Assign the env to each component that defined inside the callback.
127
     *
128
     * Callback function has one parameter with this config.
129
     *
130
     * @param string   $env
131
     * @param callable $callback function(\luya\Config $config)
132
     *
133
     * @return $this
134
     */
135
    public function env($env, callable $callback)
136
    {
137
        $this->_env = $env;
138
        
139
        try {
140
            $callback($this);
141
        } finally {
142
            $this->_env = null;
143
        }
144
        
145
        return $this;
146
    }
147
148
    /**
149
     * register application level config
150
     *
151
     * @param array $config The array to configure
152
     * @return ConfigDefinition
153
     */
154
    public function application(array $config)
155
    {
156
        return $this->addDefinition(new ConfigDefinition(ConfigDefinition::GROUP_APPLICATIONS, md5(serialize($config)), $config));
157
    }
158
159
    /**
160
     * Register one or more bootstrap entries into the bootstrap section.
161
     *
162
     * @param array $config An array with bootstrap entries, its common to use the module name
163
     * @return ConfigDefinition
164
     */
165
    public function bootstrap(array $config)
166
    {
167
        return $this->addDefinition(new ConfigDefinition(ConfigDefinition::GROUP_BOOTSTRAPS, md5(serialize($config)), $config));
168
    }
169
170
    /**
171
     * Register a module.
172
     *
173
     * @param string $id The module identifier.
174
     * @param string|array $config The configuration for the given module. If a string is given this will be taken as `class` property.
175
     * @return ConfigDefinition
176
     */
177
    public function module($id, $config)
178
    {
179
        return $this->addDefinition(new ConfigDefinition(ConfigDefinition::GROUP_MODULES, $id, $config));
180
    }
181
182
    /**
183
     * Register a component
184
     *
185
     * @param string $id The id of the component
186
     * @param string|array $config The configuration for the given module. If a string is given this will be taken as `class` property.
187
     * @param string $runtime The runtime for the component: all, web or console
188
     * @return ConfigDefinition
189
     */
190
    public function component($id, $config, $runtime = self::RUNTIME_ALL)
191
    {
192
        return $this->addDefinition(new ConfigDefinition(ConfigDefinition::GROUP_COMPONENTS, $id, $config))->runtime($runtime);
193
    }
194
195
    /**
196
     * Register a web runtime component.
197
     *
198
     * @param string $id The id of the component
199
     * @param string|array $config The configuration for the given module. If a string is given this will be taken as `class` property.
200
     * @return ConfigDefinition
201
     */
202
    public function webComponent($id, $config)
203
    {
204
        return $this->component($id, $config, self::RUNTIME_WEB);
205
    }
206
207
    /**
208
     * Register a console runtime component.
209
     *
210
     * @param string $id The id of the component
211
     * @param string|array $config The configuration for the given module. If a string is given this will be taken as `class` property.
212
     * @return ConfigDefinition
213
     */
214
    public function consoleComponent($id, $config)
215
    {
216
        return $this->component($id, $config, self::RUNTIME_CONSOLE);
217
    }
218
219
    private $_definitions = [];
220
221
    /**
222
     * Add a defintion into the defintions bag.
223
     *
224
     * @param ConfigDefinition $definition
225
     * @return ConfigDefinition
226
     */
227
    private function addDefinition(ConfigDefinition $definition)
228
    {
229
        if ($this->_env !== null) {
230
            $definition->env($this->_env);
231
        }
232
        
233
        $this->_definitions[] = $definition;
234
235
        return $definition;
236
    }
237
238
    private $_isCliRuntime;
239
240
    /**
241
     * Whether runtime is cli or not
242
     *
243
     * @return boolean
244
     */
245
    public function isCliRuntime()
246
    {
247
        if ($this->_isCliRuntime === null) {
248
            $this->_isCliRuntime = strtolower(php_sapi_name()) === 'cli';
249
        }
250
251
        return $this->_isCliRuntime;
252
    }
253
254
    /**
255
     * Setter method for runtime.
256
     *
257
     * > This method is mainly used for unit testing.
258
     *
259
     * @param boolean $value
260
     */
261
    public function setCliRuntime($value)
262
    {
263
        $this->_isCliRuntime = $value;
264
    }
265
266
    /**
267
     * Export the given configuration as array for certain envs.
268
     *
269
     * @param array $envs A list of environments to export. if nothing is given all enviroments will be returned.
270
     * @return array The configuration array
271
     */
272
    public function toArray(array $envs = [])
273
    {
274
        $config = [];
275
        $envs = array_merge($envs, [self::ENV_ALL]);
276
        foreach ($this->_definitions as $definition) { /** @var ConfigDefinition $definition */
277
            // validate if current export env is in the list of envs
278
            if (!$definition->validateEnvs($envs)) {
279
                continue;
280
            }
281
            // validate runtime circumstances
282
            if ($definition->validateRuntime(self::RUNTIME_ALL)) {
283
                $this->appendConfig($config, $definition);
284
            } elseif ($this->isCliRuntime() && $definition->validateRuntime(self::RUNTIME_CONSOLE)) {
285
                $this->appendConfig($config, $definition);
286
            } elseif (!$this->isCliRuntime() && $definition->validateRuntime(self::RUNTIME_WEB)) {
287
                $this->appendConfig($config, $definition);
288
            }
289
        }
290
291
        return $config;
292
    }
293
294
    /**
295
     * Append a given defintion int othe config
296
     *
297
     * @param array $config
298
     * @param ConfigDefinition $definition
299
     */
300
    private function appendConfig(&$config, ConfigDefinition $definition)
301
    {
302
        switch ($definition->getGroup()) {
303
            case ConfigDefinition::GROUP_APPLICATIONS:
304
                foreach ($definition->getConfig() as $k => $v) {
305
                    $config[$k] = $v;
306
                }
307
                break;
308
            case ConfigDefinition::GROUP_COMPONENTS:
309
                $this->handleKeyBaseMerge($config, $definition, 'components');
310
                break;
311
312
            case ConfigDefinition::GROUP_MODULES:
313
                $this->handleKeyBaseMerge($config, $definition, 'modules');
314
                break;
315
316
            case ConfigDefinition::GROUP_BOOTSTRAPS:
317
                if (!array_key_exists('bootstrap', $config)) {
318
                    $config['bootstrap'] = [];
319
                }
320
                foreach ($definition->getConfig() as $v) {
321
                    $config['bootstrap'][] = $v;
322
                }
323
        }
324
    }
325
326
    /**
327
     * Add a array key based component defintion.
328
     *
329
     * @param array $config
330
     * @param ConfigDefinition $definition
331
     * @param string $section
332
     */
333
    private function handleKeyBaseMerge(&$config, ConfigDefinition $definition, $section)
334
    {
335
        // ass missing section key
336
        if (!array_key_exists($section, $config)) {
337
            $config[$section] = [];
338
        }
339
340
        // if key exists, merge otherwise create key
341
        if (isset($config[$section][$definition->getKey()])) {
342
            $config[$section][$definition->getKey()] = ArrayHelper::merge($config[$section][$definition->getKey()], $definition->getConfig());
343
        } else {
344
            $config[$section][$definition->getKey()] = $definition->getConfig();
345
        }
346
    }
347
}