Test Failed
Pull Request — master (#68)
by Björn
07:42
created

IncludeCollector::getIncluded()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 11
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 11
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 7
nc 3
nop 1
1
<?php
2
3
namespace ComposerRequireChecker\NodeVisitor;
4
5
use FilesystemIterator;
6
use InvalidArgumentException;
7
use PhpParser\Node;
8
use PhpParser\Node\Expr;
9
use PhpParser\Node\Expr\BinaryOp\Concat;
10
use PhpParser\Node\Expr\ConstFetch;
11
use PhpParser\Node\Expr\Include_;
12
use PhpParser\Node\Expr\Variable;
13
use PhpParser\Node\Scalar\MagicConst\Dir;
14
use PhpParser\Node\Scalar\MagicConst\File;
15
use PhpParser\Node\Scalar\String_;
16
use PhpParser\NodeVisitorAbstract;
17
use RecursiveDirectoryIterator;
18
use RecursiveIteratorIterator;
19
20
final class IncludeCollector extends NodeVisitorAbstract
21
{
22
    /**
23
     * @var Expr[]
24
     */
25
    private $included = [];
26
27
    /**
28
     * {@inheritDoc}
29
     */
30
    public function beforeTraverse(array $nodes)
31
    {
32
        $this->included = [];
33
        return parent::beforeTraverse($nodes);
0 ignored issues
show
Bug introduced by
Are you sure the usage of parent::beforeTraverse($nodes) targeting PhpParser\NodeVisitorAbstract::beforeTraverse() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
34
    }
35
36
    /**
37
     * @param string $file
38
     * @return string[]
39
     */
40
    public function getIncluded(string $file): array
41
    {
42
        $included = [];
43
        foreach ($this->included as $exp) {
44
            try {
45
                $this->computePath($included, $this->processIncludePath($exp, $file), $file);
46
            } catch(InvalidArgumentException $x) {
47
                var_dump($x->getMessage());
0 ignored issues
show
Security Debugging Code introduced by
var_dump($x->getMessage()) looks like debug code. Are you sure you do not want to remove it?
Loading history...
48
            }
49
        }
50
        return $included;
51
    }
52
53
    /**
54
     * @param array $included
55
     * @param string $path
56
     * @param string $self
57
     * @return void
58
     */
59
    private function computePath(array &$included, string $path, string $self)
60
    {
61
        if (!preg_match('#^([A-Z]:)?/#i', str_replace('\\', '/', $path))) {
62
            $path = dirname($self).'/'.$path;
63
        }
64
        if (false === strpos($path, '{var}')) {
65
            $included[] = $path;
66
            return;
67
        }
68
        $parts = explode('{var}', $path);
69
        $regex = [];
70
        foreach($parts as $part) {
71
            $regex[] = preg_quote(str_replace('\\', '/', $part), '/');
72
        }
73
        $regex = '/^'.implode('.+', $regex).'$/';
74
        $self = str_replace('\\', '/', $self);
75
        foreach (new RecursiveIteratorIterator(
76
            new RecursiveDirectoryIterator(
77
                $parts[0],
78
                FilesystemIterator::CURRENT_AS_PATHNAME | FilesystemIterator::SKIP_DOTS
79
            )
80
        ) as $file) {
81
            $rfile = str_replace('\\', '/', $file);
82
            if ($rfile !== $self && preg_match('/\\.php$/i', $rfile) && preg_match($regex, $rfile)) {
83
                $included[] = $file;
84
            }
85
        }
86
    }
87
88
    /**
89
     * @param string|Exp $exp
0 ignored issues
show
Bug introduced by
The type ComposerRequireChecker\NodeVisitor\Exp was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
90
     * @param string $file
91
     * @return string
92
     * @throws InvalidArgumentException
93
     */
94
    private function processIncludePath($exp, string $file): string
95
    {
96
        if (is_string($exp)) {
97
            return $exp;
98
        }
99
        if ($exp instanceof Concat) {
100
            return $this->processIncludePath($exp->left, $file).$this->processIncludePath($exp->right, $file);
101
        }
102
        if ($exp instanceof Dir) {
103
            return dirname($file);
104
        }
105
        if ($exp instanceof File) {
106
            return $file;
107
        }
108
        if ($exp instanceof ConstFetch && $exp->name === 'DIRECTORY_SEPARATOR') {
0 ignored issues
show
introduced by
The condition $exp->name === 'DIRECTORY_SEPARATOR' is always false. If $exp->name === 'DIRECTORY_SEPARATOR' can have other possible types, add them to vendor/nikic/php-parser/...ode/Expr/ConstFetch.php:10
Loading history...
109
            return DIRECTORY_SEPARATOR;
110
        }
111
        if ($exp instanceof String_) {
112
            return $exp->value;
113
        }
114
        if ($exp instanceof Variable || $exp instanceof ConstFetch) {
115
            return '{var}';
116
        }
117
        throw new InvalidArgumentException('can\'t yet handle '.$exp->getType());
118
    }
119
120
    /**
121
     * {@inheritDoc}
122
     */
123
    public function enterNode(Node $node)
124
    {
125
        if ($node instanceof Include_) {
126
            $this->included[] = $node->expr;
127
        }
128
    }
129
}
130