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 ( 3094b9...9c33be )
by Miles
06:34
created

FileResource::checkBooleanValue()   B

Complexity

Conditions 8
Paths 14

Size

Total Lines 20
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 16
CRAP Score 8

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 20
ccs 16
cts 16
cp 1
rs 7.7778
cc 8
eloc 15
nc 14
nop 2
crap 8
1
<?php
2
3
/**
4
 * This file is part of the m1\vars library
5
 *
6
 * Copyright (c) Miles Croxford <[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/vars
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/vars/blob/master/LICENSE
16
 * @link        http://github.com/m1/vars/blob/master/README.MD Documentation
17
 */
18
19
namespace M1\Vars\Resource;
20
21
use M1\Vars\Traits\FileTrait;
22
use Symfony\Component\Filesystem\Filesystem;
23
24
/**
25
 * File Resource enables interaction with files as resources
26
 *
27
 * @since 0.1.0
28
 */
29
class FileResource extends AbstractResource
30
{
31
    /**
32
     * Basic file interaction logic
33
     */
34
    use FileTrait;
35
36
    /**
37
     * The env separator for environment replacements
38
     *
39
     * @var string
40
     */
41
    private static $env_separator = '_ENV::';
42
43
    /**
44
     * The filename of the loaded file
45
     *
46
     * @var string
47
     */
48
    private $filename;
49
50
    /**
51
     * The parent ResourceProvider
52
     *
53
     * @var \M1\Vars\Resource\ResourceProvider
54
     */
55
    private $provider;
56
57
    /**
58
     * The raw content from the passed file
59
     *
60
     * @var mixed
61
     */
62
    private $raw_content = array();
63
64
    /**
65
     * The file resource constructor to get and parse the content from files
66
     *
67
     * @param \M1\Vars\Resource\ResourceProvider $provider The parent ResourceProvider
68
     * @param string                             $file     The passed file
69
     */
70 62
    public function __construct(ResourceProvider $provider, $file)
71
    {
72 62
        $this->provider = $provider;
73 62
        $this->vars = $provider->vars;
74
75 62
        $this->makePaths($file);
76 62
        $this->validate();
77
78 62
        $content = $this->loadContent($this->file);
79 54
        $this->raw_content = $content;
80
81 54
        if ($content) {
82 50
            $this->content = $this->searchForResources($content);
83 49
        }
84 53
    }
85
86
    /**
87
     * Make the paths used for the filename variable
88
     *
89
     * @param string $file The passed file
90
     */
91 62
    private function makePaths($file)
92
    {
93 62
        $file = realpath($file);
94
95 62
        $base_path = $this->provider->vars->getBasePath();
96
97 62
        $filesystem = new Filesystem();
98 62
        $abs_path = $filesystem->makePathRelative(
99 62
            $file,
100
            $base_path
101 62
        );
102
103 62
        $this->file = $file;
104 62
        $this->filename = rtrim($abs_path, "/");
105 62
    }
106
107
    /**
108
     * Search for imports in the files and does the replacement variables
109
     *
110
     * @param mixed $content The file content received from the loader
111
     *
112
     * @return array Returns the parsed content
113
     */
114 50
    private function searchForResources($content = array())
115
    {
116 50
        $returned_content = array();
117
118 50
        foreach ($content as $ck => $cv) {
119 50
            $returned_content = $this->parseContent($ck, $cv, $returned_content);
120 49
        }
121
122 49
        return $returned_content;
123
    }
124
125
    /**
126
     * Parses the contents inside the content array
127
     *
128
     * @param mixed $key              The key of the content array
129
     * @param mixed $value            The value of the key
130
     * @param array $returned_content The modified content array to return
131
     *
132
     * @return array Returns the modified content array
133
     */
134 50
    private function parseContent($key, $value, $returned_content)
135
    {
136 50
        if ($key === 'imports' && !is_null($value) && !empty($value)) {
137 41
            $imported_resource = $this->useImports($value);
138
139 40
            if ($imported_resource) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $imported_resource of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
140 38
                $returned_content = array_replace_recursive($returned_content, $imported_resource);
141 38
            }
142
143 49
        } elseif (is_array($value)) {
144 11
            $returned_content[$key] = $this->searchForResources($value);
145 11
        } else {
146 48
            $returned_content[$key] = $this->parseText($value);
147
        }
148
149 49
        return $returned_content;
150
    }
151
    /**
152
     * Parses the text for option and environment replacements and replaces the text
153
     *
154
     * @param string $text The text to be parsed
155
     *
156
     * @return string|null The parsed string
157
     */
158 48
    private function parseText($text)
159
    {
160 48
        if (substr($text, 0, 6) === self::$env_separator) {
161 1
            $variable = trim(substr($text, strlen(self::$env_separator)));
162
163 1
            if ($variable) {
164 1
                $value = getenv($variable);
165
166 1
                if ($value) {
167 1
                    return $value;
168
                }
169 1
            }
170 1
        } else {
171 47
            return strtr($text, $this->vars->getVariables());
172
        }
173
174 1
        return null;
175
    }
176
177
    /**
178
     * Use the import arrays to import resources
179
     *
180
     * @param mixed $imports The resources wanting to be imported
181
     *
182
     * @return array The parsed imported resources
183
     */
184 41
    private function useImports($imports)
185
    {
186 41
        $imported_resources = array();
187
188 41
        if ((is_array($imports) && $this->isAssoc($imports)) || is_string($imports)) {
189 35
            $imports = array($imports);
190 35
        }
191
192 41
        foreach ($imports as $import) {
0 ignored issues
show
Bug introduced by
The expression $imports of type object|integer|double|null|array|boolean is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
193 41
            $imported_resources = $this->processImport($import, $imported_resources);
194 40
        }
195
196 40
        return $imported_resources;
197
    }
198
199
    /**
200
     * Processes the import and gets individual import if set and passes them off to import2Resources()
201
     *
202
     * @param mixed $import The import to be processed
203
     * @param array $imported_resources The array of imported resources
204
     *
205
     * @return array The parsed imported resources
206
     */
207 41
    private function processImport($import, array $imported_resources)
208
    {
209 41
        if (is_array($import) && array_key_exists('resource', $import) && is_array($import['resource'])) {
210 1
            foreach ($import['resource'] as $resource) {
211
                $temp = array(
212 1
                    'resource' => $resource,
213 1
                    'relative' => $this->checkBooleanValue('relative', $import),
214 1
                    'recursive' => $this->checkBooleanValue('recursive', $import),
215 1
                );
216
217 1
                $imported_resources = $this->import2Resource($temp, $imported_resources);
218 1
            }
219 1
        } else {
220 41
            $imported_resources = $this->import2Resource($import, $imported_resources);
221
        }
222
223 40
        return $imported_resources;
224
    }
225
226
    /**
227
     * Creates the resource from the import then imports it
228
     *
229
     * @param array|string $import The string|array to be converted to a resource
230
     * @param array $imported_resources The array of imported resources
231
     *
232
     * @return array The imported resources
233
     */
234 41
    private function import2Resource($import, array $imported_resources)
235
    {
236 41
        $resource = $this->createResource($import);
237
238 40
        if ($resource) {
239 40
            $imported_resources = $this->importResource($resource, $imported_resources);
240 40
        }
241
242 40
        return $imported_resources;
243
    }
244
245
    /**
246
     * Creates resource from the import
247
     *
248
     * @param array|string $import The import to create a resource from
249
     *
250
     * @return array|\M1\Vars\Resource\ResourceProvider The resource of the import
251
     */
252 41
    private function createResource($import)
253
    {
254 41
        if (is_array($import) && array_key_exists('resource', $import)) {
255 10
            $import_resource = $import;
256 10
            $import_resource['relative'] = $this->checkBooleanValue('relative', $import_resource);
257 10
            $import_resource['recursive'] = $this->checkBooleanValue('recursive', $import_resource);
258
259 41
        } elseif (is_string($import)) {
260 33
            $import_resource = array('resource' => $import, 'relative' => true, 'recursive' => true);
261 33
        }
262
263 41
        $import_resource = new ResourceProvider(
264 41
            $this->provider->vars,
265 41
            sprintf('%s/%s', dirname($this->file), $import_resource['resource']),
0 ignored issues
show
Bug introduced by
The variable $import_resource 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...
266 41
            $import_resource['relative'],
0 ignored issues
show
Bug introduced by
It seems like $import_resource['relative'] can also be of type string; however, M1\Vars\Resource\ResourceProvider::__construct() does only seem to accept boolean, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
267 41
            $import_resource['recursive']
0 ignored issues
show
Bug introduced by
It seems like $import_resource['recursive'] can also be of type string; however, M1\Vars\Resource\ResourceProvider::__construct() does only seem to accept boolean, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
268 41
        );
269
270 40
        return $import_resource;
271
    }
272
273
    /**
274
     * Import resource into the imported resources and merge contents
275
     *
276
     * @param ResourceProvider $provider The new imported resource
277
     * @param array            $imported_resources The imported resources
278
     *
279
     * @return array The modified imported resources
280
     */
281 40
    private function importResource(ResourceProvider $provider, $imported_resources)
282
    {
283 40
        $content = $provider->getContent();
284 40
        $parent_content = $provider->getParentContent();
285
286 40
        if (!empty($content)) {
287 38
            $imported_resources = array_replace_recursive($imported_resources, $content);
288 38
        }
289
290 40
        if (!empty($parent_content)) {
291 7
            $this->provider->addParentContent($parent_content);
292 7
        }
293
294 40
        return $imported_resources;
295
    }
296
297
    /**
298
     * Returns whether the passed array is associative
299
     *
300
     * @param array $array The passed array
301
     *
302
     * @return bool Is the passed array associative
303
     */
304 11
    private function isAssoc(array $array)
305
    {
306 11
        return array_keys($array) !== range(0, count($array) - 1);
307
    }
308
309
    /**
310
     * Checks if the passed boolean value is true or false
311
     *
312
     * @param string $value  The value to check
313
     * @param mixed  $import The passed import
314
     *
315
     * @return bool Returns the value of the boolean
316
     */
317 10
    public function checkBooleanValue($value, $import)
318
    {
319 10
        $value = (isset($import[$value])) ? $import[$value] : true;
320
321 10
        switch (strtolower($value)) {
322 10
            case false:
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing strtolower($value) of type string to the boolean false. If you are specifically checking for an empty string, consider using the more explicit === '' instead.
Loading history...
323 10
            case 'false':
324 10
            case 'no':
325 9
                $value = false;
326 9
                break;
327 10
            case true:
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing strtolower($value) of type string to the boolean true. If you are specifically checking for a non-empty string, consider using the more explicit !== '' instead.
Loading history...
328 10
            case 'yes':
329 10
            case 'true':
330 10
            default:
331 10
                $value = true;
332 10
                break;
333 10
        }
334
335 10
        return $value;
336
    }
337
338
    /**
339
     * Returns the filename of the resource
340
     *
341
     * @return mixed The filename
342
     */
343 3
    public function getFilename()
344
    {
345 3
        return $this->filename;
346
    }
347
348
    /**
349
     * Returns the raw content of the resource
350
     *
351
     * @return array|mixed The raw content
352
     */
353 1
    public function getRawContent()
354
    {
355 1
        return $this->raw_content;
356
    }
357
}
358