Issues (8)

src/Cli/Command.php (5 issues)

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Scheb\Tombstone\Analyzer\Cli;
6
7
use Scheb\Tombstone\Analyzer\Analyzer;
8
use Scheb\Tombstone\Analyzer\Config\ConfigurationLoader;
9
use Scheb\Tombstone\Analyzer\Config\YamlConfigProvider;
10
use Scheb\Tombstone\Analyzer\Log\AnalyzerLogDirectoryReader;
11
use Scheb\Tombstone\Analyzer\Log\LogReaderInterface;
12
use Scheb\Tombstone\Analyzer\Matching\MethodNameStrategy;
13
use Scheb\Tombstone\Analyzer\Matching\PositionStrategy;
14
use Scheb\Tombstone\Analyzer\Report\ConsoleReportGenerator;
15
use Scheb\Tombstone\Analyzer\Report\HtmlReportGenerator;
16
use Scheb\Tombstone\Analyzer\Report\PhpReportGenerator;
17
use Scheb\Tombstone\Analyzer\Report\ReportExporter;
18
use Scheb\Tombstone\Analyzer\Source\TombstoneExtractorFactory;
19
use Scheb\Tombstone\Analyzer\Source\TombstoneExtractorInterface;
20
use Scheb\Tombstone\Analyzer\TombstoneIndex;
21
use Scheb\Tombstone\Analyzer\VampireIndex;
22
use SebastianBergmann\FinderFacade\FinderFacade;
23
use Symfony\Component\Console\Command\Command as AbstractCommand;
24
use Symfony\Component\Console\Input\InputInterface;
25
use Symfony\Component\Console\Input\InputOption;
26
use Symfony\Component\Console\Output\OutputInterface;
27
28
class Command extends AbstractCommand
29
{
30
    /**
31
     * @var InputInterface
32
     */
33
    private $input;
34
35
    /**
36
     * @var ConsoleOutput
37
     */
38
    private $output;
39
40 1
    protected function configure()
41
    {
42
        $this
43 1
            ->setName('tombstone')
44 1
            ->addOption('config', 'c', InputOption::VALUE_REQUIRED, 'Path to config file');
45 1
    }
46
47 1
    protected function execute(InputInterface $input, OutputInterface $output)
48
    {
49 1
        $this->input = $input;
50 1
        $this->output = new ConsoleOutput($output);
51
52
        try {
53 1
            $this->doExecute();
54
        } catch (\Exception $e) {
55
            $this->output->writeln($e->getMessage());
56
            $this->output->debug($e->getTraceAsString());
57
58
            return $e->getCode() ?: 1;
59
        }
60
61 1
        return 0;
62
    }
63
64 1
    private function doExecute(): void
65
    {
66 1
        $configFile = $this->input->getOption('config') ?? getcwd().DIRECTORY_SEPARATOR.'tombstone.yml';
67 1
        if (!file_exists($configFile)) {
0 ignored issues
show
It seems like $configFile can also be of type string[]; however, parameter $filename of file_exists() does only seem to accept string, 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

67
        if (!file_exists(/** @scrutinizer ignore-type */ $configFile)) {
Loading history...
68
            throw new \InvalidArgumentException(sprintf('Could not find configuration file %s', $configFile));
0 ignored issues
show
It seems like $configFile can also be of type string[]; however, parameter $args of sprintf() does only seem to accept string, 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

68
            throw new \InvalidArgumentException(sprintf('Could not find configuration file %s', /** @scrutinizer ignore-type */ $configFile));
Loading history...
69
        }
70
71 1
        $this->output->debug('Load config from '.$configFile);
0 ignored issues
show
Are you sure $configFile of type boolean|string|string[] can be used in concatenation? ( Ignorable by Annotation )

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

71
        $this->output->debug('Load config from './** @scrutinizer ignore-type */ $configFile);
Loading history...
72 1
        $configLoader = ConfigurationLoader::create();
73 1
        $config = $configLoader->loadConfiguration([new YamlConfigProvider($configFile)]);
74 1
        $rootDir = $config['rootDir'] ?? null;
75
76 1
        $tombstoneIndex = new TombstoneIndex($rootDir);
0 ignored issues
show
It seems like $rootDir can also be of type null; however, parameter $rootDir of Scheb\Tombstone\Analyzer...oneIndex::__construct() does only seem to accept string, 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

76
        $tombstoneIndex = new TombstoneIndex(/** @scrutinizer ignore-type */ $rootDir);
Loading history...
77 1
        $vampireIndex = new VampireIndex();
78 1
        $tombstoneExtractor = TombstoneExtractorFactory::create($config, $tombstoneIndex, $this->output);
79 1
        $logReaders = $this->createLogReaders($config);
80 1
        $analyzer = $this->createAnalyzer();
81
82 1
        $this->output->writeln('Scan source code ...');
83 1
        $files = $this->collectSourceFiles($config);
84 1
        $this->extractTombstones($files, $tombstoneExtractor);
85
86 1
        $this->output->writeln('Read logs ...');
87 1
        foreach ($logReaders as $logReader) {
88 1
            $logReader->collectVampires($vampireIndex);
89
        }
90
91 1
        $this->output->writeln('Analyze tombstones ...');
92 1
        $result = $analyzer->getResult($tombstoneIndex, $vampireIndex);
93
94 1
        $reportGenerators = [];
95 1
        if (isset($config['report']['console'])) {
96 1
            $reportGenerators[] = new ConsoleReportGenerator($this->output);
97
        }
98 1
        if (isset($config['report']['html'])) {
99 1
            $reportGenerators[] = new HtmlReportGenerator($config['report']['html'], $rootDir);
0 ignored issues
show
It seems like $rootDir can also be of type null; however, parameter $rootDir of Scheb\Tombstone\Analyzer...enerator::__construct() does only seem to accept string, 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

99
            $reportGenerators[] = new HtmlReportGenerator($config['report']['html'], /** @scrutinizer ignore-type */ $rootDir);
Loading history...
100
        }
101 1
        if (isset($config['report']['php'])) {
102 1
            $reportGenerators[] = new PhpReportGenerator($config['report']['php']);
103
        }
104
105 1
        $reportExporter = new ReportExporter($this->output, $reportGenerators);
106 1
        $reportExporter->generate($result);
107 1
    }
108
109 1
    private function createAnalyzer(): Analyzer
110
    {
111
        $matchingStrategies = [
112 1
            new MethodNameStrategy(),
113 1
            new PositionStrategy(),
114
        ];
115
116 1
        return new Analyzer($matchingStrategies);
117
    }
118
119
    /**
120
     * @param array $config
121
     * @return LogReaderInterface[]
122
     */
123 1
    private function createLogReaders(array $config): array
124
    {
125 1
        $logReaders = [];
126 1
        if (isset($config['logs']['directory'])) {
127 1
            $logReaders[] = AnalyzerLogDirectoryReader::create($config['logs']['directory'], $this->output);
128
        }
129 1
        if (isset($config['logs']['custom'])) {
130
            require_once $config['logs']['custom']['file'];
131
            $reflectionClass = new \ReflectionClass($config['logs']['custom']['class']);
132
            if (!$reflectionClass->implementsInterface(LogReaderInterface::class)) {
133
                throw new \Exception(sprintf('Class %s must implement %s', $config['logs']['custom']['class'], LogReaderInterface::class));
134
            }
135
136
            $logReaders[] = $reflectionClass->newInstance();
137
        }
138
139 1
        return $logReaders;
140
    }
141
142 1
    private function collectSourceFiles(array $config): array
143
    {
144 1
        $finder = new FinderFacade(
145 1
            $config['source']['directories'],
146 1
            $config['source']['excludes'],
147 1
            $config['source']['names'],
148 1
            $config['source']['notNames']
149
        );
150
151 1
        return $finder->findFiles();
152
    }
153
154 1
    private function extractTombstones(array $files, TombstoneExtractorInterface $tombstoneExtractor): void
155
    {
156 1
        $progress = $this->output->createProgressBar(count($files));
157 1
        foreach ($files as $file) {
158 1
            $this->output->debug($file);
159 1
            $tombstoneExtractor->extractTombstones($file);
160 1
            $progress->advance();
161
        }
162 1
        $this->output->writeln();
163 1
    }
164
}
165