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