Completed
Push — master ( 9a4776...309fd1 )
by Jani
04:31
created

Environment::getConfig()   C

Complexity

Conditions 8
Paths 16

Size

Total Lines 36
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 26
CRAP Score 8

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 36
ccs 26
cts 26
cp 1
rs 5.3846
cc 8
eloc 21
nc 16
nop 0
crap 8
1
<?php
2
3
namespace janisto\env;
4
5
/**
6
 * Environment class, used to set configuration depending on the server environment.
7
 *
8
 * @author Jani Mikkonen <[email protected]>
9
 * @license public domain (http://unlicense.org)
10
 * @link https://github.com/janisto/env
11
 */
12
class Environment
13
{
14
    /**
15
     * Environment variable. Use Apache SetEnv or export in shell.
16
     *
17
     * @var string environment variable name
18
     */
19
    protected $envName = 'APP_ENV';
20
    /**
21
     * @var array list of valid modes
22
     */
23
    protected $validModes = [
24
        'dev',
25
        'test',
26
        'stage',
27
        'prod',
28
    ];
29
    /**
30
     * @var array configuration directory(s)
31
     */
32
    protected $configDir = [];
33
    /**
34
     * @var string selected environment mode
35
     */
36
    protected $mode;
37
    /**
38
     * @var array application configuration array
39
     */
40
    public $config = [];
41
42
    /**
43
     * Constructor. Initializes the Environment class.
44
     *
45
     * @param string|array $configDir configuration directory(s)
46
     * @param string $mode override automatically set environment mode
47
     * @throws \Exception
48
     */
49 26
    public function __construct($configDir, $mode = null)
50
    {
51 26
        $this->setConfigDir($configDir);
52 24
        $this->setMode($mode);
53 22
        $this->setEnvironment();
54 18
    }
55
56
    /**
57
     * Set configuration directory(s) where the config files are stored.
58
     *
59
     * @param string|array $configDir configuration directory(s)
60
     * @throws \Exception
61
     */
62 26
    protected function setConfigDir($configDir)
63
    {
64 26
        $this->configDir = [];
65 26
        foreach ((array) $configDir as $k => $v) {
66 26
            $dir = rtrim($v, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
67 26
            if (!is_dir($dir)) {
68 2
                throw new \Exception('Invalid configuration directory "' . $dir . '".');
69
            }
70 24
            $this->configDir[$k] = $dir;
71 24
        }
72 24
    }
73
74
    /**
75
     * Set environment mode.
76
     *
77
     * @param string|null $mode environment mode
78
     * @throws \Exception
79
     */
80 24
    protected function setMode($mode)
81
    {
82 24
        if ($mode === null) {
83
            // Return mode based on environment variable.
84 14
            $mode = getenv($this->envName);
85 14
            if ($mode === false) {
86
                // Defaults to production.
87 2
                $mode = 'prod';
88 2
            }
89 14
        }
90
91
        // Check if mode is valid.
92 24
        $mode = strtolower($mode);
93 24
        if (!in_array($mode, $this->validModes, true)) {
94 2
            throw new \Exception('Invalid environment mode supplied or selected: ' . $mode);
95
        }
96
97 22
        $this->mode = $mode;
98 22
    }
99
100
    /**
101
     * Load and merge configuration files into one array.
102
     *
103
     * @return array $config array to be processed by setEnvironment
104
     * @throws \Exception
105
     */
106 22
    protected function getConfig()
107
    {
108 22
        $configMerged = [];
109 22
        foreach ($this->configDir as $configDir) {
110
            // Merge main config.
111 22
            $fileMainConfig = $configDir . 'main.php';
112 22
            if (!file_exists($fileMainConfig)) {
113 2
                throw new \Exception('Cannot find main config file "' . $fileMainConfig . '".');
114
            }
115 20
            $configMain = require($fileMainConfig);
116 20
            if (is_array($configMain)) {
117 20
                $configMerged = static::merge($configMerged, $configMain);
118 20
            }
119
120
            // Merge mode specific config.
121 20
            $fileSpecificConfig = $configDir . 'mode_' . $this->mode . '.php';
122 20
            if (!file_exists($fileSpecificConfig)) {
123 2
                throw new \Exception('Cannot find mode specific config file "' . $fileSpecificConfig . '".');
124
            }
125 18
            $configSpecific = require($fileSpecificConfig);
126 18
            if (is_array($configSpecific)) {
127 18
                $configMerged = static::merge($configMerged, $configSpecific);
128 18
            }
129
130
            // If one exists, merge local config.
131 18
            $fileLocalConfig = $configDir . 'local.php';
132 18
            if (file_exists($fileLocalConfig)) {
133 18
                $configLocal = require($fileLocalConfig);
134 18
                if (is_array($configLocal)) {
135 18
                    $configMerged = static::merge($configMerged, $configLocal);
136 18
                }
137 18
            }
138 18
        }
139
140 18
        return $configMerged;
141
    }
142
143
    /**
144
     * Sets the configuration for the selected mode.
145
     */
146 22
    protected function setEnvironment()
147
    {
148 22
        $this->config = $this->getConfig();
149 18
        $this->config['environment'] = $this->mode;
150 18
    }
151
152
    /**
153
     * Merges two or more arrays into one recursively.
154
     *
155
     * If each array has an element with the same string key value, the latter
156
     * will overwrite the former (different from array_merge_recursive).
157
     * Recursive merging will be conducted if both arrays have an element of array
158
     * type and are having the same key.
159
     * For integer-keyed elements, the elements from the latter array will
160
     * be appended to the former array.
161
     *
162
     * params: $a, $b [, array $... ]
163
     * $a array to be merged to
164
     * $b array to be merged from. You can specify additional arrays via third argument, fourth argument etc.
165
     *
166
     * @return array the merged array (the original arrays are not changed.)
167
     */
168 20
    protected static function merge()
169
    {
170 20
        $args = func_get_args();
171 20
        $res = array_shift($args);
172 20
        while (!empty($args)) {
173 20
            $next = array_shift($args);
174 20
            foreach ($next as $k => $v) {
175 20
                if (is_int($k)) {
176 18
                    if (isset($res[$k])) {
177 18
                        $res[] = $v;
178 18
                    } else {
179 10
                        $res[$k] = $v;
180
                    }
181 20
                } elseif (is_array($v) && isset($res[$k]) && is_array($res[$k])) {
182 18
                    $res[$k] = self::merge($res[$k], $v);
183 18
                } else {
184 20
                    $res[$k] = $v;
185
                }
186 20
            }
187 20
        }
188
189 20
        return $res;
190
    }
191
192
    /**
193
     * Show current Environment class values.
194
     */
195 2
    public function showDebug()
196
    {
197
        print '<div style="position: absolute; left: 0; width: 100%; height: 250px; overflow: auto;'
198
            . 'bottom: 0; z-index: 9999; color: #000; margin: 0; border-top: 1px solid #000;">'
199 2
            . '<pre style="margin: 0; background-color: #ddd; padding: 5px;">'
200 2
            . htmlspecialchars(print_r($this, true)) . '</pre></div>';
201 2
    }
202
}
203