Passed
Pull Request — master (#51)
by Dominik
04:57 queued 02:28
created

ReportCommand::getDefaultDescription()   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\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
                'composer'
39
            ),
40
            new InputOption(
41
                'no-dev',
42
                null,
43
                InputOption::VALUE_OPTIONAL,
44
                'Do not include dev dependencies',
45
                'false'
46
            ),
47
            new InputOption(
48
                'no-cache',
49
                null,
50
                InputOption::VALUE_NONE,
51
                'Disables caching of license lookups'
52
            ),
53
            new InputOption(
54
                'show-packages',
55
                null,
56
                InputOption::VALUE_NONE,
57
                'Shows the packages for each license.'
58
            ),
59
            new InputOption(
60
                'grouped',
61
                null,
62
                InputOption::VALUE_NONE,
63
                'Display the packages grouped. Only valid with the \'show-packages\' option.'
64
            ),
65
            new InputOption(
66
                'filter',
67
                null,
68
                InputOption::VALUE_IS_ARRAY | InputOption::VALUE_OPTIONAL,
69
                'Filter for specific licences.'
70
            ),
71
        ]));
72
    }
73
74
    public static function getDefaultName(): ?string
75
    {
76
        return 'report';
77
    }
78
79
    public static function getDefaultDescription(): ?string
80
    {
81
        return 'Generate a report of all licenses used in the project';
82
    }
83
84
    protected function execute(InputInterface $input, OutputInterface $output): int
85
    {
86
        if ($input->getOption('grouped') && ! $input->getOption('show-packages')) {
87
            throw new \InvalidArgumentException('The option "grouped" is only allowed with "show-packages" option');
88
        }
89
90
        $dependencies = $this->dependencyLoader->loadDependencies(
0 ignored issues
show
Bug introduced by
The method loadDependencies() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

90
        /** @scrutinizer ignore-call */ 
91
        $dependencies = $this->dependencyLoader->loadDependencies(

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
91
            $input->getOption('composer'),
92
            $input->getOption('project-path'),
93
            ($input->getOption('no-dev') ?? 'true') === 'true'
94
        );
95
96
        $dependencies = $this->filterLicenses($dependencies, $input->getOption('filter'));
97
98
        $groupedByName = $this->groupDependenciesByLicense($dependencies);
99
100
        $shouldCache = ! $input->getOption('no-cache');
101
102
        $licenses = $this->lookUpLicenses(array_keys($groupedByName), $output, $shouldCache);
103
104
        /* @var License $license */
105
        $this->outputFormattedLicenses($output, $input, $licenses, $groupedByName);
106
107
        return self::SUCCESS;
108
    }
109
110
    /**
111
     * @param  Dependency[]  $dependencies
112
     * @return array
113
     */
114
    private function groupDependenciesByLicense(array $dependencies): array
115
    {
116
        $grouped = [];
117
118
        foreach ($dependencies as $dependency) {
119
            [$license] = $dependency->getLicenses();
120
121
            if (! isset($grouped[$license])) {
122
                $grouped[$license] = [];
123
            }
124
            $grouped[$license][] = $dependency;
125
        }
126
127
        return $grouped;
128
    }
129
130
    /**
131
     * @param  Dependency[]  $dependencies
132
     * @param  string[]  $filters
133
     * @return array
134
     */
135
    private function filterLicenses(array $dependencies, array $filters): array
136
    {
137
        if ($filters === []) {
138
            return $dependencies;
139
        }
140
141
        $validLicences = [];
142
143
        foreach ($dependencies as $dependency) {
144
            foreach ($dependency->getLicenses() as $license) {
145
                if (in_array(strtolower($license), array_map('strtolower', $filters))) {
146
                    $validLicences[] = $dependency;
147
                    continue 2;
148
                }
149
            }
150
        }
151
152
        return $validLicences;
153
    }
154
155
    private function lookUpLicenses(array $licenses, OutputInterface $output, $useCache = true): array
156
    {
157
        if (! $useCache) {
158
            $this->licenseLookup->setCache(new NullAdapter);
0 ignored issues
show
Bug introduced by
The method setCache() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

158
            $this->licenseLookup->/** @scrutinizer ignore-call */ 
159
                                  setCache(new NullAdapter);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
159
        }
160
161
        $lookedUp = [];
162
        foreach ($licenses as $license) {
163
            $output->writeln("Looking up $license ...");
164
            $lookedUp[$license] = $this->licenseLookup->lookUp($license);
165
        }
166
167
        return $lookedUp;
168
    }
169
170
    /**
171
     * @param  OutputInterface  $output
172
     * @param  InputInterface  $input
173
     * @param  License[]  $licenses
174
     * @param  array  $groupedByName
175
     */
176
    protected function outputFormattedLicenses(OutputInterface $output, InputInterface $input, array $licenses, array $groupedByName): void
177
    {
178
        foreach ($licenses as $license) {
179
            $dependencies = $groupedByName[$license->getShortName()];
180
181
            $usageCount = count($dependencies);
182
            $headline = sprintf(PHP_EOL.'Count %d - %s (%s)', $usageCount, $license->getShortName(),
183
                $license->getSource());
184
            $output->writeln($headline);
185
            $licenseTable = new Table($output);
186
            $licenseTable->setHeaders(['CAN', 'CAN NOT', 'MUST']);
187
188
            $can = $license->getCan();
189
            $cannot = $license->getCannot();
190
            $must = $license->getMust();
191
            $columnWidth = max(count($can), count($cannot), count($must));
192
193
            $can = array_pad($can, $columnWidth, null);
194
            $cannot = array_pad($cannot, $columnWidth, null);
195
            $must = array_pad($must, $columnWidth, null);
196
197
            $inlineHeading = function ($key) {
198
                return is_string($key) ? $key : '';
199
            };
200
201
            $can = array_map_keys($can, $inlineHeading);
202
            $cannot = array_map_keys($cannot, $inlineHeading);
203
            $must = array_map_keys($must, $inlineHeading);
204
205
            for ($i = 0; $i < $columnWidth; $i++) {
206
                $licenseTable->addRow([
207
                    'CAN' => $can[$i],
208
                    'CANNOT' => $cannot[$i],
209
                    'MUST' => $must[$i],
210
                ]);
211
            }
212
            $licenseTable->render();
213
214
            if ($input->getOption('show-packages') || $output->isVerbose()) {
215
                $output->writeln('');
216
                $output->writeln($this->outputFormatPackages($input, $dependencies));
217
            }
218
        }
219
    }
220
221
    /**
222
     * Generates a output string for the 'show-packages' option.
223
     *
224
     * @param  InputInterface  $input
225
     * @param  array  $dependencies
226
     * @return string
227
     */
228
    protected function outputFormatPackages(InputInterface $input, array $dependencies): string
229
    {
230
        $packages = [];
231
        if ($input->getOption('grouped')) {
232
            foreach ($dependencies as $dependency) {
233
                $packages[] = $dependency->getName();
234
            }
235
236
            return 'packages: '.implode(', ', $packages);
237
        }
238
239
        foreach ($dependencies as $dependency) {
240
            $packages[] = sprintf('%s (%s)', $dependency->getName(), $dependency->getVersion());
241
        }
242
243
        return implode(PHP_EOL, $packages);
244
    }
245
}
246