Test Failed
Pull Request — master (#22)
by Alexander
02:39
created

PhpParserClassifier::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 7
c 1
b 0
f 0
nc 1
nop 1
dl 0
loc 9
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Classifier;
6
7
use PhpParser\Node;
8
use PhpParser\NodeFinder;
9
use PhpParser\NodeTraverser;
10
use PhpParser\NodeVisitor\NameResolver;
11
use PhpParser\ParserFactory;
12
use Symfony\Component\Finder\Finder;
13
14
final class PhpParserClassifier
15
{
16
    /**
17
     * @var string[]
18
     */
19
    private array $interfaces = [];
20
    /**
21
     * @var string[]
22
     */
23
    private array $attributes = [];
24
    private \PhpParser\Parser $parser;
25
    private NodeTraverser $traverser;
26
    private NodeFinder $nodeFinder;
27
28
    public function __construct(private string $directory)
29
    {
30
        $traverser = new NodeTraverser;
31
        $nameResolver = new NameResolver();
32
        $traverser->addVisitor($nameResolver);
33
        $this->parser = (new ParserFactory())
34
            ->create(ParserFactory::PREFER_PHP7);
35
        $this->traverser = $traverser;
36
        $this->nodeFinder = new NodeFinder();
37
38
    }
39
40
    public function withInterface(string|array $interfaces): self
41
    {
42
        $new = clone $this;
43
        foreach ((array) $interfaces as $interface) {
44
            $new->interfaces[] = $interface;
45
        }
46
        return $new;
47
    }
48
49
    public function withAttribute(string|array $attributes): self
50
    {
51
        $new = clone $this;
52
        foreach ((array) $attributes as $attribute) {
53
            $new->attributes[] = $attribute;
54
        }
55
        return $new;
56
    }
57
58
    public function find(): iterable
59
    {
60
        $countInterfaces = count($this->interfaces);
61
        $countAttributes = count($this->attributes);
62
63
        if ($countInterfaces === 0 && $countAttributes === 0) {
64
            return [];
65
        }
66
67
        $files = (new Finder())
68
            ->in($this->directory)
69
            ->name('*.php')
70
            ->sortByName()
71
            ->files();
72
73
        $interfaces = $this->interfaces;
74
        $attributes = $this->attributes;
75
76
        foreach ($files as $file) {
77
            $nodes = $this->parser->parse(file_get_contents($file->getRealPath()));
78
            $this->traverser->traverse($nodes);
0 ignored issues
show
Bug introduced by
It seems like $nodes can also be of type null; however, parameter $nodes of PhpParser\NodeTraverser::traverse() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

78
            $this->traverser->traverse(/** @scrutinizer ignore-type */ $nodes);
Loading history...
79
            /**
80
             * @var $result Node\Stmt\Class_[]
81
             */
82
            $result = $this->nodeFinder->find(
83
                $nodes,
84
                function (Node $node) use ($interfaces, $countInterfaces, $attributes, $countAttributes) {
85
                    if (!$node instanceof Node\Stmt\Class_) {
86
                        return false;
87
                    }
88
                    $interfacesNames = array_map(fn (Node\Name $name) => $name->toString(), $node->implements);
89
                    if (count(array_intersect($interfaces, $interfacesNames)) !== $countInterfaces) {
90
                        return false;
91
                    }
92
                    $attributesNames = [];
93
                    foreach ($node->attrGroups as $attrGroup) {
94
                        foreach ($attrGroup->attrs as $attr) {
95
                            $attributesNames[] = $attr->name->toString();
96
                        }
97
                    }
98
                    if (count(array_intersect($attributes, $attributesNames)) !== $countAttributes) {
99
                        return false;
100
                    }
101
                    return true;
102
                }
103
            );
104
            foreach ($result as $class) {
105
                yield $class->namespacedName->toString();
0 ignored issues
show
Bug introduced by
The method toString() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

105
                yield $class->namespacedName->/** @scrutinizer ignore-call */ toString();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
106
            }
107
        }
108
    }
109
110
}
111