Completed
Branch master (7b4639)
by Johannes
13:16
created

CheckCommand::execute()   B

Complexity

Conditions 4
Paths 6

Size

Total Lines 48
Code Lines 27

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 1
Metric Value
dl 0
loc 48
rs 8.7397
c 3
b 0
f 1
cc 4
eloc 27
nc 6
nop 2
1
<?php
2
/**
3
 * Created by PhpStorm.
4
 * User: matthias
5
 * Date: 28.11.15
6
 * Time: 16:16
7
 */
8
9
namespace ComposerRequireChecker\Cli;
10
11
use ComposerRequireChecker\ASTLocator\LocateASTFromFiles;
12
use ComposerRequireChecker\DefinedExtensionsResolver\DefinedExtensionsResolver;
13
use ComposerRequireChecker\DefinedSymbolsLocator\LocateDefinedSymbolsFromASTRoots;
14
use ComposerRequireChecker\DefinedSymbolsLocator\LocateDefinedSymbolsFromExtensions;
15
use ComposerRequireChecker\Exception\InvalidInputFileException;
16
use ComposerRequireChecker\FileLocator\LocateComposerPackageDirectDependenciesSourceFiles;
17
use ComposerRequireChecker\FileLocator\LocateComposerPackageSourceFiles;
18
use ComposerRequireChecker\GeneratorUtil\ComposeGenerators;
19
use ComposerRequireChecker\UsedSymbolsLocator\LocateUsedSymbolsFromASTRoots;
20
use PhpParser\ParserFactory;
21
use Symfony\Component\Console\Command\Command;
22
use Symfony\Component\Console\Input\InputArgument;
23
use Symfony\Component\Console\Input\InputInterface;
24
use Symfony\Component\Console\Input\InputOption;
25
use Symfony\Component\Console\Output\OutputInterface;
26
27
class CheckCommand extends Command
28
{
29
    protected function configure()
30
    {
31
        $this
32
            ->setName('check')
33
            ->setDescription('check the defined dependencies against your code')
34
            ->addOption(
35
                'config-file',
36
                null,
37
                InputOption::VALUE_REQUIRED,
38
                'the config.json file to configure the checking options'
39
            )
40
            ->addArgument(
41
                'composer-json',
42
                InputArgument::OPTIONAL,
43
                'the composer.json of your package, that should be checked',
44
                './composer.json'
45
            )
46
        ;
47
    }
48
49
    protected function execute(InputInterface $input, OutputInterface $output) : int
50
    {
51
52
        if(!$output->isQuiet()) {
53
            $output->writeln($this->getApplication()->getLongVersion());
54
        }
55
56
        $composerJson = $input->getArgument('composer-json');
57
        $this->checkJsonFile($composerJson);
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
        $options = $this->getCheckOptions($input);
71
72
        $definedExtensionSymbols = (new LocateDefinedSymbolsFromExtensions())->__invoke(
73
            (new DefinedExtensionsResolver())->__invoke($composerJson, $options->getPhpCoreExtensions())
74
        );
75
76
        $usedSymbols = (new LocateUsedSymbolsFromASTRoots())->__invoke($sourcesASTs($getPackageSourceFiles($composerJson)));
77
78
        $unknownSymbols = array_diff(
79
            $usedSymbols,
80
            $definedVendorSymbols,
81
            $definedExtensionSymbols,
82
            $options->getSymbolWhitelist()
83
        );
84
85
        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...
86
            $output->writeln("There were no unknown symbols found.");
87
            return 0;
88
        }
89
90
        $output->writeln("The following unknown symbols were found:");
91
        foreach ($unknownSymbols as $unknownSymbol) {
92
            $output->writeln("  " . $unknownSymbol);
93
        }
94
95
        return ((int) (bool) $unknownSymbols);
96
    }
97
98
    private function getCheckOptions(InputInterface $input) : Options
99
    {
100
        $fileName = $input->getOption('config-file');
101
        if(!$fileName) {
102
            return new Options();
103
        }
104
105
        if(!is_readable($fileName)) {
106
            throw new \InvalidArgumentException('unable to read ' . $fileName);
107
        }
108
109
        $jsonData = json_decode(file_get_contents($fileName), true);
110
        if(false === $jsonData) {
111
            throw new \Exception('error parsing the config file: ' . json_last_error_msg());
112
        }
113
114
        return new Options($jsonData);
115
116
    }
117
118
    /**
119
     * @param string $jsonFile
120
     * @throws InvalidInputFileException
121
     * @internal param string $composerJson the path to composer.json
122
     */
123
    private function checkJsonFile(string $jsonFile)
124
    {
125
        if(!is_readable($jsonFile)) {
126
            throw new InvalidInputFileException('cannot read ' . $jsonFile);
127
        }
128
129
        if(false == json_decode(file_get_contents($jsonFile))) {
130
            throw new InvalidInputFileException('error parsing ' . $jsonFile . ': ' . json_last_error_msg());
131
        }
132
133
    }
134
135
}