Failed Conditions
Pull Request — master (#33)
by Matthias
02:41
created

CheckCommand   B

Complexity

Total Complexity 11

Size/Duplication

Total Lines 107
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 16

Test Coverage

Coverage 22.95%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 11
c 1
b 0
f 0
lcom 2
cbo 16
dl 0
loc 107
ccs 14
cts 61
cp 0.2295
rs 8.4614

4 Methods

Rating   Name   Duplication   Size   Complexity  
A getCheckOptions() 0 8 2
A checkJsonFile() 0 5 1
A configure() 0 18 1
B execute() 0 64 7
1
<?php
2
3
namespace ComposerRequireChecker\Cli;
4
5
use ComposerRequireChecker\ASTLocator\LocateASTFromFiles;
6
use ComposerRequireChecker\DefinedExtensionsResolver\DefinedExtensionsResolver;
7
use ComposerRequireChecker\DefinedSymbolsLocator\LocateDefinedSymbolsFromASTRoots;
8
use ComposerRequireChecker\DefinedSymbolsLocator\LocateDefinedSymbolsFromExtensions;
9
use ComposerRequireChecker\DependencyGuesser\DependencyGuesser;
10
use ComposerRequireChecker\FileLocator\LocateComposerPackageDirectDependenciesSourceFiles;
11
use ComposerRequireChecker\FileLocator\LocateComposerPackageSourceFiles;
12
use ComposerRequireChecker\GeneratorUtil\ComposeGenerators;
13
use ComposerRequireChecker\JsonLoader;
14
use ComposerRequireChecker\UsedSymbolsLocator\LocateUsedSymbolsFromASTRoots;
15
use PhpParser\ParserFactory;
16
use Symfony\Component\Console\Command\Command;
17
use Symfony\Component\Console\Helper\Table;
18
use Symfony\Component\Console\Input\InputArgument;
19
use Symfony\Component\Console\Input\InputInterface;
20
use Symfony\Component\Console\Input\InputOption;
21
use Symfony\Component\Console\Output\OutputInterface;
22
23
class CheckCommand extends Command
24
{
25 1
    protected function configure()
26
    {
27
        $this
28 1
            ->setName('check')
29 1
            ->setDescription('check the defined dependencies against your code')
30 1
            ->addOption(
31 1
                'config-file',
32 1
                null,
33 1
                InputOption::VALUE_REQUIRED,
34 1
                'the config.json file to configure the checking options'
35
            )
36 1
            ->addArgument(
37 1
                'composer-json',
38 1
                InputArgument::OPTIONAL,
39 1
                'the composer.json of your package, that should be checked',
40 1
                './composer.json'
41
            );
42 1
    }
43
44
    protected function execute(InputInterface $input, OutputInterface $output): int
45
    {
46
47
        if (!$output->isQuiet()) {
48
            $output->writeln($this->getApplication()->getLongVersion());
49
        }
50
51
        $composerJson = realpath($input->getArgument('composer-json'));
52
        if (false === $composerJson) {
53
            throw new \InvalidArgumentException('file not found: [' . $input->getArgument('composer-json') . ']');
54
        }
55
        $this->checkJsonFile($composerJson);
56
57
        $options = $this->getCheckOptions($input);
58
59
        $getPackageSourceFiles = new LocateComposerPackageSourceFiles();
60
61
        $sourcesASTs = new LocateASTFromFiles((new ParserFactory())->create(ParserFactory::PREFER_PHP7));
62
63
        $definedVendorSymbols = (new LocateDefinedSymbolsFromASTRoots())->__invoke($sourcesASTs(
64
            (new ComposeGenerators())->__invoke(
65
                $getPackageSourceFiles($composerJson),
66
                (new LocateComposerPackageDirectDependenciesSourceFiles())->__invoke($composerJson)
67
            )
68
        ));
69
70
        $definedExtensionSymbols = (new LocateDefinedSymbolsFromExtensions())->__invoke(
71
            (new DefinedExtensionsResolver())->__invoke($composerJson, $options->getPhpCoreExtensions())
72
        );
73
74
        $usedSymbols = (new LocateUsedSymbolsFromASTRoots())
75
            ->__invoke($sourcesASTs($getPackageSourceFiles($composerJson)));
76
77
        if (!count($usedSymbols)) {
78
            throw new \LogicException('There were no symbols found, please check your configuration.');
79
        }
80
81
        $unknownSymbols = array_diff(
82
            $usedSymbols,
83
            $definedVendorSymbols,
84
            $definedExtensionSymbols,
85
            $options->getSymbolWhitelist()
86
        );
87
88
        if (!$unknownSymbols) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $unknownSymbols of type string[] is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
89
            $output->writeln("There were no unknown symbols found.");
90
            return 0;
91
        }
92
93
        $output->writeln("The following unknown symbols were found:");
94
        $table = new Table($output);
95
        $table->setHeaders(['unknown symbol', 'guessed dependency']);
96
        $guesser = new DependencyGuesser();
97
        foreach ($unknownSymbols as $unknownSymbol) {
98
            $guessedDependencies = [];
99
            foreach ($guesser($unknownSymbol) as $guessedDependency) {
100
                $guessedDependencies[] = $guessedDependency;
101
            }
102
            $table->addRow([$unknownSymbol, implode("\n", $guessedDependencies)]);
103
        }
104
        $table->render();
105
106
        return ((int)(bool)$unknownSymbols);
107
    }
108
109
    private function getCheckOptions(InputInterface $input): Options
110
    {
111
        $fileName = $input->getOption('config-file');
112
        if (!$fileName) {
113
            return new Options();
114
        }
115
        return new Options((new JsonLoader($fileName))->getData());
116
    }
117
118
    /**
119
     * @param string $jsonFile
120
     * @throws \ComposerRequireChecker\Exception\InvalidJsonException
121
     * @throws \ComposerRequireChecker\Exception\NotReadableException
122
     * @internal param string $composerJson the path to composer.json
123
     */
124
    private function checkJsonFile(string $jsonFile)
125
    {
126
        // JsonLoader throws an exception if it cannot load the file
127
        new JsonLoader($jsonFile);
128
    }
129
}
130