Passed
Push — master ( ec97b4...705ca1 )
by Dominik
02:29
created

ReportCommand::outputFormattedLicenses()   B

Complexity

Conditions 6
Paths 5

Size

Total Lines 41
Code Lines 29

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 42

Importance

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