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 ( 777b8c...ce01dd )
by Miles
02:28
created

FileResource::createImportName()   B

Complexity

Conditions 4
Paths 5

Size

Total Lines 25
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 16
CRAP Score 4

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 25
ccs 16
cts 16
cp 1
rs 8.5806
cc 4
eloc 13
nc 5
nop 1
crap 4
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.3.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 M1\Vars\Traits\ResourceFlagsTrait;
23
use Symfony\Component\Filesystem\Filesystem;
24
25
/**
26
 * File Resource enables interaction with files as resources
27
 *
28
 * @since 0.1.0
29
 */
30
class FileResource extends AbstractResource
31
{
32
    /**
33
     * Basic file interaction logic
34
     */
35
    use FileTrait;
36
37
    use ResourceFlagsTrait;
38
39
    /**
40
     * The env separator for environment replacements
41
     *
42
     * @var string
43
     */
44
    private static $env_separator = '_ENV::';
45
46
    /**
47
     * The filename of the loaded file
48
     *
49
     * @var string
50
     */
51
    private $filename;
52
53
    /**
54
     * The parent ResourceProvider
55
     *
56
     * @var \M1\Vars\Resource\ResourceProvider
57
     */
58
    private $provider;
59
60
    /**
61
     * The raw content from the passed file
62
     *
63
     * @var mixed
64
     */
65
    private $raw_content = array();
66
67
    /**
68
     * The file resource constructor to get and parse the content from files
69
     *
70
     * @param \M1\Vars\Resource\ResourceProvider $provider The parent ResourceProvider
71
     * @param string                             $file     The passed file
72
     */
73 66
    public function __construct(ResourceProvider $provider, $file)
74
    {
75 66
        $this->provider = $provider;
76 66
        $this->vars = $provider->vars;
77
78 66
        $this->makePaths($file);
79 66
        $this->validate();
80
81 66
        $content = $this->loadContent($this->file);
82 59
        $this->raw_content = $content;
83
84 59
        if ($content) {
85 55
            $this->content = $this->searchForResources($content);
86 53
        }
87 57
    }
88
89
    /**
90
     * Make the paths used for the filename variable
91
     *
92
     * @param string $file The passed file
93
     */
94 66
    private function makePaths($file)
95
    {
96 66
        $file = realpath($file);
97
98 66
        $base_path = $this->provider->vars->getBasePath();
99
100 66
        $filesystem = new Filesystem();
101 66
        $abs_path = $filesystem->makePathRelative(
102 66
            $file,
103
            $base_path
104 66
        );
105
106 66
        $this->file = $file;
107 66
        $this->filename = rtrim($abs_path, "/");
108 66
    }
109
110
    /**
111
     * Search for imports in the files and does the replacement variables
112
     *
113
     * @param mixed $content The file content received from the loader
114
     *
115
     * @return array Returns the parsed content
116
     */
117 55
    private function searchForResources($content = array())
118
    {
119 55
        $returned_content = array();
120
121 55
        foreach ($content as $ck => $cv) {
122 55
            $returned_content = $this->parseContent($ck, $cv, $returned_content);
123 53
        }
124
125 53
        return $returned_content;
126
    }
127
128
    /**
129
     * Parses the contents inside the content array
130
     *
131
     * @param mixed $key              The key of the content array
132
     * @param mixed $value            The value of the key
133
     * @param array $returned_content The modified content array to return
134
     *
135
     * @return array Returns the modified content array
136
     */
137 55
    private function parseContent($key, $value, $returned_content)
138
    {
139 55
        if ($key === 'imports' && !is_null($value) && !empty($value)) {
140 46
            $imported_resource = $this->useImports($value);
141
142 44
            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...
143 41
                $returned_content = array_replace_recursive($returned_content, $imported_resource);
144 41
            }
145
146 53
        } elseif (is_array($value)) {
147 11
            $returned_content[$key] = $this->searchForResources($value);
148 11
        } else {
149 51
            $returned_content[$key] = $this->parseText($value);
150
        }
151
152 53
        return $returned_content;
153
    }
154
    /**
155
     * Parses the text for option and environment replacements and replaces the text
156
     *
157
     * @param string $text The text to be parsed
158
     *
159
     * @return string|null The parsed string
160
     */
161 51
    private function parseText($text)
162
    {
163 51
        if (substr($text, 0, 6) === self::$env_separator) {
164 1
            $variable = trim(substr($text, strlen(self::$env_separator)));
165
166 1
            if ($variable) {
167 1
                $value = getenv($variable);
168
169 1
                if ($value) {
170 1
                    return $value;
171
                }
172 1
            }
173 1
        } else {
174 50
            return strtr($text, $this->vars->getVariables());
175
        }
176
177 1
        return null;
178
    }
179
180
    /**
181
     * Use the import arrays to import resources
182
     *
183
     * @param mixed $imports The resources wanting to be imported
184
     *
185
     * @return array The parsed imported resources
186
     */
187 46
    private function useImports($imports)
188
    {
189 46
        $imported_resources = array();
190
191 46
        if ((is_array($imports) && $this->isAssoc($imports)) || is_string($imports)) {
192 40
            $imports = array($imports);
193 40
        }
194
195 46
        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...
196 46
            $imported_resources = $this->processImport($import, $imported_resources);
197 44
        }
198
199 44
        return $imported_resources;
200
    }
201
202
    /**
203
     * Processes the import and gets individual import if set and passes them off to import2Resources()
204
     *
205
     * @param mixed $import The import to be processed
206
     * @param array $imported_resources The array of imported resources
207
     *
208
     * @return array The parsed imported resources
209
     */
210 46
    private function processImport($import, array $imported_resources)
211
    {
212 46
        if (is_array($import) && array_key_exists('resource', $import) && is_array($import['resource'])) {
213 1
            foreach ($import['resource'] as $resource) {
214
                $temp = array(
215 1
                    'resource' => $resource,
216 1
                    'relative' => $this->checkBooleanValue('relative', $import),
217 1
                    'recursive' => $this->checkBooleanValue('recursive', $import),
218 1
                );
219
220 1
                $imported_resources = $this->import2Resource($temp, $imported_resources);
221 1
            }
222 1
        } else {
223 46
            $imported_resources = $this->import2Resource($import, $imported_resources);
224
        }
225
226 44
        return $imported_resources;
227
    }
228
229
    /**
230
     * Creates the resource from the import then imports it
231
     *
232
     * @param array|string $import The string|array to be converted to a resource
233
     * @param array $imported_resources The array of imported resources
234
     *
235
     * @return array The imported resources
236
     */
237 46
    private function import2Resource($import, array $imported_resources)
238
    {
239 46
        $resource = $this->createResource($import);
240
241 44
        if ($resource) {
242 44
            $imported_resources = $this->importResource($resource, $imported_resources);
243 44
        }
244
245 44
        return $imported_resources;
246
    }
247
248
    /**
249
     * Creates resource from the import
250
     *
251
     * @param array|string $import The import to create a resource from
252
     *
253
     * @return array|\M1\Vars\Resource\ResourceProvider The resource of the import
254
     */
255 46
    private function createResource($import)
256
    {
257 46
        if (is_array($import) && array_key_exists('resource', $import)) {
258 10
            $import_resource = $import;
259 10
            $import_resource['relative'] = $this->checkBooleanValue('relative', $import_resource);
260 10
            $import_resource['recursive'] = $this->checkBooleanValue('recursive', $import_resource);
261
262 46
        } elseif (is_string($import)) {
263 38
            $import_resource = array('resource' => $import, 'relative' => true, 'recursive' => true);
264 38
        }
265
266 46
        $import_resource = new ResourceProvider(
267 46
            $this->provider->vars,
268 46
            $this->createImportName($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...
Bug introduced by
It seems like $import_resource['resource'] can also be of type boolean; however, M1\Vars\Resource\FileResource::createImportName() does only seem to accept string, 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...
269 46
            $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...
270 46
            $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...
271 46
        );
272
273 44
        return $import_resource;
274
    }
275
276
    /**
277
     * Creates the correctly formatted resource name with paths
278
     *
279
     * @param string $resource The resource to create the import name for
280
     *
281
     * @return string The parsed resource
282
     */
283 46
    private function createImportName($resource)
284
    {
285 46
        $resource = $this->explodeResourceIfElse($resource);
286 46
        $resource_pieces = array();
287
        
288 46
        foreach ($resource as $r) {
289 46
            $suppress = false;
290
291 46
            if ($this->checkSuppression($r)) {
292 1
                $suppress = true;
293 1
                $r = trim($r, "@");
294 1
            }
295
296 46
            $r = sprintf('%s/%s', dirname($this->file), $r);
297
298 46
            if ($suppress) {
299 1
                $r = "@".$r;
300 1
            }
301
302 46
            $resource_pieces[] = $r;
303 46
        }
304
305 46
        return $this->implodeResourceIfElse($resource_pieces);
306
307
    }
308
309
    /**
310
     * Import resource into the imported resources and merge contents
311
     *
312
     * @param ResourceProvider $provider The new imported resource
313
     * @param array            $imported_resources The imported resources
314
     *
315
     * @return array The modified imported resources
316
     */
317 44
    private function importResource(ResourceProvider $provider, $imported_resources)
318
    {
319 44
        $content = $provider->getContent();
320 44
        $parent_content = $provider->getParentContent();
321
322 44
        if (!empty($content)) {
323 41
            $imported_resources = array_replace_recursive($imported_resources, $content);
324 41
        }
325
326 44
        if (!empty($parent_content)) {
327 7
            $this->provider->addParentContent($parent_content);
328 7
        }
329
330 44
        return $imported_resources;
331
    }
332
333
    /**
334
     * Returns whether the passed array is associative
335
     *
336
     * @param array $array The passed array
337
     *
338
     * @return bool Is the passed array associative
339
     */
340 11
    private function isAssoc(array $array)
341
    {
342 11
        return array_keys($array) !== range(0, count($array) - 1);
343
    }
344
345
    /**
346
     * Checks if the passed boolean value is true or false
347
     *
348
     * @param string $value  The value to check
349
     * @param mixed  $import The passed import
350
     *
351
     * @return bool Returns the value of the boolean
352
     */
353 10
    public function checkBooleanValue($value, $import)
354
    {
355 10
        $value = (isset($import[$value])) ? $import[$value] : true;
356
357 10
        switch (strtolower($value)) {
358 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...
359 10
            case 'false':
360 10
            case 'no':
361 9
                $value = false;
362 9
                break;
363 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...
364 10
            case 'yes':
365 10
            case 'true':
366 10
            default:
367 10
                $value = true;
368 10
                break;
369 10
        }
370
371 10
        return $value;
372
    }
373
374
    /**
375
     * Returns the filename of the resource
376
     *
377
     * @return mixed The filename
378
     */
379 3
    public function getFilename()
380
    {
381 3
        return $this->filename;
382
    }
383
384
    /**
385
     * Returns the raw content of the resource
386
     *
387
     * @return array|mixed The raw content
388
     */
389 1
    public function getRawContent()
390
    {
391 1
        return $this->raw_content;
392
    }
393
}
394