Environment::getValueFromArray()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 5
c 1
b 0
f 0
dl 0
loc 7
rs 10
cc 2
nc 2
nop 2
1
<?php
2
3
namespace marcovtwout\YiiEnvironment;
4
5
use Exception;
6
7
/**
8
 * @name Environment
9
 * @author Marco van 't Wout | Tremani
10
 *
11
 * Simple class used to set configuration and debugging depending on environment.
12
 * Using this you can predefine configurations for use in different environments,
13
 * like _development, testing, staging and production_.
14
 *
15
 * @see README.md
16
 */
17
class Environment
18
{
19
    /**
20
     * Inherit key that can be used in configConsole
21
     */
22
    const INHERIT_KEY = 'inherit';
23
    
24
    /**
25
     * @var string name of env var to check
26
     */
27
    public $envVar = 'YII_ENVIRONMENT';
28
    
29
    /**
30
     * @var array list of valid modes
31
     */
32
    public $validModes = [
33
        100 => 'DEVELOPMENT',
34
        200 => 'TEST',
35
        300 => 'STAGING',
36
        400 => 'PRODUCTION'
37
    ];
38
        
39
    /**
40
     * @var array @see https://www.yiiframework.com/doc/guide/2.0/en/concept-configurations#environment-constants
41
     */
42
    public $modeToYiiEnvMap = [
43
        'DEVELOPMENT' => 'dev',
44
        'TEST' => 'test',
45
        'STAGING' => 'prod',
46
        'PRODUCTION' => 'prod'
47
    ];
48
    
49
    /**
50
     * @var bool debug on/off
51
     */
52
    public $yiiDebug;
53
    
54
    /**
55
     * @var array web config array
56
     */
57
    public $configWeb;
58
    
59
    /**
60
     * @var array console config array
61
     */
62
    public $configConsole;
63
        
64
    /**
65
     * @var string selected environment mode
66
     */
67
    protected $mode;
68
69
    /**
70
     * @var string config dir
71
     */
72
    protected $configDir;
73
    
74
    /**
75
     * Initilizes the Environment class with the given mode
76
     * @param string $mode used to override automatically setting mode
77
     * @param string $configDir override default configDir
78
     * @throws Exception
79
     */
80
    public function __construct($mode = null, $configDir = null)
81
    {
82
        $this->setConfigDir($configDir);
83
        $this->setMode($mode);
84
        $this->setEnvironment();
85
    }
86
    
87
    /**     
88
     * @return string Yii2 environment.
89
     */
90
    public function getYiiEnv()
91
    {
92
        return $this->modeToYiiEnvMap[$this->mode];
93
    }
94
95
    /**
96
     * Set config dir.
97
     * @param string|null $configDir
98
     */
99
    protected function setConfigDir($configDir)
100
    {
101
        if ($configDir !== null) {
102
            $this->configDir = rtrim($configDir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
103
        } else {
104
            $this->configDir = __DIR__ . '/../../../config/';
105
        }
106
    }
107
108
    /**
109
     * Set environment mode, if valid mode can be determined.
110
     * @param string $mode if left empty, determine automatically
111
     */
112
    protected function setMode($mode = null)
113
    {
114
        // If not overridden, determine automatically
115
        if ($mode === null) {
116
            $mode = $this->determineMode();
117
        }
118
119
        // Check if mode is valid
120
        $mode = strtoupper($mode);
121
        if (!in_array($mode, $this->validModes, true)) {
122
            throw new Exception('Invalid environment mode supplied or selected.');
123
        }
124
125
        $this->mode = $mode;
126
    }
127
128
    /**
129
     * Determine current environment mode depending on environment variable.
130
     * Also checks if there is a mode file that might override this environment.
131
     * Override this function if you want to implement your own method.
132
     * @return string mode
133
     */
134
    protected function determineMode()
135
    {
136
        if (file_exists($this->getModeFilePath())) {
137
            // Is there a mode file?
138
            $mode = trim(file_get_contents($this->getModeFilePath()));
139
        } else {
140
            // Else, return mode based on environment var
141
            $mode = getenv($this->envVar);
142
            if ($mode === false) {
143
                throw new Exception('"Environment mode cannot be determined, see class for instructions.');
144
            }
145
        }
146
        return $mode;
147
    }
148
149
    /**
150
     * @return string mode file path
151
     */
152
    protected function getModeFilePath()
153
    {
154
        return $this->configDir . 'mode.php';
155
    }
156
157
    /**
158
     * Load and merge config files into one array.
159
     * @return array $config array to be processed by setEnvironment.
160
     */
161
    protected function getConfig()
162
    {
163
        // Load main config
164
        $fileMainConfig = $this->configDir . 'main.php';
165
        if (!file_exists($fileMainConfig)) {
166
            throw new Exception('Cannot find main config file "' . $fileMainConfig . '".');
167
        }
168
        $configMain = require($fileMainConfig);
169
170
        // Load specific config
171
        $fileSpecificConfig = $this->configDir . 'mode_' . strtolower($this->mode) . '.php';
172
        if (!file_exists($fileSpecificConfig)) {
173
            throw new Exception('Cannot find mode specific config file "' . $fileSpecificConfig . '".');
174
        }
175
        $configSpecific = require($fileSpecificConfig);
176
177
        // Merge specific config into main config
178
        $config = self::mergeArray($configMain, $configSpecific);
179
180
        // If one exists, load and merge local config
181
        $fileLocalConfig = $this->configDir . 'local.php';
182
        if (file_exists($fileLocalConfig)) {
183
            $configLocal = require($fileLocalConfig);
184
            $config = self::mergeArray($config, $configLocal);
185
        }
186
187
        // Return
188
        return $config;
189
    }
190
191
    /**
192
     * Sets the environment and configuration for the selected mode.
193
     */
194
    protected function setEnvironment()
195
    {
196
        $config = $this->getConfig();
197
198
        // Set attributes
199
        $this->yiiDebug = $config['yiiDebug'];
200
        $this->configWeb = $config['configWeb'];
201
        $this->configWeb['params']['environment'] = strtolower($this->mode);
202
203
        // Set console attributes and related actions
204
        if (isset($config['configConsole']) && !empty($config['configConsole'])) {
205
            $this->configConsole = $config['configConsole'];
206
            $this->processInherits($this->configConsole); // Process configConsole for inherits
207
            $this->configConsole['params']['environment'] = strtolower($this->mode);
208
        }
209
    }
210
211
    /**
212
     * Merges two arrays into one recursively.
213
     * @param array $a array to be merged to
214
     * @param array $b array to be merged from
215
     * @return array the merged array (the original arrays are not changed.)
216
     *
217
     * Taken from Yii's CMap::mergeArray, since php does not supply a native
218
     * function that produces the required result.
219
     * @see http://www.yiiframework.com/doc/api/1.1/CMap#mergeArray-detail
220
     */
221
    protected static function mergeArray($a, $b)
222
    {
223
        foreach ($b as $k => $v) {
224
            if (is_integer($k)) {
225
                $a[] = $v;
226
            } elseif (is_array($v) && isset($a[$k]) && is_array($a[$k])) {
227
                $a[$k] = self::mergeArray($a[$k], $v);
228
            } else {
229
                $a[$k] = $v;
230
            }
231
        }
232
        return $a;
233
    }
234
235
    /**
236
     * Loop through console config array, replacing values called 'inherit' by values from $this->configWeb
237
     * @param array $array target array
238
     * @param array $path array that keeps track of current path
239
     */
240
    private function processInherits(&$array, $path = [])
241
    {
242
        foreach ($array as $key => &$value) {
243
            if (is_array($value)) {
244
                $this->processInherits($value, array_merge($path, [$key]));
245
            }
246
247
            if ($value === self::INHERIT_KEY) {
248
                $value = $this->getValueFromArray($this->configWeb, array_reverse(array_merge($path, [$key])));
249
            }
250
        }
251
    }
252
253
    /**
254
     * Walk $array through $path until the end, and return value
255
     * @param array $array target
256
     * @param array $path path array, from deep key to shallow key
257
     * @return mixed
258
     */
259
    private function getValueFromArray(&$array, $path)
260
    {
261
        if (count($path) > 1) {
262
            $key = end($path);
0 ignored issues
show
Unused Code introduced by
The assignment to $key is dead and can be removed.
Loading history...
263
            return $this->getValueFromArray($array[array_pop($path)], $path);
264
        } else {
265
            return $array[reset($path)];
266
        }
267
    }
268
}
269