Passed
Pull Request — master (#68)
by Björn
03:16
created

IncludeCollector::computePath()   B

Complexity

Conditions 8
Paths 14

Size

Total Lines 25
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 72

Importance

Changes 0
Metric Value
eloc 19
dl 0
loc 25
ccs 0
cts 19
cp 0
rs 8.4444
c 0
b 0
f 0
cc 8
nc 14
nop 3
crap 72
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 1
    public function beforeTraverse(array $nodes)
31
    {
32 1
        $this->included = [];
33 1
        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 1
    public function getIncluded(string $file): array
41
    {
42 1
        $included = [];
43 1
        foreach ($this->included as $exp) {
44
            try {
45
                $this->computePath($included, $this->processIncludePath($exp, $file), $file);
46
            } catch(InvalidArgumentException $exception) {
47
                // not sure there's anything sensible to do here
48
            }
49
        }
50 1
        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|Expr $exp
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') {
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 1
    public function enterNode(Node $node)
124
    {
125 1
        if ($node instanceof Include_) {
126
            $this->included[] = $node->expr;
127
        }
128 1
    }
129
}
130