Passed
Push — master ( dc1c28...b12c89 )
by Allan
02:38
created

PatchCommand::configureEnvironmentForBehaviour()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 3
c 0
b 0
f 0
nc 1
nop 1
dl 0
loc 6
rs 10
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 Composer\Script\ScriptEvents;
12
13
use Vaimo\ComposerPatches\Patch\Definition as Patch;
14
use Vaimo\ComposerPatches\Patch\Definition as PatchDefinition;
15
16
use Vaimo\ComposerPatches\Repository\PatchesApplier\ListResolvers;
17
use Vaimo\ComposerPatches\Config;
18
19
use Vaimo\ComposerPatches\Environment;
20
21
class PatchCommand extends \Composer\Command\BaseCommand
22
{
23
    protected function configure()
24
    {
25
        $this->setName('patch');
26
        $this->setDescription('Apply registered patches to current project');
27
28
        $this->addArgument(
29
            'targets',
30
            \Symfony\Component\Console\Input\InputArgument::IS_ARRAY,
31
            'Packages for the patcher to target',
32
            array()
33
        );
34
35
        $this->addOption(
36
            '--redo',
37
            null,
38
            InputOption::VALUE_NONE,
39
            'Re-patch all packages or a specific package when targets defined'
40
        );
41
42
        $this->addOption(
43
            '--undo',
44
            null,
45
            InputOption::VALUE_NONE,
46
            'Remove all patches or a specific patch when targets defined'
47
        );
48
49
        $this->addOption(
50
            '--no-dev',
51
            null,
52
            InputOption::VALUE_NONE,
53
            'Disables installation of require-dev packages'
54
        );
55
56
        $this->addOption(
57
            '--filter',
58
            null,
59
            InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY,
60
            'Apply only those patch files/sources that match with provided filter'
61
        );
62
        
63
        $this->addOption(
64
            '--explicit',
65
            null,
66
            InputOption::VALUE_NONE,
67
            'Show information for every patch that gets re-applied (due to package reset)'
68
        );
69
70
        $this->addOption(
71
            '--show-reapplies',
72
            null,
73
            InputOption::VALUE_NONE,
74
            'Alias for \'explicit\' argument'
75
        );
76
        
77
        $this->addOption(
78
            '--from-source',
79
            null,
80
            InputOption::VALUE_NONE,
81
            'Apply patches based on information directly from packages in vendor folder'
82
        );
83
84
        $this->addOption(
85
            '--graceful',
86
            null,
87
            InputOption::VALUE_NONE,
88
            'Continue even when some patch fails to apply'
89
        );
90
91
        $this->addOption(
92
            '--force',
93
            null,
94
            InputOption::VALUE_NONE,
95
            'Force package reset even when it has local change'
96
        );
97
    }
98
99
    protected function getBehaviourFlags(InputInterface $input)
100
    {
101
        return array(
102
            'redo' => (bool)$input->getOption('redo'),
103
            'undo' => (bool)$input->getOption('undo'),
104
            'force' => (bool)$input->getOption('force')
105
        );
106
    }
107
108
    /**
109
     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
110
     *
111
     * @param InputInterface $input
112
     * @param OutputInterface $output
113
     * @return int|void|null
114
     * @throws \Exception
115
     */
116
    protected function execute(InputInterface $input, OutputInterface $output)
117
    {
118
        $configDefaults = new \Vaimo\ComposerPatches\Config\Defaults();
119
        $defaults = $configDefaults->getPatcherConfig();
120
121
        $composer = $this->getComposer();
122
123
        $appIO = $this->getIO();
124
        
125
        $isExplicit = $input->getOption('explicit') || $input->getOption('show-reapplies');
126
        
127
        $isDevMode = !$input->getOption('no-dev');
128
129
        $behaviourFlags = $this->getBehaviourFlags($input);
130
        $shouldUndo = !$behaviourFlags['redo'] && $behaviourFlags['undo'];
131
        
132
        $bootstrapFactory = new \Vaimo\ComposerPatches\Factories\BootstrapFactory($composer, $appIO);
133
        
134
        $filters = array(
135
            Patch::SOURCE => $input->getOption('filter'),
136
            Patch::TARGETS => $input->getArgument('targets')
137
        );
138
139
        $configFactory = new \Vaimo\ComposerPatches\Factories\ConfigFactory($composer, array(
140
            Config::PATCHER_FORCE_REAPPLY => $behaviourFlags['redo'],
141
            Config::PATCHER_FROM_SOURCE => (bool)$input->getOption('from-source'),
142
            Config::PATCHER_GRACEFUL => (bool)$input->getOption('graceful')
143
                || $behaviourFlags['redo']
144
                || $behaviourFlags['undo'],
145
            Config::PATCHER_SOURCES => array_fill_keys(array_keys($defaults[Config::PATCHER_SOURCES]), true)
0 ignored issues
show
Bug introduced by
It seems like $defaults[Vaimo\Composer...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

145
            Config::PATCHER_SOURCES => array_fill_keys(array_keys(/** @scrutinizer ignore-type */ $defaults[Config::PATCHER_SOURCES]), true)
Loading history...
146
        ));
147
148
        if ($behaviourFlags['redo'] && !array_filter($filters)) {
149
            $isExplicit = true;
150
        }
151
152
        $hasFilers = (bool)array_filter($filters);
153
154
        if (!$hasFilers && $behaviourFlags['redo']) {
155
            $filters[Patch::SOURCE] = array('*');
156
        }
157
        
158
        $listResolver = $this->createListResolver($behaviourFlags, $filters);
159
        
160
        $this->configureEnvironmentForBehaviour($behaviourFlags);
161
162
        $outputTriggers = $this->resolveOutputTriggers($filters, $isExplicit);
163
        $outputStrategy = new \Vaimo\ComposerPatches\Strategies\OutputStrategy($outputTriggers);
164
        $bootstrap = $bootstrapFactory->create($configFactory, $listResolver, $outputStrategy);
165
166
        $runtimeUtils = new \Vaimo\ComposerPatches\Utils\RuntimeUtils();
167
        $lockSanitizer = new \Vaimo\ComposerPatches\Repository\Lock\Sanitizer($appIO);
168
        $repository = $composer->getRepositoryManager()->getLocalRepository();
169
        
170
        $result = $runtimeUtils->executeWithPostAction(
171
            function () use ($shouldUndo, $filters, $bootstrap, $isDevMode) {
172
                if ($shouldUndo && !array_filter($filters)) {
173
                    $bootstrap->stripPatches($isDevMode);
174
                    
175
                    return true;
176
                }
177
178
                return $bootstrap->applyPatches($isDevMode);
179
            },
180
            function () use ($repository, $lockSanitizer) {
181
                $repository->write();
182
                $lockSanitizer->sanitize();
183
            }
184
        );
185
186
        $composer->getEventDispatcher()->dispatchScript(ScriptEvents::POST_INSTALL_CMD, $isDevMode);
187
188
        return (int)!$result;
189
    }
190
    
191
    private function configureEnvironmentForBehaviour(array $behaviourFlags)
192
    {
193
        $runtimeUtils = new \Vaimo\ComposerPatches\Utils\RuntimeUtils();
194
195
        $runtimeUtils->setEnvironmentValues(array(
196
            Environment::FORCE_RESET => (int)$behaviourFlags['force']
197
        ));
198
    }
199
    
200
    private function resolveOutputTriggers(array $filters, $isExplicit)
201
    {
202
        $hasFilers = (bool)array_filter($filters);
203
        
204
        $outputTriggerFlags = array(
205
            Patch::STATUS_NEW => !$hasFilers,
206
            Patch::STATUS_CHANGED => !$hasFilers,
207
            Patch::STATUS_MATCH => true,
208
            Patch::SOURCE => $isExplicit,
209
            Patch::URL => $isExplicit
210
        );
211
212
        return array_keys(
213
            array_filter($outputTriggerFlags)
214
        );
215
    }
216
    
217
    private function createListResolver(array $behaviourFlags, array $filters)
218
    {
219
        $listResolver = new ListResolvers\FilteredListResolver($filters);
220
221
        $shouldUndo = !$behaviourFlags['redo'] && $behaviourFlags['undo'];
222
        $isDefaultBehaviour = !$behaviourFlags['redo'] && !$behaviourFlags['undo'];
223
        
224
        if ($shouldUndo) {
225
            $listResolver = new ListResolvers\InvertedListResolver($listResolver);
226
        } else {
227
            $listResolver = new ListResolvers\InclusiveListResolver($listResolver);
228
        }
229
230
        if ($isDefaultBehaviour) {
231
            $listResolver = new ListResolvers\ChangesListResolver($listResolver);
232
        }
233
        
234
        return $listResolver;
235
    }
236
}
237