Completed
Pull Request — master (#28)
by
unknown
04:22
created

CheckCommand::execute()   B

Complexity

Conditions 7
Paths 12

Size

Total Lines 64
Code Lines 39

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 56

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 64
ccs 0
cts 39
cp 0
rs 7.2058
cc 7
eloc 39
nc 12
nop 2
crap 56

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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\Exception\InvalidInputFileException;
11
use ComposerRequireChecker\FileLocator\LocateComposerPackageDirectDependenciesSourceFiles;
12
use ComposerRequireChecker\FileLocator\LocateComposerPackageSourceFiles;
13
use ComposerRequireChecker\GeneratorUtil\ComposeGenerators;
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
        ;
43 1
    }
44
45
    protected function execute(InputInterface $input, OutputInterface $output) : int
46
    {
47
48
        if(!$output->isQuiet()) {
49
            $output->writeln($this->getApplication()->getLongVersion());
50
        }
51
52
        $composerJson = realpath($input->getArgument('composer-json'));
53
        if(false === $composerJson) {
54
            throw new \InvalidArgumentException('file not found: [' . $input->getArgument('composer-json') . ']');
55
        }
56
        $this->checkJsonFile($composerJson);
57
58
        $options = $this->getCheckOptions($input);
59
60
        $getPackageSourceFiles = new LocateComposerPackageSourceFiles();
61
62
        $sourcesASTs = new LocateASTFromFiles((new ParserFactory())->create(ParserFactory::PREFER_PHP7));
63
64
        $definedVendorSymbols = (new LocateDefinedSymbolsFromASTRoots())->__invoke($sourcesASTs(
65
            (new ComposeGenerators())->__invoke(
66
                $getPackageSourceFiles($composerJson),
67
                (new LocateComposerPackageDirectDependenciesSourceFiles())->__invoke($composerJson)
68
            )
69
        ));
70
71
        $definedExtensionSymbols = (new LocateDefinedSymbolsFromExtensions())->__invoke(
72
            (new DefinedExtensionsResolver())->__invoke($composerJson, $options->getPhpCoreExtensions())
73
        );
74
75
        $usedSymbols = (new LocateUsedSymbolsFromASTRoots())->__invoke($sourcesASTs($getPackageSourceFiles($composerJson)));
76
77
        if (!count($usedSymbols)) {
78
            $output->writeln("There were no symbols found, please check your configuration.");
79
            return 255;
80
        }
81
82
        $unknownSymbols = array_diff(
83
            $usedSymbols,
84
            $definedVendorSymbols,
85
            $definedExtensionSymbols,
86
            $options->getSymbolWhitelist()
87
        );
88
89
        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...
90
            $output->writeln("There were no unknown symbols found.");
91
            return 0;
92
        }
93
94
        $output->writeln("The following unknown symbols were found:");
95
        $table = new Table($output);
96
        $table->setHeaders(['unknown symbol', 'guessed dependency']);
97
        $guesser = new DependencyGuesser();
98
        foreach ($unknownSymbols as $unknownSymbol) {
99
            $guessedDependencies = [];
100
            foreach($guesser($unknownSymbol) as $guessedDependency) {
101
                $guessedDependencies[] = $guessedDependency;
102
            }
103
            $table->addRow([$unknownSymbol, implode("\n", $guessedDependencies)]);
104
        }
105
        $table->render();
106
107
        return ((int) (bool) $unknownSymbols);
108
    }
109
110
    private function getCheckOptions(InputInterface $input) : Options
111
    {
112
        $fileName = $input->getOption('config-file');
113
        if(!$fileName) {
114
            return new Options();
115
        }
116
117
        if(!is_readable($fileName)) {
118
            throw new \InvalidArgumentException('unable to read ' . $fileName);
119
        }
120
121
        $jsonData = json_decode(file_get_contents($fileName), true);
122
        if(false === $jsonData) {
123
            throw new \Exception('error parsing the config file: ' . json_last_error_msg());
124
        }
125
126
        return new Options($jsonData);
127
128
    }
129
130
    /**
131
     * @param string $jsonFile
132
     * @throws InvalidInputFileException
133
     * @internal param string $composerJson the path to composer.json
134
     */
135
    private function checkJsonFile(string $jsonFile)
136
    {
137
        if(!is_readable($jsonFile)) {
138
            throw new InvalidInputFileException('cannot read ' . $jsonFile);
139
        }
140
141
        if(false == json_decode(file_get_contents($jsonFile))) {
142
            throw new InvalidInputFileException('error parsing ' . $jsonFile . ': ' . json_last_error_msg());
143
        }
144
145
    }
146
147
}
148