Completed
Push — master ( 4e196d...ac27f9 )
by Basil
04:28 queued 02:10
created

Config::toArray()   B

Complexity

Conditions 8
Paths 6

Size

Total Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 8
nc 6
nop 1
dl 0
loc 21
rs 8.4444
c 0
b 0
f 0
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 one or more bootstrap entries into the bootstrap section.
136
     *
137
     * @param array $config An array with bootstrap entries, its common to use the module name
138
     * @return ConfigDefinition
139
     */
140
    public function bootstrap(array $config)
141
    {
142
        return $this->addDefinition(new ConfigDefinition(ConfigDefinition::GROUP_BOOTSTRAPS, md5(serialize($config)), $config));
143
    }
144
145
    /**
146
     * Register a module.
147
     *
148
     * @param string $id The module identifier.
149
     * @param string|array $config The configuration for the given module. If a string is given this will be taken as `class` property.
150
     * @return ConfigDefinition
151
     */
152
    public function module($id, $config)
153
    {
154
        return $this->addDefinition(new ConfigDefinition(ConfigDefinition::GROUP_MODULES, $id, $config));
155
    }
156
157
    /**
158
     * Register a component
159
     *
160
     * @param string $id The id of the component
161
     * @param string|array $config The configuration for the given module. If a string is given this will be taken as `class` property.
162
     * @param string $runtime The runtime for the component: all, web or console
163
     * @return ConfigDefinition
164
     */
165
    public function component($id, $config, $runtime = self::RUNTIME_ALL)
166
    {
167
        return $this->addDefinition(new ConfigDefinition(ConfigDefinition::GROUP_COMPONENTS, $id, $config))->runtime($runtime);
168
    }
169
170
    /**
171
     * Register a web runtime component.
172
     *
173
     * @param string $id The id of the component
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 webComponent($id, $config)
178
    {
179
        return $this->component($id, $config, self::RUNTIME_WEB);
180
    }
181
182
    /**
183
     * Register a console runtime 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
     * @return ConfigDefinition
188
     */
189
    public function consoleComponent($id, $config)
190
    {
191
        return $this->component($id, $config, self::RUNTIME_CONSOLE);
192
    }
193
194
    private $_definitions = [];
195
196
    /**
197
     * Add a defintion into the defintions bag.
198
     *
199
     * @param ConfigDefinition $definition
200
     * @return ConfigDefinition
201
     */
202
    private function addDefinition(ConfigDefinition $definition)
203
    {
204
        $this->_definitions[] = $definition;
205
206
        return $definition;
207
    }
208
209
    private $_isCliRuntime;
210
211
    /**
212
     * Whether runtime is cli or not
213
     *
214
     * @return boolean
215
     */
216
    public function isCliRuntime()
217
    {
218
        if ($this->_isCliRuntime === null) {
219
            $this->_isCliRuntime = strtolower(php_sapi_name()) === 'cli';
220
        }
221
222
        return $this->_isCliRuntime;
223
    }
224
225
    /**
226
     * Setter method for runtime.
227
     * 
228
     * > This method is mainly used for unit testing.
229
     *
230
     * @param boolean $value
231
     */
232
    public function setCliRuntime($value)
233
    {
234
        $this->_isCliRuntime = $value;
235
    }
236
237
    /**
238
     * Export the given configuration as array for certain envs.
239
     *
240
     * @param array $envs A list of environments to export. if nothing is given all enviroments will be returned.
241
     * @return array The configuration array
242
     */
243
    public function toArray(array $envs = [])
244
    {
245
        $config = [];
246
        $envs = array_merge($envs, [self::ENV_ALL]);
247
        foreach ($this->_definitions as $definition) { /** @var ConfigDefinition $definition */
248
            // validate if current export env is in the list of envs
249
            if (!$definition->validateEnvs($envs)) {
250
                continue;
251
            }
252
            // validate runtime circumstances
253
            if ($definition->validateRuntime(self::RUNTIME_ALL)) {
254
                $this->appendConfig($config, $definition);
255
            } elseif ($this->isCliRuntime() && $definition->validateRuntime(self::RUNTIME_CONSOLE)) {
256
                $this->appendConfig($config, $definition);
257
            } elseif (!$this->isCliRuntime() && $definition->validateRuntime(self::RUNTIME_WEB)) {
258
                $this->appendConfig($config, $definition);
259
            }
260
        }
261
262
        return $config;
263
    }
264
265
    /**
266
     * Append a given defintion int othe config
267
     *
268
     * @param array $config
269
     * @param ConfigDefinition $definition
270
     */
271
    private function appendConfig(&$config, ConfigDefinition $definition)
272
    {
273
        switch ($definition->getGroup()) {
274
            case ConfigDefinition::GROUP_APPLICATIONS:
275
                foreach ($definition->getConfig() as $k => $v) {
276
                    $config[$k] = $v;
277
                }
278
                break;
279
            case ConfigDefinition::GROUP_COMPONENTS:
280
                $this->handleKeyBaseMerge($config, $definition, 'components');
281
                break;
282
283
            case ConfigDefinition::GROUP_MODULES:
284
                $this->handleKeyBaseMerge($config, $definition, 'modules');
285
                break;
286
287
            case ConfigDefinition::GROUP_BOOTSTRAPS:
288
                if (!array_key_exists('bootstrap', $config)) {
289
                    $config['bootstrap'] = [];
290
                }
291
                foreach ($definition->getConfig() as $v) {
292
                    $config['bootstrap'][] = $v;
293
                }
294
        }
295
    }
296
297
    /**
298
     * Add a array key based component defintion.
299
     *
300
     * @param array $config
301
     * @param ConfigDefinition $definition
302
     * @param string $section
303
     */
304
    private function handleKeyBaseMerge(&$config, ConfigDefinition $definition, $section)
305
    {
306
        // ass missing section key
307
        if (!array_key_exists($section, $config)) {
308
            $config[$section] = [];
309
        }
310
311
        // if key exists, merge otherwise create key
312
        if (isset($config[$section][$definition->getKey()])) {
313
            $config[$section][$definition->getKey()] = ArrayHelper::merge($config[$section][$definition->getKey()], $definition->getConfig());
314
        } else {
315
            $config[$section][$definition->getKey()] = $definition->getConfig();
316
        }
317
    }
318
}