Passed
Pull Request — master (#32)
by Jonas
02:47
created

ReportCommand::getDefaultName()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 1
c 1
b 0
f 0
dl 0
loc 3
ccs 0
cts 2
cp 0
rs 10
cc 1
nc 1
nop 0
crap 2
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Dominikb\ComposerLicenseChecker;
6
7
use Dominikb\ComposerLicenseChecker\Contracts\DependencyLoaderAware;
8
use Dominikb\ComposerLicenseChecker\Contracts\LicenseLookupAware;
9
use Dominikb\ComposerLicenseChecker\Traits\DependencyLoaderAwareTrait;
10
use Dominikb\ComposerLicenseChecker\Traits\LicenseLookupAwareTrait;
11
use Symfony\Component\Cache\Adapter\NullAdapter;
12
use Symfony\Component\Console\Attribute\AsCommand;
13
use Symfony\Component\Console\Command\Command;
14
use Symfony\Component\Console\Helper\Table;
15
use Symfony\Component\Console\Input\InputDefinition;
16
use Symfony\Component\Console\Input\InputInterface;
17
use Symfony\Component\Console\Input\InputOption;
18
use Symfony\Component\Console\Output\OutputInterface;
19
20
class ReportCommand extends Command implements LicenseLookupAware, DependencyLoaderAware
21
{
22
    use LicenseLookupAwareTrait, DependencyLoaderAwareTrait;
23
24
    protected function configure()
25
    {
26
        $this->setDefinition(new InputDefinition([
27
            new InputOption(
28
                'project-path',
29
                'p',
30
                InputOption::VALUE_OPTIONAL,
31
                'Path to directory of composer.json file',
32
                realpath('.')
33
            ),
34
            new InputOption(
35
                'composer',
36
                'c',
37
                InputOption::VALUE_OPTIONAL,
38
                'Path to composer executable',
39
                realpath('./vendor/bin/composer')
40
            ),
41
            new InputOption(
42
                'no-cache',
43
                null,
44
                InputOption::VALUE_NONE,
45
                'Disables caching of license lookups'
46
            ),
47
            new InputOption(
48
                'show-packages',
49
                null,
50
                InputOption::VALUE_NONE,
51
                'Shows the packages for each license.'
52
            ),
53
            new InputOption(
54
                'grouped',
55
                null,
56
                InputOption::VALUE_NONE,
57
                'Display the packages grouped. Only valid with the \'show-packages\' option.'
58
            ),
59
            new InputOption(
60
                'filter',
61
                null,
62
                InputOption::VALUE_IS_ARRAY | InputOption::VALUE_OPTIONAL,
63
                'Filter for specific licences.'
64
            ),
65
        ]));
66
    }
67
68
    public static function getDefaultName(): ?string
69
    {
70
        return 'report';
71
    }
72
73
    protected function execute(InputInterface $input, OutputInterface $output): int
74
    {
75
        if ($input->getOption('grouped') && ! $input->getOption('show-packages')) {
76
            throw new \InvalidArgumentException('The option "grouped" is only allowed with "show-packages" option');
77
        }
78
79
        $dependencies = $this->dependencyLoader->loadDependencies(
80
            $input->getOption('composer'),
81
            $input->getOption('project-path')
82
        );
83
84
        $dependencies = $this->filterLicenses($dependencies, $input->getOption('filter'));
85
86
        $groupedByName = $this->groupDependenciesByLicense($dependencies);
87
88
        $shouldCache = ! $input->getOption('no-cache');
89
90
        $licenses = $this->lookUpLicenses(array_keys($groupedByName), $output, $shouldCache);
91
92
        /* @var License $license */
93
        $this->outputFormattedLicenses($output, $input, $licenses, $groupedByName);
94
95
        return 0;
96
    }
97
98
    /**
99
     * @param  Dependency[]  $dependencies
100
     * @return array
101
     */
102
    private function groupDependenciesByLicense(array $dependencies): array
103
    {
104
        $grouped = [];
105
106
        foreach ($dependencies as $dependency) {
107
            [$license] = $dependency->getLicenses();
108
109
            if (! isset($grouped[$license])) {
110
                $grouped[$license] = [];
111
            }
112
            $grouped[$license][] = $dependency;
113
        }
114
115
        return $grouped;
116
    }
117
118
    /**
119
     * @param  Dependency[]  $dependencies
120
     * @param  string[]  $filters
121
     * @return array
122
     */
123
    private function filterLicenses(array $dependencies, array $filters): array
124
    {
125
        if ($filters === []) {
126
            return $dependencies;
127
        }
128
129
        $validLicences = [];
130
131
        foreach ($dependencies as $dependency) {
132
            foreach ($dependency->getLicenses() as $license) {
133
                if (in_array(strtolower($license), array_map('strtolower', $filters))) {
134
                    $validLicences[] = $dependency;
135
                    continue 2;
136
                }
137
            }
138
        }
139
140
        return $validLicences;
141
    }
142
143
    private function lookUpLicenses(array $licenses, OutputInterface $output, $useCache = true): array
144
    {
145
        if (! $useCache) {
146
            $this->licenseLookup->setCache(new NullAdapter);
147
        }
148
149
        $lookedUp = [];
150
        foreach ($licenses as $license) {
151
            $output->writeln("Looking up $license ...");
152
            $lookedUp[$license] = $this->licenseLookup->lookUp($license);
153
        }
154
155
        return $lookedUp;
156
    }
157
158
    /**
159
     * @param  OutputInterface  $output
160
     * @param  InputInterface  $input
161
     * @param  License[]  $licenses
162
     * @param  array  $groupedByName
163
     */
164
    protected function outputFormattedLicenses(OutputInterface $output, InputInterface $input, array $licenses, array $groupedByName): void
165
    {
166
        foreach ($licenses as $license) {
167
            $dependencies = $groupedByName[$license->getShortName()];
168
169
            $usageCount = count($dependencies);
170
            $headline = sprintf(PHP_EOL.'Count %d - %s (%s)', $usageCount, $license->getShortName(),
171
                $license->getSource());
172
            $output->writeln($headline);
173
            $licenseTable = new Table($output);
174
            $licenseTable->setHeaders(['CAN', 'CAN NOT', 'MUST']);
175
176
            $can = $license->getCan();
177
            $cannot = $license->getCannot();
178
            $must = $license->getMust();
179
            $columnWidth = max(count($can), count($cannot), count($must));
180
181
            $can = array_pad($can, $columnWidth, null);
182
            $cannot = array_pad($cannot, $columnWidth, null);
183
            $must = array_pad($must, $columnWidth, null);
184
185
            $inlineHeading = function ($key) {
186
                return is_string($key) ? $key : '';
187
            };
188
189
            $can = array_map_keys($can, $inlineHeading);
190
            $cannot = array_map_keys($cannot, $inlineHeading);
191
            $must = array_map_keys($must, $inlineHeading);
192
193
            for ($i = 0; $i < $columnWidth; $i++) {
194
                $licenseTable->addRow([
195
                    'CAN'    => $can[$i],
196
                    'CANNOT' => $cannot[$i],
197
                    'MUST'   => $must[$i],
198
                ]);
199
            }
200
            $licenseTable->render();
201
202
            if ($input->getOption('show-packages') || $output->isVerbose()) {
203
                $output->writeln('');
204
                $output->writeln($this->outputFormatPackages($input, $dependencies));
205
            }
206
        }
207
    }
208
209
    /**
210
     * Generates a output string for the 'show-packages' option.
211
     *
212
     * @param  InputInterface  $input
213
     * @param  array  $dependencies
214
     * @return string
215
     */
216
    protected function outputFormatPackages(InputInterface $input, array $dependencies): string
217
    {
218
        $packages = [];
219
        if ($input->getOption('grouped')) {
220
            foreach ($dependencies as $dependency) {
221
                $packages[] = $dependency->getName();
222
            }
223
224
            return 'packages: '.implode(', ', $packages);
225
        }
226
227
        foreach ($dependencies as $dependency) {
228
            $packages[] = sprintf('%s (%s)', $dependency->getName(), $dependency->getVersion());
229
        }
230
231
        return implode(PHP_EOL, $packages);
232
    }
233
}
234