Passed
Push — 0.7.0 ( c2424f...06148a )
by Alexander
02:45
created

Loader::sanitizeValue()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 39
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 4
eloc 26
c 1
b 0
f 0
nc 4
nop 1
dl 0
loc 39
rs 9.504
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\Loader;
24
25
/**
26
 * Allows will load the .env file and process it.
27
 * 
28
 * @author Alexander Campo <[email protected]>
29
 */
30
final class Loader
31
{
32
    /**
33
     * The Repository creator instance.
34
     * 
35
     * @var \Syscodes\Dotenv\Repository\RepositoryCreator $repository
36
     */
37
    protected $repository;
38
39
    /**
40
     * Constructor. Create a new Loader instance.
41
     * 
42
     * @param  \Syscodes\Dotenv\Repository\RepositoryCreator  $repository
43
     * 
44
     * @return void 
45
     */
46
    public function __construct($repository)
47
    {
48
        $this->repository = $repository;
49
    }
50
51
    /**
52
     * Will load the .env file and process it. So that we end all settings in the PHP 
53
     * environment vars: getenv(), $_ENV, and $_SERVER.
54
     * 
55
     * @param  array  $entries
56
     * 
57
     * @return bool
58
     */
59
    public function load(array $entries)
60
    {
61
        foreach ($entries as $line) {
62
            // Is it a comment?
63
            if ($this->isComment($line)) {
64
                continue;
65
            }
66
67
            // If there is an equal sign, then we know we
68
            // are assigning a variable.
69
            if ($this->checkedLikeSetter($line)) {
70
                list($name, $value) = $this->normaliseEnvironmentVariable($line);
71
                $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\Loader\Loader::setVariable(). ( Ignorable by Annotation )

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

71
                $this->setVariable(/** @scrutinizer ignore-type */ $name, $value);
Loading history...
72
            }
73
        }
74
75
        return true;
76
    }
77
78
    /**
79
     * Determine if the line in the file is a comment.
80
     * 
81
     * @param  string  $line
82
     * 
83
     * @return bool
84
     */
85
    protected function isComment(string $line)
86
    {
87
        return strpos(ltrim($line), '#') === 0;
88
    }
89
90
    /**
91
     * Determine if the given line looks like it's setting a variable.
92
     * 
93
     * @param  string  $line
94
     * 
95
     * @return bool
96
     */
97
    protected function checkedLikeSetter(string $line)
98
    {
99
        return strpos($line, '=') !== false;
100
    }
101
102
    /**
103
     * Normalise the given environment variable.
104
     * 
105
     * @param  string  $name
106
     * @param  string  $value
107
     * 
108
     * @return array
109
     */
110
    public function normaliseEnvironmentVariable(string $name, string $value = '')
111
    {
112
        // Split our compound string into it's parts.
113
        if (strpos($name, '=') !== false) {
114
            list($name, $value) = explode('=', $name, 2);
115
        }
116
        
117
        $name  = trim($name);
118
        $value = trim($value);
119
        
120
        // Sanitize the name
121
        $name = $this->sanitizeName($name);
122
        
123
        // Sanitize the value
124
        $value = $this->sanitizeValue($value);
125
        
126
        // Get environment variables
127
        $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\Loader\L...:getResolverVariables(). ( Ignorable by Annotation )

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

127
        $value = $this->getResolverVariables(/** @scrutinizer ignore-type */ $value);
Loading history...
128
        
129
        return [$name, $value];
130
    }
131
132
    /**
133
     * Strips quotes and the optional leading "export " from the environment variable name.
134
     * 
135
     * @param  string  $name
136
     * 
137
     * @return array
138
     */
139
    protected function sanitizeName(string $name)
140
    {
141
        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...
142
    }
143
144
    /**
145
     * Sets the variable into the environment. 
146
     * Will parse the string to look for {name}={value} pattern.
147
     * 
148
     * @param  string  $name
149
     * @param  string|null  $value  (null by default)
150
     * 
151
     * @return void
152
     */
153
    protected function setVariable(string $name, $value = null)
154
    {        
155
        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

155
        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...
156
    }
157
158
    /**
159
     * Strips quotes from the environment variable value.
160
     * 
161
     * This was borrowed from the excellent phpdotenv with very few changes.
162
     * https://github.com/vlucas/phpdotenv
163
     * 
164
     * @param  string  $value
165
     * 
166
     * @return array
167
     * 
168
     * @throws \InvalidArgumentException
169
     */
170
    protected function sanitizeValue($value)
171
    {
172
        if ( ! $value) {
173
            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...
174
        }
175
        
176
        // Does it begin with a quote?
177
        if (strpbrk($value[0], '"\'') !== false) {
178
            // value starts with a quote
179
            $quote = $value[0];
180
181
            $regexPattern = sprintf(
182
					'/^
183
					%1$s          # match a quote at the start of the value
184
					(             # capturing sub-pattern used
185
								  (?:          # we do not need to capture this
186
								   [^%1$s\\\\] # any character other than a quote or backslash
187
								   |\\\\\\\\   # or two backslashes together
188
								   |\\\\%1$s   # or an escaped quote e.g \"
189
								  )*           # as many characters that match the previous rules
190
					)             # end of the capturing sub-pattern
191
					%1$s          # and the closing quote
192
					.*$           # and discard any string after the closing quote
193
					/mx', $quote
194
            );
195
            
196
            $value = preg_replace($regexPattern, '$1', $value);
197
            $value = str_replace("\\$quote", $quote, $value);
198
            $value = str_replace('\\\\', '\\', $value);
199
        } else {
200
            $parts = explode(' #', $value, 2);
201
            $value = trim($parts[0]);
202
            // Unquoted values cannot contain whitespace
203
            if (preg_match('/\s+/', $value) > 0) {
204
                throw new InvalidArgumentException('.env values containing spaces must be surrounded by quotes.');
0 ignored issues
show
Bug introduced by
The type Syscodes\Dotenv\Loader\InvalidArgumentException was not found. Did you mean InvalidArgumentException? If so, make sure to prefix the type with \.
Loading history...
205
            }
206
        }
207
        
208
        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...
209
    }
210
    
211
    /**
212
     * Resolve the nested variables.
213
     * 
214
     * Look for ${varname} patterns in the variable value and replace with an existing
215
     * environment variable.
216
     * 
217
     * This was borrowed from the excellent phpdotenv with very few changes.
218
     * https://github.com/vlucas/phpdotenv
219
     * 
220
     * @param  string  $value
221
     * 
222
     * @return string
223
     */
224
    protected function getResolverVariables(string $value)
225
    {
226
        if (strpos($value, '$') !== false) {
227
            $repository = $this->repository;
228
229
            $value = preg_replace_callback('~\${([a-zA-Z0-9_]+)}~', function ($pattern) use ($repository) {
230
                $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

230
                /** @scrutinizer ignore-call */ 
231
                $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...
231
232
                if (is_null($nestedVariable)) {
233
                    return $pattern[0];
234
                }
235
                
236
                return $nestedVariable;
237
                
238
            }, $value);
239
        }
240
        
241
        return $value;
242
    }
243
}