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 ( c440df...acd042 )
by Miles
04:28
created

ValueParser   A

Complexity

Total Complexity 35

Size/Duplication

Total Lines 318
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 3

Test Coverage

Coverage 100%

Importance

Changes 2
Bugs 0 Features 0
Metric Value
wmc 35
c 2
b 0
f 0
lcom 1
cbo 3
dl 0
loc 318
ccs 98
cts 98
cp 1
rs 9

11 Methods

Rating   Name   Duplication   Size   Complexity  
A parse() 0 13 2
A parseValue() 0 21 4
B parseVariables() 0 23 5
A fetchVariableMatches() 0 10 4
B fetchVariable() 0 22 6
B parseString() 0 25 3
A parseNull() 0 4 1
A parseUnquotedString() 0 8 2
B parseBool() 0 16 5
A parseNumber() 0 8 2
A stripComments() 0 4 1
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     0.2.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
            $replacements = array();
161
162 6
            if ($this->isVariableClone($value, $matches, $quoted_string)) {
163 6
                return $this->fetchVariable($value, $matches[1][0], $matches, $quoted_string);
164
            }
165
166 3
            for ($i = 0; $i <= (count($matches[0]) - 1); $i++) {
167 3
                $replacement = $this->fetchVariable($value, $matches[1][$i], $matches, $quoted_string);
168 3
                $replacements[$matches[0][$i]] = $replacement;
169 3
            }
170
171 3
            if (!empty($replacements)) {
172 3
                $value = strtr($value, $replacements);
173 3
            }
174 3
        }
175
176 18
        return $value;
177
    }
178
179
    /**
180
     * Get variable matches inside a string
181
     *
182
     * @param string $value The value to parse
183
     *
184
     * @return array The variable matches
185
     */
186 21
    private function fetchVariableMatches($value)
187
    {
188 21
        preg_match_all('/'.self::REGEX_ENV_VARIABLE.'/', $value, $matches);
189
190 21
        if (!is_array($matches) || !isset($matches[0]) || empty($matches[0])) {
191 18
            return false;
192
        }
193
194 6
        return $matches;
195
    }
196
197
    /**
198
     * Parses a .env variable
199
     *
200
     * @param string $value         The value to parse
201
     * @param string $variable_name The variable name to get
202
     * @param array  $matches       The matches of the variables
203
     * @param bool   $quoted_string Is the value in a quoted string
204
     *
205
     * @throws \M1\Env\Exception\ParseException If the variable can not be found
206
     *
207
     * @return string The parsed value
208
     */
209 6
    private function fetchVariable($value, $variable_name, $matches, $quoted_string)
210
    {
211
212 6
        if (!isset($this->lines[$variable_name])) {
213 3
            throw new ParseException(
214 3
                sprintf('Variable has not been defined: %s', $variable_name),
215 3
                $this->origin_exception,
216 3
                $this->file,
217 3
                $value,
218 3
                $this->line_num
219 3
            );
220
        }
221
222 3
        $replacement = $this->lines[$variable_name];
223
224 3
        if (is_bool($replacement) &&
225 3
            ($quoted_string || count($matches[0]) >= 2)) {
226 3
            $replacement = ($replacement) ? 'true' : 'false';
227 3
        }
228
229 3
        return $replacement;
230
    }
231
232
    /**
233
     * Parses a .env string
234
     *
235
     * @param string $value    The value to parse
236
     *
237
     * @throws \M1\Env\Exception\ParseException If the string has a missing end quote
238
     *
239
     * @return string The parsed string
240
     */
241 12
    private function parseString($value)
242
    {
243 12
        if ($this->startsWith('\'', $value)) {
244 9
            $regex =  self::REGEX_QUOTE_SINGLE_STRING;
245 9
            $symbol = "'";
246 9
        } else {
247 6
            $regex = self::REGEX_QUOTE_DOUBLE_STRING;
248 6
            $symbol = '"';
249
        }
250
251 12
        if (!preg_match('/'.$regex.'/', $value, $matches)) {
252 3
            throw new ParseException(
253 3
                sprintf('Missing end %s quote', $symbol),
254 3
                $this->origin_exception,
255 3
                $this->file,
256 3
                $value,
257 3
                $this->line_num
258 3
            );
259
        }
260
261 9
        $value = trim($matches[0], $symbol);
262 9
        $value = strtr($value, self::$character_map);
263
264 9
        return $this->parseVariables($value, true);
265
    }
266
267
    /**
268
     * Parses a .env null value
269
     *
270
     * @param string $value The value to parse
271
     *
272
     * @return null Null value
273
     */
274 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...
275
    {
276 3
        return null;
277
    }
278
279
    /**
280
     * Parses a .env unquoted string
281
     *
282
     * @param string $value The value to parse
283
     *
284
     * @return string The parsed string
285
     */
286 24
    private function parseUnquotedString($value)
287
    {
288 24
        if ($value == "") {
289 3
            return null;
290
        }
291
292 21
        return $this->parseVariables($value);
293
    }
294
295
    /**
296
     * Parses a .env bool
297
     *
298
     * @param string $value The value to parse
299
     *
300
     * @return bool The parsed bool
301
     */
302 6
    private function parseBool($value)
303
    {
304 6
        switch (strtolower($value)) {
305 6
            case 'true':
306 6
            case 'yes':
307 6
                $value = true;
308 6
                break;
309 3
            case 'false':
310 3
            case 'no':
311 3
            default:
312 3
                $value = false;
313 3
                break;
314 6
        }
315
316 6
        return $value;
317
    }
318
319
    /**
320
     * Parses a .env number
321
     *
322
     * @param string $value The value to parse
323
     *
324
     * @return int|float The parsed bool
325
     */
326 6
    private function parseNumber($value)
327
    {
328 6
        if (strpos($value, '.') !== false) {
329 6
            return (float) $value;
330
        }
331
332 6
        return (int) $value;
333
    }
334
335
    /**
336
     * Strips comments from a value
337
     *
338
     * @param string $value The value to strip comments from
339
     *
340
     * @return string value
341
     */
342 27
    private function stripComments($value)
343
    {
344 27
        return trim(explode("#", $value, 2)[0]);
345
    }
346
}