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

IncludeCollector::computePath()   B

Complexity

Conditions 8
Paths 14

Size

Total Lines 25
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 19
CRAP Score 8

Importance

Changes 0
Metric Value
eloc 19
dl 0
loc 25
rs 8.4444
c 0
b 0
f 0
ccs 19
cts 19
cp 1
cc 8
nc 14
nop 3
crap 8
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 2
    public function beforeTraverse(array $nodes)
31
    {
32 2
        $this->included = [];
33 2
        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 9
    public function getIncluded(string $file): array
41
    {
42 9
        $included = [];
43 9
        foreach ($this->included as $exp) {
44
            try {
45 7
                $this->computePath($included, $this->processIncludePath($exp, $file), $file);
46 7
            } catch (InvalidArgumentException $exception) {
47
                // not sure there's anything sensible to do here
48
            }
49
        }
50 9
        return $included;
51
    }
52
53
    /**
54
     * @param array $included
55
     * @param string $path
56
     * @param string $self
57
     * @return void
58
     */
59 7
    private function computePath(array &$included, string $path, string $self)
60
    {
61 7
        if (!preg_match('#^([A-Z]:)?/#i', str_replace('\\', '/', $path))) {
62 4
            $path = dirname($self) . '/' . $path;
63
        }
64 7
        if (false === strpos($path, '{var}')) {
65 5
            $included[] = $path;
66 5
            return;
67
        }
68 2
        $parts = explode('{var}', $path);
69 2
        $regex = [];
70 2
        foreach ($parts as $part) {
71 2
            $regex[] = preg_quote(str_replace('\\', '/', $part), '/');
72
        }
73 2
        $regex = '/^' . implode('.+', $regex) . '$/';
74 2
        $self = str_replace('\\', '/', $self);
75 2
        foreach (new RecursiveIteratorIterator(
76 2
            new RecursiveDirectoryIterator(
77 2
                $parts[0],
78 2
                FilesystemIterator::CURRENT_AS_PATHNAME | FilesystemIterator::SKIP_DOTS
79
            )
80
        ) as $file) {
81 2
            $rfile = str_replace('\\', '/', $file);
82 2
            if ($rfile !== $self && preg_match('/\\.php$/i', $rfile) && preg_match($regex, $rfile)) {
83 2
                $included[] = $file;
84
            }
85
        }
86 2
    }
87
88
    /**
89
     * @param string|Expr $exp
90
     * @param string $file
91
     * @return string
92
     * @throws InvalidArgumentException
93
     */
94 7
    private function processIncludePath($exp, string $file): string
95
    {
96 7
        if (is_string($exp)) {
97 2
            return $exp;
98
        }
99 5
        if ($exp instanceof Concat) {
100 4
            return $this->processIncludePath($exp->left, $file) . $this->processIncludePath($exp->right, $file);
101
        }
102 5
        if ($exp instanceof Dir) {
103 1
            return dirname($file);
104
        }
105 5
        if ($exp instanceof File) {
106 1
            return $file;
107
        }
108 5
        if ($exp instanceof ConstFetch && "$exp->name" === 'DIRECTORY_SEPARATOR') {
109
            return DIRECTORY_SEPARATOR;
110
        }
111 5
        if ($exp instanceof String_) {
112 5
            return $exp->value;
113
        }
114 2
        if ($exp instanceof Variable || $exp instanceof ConstFetch) {
115 2
            return '{var}';
116
        }
117
        throw new InvalidArgumentException('can\'t yet handle ' . $exp->getType());
118
    }
119
120
    /**
121
     * {@inheritDoc}
122
     */
123 6
    public function enterNode(Node $node)
124
    {
125 6
        if ($node instanceof Include_) {
126 4
            $this->included[] = $node->expr;
127
        }
128 6
    }
129
}
130