Passed
Push — master ( ad1278...4a352c )
by Allan
04:23
created

ListCommand::configure()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 69
Code Lines 48

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 48
nc 1
nop 0
dl 0
loc 69
rs 9.1344
c 0
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * Copyright © Vaimo Group. All rights reserved.
4
 * See LICENSE_VAIMO.txt for license details.
5
 */
6
namespace Vaimo\ComposerPatches\Composer\Commands;
7
8
use Composer\Composer;
9
use Symfony\Component\Console\Input\InputInterface;
10
use Symfony\Component\Console\Output\OutputInterface;
11
use Vaimo\ComposerPatches\Patch\Definition as PatchDefinition;
12
13
use Vaimo\ComposerPatches\Interfaces\ListResolverInterface as ListResolver;
14
use Symfony\Component\Console\Input\InputOption;
15
use Vaimo\ComposerPatches\Repository\PatchesApplier\ListResolvers;
16
use Vaimo\ComposerPatches\Config;
17
use Vaimo\ComposerPatches\Patch\DefinitionList\LoaderComponents;
18
19
class ListCommand extends \Composer\Command\BaseCommand
20
{
21
    protected function configure()
22
    {
23
        parent::configure();
24
25
        $this->setName('patch:list');
26
27
        $this->setDescription('List all registered and eligible (based on project config) patches');
28
29
        $this->addArgument(
30
            'targets',
31
            \Symfony\Component\Console\Input\InputArgument::IS_ARRAY,
32
            'Packages for the patcher to target',
33
            array()
34
        );
35
36
        $this->addOption(
37
            '--no-dev',
38
            null,
39
            InputOption::VALUE_NONE,
40
            'Disables installation of require-dev packages'
41
        );
42
43
        $this->addOption(
44
            '--filter',
45
            null,
46
            InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY,
47
            'Mark certain patches with MATCH in the output list'
48
        );
49
50
        $this->addOption(
51
            '--excluded',
52
            null,
53
            InputOption::VALUE_NONE,
54
            'Include patches that have been ruled out based on some constraint mismatch'
55
        );
56
57
        $this->addOption(
58
            '--with-excludes',
59
            null,
60
            InputOption::VALUE_NONE,
61
            'Alias for \'excluded\' argument'
62
        );
63
64
        $this->addOption(
65
            '--with-affected',
66
            null,
67
            InputOption::VALUE_NONE,
68
            'Mark patches that would get re-applied when changed/new patches are added (due to package reinstall)'
69
        );
70
        
71
        $this->addOption(
72
            '--brief',
73
            null,
74
            InputOption::VALUE_NONE,
75
            'Show more compact output of the list (remove description, owner , etc)'
76
        );
77
        
78
        $this->addOption(
79
            '--status',
80
            null,
81
            InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY,
82
            'Match specific statuses (changed, new, applied, removed)'
83
        );
84
85
        $this->addOption(
86
            '--from-source',
87
            null,
88
            InputOption::VALUE_NONE,
89
            'Use latest information from package configurations in vendor folder'
90
        );
91
    }
92
93
    protected function execute(InputInterface $input, OutputInterface $output)
94
    {
95
        $composer = $this->getComposer();
96
97
        $isDevMode = !$input->getOption('no-dev');
98
        $withExcluded = $input->getOption('excluded') || $input->getOption('with-excludes');
99
        $withAffected = $input->getOption('with-affected');
100
        $beBrief = $input->getOption('brief');
101
        
102
        $filters = array(
103
            PatchDefinition::SOURCE => $input->getOption('filter'),
104
            PatchDefinition::TARGETS => $input->getArgument('targets')
105
        );
106
107
        $statusFilters = array_map('strtolower', $input->getOption('status'));
0 ignored issues
show
Bug introduced by
It seems like $input->getOption('status') can also be of type boolean and null and string; however, parameter $arr1 of array_map() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

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

107
        $statusFilters = array_map('strtolower', /** @scrutinizer ignore-type */ $input->getOption('status'));
Loading history...
108
        
109
        $pluginConfig = $this->createConfigWithEnabledSources($composer);
110
        
111
        
112
        $filteredPool = $this->createLoaderPool();
113
        
114
        $packageInfoResolver = new \Vaimo\ComposerPatches\Package\InfoResolver(
115
            $composer->getInstallationManager(),
116
            $composer->getConfig()->get(\Vaimo\ComposerPatches\Composer\ConfigKeys::VENDOR_DIR)
117
        );
118
        
119
        $unfilteredPool = $this->createLoaderPool(array(
120
            'constraints' => false,
121
            'platform' => false,
122
            'local-exclude' => false,
123
            'root-patch' => false,
124
            'global-exclude' => false,
125
            'targets-resolver' => new LoaderComponents\TargetsResolverComponent($packageInfoResolver, true)
126
        ));
127
128
        $hasFilers = (bool)array_filter($filters);
129
        
130
        $listResolver = new ListResolvers\FilteredListResolver($filters);
131
        
132
        $loaderFactory = new \Vaimo\ComposerPatches\Factories\PatchesLoaderFactory($composer);
133
        
134
        $repository = $composer->getRepositoryManager()->getLocalRepository();
135
        
136
        $filteredLoader = $loaderFactory->create($filteredPool, $pluginConfig, $isDevMode);
137
        $filteredPatches = $filteredLoader->loadFromPackagesRepository($repository);
138
        
139
        $queueGenerator = $this->createQueueGenerator($composer, $pluginConfig, $listResolver);
140
141
        $repoStateGenerator = $this->createStateGenerator($composer);
142
        
143
        $repositoryState = $repoStateGenerator->generate($repository);
144
        
145
        $applyQueue = $queueGenerator->generateApplyQueue($filteredPatches, $repositoryState);
146
        $removeQueue = $queueGenerator->generateRemovalQueue($applyQueue, $repositoryState);
147
        $applyQueue = array_map('array_filter', $applyQueue);
148
149
        $patchListUtils = new \Vaimo\ComposerPatches\Utils\PatchListUtils();
150
        $patchListUpdater = new \Vaimo\ComposerPatches\Patch\DefinitionList\Updater();
151
        
152
        $filteredPatches = $patchListUtils->mergeLists(
153
            $filteredPatches,
154
            $removeQueue
155
        );
156
157
        if ($withAffected) {
158
            $applyQueue = $patchListUpdater->embedInfoToItems(
159
                $applyQueue,
160
                array(PatchDefinition::STATUS => 'affected'),
161
                true
162
            );
163
        }
164
        
165
        $filteredPatches = $patchListUtils->mergeLists(
166
            $filteredPatches,
167
            $patchListUtils->intersectListsByName($applyQueue, $filteredPatches)
168
        );
169
        
170
        $filteredPatches = $patchListUpdater->embedInfoToItems(
171
            $filteredPatches,
172
            array(PatchDefinition::STATUS => 'applied'),
173
            true
174
        );
175
        
176
        if ($hasFilers) {
177
            $filteredPatches = $listResolver->resolvePatchesQueue($filteredPatches);
178
        }
179
180
        $filterUtils = new \Vaimo\ComposerPatches\Utils\FilterUtils();
181
182
        if ($statusFilters) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $statusFilters of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
183
            $statusFilter = $filterUtils->composeRegex($statusFilters, '/');
184
185
            $filteredPatches = $patchListUtils->applyDefinitionKeyValueFilter(
186
                $filteredPatches,
187
                $statusFilter,
188
                PatchDefinition::STATUS
189
            );
190
        }
191
        
192
        $patches = array_filter($filteredPatches);
193
        
194
        $shouldAddExcludes = $withExcluded
195
            && (!$statusFilters || preg_match($filterUtils->composeRegex($statusFilters, '/'), 'excluded'));
0 ignored issues
show
Bug Best Practice introduced by
The expression $statusFilters of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
196
        
197
        if ($shouldAddExcludes) {
198
            $unfilteredLoader = $loaderFactory->create($unfilteredPool, $pluginConfig, $isDevMode);
199
200
            $allPatches = $unfilteredLoader->loadFromPackagesRepository($repository);
201
            
202
            $patchesQueue = $listResolver->resolvePatchesQueue($allPatches);
203
            
204
            $excludedPatches = $patchListUpdater->updateStatuses(
205
                array_filter($patchListUtils->diffListsByPath($patchesQueue, $filteredPatches)),
206
                'excluded'
207
            );
208
209
            $patches = array_replace_recursive(
210
                $patches,
211
                $patchListUpdater->updateStatuses($excludedPatches, 'excluded')
212
            );
213
214
            array_walk($patches, function (array &$group) {
215
                ksort($group);
216
            }, $patches);
217
        }
218
        
219
        if ($beBrief) {
220
            $patches = $patchListUpdater->embedInfoToItems($patches, array(
221
                PatchDefinition::LABEL => false,
222
                PatchDefinition::OWNER => false
223
            ));
224
        }
225
        
226
        $this->generateOutput($output, $patches);
227
    }
228
    
229
    private function createConfigWithEnabledSources(Composer $composer)
230
    {
231
        $configDefaults = new \Vaimo\ComposerPatches\Config\Defaults();
232
233
        $defaultValues = $configDefaults->getPatcherConfig();
234
235
        $sourceKeys = array_keys($defaultValues[Config::PATCHER_SOURCES]);
0 ignored issues
show
Bug introduced by
It seems like $defaultValues[Vaimo\Com...onfig::PATCHER_SOURCES] can also be of type boolean; however, parameter $input of array_keys() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

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

235
        $sourceKeys = array_keys(/** @scrutinizer ignore-type */ $defaultValues[Config::PATCHER_SOURCES]);
Loading history...
236
237
        $pluginConfig = array(
238
            Config::PATCHER_SOURCES => array_fill_keys($sourceKeys, true)
239
        );
240
241
        $configFactory = new \Vaimo\ComposerPatches\Factories\ConfigFactory($composer);
242
        
243
        return $configFactory->create(array($pluginConfig));
244
    }
245
    
246
    private function createStateGenerator(Composer $composer)
247
    {
248
        $packageCollector = new \Vaimo\ComposerPatches\Package\Collector(
249
            array($composer->getPackage())
250
        );
251
252
        return new \Vaimo\ComposerPatches\Repository\StateGenerator(
253
            $packageCollector
254
        );
255
    }
256
    
257
    private function createQueueGenerator(Composer $composer, Config $config, ListResolver $listResolver)
258
    {
259
        $stateAnalyserFactory = new \Vaimo\ComposerPatches\Factories\RepositoryStateAnalyserFactory(
260
            $composer
261
        );
262
        
263
        $changesListResolver = new ListResolvers\ChangesListResolver($listResolver);
264
265
        $stateAnalyser = $stateAnalyserFactory->create($config);
266
        
267
        return new \Vaimo\ComposerPatches\Repository\PatchesApplier\QueueGenerator(
268
            $changesListResolver,
269
            $stateAnalyser
270
        );
271
    }
272
    
273
    private function createLoaderPool(array $componentUpdates = array())
274
    {
275
        $componentPool = new \Vaimo\ComposerPatches\Patch\DefinitionList\Loader\ComponentPool(
276
            $this->getComposer(),
277
            $this->getIO()
278
        );
279
280
        foreach ($componentUpdates as $componentName => $replacement) {
281
            $componentPool->registerComponent($componentName, $replacement);
282
        }
283
        
284
        return $componentPool;
285
    }
286
    
287
    private function generateOutput(OutputInterface $output, array $list)
288
    {
289
        $statusConfig = new \Vaimo\ComposerPatches\Package\PatchApplier\StatusConfig();
290
291
        $statusDecorators = $statusConfig->getLabels();
292
        
293
        foreach ($list as $packageName => $patches) {
294
            $output->writeln(sprintf('<info>%s</info>', $packageName));
295
296
            foreach ($patches as $path => $info) {
297
                $status = isset($info[PatchDefinition::STATUS])
298
                    ? $info[PatchDefinition::STATUS]
299
                    : 'unknown';
300
301
                $statusLabel = sprintf(' [%s]', $statusDecorators[$status]);
302
                $owner = $info[PatchDefinition::OWNER];
303
                
304
                if ($owner === PatchDefinition::OWNER_UNKNOWN) {
305
                    $patchInfoLabel = sprintf('  ~ %s%s', $path, $statusLabel);
306
                } elseif ($owner) {
307
                    $patchInfoLabel = sprintf('  ~ <info>%s</info>: %s%s', $owner, $path, $statusLabel);
308
                } else {
309
                    $patchInfoLabel = sprintf('%s%s', $path, $statusLabel);
310
                }
311
312
                $output->writeln($patchInfoLabel);
313
314
                $descriptionLines = array_filter(
315
                    explode(PHP_EOL, $info[PatchDefinition::LABEL])
316
                );
317
                
318
                foreach ($descriptionLines as $line) {
319
                    $output->writeln(sprintf('    <comment>%s</comment>', $line));
320
                }
321
            }
322
323
            $output->writeln('');
324
        }
325
    }
326
}
327