Passed
Push — master ( 50f068...bac002 )
by Satoshi
02:14
created

DependencyDumper::notifyAnalysedFileException()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 2
nc 2
nop 1
dl 0
loc 4
rs 10
c 0
b 0
f 0
1
<?php
2
declare(strict_types=1);
3
4
namespace DependencyAnalyzer;
5
6
use DependencyAnalyzer\DependencyDumper\CollectDependenciesVisitor;
7
use DependencyAnalyzer\DependencyDumper\ObserverInterface;
8
use DependencyAnalyzer\DependencyGraph\ClassLikeAggregate;
9
use DependencyAnalyzer\Exceptions\AnalysedFileException;
10
use DependencyAnalyzer\Exceptions\UnexpectedException;
11
use PHPStan\AnalysedCodeException;
12
use PHPStan\Analyser\ScopeContext;
13
use PHPStan\Parser\Parser;
14
use PHPStan\Analyser\NodeScopeResolver;
15
use PHPStan\Analyser\ScopeFactory;
16
use PHPStan\DependencyInjection\ContainerFactory;
17
use PHPStan\File\FileFinder;
18
use PHPStan\ShouldNotHappenException;
19
20
class DependencyDumper
21
{
22
    /**
23
     * @var CollectDependenciesVisitor
24
     */
25
    protected $collectNodeVisitor;
26
27
    /**
28
     * @var NodeScopeResolver
29
     */
30
    protected $nodeScopeResolver;
31
32
    /**
33
     * @var Parser
34
     */
35
    protected $parser;
36
37
    /**
38
     * @var ScopeFactory
39
     */
40
    protected $scopeFactory;
41
42
    /**
43
     * @var FileFinder
44
     */
45
    protected $fileFinder;
46
47
    /**
48
     * @var ObserverInterface
49
     */
50
    protected $observer = null;
51
52
    public function __construct(
53
        NodeScopeResolver $nodeScopeResolver,
54
        Parser $parser,
55
        ScopeFactory $scopeFactory,
56
        FileFinder $fileFinder,
57
        CollectDependenciesVisitor $collectNodeVisitor
58
    )
59
    {
60
        $this->nodeScopeResolver = $nodeScopeResolver;
61
        $this->parser = $parser;
62
        $this->scopeFactory = $scopeFactory;
63
        $this->fileFinder = $fileFinder;
64
        $this->collectNodeVisitor = $collectNodeVisitor;
65
    }
66
67
    public static function createFromConfig(string $currentDir, string $tmpDir, array $additionalConfigFiles): self
68
    {
69
        $phpStanContainer = (new ContainerFactory($currentDir))->create($tmpDir, $additionalConfigFiles, []);
70
71
        return new self(
72
            $phpStanContainer->getByType(NodeScopeResolver::class),
73
            $phpStanContainer->getByType(Parser::class),
74
            $phpStanContainer->getByType(ScopeFactory::class),
75
            $phpStanContainer->getByType(FileFinder::class),
76
            $phpStanContainer->getByType(CollectDependenciesVisitor::class)
77
        );
78
    }
79
80
    public function dump(array $paths, array $excludePaths = []): DependencyGraph
81
    {
82
        $excludeFiles = $this->getAllFilesRecursive($excludePaths);
83
        $files = $this->getAllFilesRecursive($paths);
84
85
        $this->notifyDumpStart(array_reduce($files, function (int $max, string $file) use ($excludeFiles) {
86
            if (!in_array($file, $excludeFiles)) {
87
                $max++;
88
            }
89
90
            return $max;
91
        }, 0));
92
        foreach ($files as $file) {
93
            if (!in_array($file, $excludeFiles)) {
94
                $this->notifyCurrentFile($file);
95
96
                try {
97
                    $this->dumpFile($file);
98
                } catch (AnalysedFileException $e) {
99
                    $this->notifyAnalysedFileException($e);
100
                }
101
            }
102
        }
103
        $this->notifyDumpEnd();
104
105
        return $this->collectNodeVisitor->createDependencyGraph();
106
    }
107
108
    protected function dumpFile(string $file): void
109
    {
110
        try {
111
            $this->collectNodeVisitor->setFile($file);
112
113
            // collect dependencies in $this->collectNodeVisitor
114
            $this->nodeScopeResolver->processNodes(
115
                $this->parser->parseFile($file),
116
                $this->scopeFactory->create(ScopeContext::create($file)),
117
                \Closure::fromCallable($this->collectNodeVisitor)  // type hint of processNodes() is \Closure...
118
            );
119
        } catch (ShouldNotHappenException $e) {
120
            throw new AnalysedFileException($file, 'analysing file is failed, because unexpected error', 0, $e);
121
        } catch (AnalysedCodeException $e) {
122
            throw new AnalysedFileException($file, 'analysing file is failed, because unexpected error', 0, $e);
123
        }
124
    }
125
126
    protected function getAllFilesRecursive(array $paths): array
127
    {
128
        try {
129
            $fileFinderResult = $this->fileFinder->findFiles($paths);
130
        } catch (\PHPStan\File\PathNotFoundException $e) {
131
            throw new AnalysedFileException($e->getPath(), 'path was not found.', 0, $e);
132
        }
133
134
        return $fileFinderResult->getFiles();
135
    }
136
137
    public function setObserver(ObserverInterface $observer = null): self
138
    {
139
        $this->observer = $observer;
140
        $this->collectNodeVisitor->setObserver($observer);
141
142
        return $this;
143
    }
144
145
    protected function notifyDumpStart(int $max): void
146
    {
147
        if ($this->observer) {
148
            $this->observer->start($max);
149
        }
150
    }
151
152
    protected function notifyCurrentFile(string $file): void
153
    {
154
        if ($this->observer) {
155
            $this->observer->update($file);
156
        }
157
    }
158
159
    protected function notifyAnalysedFileException(AnalysedFileException $e): void
160
    {
161
        if ($this->observer) {
162
            $this->observer->notifyAnalyzeFileError($e);
163
        }
164
    }
165
166
    protected function notifyDumpEnd(): void
167
    {
168
        if ($this->observer) {
169
            $this->observer->end();
170
        }
171
    }
172
}
173