GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — master ( 050f47...66d8e7 )
by Miles
02:10
created

ValueParser::fetchVariable()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 12
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 3

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 12
ccs 7
cts 7
cp 1
rs 9.4286
cc 3
eloc 6
nc 3
nop 4
crap 3
1
<?php
2
3
/**
4
 * This file is part of the m1\env library
5
 *
6
 * (c) m1 <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 *
11
 * @package     m1/env
12
 * @version     1.0.0
13
 * @author      Miles Croxford <[email protected]>
14
 * @copyright   Copyright (c) Miles Croxford <[email protected]>
15
 * @license     http://github.com/m1/env/blob/master/LICENSE.md
16
 * @link        http://github.com/m1/env/blob/master/README.md Documentation
17
 */
18
19
namespace M1\Env\Parser;
20
21
use M1\Env\Exception\ParseException;
22
use M1\Env\Traits\ValueTypeCheckable;
23
24
/**
25
 * The value parser for Env
26
 *
27
 * @since 0.2.0
28
 */
29
class ValueParser extends AbstractParser
30
{
31
    /**
32
     * The trait for checking types
33
     */
34
    use ValueTypeCheckable;
35
36
    /**
37
     * The regex to get variables '$(VARIABLE)' in .env
38
     * Unescaped: ${(.*?)}
39
     *
40
     * @var string REGEX_ENV_VARIABLE
41
     */
42
    const REGEX_ENV_VARIABLE = '\\${(.*?)}';
43
44
    /**
45
     * The regex to get the content between double quote (") strings, ignoring escaped quotes.
46
     * Unescaped: "(?:[^"\\]*(?:\\.)?)*"
47
     *
48
     * @var string REGEX_QUOTE_DOUBLE_STRING
49
     */
50
    const REGEX_QUOTE_DOUBLE_STRING = '"(?:[^\"\\\\]*(?:\\\\.)?)*\"';
51
52
    /**
53
     * The regex to get the content between single quote (') strings, ignoring escaped quotes
54
     * Unescaped: '(?:[^'\\]*(?:\\.)?)*'
55
     *
56
     * @var string REGEX_QUOTE_SINGLE_STRING
57
     */
58
    const REGEX_QUOTE_SINGLE_STRING = "'(?:[^'\\\\]*(?:\\\\.)?)*'";
59
60
    /**
61
     * The bool variants available in .env
62
     *
63
     * @var array $bool_variants
64
     */
65
    private static $bool_variants = array(
0 ignored issues
show
Unused Code introduced by
The property $bool_variants is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
66
        'true', 'false', 'yes', 'no'
67
    );
68
69
    /**
70
     * The map to convert escaped characters into real characters
71
     *
72
     * @var array $character_map
73
     */
74
    private static $character_map = array(
75
        "\\n" => "\n",
76
        "\\\"" => "\"",
77
        '\\\'' => "'",
78
        '\\t' => "\t"
79
    );
80
81
    /**
82
     * The line num of the current value
83
     *
84
     * @var int $line_num
85
     */
86
    private $line_num;
87
88
    /**
89
     * The current parsed values/lines
90
     *
91
     * @var array $lines
92
     */
93
    private $lines;
94
95
    /**
96
     * Parses a .env value
97
     *
98
     * @param string $value    The value to parse
99
     * @param array  $lines    The array of already parsed lines
100
     * @param int    $line_num The line num of the value
101
     *
102
     * @return string|null The parsed key, or null if the key is a comment
103
     */
104 30
    public function parse($value, $lines, $line_num)
105
    {
106 30
        $this->lines = $lines;
107 30
        $this->line_num = $line_num;
108
109 30
        $value = trim($value);
110
111 30
        if ($this->startsWith('#', $value)) {
112 3
            return null;
113
        }
114
115 30
        return $this->parseValue($value);
116
    }
117
118
    /**
119
     * Parses a .env value
120
     *
121
     * @param string $value The value to parse
122
     *
123
     * @return string|null The parsed value, or null if the value is null
124
     */
125 30
    private function parseValue($value)
126
    {
127 30
        $types = array('string', 'bool', 'number', 'null');
128
129 30
        foreach ($types as $type) {
130 30
            $parsed_value = $value;
131
132 30
            if ($type !== 'string') {
133 27
                $parsed_value = $this->stripComments($value);
134 27
            }
135
136 30
            $is_function = sprintf('is%s', ucfirst($type));
137 30
            $parse_function = sprintf('parse%s', ucfirst($type));
138
            
139 30
            if ($this->$is_function($parsed_value)) {
140 21
                return $this->$parse_function($parsed_value);
141
            }
142 27
        }
143
144 24
        return $this->parseUnquotedString($parsed_value);
0 ignored issues
show
Bug introduced by
The variable $parsed_value does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
145
    }
146
147
    /**
148
     * Parses a .env variable
149
     *
150
     * @param string $value         The value to parse
151
     * @param bool   $quoted_string Is the value in a quoted string
152
     *
153
     * @return string The parsed value
154
     */
155 21
    private function parseVariables($value, $quoted_string = false)
156
    {
157 21
        $matches = $this->fetchVariableMatches($value);
158
159 21
        if (is_array($matches)) {
160 6
            if ($this->isVariableClone($value, $matches, $quoted_string)) {
161 6
                return $this->fetchVariable($value, $matches[1][0], $matches, $quoted_string);
162
            }
163
164 3
            $value = $this->doReplacements($value, $matches, $quoted_string);
165 3
        }
166
167 18
        return $value;
168
    }
169
170
    /**
171
     * Get variable matches inside a string
172
     *
173
     * @param string $value The value to parse
174
     *
175
     * @return array The variable matches
176
     */
177 21
    private function fetchVariableMatches($value)
178
    {
179 21
        preg_match_all('/'.self::REGEX_ENV_VARIABLE.'/', $value, $matches);
180
181 21
        if (!is_array($matches) || !isset($matches[0]) || empty($matches[0])) {
182 18
            return false;
183
        }
184
185 6
        return $matches;
186
    }
187
188
    /**
189
     * Parses a .env variable
190
     *
191
     * @param string $value         The value to parse
192
     * @param string $variable_name The variable name to get
193
     * @param array  $matches       The matches of the variables
194
     * @param bool   $quoted_string Is the value in a quoted string
195
     *
196
     * @throws \M1\Env\Exception\ParseException If the variable can not be found
197
     *
198
     * @return string The parsed value
199
     */
200 6
    private function fetchVariable($value, $variable_name, $matches, $quoted_string)
201
    {
202 6
        $this->checkVariableExists($value, $variable_name, $this->lines);
203
204 3
        $replacement = $this->lines[$variable_name];
205
206 3
        if ($this->isBoolInString($replacement, $quoted_string, count($matches[0]))) {
207 3
            $replacement = ($replacement) ? 'true' : 'false';
208 3
        }
209
210 3
        return $replacement;
211
    }
212
213
    /**
214
     * Checks to see if a variable exists
215
     *
216
     * @param string $value    The value to throw an error with if doesn't exist
217
     * @param string $variable The variable name to get
218
     * @param array  $lines    The lines already parsed
219
     *
220
     * @throws \M1\Env\Exception\ParseException If the variable can not be found
221
     */
222 6
    private function checkVariableExists($value, $variable, $lines)
223
    {
224 6
        if (!isset($lines[$variable])) {
225 3
            throw new ParseException(
226 3
                sprintf('Variable has not been defined: %s', $variable),
227 3
                $this->origin_exception,
228 3
                $this->file,
229 3
                $value,
230 3
                $this->line_num
231 3
            );
232
        }
233 3
    }
234
235
    /**
236
     * Checks to see if a variable exists
237
     *
238
     * @param string $value         The value to throw an error with if doesn't exist
239
     * @param array  $matches       The matches of the variables
240
     * @param bool   $quoted_string Is the value in a quoted string
241
     *
242
     * @return string The parsed value
243
     */
244 3
    private function doReplacements($value, $matches, $quoted_string)
245
    {
246 3
        $replacements = array();
247
248 3
        for ($i = 0; $i <= (count($matches[0]) - 1); $i++) {
249 3
            $replacement = $this->fetchVariable($value, $matches[1][$i], $matches, $quoted_string);
250 3
            $replacements[$matches[0][$i]] = $replacement;
251 3
        }
252
253 3
        if (!empty($replacements)) {
254 3
            $value = strtr($value, $replacements);
255 3
        }
256
257 3
        return $value;
258
    }
259
260
    /**
261
     * Parses a .env string
262
     *
263
     * @param string $value    The value to parse
264
     *
265
     * @return string The parsed string
266
     */
267 12
    private function parseString($value)
268
    {
269 12
        $regex = self::REGEX_QUOTE_DOUBLE_STRING;
270 12
        $symbol = '"';
271
272 12
        if ($this->startsWith('\'', $value)) {
273 9
            $regex =  self::REGEX_QUOTE_SINGLE_STRING;
274 9
            $symbol = "'";
275 9
        }
276
277 12
        $matches = $this->fetchStringMatches($value, $regex, $symbol);
278
279 9
        $value = trim($matches[0], $symbol);
280 9
        $value = strtr($value, self::$character_map);
281
282 9
        return $this->parseVariables($value, true);
283
    }
284
285
    /**
286
     * Gets the regex matches in the string
287
     *
288
     * @param string $regex    The regex to use
289
     * @param string $value    The value to parse
290
     * @param string $symbol   The symbol we're parsing for
291
     *
292
     * @throws \M1\Env\Exception\ParseException If the string has a missing end quote
293
     *
294
     * @return array The matches based on the regex and the value
295
     */
296 12
    private function fetchStringMatches($value, $regex, $symbol)
297
    {
298 12
        if (!preg_match('/'.$regex.'/', $value, $matches)) {
299 3
            throw new ParseException(
300 3
                sprintf('Missing end %s quote', $symbol),
301 3
                $this->origin_exception,
302 3
                $this->file,
303 3
                $value,
304 3
                $this->line_num
305 3
            );
306
        }
307
308 9
        return $matches;
309
    }
310
    /**
311
     * Parses a .env null value
312
     *
313
     * @param string $value The value to parse
314
     *
315
     * @return null Null value
316
     */
317 3
    private function parseNull($value)
0 ignored issues
show
Unused Code introduced by
The parameter $value is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
318
    {
319 3
        return null;
320
    }
321
322
    /**
323
     * Parses a .env unquoted string
324
     *
325
     * @param string $value The value to parse
326
     *
327
     * @return string The parsed string
328
     */
329 24
    private function parseUnquotedString($value)
330
    {
331 24
        if ($value == "") {
332 3
            return null;
333
        }
334
335 21
        return $this->parseVariables($value);
336
    }
337
338
    /**
339
     * Parses a .env bool
340
     *
341
     * @param string $value The value to parse
342
     *
343
     * @return bool The parsed bool
344
     */
345 6
    private function parseBool($value)
346
    {
347 6
        $value = strtolower($value);
348
349 6
        return $value === "true" || $value === "yes";
350
    }
351
352
    /**
353
     * Parses a .env number
354
     *
355
     * @param string $value The value to parse
356
     *
357
     * @return int|float The parsed bool
358
     */
359 6
    private function parseNumber($value)
360
    {
361 6
        if (strpos($value, '.') !== false) {
362 6
            return (float) $value;
363
        }
364
365 6
        return (int) $value;
366
    }
367
368
    /**
369
     * Strips comments from a value
370
     *
371
     * @param string $value The value to strip comments from
372
     *
373
     * @return string value
374
     */
375 27
    private function stripComments($value)
376
    {
377 27
        return trim(explode("#", $value, 2)[0]);
378
    }
379
}
380