Passed
Push — 0.7.0 ( 7cf8ff...8cbd21 )
by Alexander
04:15 queued 11s
created

Dotenv::filePath()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
c 0
b 0
f 0
nc 1
nop 0
dl 0
loc 3
rs 10
1
<?php 
2
3
/**
4
 * Lenevor Framework
5
 *
6
 * LICENSE
7
 *
8
 * This source file is subject to the new BSD license that is bundled
9
 * with this package in the file license.md.
10
 * It is also available through the world-wide-web at this URL:
11
 * https://lenevor.com/license
12
 * If you did not receive a copy of the license and are unable to
13
 * obtain it through the world-wide-web, please send an email
14
 * to [email protected] so we can send you a copy immediately.
15
 *
16
 * @package     Lenevor
17
 * @subpackage  Base
18
 * @link        https://lenevor.com
19
 * @copyright   Copyright (c) 2019 - 2021 Alexander Campo <[email protected]>
20
 * @license     https://opensource.org/licenses/BSD-3-Clause New BSD license or see https://lenevor.com/license or see /license.md
21
 */
22
23
namespace Syscodes\Dotenv;
24
25
use InvalidArgumentException;
26
use Syscodes\Dotenv\Store\StoreBuilder;
27
use Syscodes\Dotenv\Repository\RepositoryCreator;
28
29
/**
30
 * Manages .env files.
31
 * 
32
 * @author Alexander Campo <[email protected]>
33
 */
34
final class Dotenv
35
{
36
    protected const ALFANUMERIC_REGEX = '/\${([a-zA-Z0-9_]+)}/';
37
38
    /**
39
     * The loader instance.
40
     * 
41
     * @var \Syscodes\Dotenv\Loader\Loader $loader
42
     */
43
    protected $loader;
44
45
    /**
46
     * The Parser instance.
47
     * 
48
     * @var \Syscodes\Dotenv\Loader\Parser $parser
49
     */
50
    protected $parser;
51
52
    /**
53
     * The Repository creator instance.
54
     * 
55
     * @var \Syscodes\Dotenv\Repository\RepositoryCreator $repository
56
     */
57
    protected $repository;
58
59
    /**
60
     * The file store instance.
61
     * 
62
     * @var \Syscodes\Dotenv\Repository\FileStore $store
0 ignored issues
show
Bug introduced by
The type Syscodes\Dotenv\Repository\FileStore was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
63
     */
64
    protected $store;
65
66
    /**
67
     * Activate use of putenv, by default is true.
68
     * 
69
     * @var bool $usePutenv 
70
     */
71
    protected $usePutenv = true;
72
73
    /**
74
     * Constructor. Create a new Dotenv instance.
75
     * 
76
     * @param  \Syscodes\Dotenv\Store\StoreBuilder  $store
77
     * @param  \Syscodes\Dotenv\Repository\RepositoryCreator  $repository
78
     * 
79
     * @return void
80
     */
81
    public function __construct($store, RepositoryCreator $repository)
82
    {
83
        $this->store      = $store;
0 ignored issues
show
Documentation Bug introduced by
It seems like $store of type Syscodes\Dotenv\Store\StoreBuilder is incompatible with the declared type Syscodes\Dotenv\Repository\FileStore of property $store.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
84
        $this->repository = $repository;
85
    }
86
87
    /**
88
     * Create a new Dotenv instance.
89
     * Builds the path to our file.
90
     * 
91
     * @param  \Syscodes\Dotenv\Repository\RepositoryCreator  $repository
92
     * @param  string|string[]  $path
93
     * @param  string|string[]  $names
94
     * @param  bool  $modeEnabled  (true by default)
95
     * 
96
     * @return \Syscodes\Dotenv\Dotenv
97
     */
98
    public static function create(RepositoryCreator $repository, $paths, $names = null, bool $modeEnabled = true)
99
    {
100
        $store = $names === null ? StoreBuilder::createWithDefaultName() : StoreBuilder::createWithNoNames();
101
102
        foreach ((array) $paths as $path) {
103
            $store = $store->addPath($path);
104
        }
105
        
106
        foreach ((array) $names as $name) {
107
            $store = $store->addName($name);
108
        }
109
110
        if ($modeEnabled) {
111
            $store = $store->modeEnabled();
112
        }
113
114
        return new self($store->make(), $repository);
0 ignored issues
show
Bug introduced by
$store->make() of type Syscodes\Dotenv\Store\FileStore is incompatible with the type Syscodes\Dotenv\Store\StoreBuilder expected by parameter $store of Syscodes\Dotenv\Dotenv::__construct(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

114
        return new self(/** @scrutinizer ignore-type */ $store->make(), $repository);
Loading history...
115
    }
116
117
    /**
118
     * Determine if the line in the file is a comment.
119
     * 
120
     * @param  string  $line
121
     * 
122
     * @return bool
123
     */
124
    protected function isComment(string $line)
125
    {
126
        return strpos(ltrim($line), '#') === 0;
127
    }
128
129
    /**
130
     * Determine if the given line looks like it's setting a variable.
131
     * 
132
     * @param  string  $line
133
     * 
134
     * @return bool
135
     */
136
    protected function checkedLikeSetter(string $line)
137
    {
138
        return strpos($line, '=') !== false;
139
    }
140
141
    /**
142
     * Will load the .env file and process it. So that we end all settings in the PHP 
143
     * environment vars: getenv(), $_ENV, and $_SERVER.
144
     * 
145
     * @return bool
146
     */
147
    public function load()
148
    {
149
        foreach ($this->store->read() as $line) {
150
            // Is it a comment?
151
            if ($this->isComment($line)) {
152
                continue;
153
            }
154
155
            // If there is an equal sign, then we know we
156
            // are assigning a variable.
157
            if ($this->checkedLikeSetter($line)) {
158
                $this->setVariable($line);
159
            }
160
        }
161
162
        return true;
163
    }
164
165
    /**
166
     * Sets the variable into the environment. 
167
     * Will parse the string to look for {name}={value} pattern.
168
     * 
169
     * @param  string  $name
170
     * @param  string|null  $value
171
     * 
172
     * @return void
173
     */
174
    protected function setVariable(string $name, $value = null)
175
    {
176
        list($name, $value) = $this->normaliseEnvironmentVariable($name, $value);
177
        
178
        $notHttpName = 0 !== strpos($name, 'HTTP_');
179
180
        if ($this->usePutenv) {
181
            putenv("$name=$value");
182
        }   
183
        
184
        if (empty($_ENV[$name])) {
185
            $_ENV[$name] = $value;
186
        }
187
188
        if ($notHttpName) {
189
            $_SERVER[$name] = $value;
190
        }
191
    }
192
193
    /**
194
     * Normalise the given environment variable.
195
     * 
196
     * @param  string  $name
197
     * @param  string  $value
198
     * 
199
     * @return array
200
     */
201
    public function normaliseEnvironmentVariable(string $name, $value)
202
    {
203
        // Split our compound string into it's parts.
204
        if (strpos($name, '=') !== false) {
205
            list($name, $value) = explode('=', $name, 2);
206
        }
207
        
208
        $name  = trim($name);
209
        $value = trim($value);
210
        
211
        // Sanitize the name
212
        list($name, $value) = $this->sanitizeName($name, $value);
213
        
214
        // Sanitize the value
215
        list($name, $value) = $this->sanitizeValue($name, $value);
216
        
217
        $value = $this->resolveNestedVariables($value);
218
        
219
        return [$name, $value];
220
    }
221
222
    /**
223
     * Strips quotes and the optional leading "export " from the environment variable name.
224
     * 
225
     * @param  string  $name
226
     * @param  string  $value
227
     * 
228
     * @return array
229
     */
230
    protected function sanitizeName(string $name, $value)
231
    {
232
        $name = str_replace(array('export ', '\'', '"'), '', $name);
233
234
        return [$name, $value];
235
    }
236
237
    /**
238
     * Strips quotes from the environment variable value.
239
     * 
240
     * This was borrowed from the excellent phpdotenv with very few changes.
241
     * https://github.com/vlucas/phpdotenv
242
     * 
243
     * @param  string  $name
244
     * @param  string  $value
245
     * 
246
     * @return array
247
     * 
248
     * @throws \InvalidArgumentException
249
     */
250
    protected function sanitizeValue(string $name, $value)
251
    {
252
        if ( ! $value) {
253
            return [$name, $value];
254
        }
255
        
256
        // Does it begin with a quote?
257
        if (strpbrk($value[0], '"\'') !== false) {
258
            // value starts with a quote
259
            $quote = $value[0];
260
261
            $regexPattern = sprintf(
262
					'/^
263
					%1$s          # match a quote at the start of the value
264
					(             # capturing sub-pattern used
265
								  (?:          # we do not need to capture this
266
								   [^%1$s\\\\] # any character other than a quote or backslash
267
								   |\\\\\\\\   # or two backslashes together
268
								   |\\\\%1$s   # or an escaped quote e.g \"
269
								  )*           # as many characters that match the previous rules
270
					)             # end of the capturing sub-pattern
271
					%1$s          # and the closing quote
272
					.*$           # and discard any string after the closing quote
273
					/mx', $quote
274
            );
275
            
276
            $value = preg_replace($regexPattern, '$1', $value);
277
            $value = str_replace("\\$quote", $quote, $value);
278
            $value = str_replace('\\\\', '\\', $value);
279
        } else {
280
            $parts = explode(' #', $value, 2);
281
            $value = trim($parts[0]);
282
            // Unquoted values cannot contain whitespace
283
            if (preg_match('/\s+/', $value) > 0) {
284
                throw new InvalidArgumentException('.env values containing spaces must be surrounded by quotes.');
285
            }
286
        }
287
        
288
        return [$name, $value];
289
    }
290
    
291
    /**
292
     * Resolve the nested variables.
293
     * 
294
     * Look for ${varname} patterns in the variable value and replace with an existing
295
     * environment variable.
296
     * 
297
     * This was borrowed from the excellent phpdotenv with very few changes.
298
     * https://github.com/vlucas/phpdotenv
299
     * 
300
     * @param  string  $value
301
     * 
302
     * @return string
303
     */
304
    protected function resolveNestedVariables(string $value)
305
    {
306
        if (strpos($value, '$') !== false) {
307
            $loader = $this;
308
            $value = preg_replace_callback(self::ALFANUMERIC_REGEX, function ($matchedPatterns) use ($loader) {
309
                
310
                $nestedVariable = $loader->getVariable($matchedPatterns[1]);
311
312
                if (is_null($nestedVariable)) {
313
                    return $matchedPatterns[0];
314
                }
315
                
316
                return $nestedVariable;
317
                
318
            }, $value);
319
        }
320
        
321
        return $value;
322
    }
323
    
324
    /**
325
     * Search the different places for environment variables and return first value found.
326
     * This was borrowed from the excellent phpdotenv with very few changes.
327
     * https://github.com/vlucas/phpdotenv
328
     * 
329
     * @param  string  $name
330
     * 
331
     * @return string|null
332
     */
333
    protected function getVariable(string $name)
334
    {
335
        switch (true) {
336
            case array_key_exists($name, $_ENV):
337
                return $_ENV[$name];
338
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
339
            case array_key_exists($name, $_SERVER):
340
                return $_SERVER[$name];
341
                break;
342
            default:
343
                $value = getenv($name);
344
                // switch getenv default to null
345
                return $value === false ? null : $value;
346
        }
347
    }
348
}