ReportCommand::lookUpLicenses()   A
last analyzed

Complexity

Conditions 3
Paths 4

Size

Total Lines 13
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 7
c 1
b 0
f 0
dl 0
loc 13
ccs 0
cts 8
cp 0
rs 10
cc 3
nc 4
nop 3
crap 12
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
    protected function execute(InputInterface $input, OutputInterface $output): int
80
    {
81
        if ($input->getOption('grouped') && ! $input->getOption('show-packages')) {
82
            throw new \InvalidArgumentException('The option "grouped" is only allowed with "show-packages" option');
83
        }
84
85
        $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

85
        /** @scrutinizer ignore-call */ 
86
        $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...
86
            $input->getOption('composer'),
87
            $input->getOption('project-path'),
88
            ($input->getOption('no-dev') ?? 'true') === 'true'
89
        );
90
91
        $dependencies = $this->filterLicenses($dependencies, $input->getOption('filter'));
92
93
        $groupedByName = $this->groupDependenciesByLicense($dependencies);
94
95
        $shouldCache = ! $input->getOption('no-cache');
96
97
        $licenses = $this->lookUpLicenses(array_keys($groupedByName), $output, $shouldCache);
98
99
        /* @var License $license */
100
        $this->outputFormattedLicenses($output, $input, $licenses, $groupedByName);
101
102
        return self::SUCCESS;
103
    }
104
105
    /**
106
     * @param  Dependency[]  $dependencies
107
     * @return array
108
     */
109
    private function groupDependenciesByLicense(array $dependencies): array
110
    {
111
        $grouped = [];
112
113
        foreach ($dependencies as $dependency) {
114
            [$license] = $dependency->getLicenses();
115
116
            if (! isset($grouped[$license])) {
117
                $grouped[$license] = [];
118
            }
119
            $grouped[$license][] = $dependency;
120
        }
121
122
        return $grouped;
123
    }
124
125
    /**
126
     * @param  Dependency[]  $dependencies
127
     * @param  string[]  $filters
128
     * @return array
129
     */
130
    private function filterLicenses(array $dependencies, array $filters): array
131
    {
132
        if ($filters === []) {
133
            return $dependencies;
134
        }
135
136
        $validLicences = [];
137
138
        foreach ($dependencies as $dependency) {
139
            foreach ($dependency->getLicenses() as $license) {
140
                if (in_array(strtolower($license), array_map('strtolower', $filters))) {
141
                    $validLicences[] = $dependency;
142
                    continue 2;
143
                }
144
            }
145
        }
146
147
        return $validLicences;
148
    }
149
150
    private function lookUpLicenses(array $licenses, OutputInterface $output, $useCache = true): array
151
    {
152
        if (! $useCache) {
153
            $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

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