Completed
Pull Request — master (#1952)
by Basil
02:18
created

Config   A

Complexity

Total Complexity 26

Size/Duplication

Total Lines 209
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 2

Importance

Changes 0
Metric Value
wmc 26
lcom 1
cbo 2
dl 0
loc 209
rs 10
c 0
b 0
f 0

12 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 6 1
A application() 0 4 1
A module() 0 4 1
A component() 0 4 1
A webComponent() 0 4 1
A consoleComponent() 0 4 1
A addDefinition() 0 6 1
A isCliRuntime() 0 8 2
A setCliRuntime() 0 4 1
B toArray() 0 21 8
A appendConfig() 0 17 5
A handleKeyBaseMerge() 0 14 3
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
    /**
124
     * register application level config
125
     *
126
     * @param array $config The array to configure
127
     * @return ConfigDefinition
128
     */
129
    public function application(array $config)
130
    {
131
        return $this->addDefinition(new ConfigDefinition(ConfigDefinition::GROUP_APPLICATIONS, md5(serialize($config)), $config));
132
    }
133
134
    /**
135
     * Register a module.
136
     *
137
     * @param string $id The module identifier.
138
     * @param string|array $config The configuration for the given module. If a string is given this will be taken as `class` property.
139
     * @return ConfigDefinition
140
     */
141
    public function module($id, $config)
142
    {
143
        return $this->addDefinition(new ConfigDefinition(ConfigDefinition::GROUP_MODULES, $id, $config));
144
    }
145
146
    /**
147
     * Register a component
148
     *
149
     * @param string $id The id of the component
150
     * @param string|array $config The configuration for the given module. If a string is given this will be taken as `class` property.
151
     * @param string $runtime The runtime for the component: all, web or console
152
     * @return ConfigDefinition
153
     */
154
    public function component($id, $config, $runtime = self::RUNTIME_ALL)
155
    {
156
        return $this->addDefinition(new ConfigDefinition(ConfigDefinition::GROUP_COMPONENTS, $id, $config))->runtime($runtime);
157
    }
158
159
    /**
160
     * Register a web runtime component.
161
     *
162
     * @param string $id The id of the component
163
     * @param string|array $config The configuration for the given module. If a string is given this will be taken as `class` property.
164
     * @return ConfigDefinition
165
     */
166
    public function webComponent($id, $config)
167
    {
168
        return $this->component($id, $config, self::RUNTIME_WEB);
169
    }
170
171
    /**
172
     * Register a console runtime component.
173
     *
174
     * @param string $id The id of the component
175
     * @param string|array $config The configuration for the given module. If a string is given this will be taken as `class` property.
176
     * @return ConfigDefinition
177
     */
178
    public function consoleComponent($id, $config)
179
    {
180
        return $this->component($id, $config, self::RUNTIME_CONSOLE);
181
    }
182
183
    private $_definitions = [];
184
185
    /**
186
     * Add a defintion into the defintions bag.
187
     *
188
     * @param ConfigDefinition $definition
189
     * @return ConfigDefinition
190
     */
191
    private function addDefinition(ConfigDefinition $definition)
192
    {
193
        $this->_definitions[] = $definition;
194
195
        return $definition;
196
    }
197
198
    private $_isCliRuntime;
199
200
    /**
201
     * Whether runtime is cli or not
202
     *
203
     * @return boolean
204
     */
205
    public function isCliRuntime()
206
    {
207
        if ($this->_isCliRuntime === null) {
208
            $this->_isCliRuntime = strtolower(php_sapi_name()) === 'cli';
209
        }
210
211
        return $this->_isCliRuntime;
212
    }
213
214
    /**
215
     * Setter method for runtime.
216
     * 
217
     * > This method is mainly used for unit testing.
218
     *
219
     * @param boolean $value
220
     */
221
    public function setCliRuntime($value)
222
    {
223
        $this->_isCliRuntime = $value;
224
    }
225
226
    /**
227
     * Export the given configuration as array for certain envs.
228
     *
229
     * @param array $envs A list of environments to export. if nothing is given all enviroments will be returned.
230
     * @return array The configuration array
231
     */
232
    public function toArray(array $envs = [])
233
    {
234
        $config = [];
235
        $envs = array_merge($envs, [self::ENV_ALL]);
236
        foreach ($this->_definitions as $definition) { /** @var ConfigDefinition $definition */
237
            // validate if current export env is in the list of envs
238
            if (!$definition->validateEnvs($envs)) {
239
                continue;
240
            }
241
            // validate runtime circumstances
242
            if ($definition->validateRuntime(self::RUNTIME_ALL)) {
243
                $this->appendConfig($config, $definition);
244
            } elseif ($this->isCliRuntime() && $definition->validateRuntime(self::RUNTIME_CONSOLE)) {
245
                $this->appendConfig($config, $definition);
246
            } elseif (!$this->isCliRuntime() && $definition->validateRuntime(self::RUNTIME_WEB)) {
247
                $this->appendConfig($config, $definition);
248
            }
249
        }
250
251
        return $config;
252
    }
253
254
    /**
255
     * Append a given defintion int othe config
256
     *
257
     * @param array $config
258
     * @param ConfigDefinition $definition
259
     */
260
    private function appendConfig(&$config, ConfigDefinition $definition)
261
    {
262
        switch ($definition->getGroup()) {
263
            case ConfigDefinition::GROUP_APPLICATIONS:
264
                foreach ($definition->getConfig() as $k => $v) {
265
                    $config[$k] = $v;
266
                }
267
                break;
268
            case ConfigDefinition::GROUP_COMPONENTS:
269
                $this->handleKeyBaseMerge($config, $definition, 'components');
270
                break;
271
272
            case ConfigDefinition::GROUP_MODULES:
273
                $this->handleKeyBaseMerge($config, $definition, 'modules');
274
                break;
275
        }
276
    }
277
278
    /**
279
     * Add a array key based component defintion.
280
     *
281
     * @param array $config
282
     * @param ConfigDefinition $definition
283
     * @param string $section
284
     */
285
    private function handleKeyBaseMerge(&$config, ConfigDefinition $definition, $section)
286
    {
287
        // ass missing section key
288
        if (!array_key_exists($section, $config)) {
289
            $config[$section] = [];
290
        }
291
292
        // if key exists, merge otherwise create key
293
        if (isset($config[$section][$definition->getKey()])) {
294
            $config[$section][$definition->getKey()] = ArrayHelper::merge($config[$section][$definition->getKey()], $definition->getConfig());
295
        } else {
296
            $config[$section][$definition->getKey()] = $definition->getConfig();
297
        }
298
    }
299
}