Passed
Push — master ( 795c19...52755b )
by smiley
01:38
created

Env::__loadEnv()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 5
nc 2
nop 4
dl 0
loc 7
rs 9.4285
c 0
b 0
f 0
1
<?php
2
/**
3
 * Trait Env
4
 *
5
 * @filesource   Env.php
6
 * @created      25.11.2017
7
 * @package      chillerlan\Traits
8
 * @author       Smiley <[email protected]>
9
 * @copyright    2017 Smiley
10
 * @license      MIT
11
 */
12
13
namespace chillerlan\Traits;
14
15
/**
16
 * Loads .env config files into the environment
17
 *
18
 * $_ENV > getenv()!
19
 *
20
 * @link https://github.com/vlucas/phpdotenv
21
 */
22
trait Env{
23
24
	/**
25
	 * @param string      $path
26
	 * @param string|null $filename
27
	 * @param bool|null   $overwrite
28
	 * @param array|null  $required
29
	 *
30
	 * @return $this
31
	 */
32
	protected function __loadEnv(string $path, string $filename = null, bool $overwrite = null, array $required = null){
33
		$overwrite = $overwrite !== null ? $overwrite : false;
34
		$content   = $this->__read(rtrim($path, DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR.($filename ?? '.env'));
35
36
		return $this
37
			->__load($content, $overwrite)
38
			->__check($required)
39
		;
40
	}
41
42
	/**
43
	 * @param string $var
44
	 *
45
	 * @return bool|mixed
46
	 */
47
	protected function __getEnv(string $var){
48
		$var = strtoupper($var);
49
50
		if(array_key_exists($var, $_ENV)){
51
			return $_ENV[$var];
52
		}
53
54
		$val = getenv($var);
55
56
		if($val !== false){
57
			return $val;
58
		}
59
60
		if(function_exists('apache_getenv')){
61
			$val = apache_getenv($var);
62
63
			if($val !== false){
64
				return $val;
65
 			}
66
67
 		}
68
69
		return false;
70
	}
71
72
	/**
73
	 * @param string $var
74
	 * @param string $value
75
	 *
76
	 * @return $this
77
	 */
78
	protected function __setEnv(string $var, string $value = null){
79
		$var   = strtoupper($var);
80
		$value = $this->__parse($value);
81
82
		// fill $_ENV explicitly, assuming variables_order="GPCS" (production)
83
		$_ENV[$var] = $value;
84
		putenv($var.'='.$value);
85
86
		if(function_exists('apache_setenv')){
87
			apache_setenv($var, $value);
88
		}
89
90
		return $this;
91
	}
92
93
	/**
94
	 * @param string $var
95
	 *
96
	 * @return $this
97
	 */
98
	protected function __unsetEnv(string $var){
99
		$var = strtoupper($var);
100
101
		unset($_ENV[$var]);
102
		putenv($var);
103
104
		return $this;
105
	}
106
107
	/**
108
	 * use with caution!
109
	 *
110
	 * @return $this
111
	 */
112
	protected function __clearEnv(){
113
		$_ENV = [];
114
115
		return $this;
116
	}
117
118
	/**
119
	 * @param string $file
120
	 *
121
	 * @return array
122
	 * @throws \chillerlan\Traits\TraitException
123
	 */
124
	private function __read(string $file):array{
125
126
		if(!is_readable($file) || !is_file($file)){
127
			throw new TraitException('invalid file: '.$file);
128
		}
129
130
		// Read file into an array of lines with auto-detected line endings
131
		$autodetect = ini_get('auto_detect_line_endings');
132
		ini_set('auto_detect_line_endings', '1');
133
		$lines = file($file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
134
		ini_set('auto_detect_line_endings', $autodetect);
135
136
		if(!is_array($lines) || empty($lines)){
137
			throw new TraitException('error while reading file: '.$file);
138
		}
139
140
		return array_map('trim', $lines);
141
	}
142
143
	/**
144
	 * @link http://php.net/variables-order
145
	 *
146
	 * @param array $data
147
	 * @param bool  $overwrite
148
	 *
149
	 * @return $this
150
	 */
151
	private function __load(array $data, bool $overwrite){
152
153
		foreach($data as $line){
154
155
			// skip empty lines and comments
156
			if(empty($line) || strpos($line, '#') === 0){
157
				continue;
158
			}
159
160
			$kv = array_map('trim', explode('=', $line, 2));
161
162
			// skip empty and numeric keys, keys with spaces, existing keys that shall not be overwritten
163
			if(empty($kv[0]) || is_numeric($kv[0]) || strpos($kv[0], ' ') !== false || (!$overwrite && array_key_exists($kv[0], $_ENV))){
164
				continue;
165
			}
166
167
			$this->__setEnv($kv[0], isset($kv[1]) ? trim($kv[1]) : null);
168
		}
169
170
		return $this;
171
	}
172
173
	/**
174
	 * @param string $value
175
	 *
176
	 * @return string|null
177
	 */
178
	private function __parse(string $value = null){
179
180
		if($value !== null){
181
182
			$q = $value[0] ?? null;
183
184
			$value = in_array($q, ["'", '"'], true)
185
				// handle quoted strings
186
				? preg_replace("/^$q((?:[^$q\\\\]|\\\\\\\\|\\\\$q)*)$q.*$/mx", '$1', $value)
187
				// skip inline comments
188
				: trim(explode('#', $value, 2)[0]);
189
190
			// handle multiline values
191
			$value = implode(PHP_EOL, explode('\\n', $value));
192
193
			// handle nested ${VARS}
194
			if(strpos($value, '$') !== false){
195
				$value = preg_replace_callback('/\${([_a-z\d]+)}/i', function($matches){
196
					return $this->__getEnv($matches[1]);
197
				}, $value);
198
			}
199
200
		}
201
202
		return $value;
203
	}
204
205
	/**
206
	 * @param array|null $required
207
	 *
208
	 * @return $this
209
	 * @throws \chillerlan\Traits\TraitException
210
	 */
211
	private function __check(array $required = null){
212
213
		if($required === null || empty($required)){
214
			return $this;
215
		}
216
217
		foreach($required as $var){
218
			if(!$this->__getEnv($var) || $this->__getEnv($var) === null){
219
				throw new TraitException('required variable not set: '.strtoupper($var));
220
			}
221
		}
222
223
		return $this;
224
	}
225
}
226