Passed
Push — master ( 119ef1...d221d7 )
by Allan
02:31
created

PatchCommand::createSourcesEnablerConfig()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 13
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 6
nc 2
nop 0
dl 0
loc 13
rs 10
c 0
b 0
f 0
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 Symfony\Component\Console\Input\InputInterface;
9
use Symfony\Component\Console\Output\OutputInterface;
10
use Symfony\Component\Console\Input\InputOption;
11
use Symfony\Component\Console\Input\InputArgument;
12
use Composer\Script\ScriptEvents;
13
14
use Vaimo\ComposerPatches\Patch\Definition as Patch;
15
use Vaimo\ComposerPatches\Patch\Definition as PatchDefinition;
16
17
use Vaimo\ComposerPatches\Repository\PatchesApplier\ListResolvers;
18
use Vaimo\ComposerPatches\Config;
19
use Vaimo\ComposerPatches\Interfaces\ListResolverInterface;
20
21
use Vaimo\ComposerPatches\Environment;
22
23
class PatchCommand extends \Composer\Command\BaseCommand
24
{
25
    protected function configure()
26
    {
27
        $this->setName('patch');
28
        $this->setDescription('Apply registered patches to current project');
29
30
        $this->addArgument(
31
            'targets',
32
            InputArgument::IS_ARRAY,
33
            'Packages for the patcher to target',
34
            array()
35
        );
36
37
        $this->addOption(
38
            '--redo',
39
            null,
40
            InputOption::VALUE_NONE,
41
            'Re-patch all packages or a specific package when targets defined'
42
        );
43
44
        $this->addOption(
45
            '--undo',
46
            null,
47
            InputOption::VALUE_NONE,
48
            'Remove all patches or a specific patch when targets defined'
49
        );
50
51
        $this->addOption(
52
            '--no-dev',
53
            null,
54
            InputOption::VALUE_NONE,
55
            'Disables installation of require-dev packages'
56
        );
57
58
        $this->addOption(
59
            '--filter',
60
            null,
61
            InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY,
62
            'Apply only those patch files/sources that match with provided filter'
63
        );
64
        
65
        $this->addOption(
66
            '--explicit',
67
            null,
68
            InputOption::VALUE_NONE,
69
            'Show information for every patch that gets re-applied (due to package reset)'
70
        );
71
72
        $this->addOption(
73
            '--show-reapplies',
74
            null,
75
            InputOption::VALUE_NONE,
76
            'Alias for \'explicit\' argument'
77
        );
78
        
79
        $this->addOption(
80
            '--from-source',
81
            null,
82
            InputOption::VALUE_NONE,
83
            'Apply patches based on information directly from packages in vendor folder'
84
        );
85
86
        $this->addOption(
87
            '--graceful',
88
            null,
89
            InputOption::VALUE_NONE,
90
            'Continue even when some patch fails to apply'
91
        );
92
93
        $this->addOption(
94
            '--force',
95
            null,
96
            InputOption::VALUE_NONE,
97
            'Force package reset even when it has local change'
98
        );
99
    }
100
    
101
    /**
102
     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
103
     *
104
     * @param InputInterface $input
105
     * @param OutputInterface $output
106
     * @return int|void|null
107
     * @throws \Exception
108
     */
109
    protected function execute(InputInterface $input, OutputInterface $output)
110
    {
111
        $composer = $this->getComposer();
112
113
        $appIO = $this->getIO();
114
        
115
        $isDevMode = !$input->getOption('no-dev');
116
117
        $behaviourFlags = $this->getBehaviourFlags($input);
118
        $shouldUndo = !$behaviourFlags['redo'] && $behaviourFlags['undo'];
119
        
120
        $bootstrapFactory = new \Vaimo\ComposerPatches\Factories\BootstrapFactory($composer, $appIO);
121
122
        $configFactory = new \Vaimo\ComposerPatches\Factories\ConfigFactory($composer, array(
123
            Config::PATCHER_FORCE_REAPPLY => $behaviourFlags['redo'],
124
            Config::PATCHER_FROM_SOURCE => (bool)$input->getOption('from-source'),
125
            Config::PATCHER_GRACEFUL => (bool)$input->getOption('graceful')
126
                || $behaviourFlags['redo']
127
                || $behaviourFlags['undo'],
128
            Config::PATCHER_SOURCES => $this->createSourcesEnablerConfig()
129
        ));
130
        
131
        $filters = $this->resolveActiveFilters($input, $behaviourFlags);
132
        
133
        $listResolver = $this->createListResolver($behaviourFlags, $filters);
134
        
135
        $this->configureEnvironmentForBehaviour($behaviourFlags);
136
137
        $outputTriggers = $this->resolveOutputTriggers($filters, $behaviourFlags);
138
        $outputStrategy = new \Vaimo\ComposerPatches\Strategies\OutputStrategy($outputTriggers);
139
        $bootstrap = $bootstrapFactory->create($configFactory, $listResolver, $outputStrategy);
140
141
        $runtimeUtils = new \Vaimo\ComposerPatches\Utils\RuntimeUtils();
142
        $lockSanitizer = new \Vaimo\ComposerPatches\Repository\Lock\Sanitizer($appIO);
143
        $repository = $composer->getRepositoryManager()->getLocalRepository();
144
        
145
        $result = $runtimeUtils->executeWithPostAction(
146
            function () use ($shouldUndo, $filters, $bootstrap, $isDevMode) {
147
                if ($shouldUndo && !array_filter($filters)) {
148
                    $bootstrap->stripPatches($isDevMode);
149
                    
150
                    return true;
151
                }
152
153
                return $bootstrap->applyPatches($isDevMode);
154
            },
155
            function () use ($repository, $lockSanitizer) {
156
                $repository->write();
157
                $lockSanitizer->sanitize();
158
            }
159
        );
160
161
        $composer->getEventDispatcher()->dispatchScript(ScriptEvents::POST_INSTALL_CMD, $isDevMode);
162
163
        return (int)!$result;
164
    }
165
    
166
    private function createSourcesEnablerConfig()
167
    {
168
        $configDefaults = new \Vaimo\ComposerPatches\Config\Defaults();
169
170
        $defaultValues = $configDefaults->getPatcherConfig();
171
        
172
        if (isset($defaultValues[Config::PATCHER_SOURCES]) && is_array($defaultValues[Config::PATCHER_SOURCES])) {
173
            $sourceTypes = 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

173
            $sourceTypes = array_keys(/** @scrutinizer ignore-type */ $defaultValues[Config::PATCHER_SOURCES]);
Loading history...
174
175
            return array_fill_keys($sourceTypes, true);
176
        }
177
178
        return array();
179
    }
180
181
    protected function getBehaviourFlags(InputInterface $input)
182
    {
183
        return array(
184
            'redo' => $this->getOptionGraceful($input, 'redo'),
185
            'undo' => $this->getOptionGraceful($input, 'undo'),
186
            'force' => $this->getOptionGraceful($input, 'force'),
187
            'explicit' => $this->getOptionGraceful($input, 'explicit')
188
                || $this->getOptionGraceful($input, 'show-reapplies')
189
        );
190
    }
191
    
192
    private function getOptionGraceful(InputInterface $input, $name)
193
    {
194
        return $input->hasOption($name) && $input->getOption($name);
195
    }
196
    
197
    private function resolveActiveFilters(InputInterface $input, array $behaviourFlags)
198
    {
199
        $filters = array(
200
            Patch::SOURCE => $input->getOption('filter'),
201
            Patch::TARGETS => $input->getArgument('targets')
202
        );
203
204
        $hasFilers = (bool)array_filter($filters);
205
206
        if (!$hasFilers && $behaviourFlags['redo']) {
207
            $filters[Patch::SOURCE] = array('*');
208
        }
209
210
        return $filters;
211
    }
212
    
213
    private function configureEnvironmentForBehaviour(array $behaviourFlags)
214
    {
215
        $runtimeUtils = new \Vaimo\ComposerPatches\Utils\RuntimeUtils();
216
217
        $runtimeUtils->setEnvironmentValues(array(
218
            Environment::FORCE_RESET => (int)$behaviourFlags['force']
219
        ));
220
    }
221
    
222
    private function resolveOutputTriggers(array $filters, array $behaviourFlags)
223
    {
224
        $hasFilers = (bool)array_filter($filters);
225
226
        $isExplicit = $behaviourFlags['explicit'];
227
        
228
        if (!$hasFilers && $behaviourFlags['redo']) {
229
            $isExplicit = true;
230
        }
231
        
232
        $outputTriggerFlags = array(
233
            Patch::STATUS_NEW => !$hasFilers,
234
            Patch::STATUS_CHANGED => !$hasFilers,
235
            Patch::STATUS_MATCH => true,
236
            Patch::SOURCE => $isExplicit,
237
            Patch::URL => $isExplicit
238
        );
239
240
        return array_keys(
241
            array_filter($outputTriggerFlags)
242
        );
243
    }
244
    
245
    private function createListResolver(array $behaviourFlags, array $filters)
246
    {
247
        $listResolver = new ListResolvers\FilteredListResolver($filters);
248
249
        $isDefaultBehaviour = !$behaviourFlags['redo'] && !$behaviourFlags['undo'];
250
251
        $listResolver = $this->attachBehaviourToListResolver($listResolver, $behaviourFlags);
252
        
253
        if ($isDefaultBehaviour) {
254
            $listResolver = new ListResolvers\ChangesListResolver($listResolver);
255
        }
256
        
257
        return $listResolver;
258
    }
259
    
260
    private function attachBehaviourToListResolver(ListResolverInterface $listResolver, array $behaviourFlags)
261
    {
262
        $shouldUndo = !$behaviourFlags['redo'] && $behaviourFlags['undo'];
263
264
        if ($shouldUndo) {
265
            return new ListResolvers\InvertedListResolver($listResolver);
266
        }
267
268
        return new ListResolvers\InclusiveListResolver($listResolver);
269
    }
270
}
271