Passed
Push — 0.7.0 ( b5eec6...c2424f )
by Alexander
02:58
created

Dotenv::getVariable()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 13
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 4
eloc 10
c 1
b 0
f 0
nc 4
nop 1
dl 0
loc 13
rs 9.9332
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
    /**
37
     * The Repository creator instance.
38
     * 
39
     * @var \Syscodes\Dotenv\Repository\RepositoryCreator $repository
40
     */
41
    protected $repository;
42
43
    /**
44
     * The file store instance.
45
     * 
46
     * @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...
47
     */
48
    protected $store;
49
50
    /**
51
     * Activate use of putenv, by default is true.
52
     * 
53
     * @var bool $usePutenv 
54
     */
55
    protected $usePutenv = true;
56
57
    /**
58
     * Constructor. Create a new Dotenv instance.
59
     * 
60
     * @param  \Syscodes\Dotenv\Store\StoreBuilder  $store
61
p  * @param  \Syscodes\Dotenv\Repository\RepositoryCreator  $repository
62
     * 
63
     * @return void
64
     */
65
    public function __construct($store, $repository)
66
    {
67
        $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...
68
        $this->repository = $repository;
69
    }
70
71
    /**
72
     * Create a new Dotenv instance.
73
     * Builds the path to our file.
74
     * 
75
     * @param  \Syscodes\Dotenv\Repository\RepositoryCreator  $repository
76
     * @param  string|string[]  $path
77
     * @param  string|string[]  $names
78
     * @param  bool  $modeEnabled  (true by default)
79
     * 
80
     * @return \Syscodes\Dotenv\Dotenv
81
     */
82
    public static function create($repository, $paths, $names = null, bool $modeEnabled = true)
83
    {
84
        $store = $names === null ? StoreBuilder::createWithDefaultName() : StoreBuilder::createWithNoNames();
85
86
        foreach ((array) $paths as $path) {
87
            $store = $store->addPath($path);
88
        }
89
        
90
        foreach ((array) $names as $name) {
91
            $store = $store->addName($name);
92
        }
93
94
        if ($modeEnabled) {
95
            $store = $store->modeEnabled();
96
        }
97
98
        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

98
        return new self(/** @scrutinizer ignore-type */ $store->make(), $repository);
Loading history...
99
    }
100
101
    /**
102
     * Determine if the line in the file is a comment.
103
     * 
104
     * @param  string  $line
105
     * 
106
     * @return bool
107
     */
108
    protected function isComment(string $line)
109
    {
110
        return strpos(ltrim($line), '#') === 0;
111
    }
112
113
    /**
114
     * Determine if the given line looks like it's setting a variable.
115
     * 
116
     * @param  string  $line
117
     * 
118
     * @return bool
119
     */
120
    protected function checkedLikeSetter(string $line)
121
    {
122
        return strpos($line, '=') !== false;
123
    }
124
125
    /**
126
     * Will load the .env file and process it. So that we end all settings in the PHP 
127
     * environment vars: getenv(), $_ENV, and $_SERVER.
128
     * 
129
     * @return bool
130
     */
131
    public function load()
132
    {        
133
        foreach ($this->store->read() as $line) {
134
            // Is it a comment?
135
            if ($this->isComment($line)) {
136
                continue;
137
            }
138
139
            // If there is an equal sign, then we know we
140
            // are assigning a variable.
141
            if ($this->checkedLikeSetter($line)) {
142
                list($name, $value) = $this->normaliseEnvironmentVariable($line);
143
                $this->setVariable($name, $value);
0 ignored issues
show
Bug introduced by
$name of type array is incompatible with the type string expected by parameter $name of Syscodes\Dotenv\Dotenv::setVariable(). ( Ignorable by Annotation )

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

143
                $this->setVariable(/** @scrutinizer ignore-type */ $name, $value);
Loading history...
144
            }
145
        }
146
147
        return true;
148
    }
149
150
    /**
151
     * Normalise the given environment variable.
152
     * 
153
     * @param  string  $name
154
     * @param  string  $value
155
     * 
156
     * @return array
157
     */
158
    public function normaliseEnvironmentVariable(string $name, string $value = '')
159
    {
160
        // Split our compound string into it's parts.
161
        if (strpos($name, '=') !== false) {
162
            list($name, $value) = explode('=', $name, 2);
163
        }
164
        
165
        $name  = trim($name);
166
        $value = trim($value);
167
        
168
        // Sanitize the name
169
        $name = $this->sanitizeName($name);
170
        
171
        // Sanitize the value
172
        $value = $this->sanitizeValue($value);
173
        
174
        // Get environment variables
175
        $value = $this->getResolverVariables($value);
0 ignored issues
show
Bug introduced by
$value of type array is incompatible with the type string expected by parameter $value of Syscodes\Dotenv\Dotenv::getResolverVariables(). ( Ignorable by Annotation )

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

175
        $value = $this->getResolverVariables(/** @scrutinizer ignore-type */ $value);
Loading history...
176
        
177
        return [$name, $value];
178
    }
179
180
    /**
181
     * Strips quotes and the optional leading "export " from the environment variable name.
182
     * 
183
     * @param  string  $name
184
     * 
185
     * @return array
186
     */
187
    protected function sanitizeName(string $name)
188
    {
189
        return str_replace(array('export ', '\'', '"'), '', $name);
0 ignored issues
show
Bug Best Practice introduced by
The expression return str_replace(array..., ''', '"'), '', $name) returns the type string which is incompatible with the documented return type array.
Loading history...
190
    }
191
192
    /**
193
     * Sets the variable into the environment. 
194
     * Will parse the string to look for {name}={value} pattern.
195
     * 
196
     * @param  string  $name
197
     * @param  string|null  $value  (null by default)
198
     * 
199
     * @return void
200
     */
201
    protected function setVariable(string $name, $value = null)
202
    {        
203
        return $this->repository->set($name, $value);
0 ignored issues
show
Bug introduced by
The method set() does not exist on Syscodes\Dotenv\Repository\RepositoryCreator. ( Ignorable by Annotation )

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

203
        return $this->repository->/** @scrutinizer ignore-call */ set($name, $value);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
204
    }
205
206
    /**
207
     * Strips quotes from the environment variable value.
208
     * 
209
     * This was borrowed from the excellent phpdotenv with very few changes.
210
     * https://github.com/vlucas/phpdotenv
211
     * 
212
     * @param  string  $value
213
     * 
214
     * @return array
215
     * 
216
     * @throws \InvalidArgumentException
217
     */
218
    protected function sanitizeValue($value)
219
    {
220
        if ( ! $value) {
221
            return $value;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $value returns the type string which is incompatible with the documented return type array.
Loading history...
222
        }
223
        
224
        // Does it begin with a quote?
225
        if (strpbrk($value[0], '"\'') !== false) {
226
            // value starts with a quote
227
            $quote = $value[0];
228
229
            $regexPattern = sprintf(
230
					'/^
231
					%1$s          # match a quote at the start of the value
232
					(             # capturing sub-pattern used
233
								  (?:          # we do not need to capture this
234
								   [^%1$s\\\\] # any character other than a quote or backslash
235
								   |\\\\\\\\   # or two backslashes together
236
								   |\\\\%1$s   # or an escaped quote e.g \"
237
								  )*           # as many characters that match the previous rules
238
					)             # end of the capturing sub-pattern
239
					%1$s          # and the closing quote
240
					.*$           # and discard any string after the closing quote
241
					/mx', $quote
242
            );
243
            
244
            $value = preg_replace($regexPattern, '$1', $value);
245
            $value = str_replace("\\$quote", $quote, $value);
246
            $value = str_replace('\\\\', '\\', $value);
247
        } else {
248
            $parts = explode(' #', $value, 2);
249
            $value = trim($parts[0]);
250
            // Unquoted values cannot contain whitespace
251
            if (preg_match('/\s+/', $value) > 0) {
252
                throw new InvalidArgumentException('.env values containing spaces must be surrounded by quotes.');
253
            }
254
        }
255
        
256
        return $value;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $value returns the type string which is incompatible with the documented return type array.
Loading history...
257
    }
258
    
259
    /**
260
     * Resolve the nested variables.
261
     * 
262
     * Look for ${varname} patterns in the variable value and replace with an existing
263
     * environment variable.
264
     * 
265
     * This was borrowed from the excellent phpdotenv with very few changes.
266
     * https://github.com/vlucas/phpdotenv
267
     * 
268
     * @param  string  $value
269
     * 
270
     * @return string
271
     */
272
    protected function getResolverVariables(string $value)
273
    {
274
        if (strpos($value, '$') !== false) {
275
            $repository = $this->repository;
276
277
            $value = preg_replace_callback('~\${([a-zA-Z0-9_]+)}~', function ($pattern) use ($repository) {
278
                $nestedVariable = $repository->get($pattern[1]);
0 ignored issues
show
Bug introduced by
The method get() does not exist on Syscodes\Dotenv\Repository\RepositoryCreator. ( Ignorable by Annotation )

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

278
                /** @scrutinizer ignore-call */ 
279
                $nestedVariable = $repository->get($pattern[1]);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
279
280
                if (is_null($nestedVariable)) {
281
                    return $pattern[0];
282
                }
283
                
284
                return $nestedVariable;
285
                
286
            }, $value);
287
        }
288
        
289
        return $value;
290
    }
291
}