Passed
Push — master ( d221d7...012d01 )
by Allan
02:58 queued 11s
created

PatchesApplier::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 30
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 1
eloc 16
c 2
b 0
f 0
nc 1
nop 8
dl 0
loc 30
rs 9.7333

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
/**
3
 * Copyright © Vaimo Group. All rights reserved.
4
 * See LICENSE_VAIMO.txt for license details.
5
 */
6
namespace Vaimo\ComposerPatches\Repository;
7
8
use Composer\Repository\WritableRepositoryInterface as Repository;
9
use Composer\Package\PackageInterface as Package;
10
use Vaimo\ComposerPatches\Patch\Definition as Patch;
11
12
/**
13
 * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
14
 */
15
class PatchesApplier
16
{
17
    /**
18
     * @var \Vaimo\ComposerPatches\Package\Collector
19
     */
20
    private $packageCollector;
21
22
    /**
23
     * @var \Vaimo\ComposerPatches\Managers\RepositoryManager
24
     */
25
    private $repositoryManager;
26
27
    /**
28
     * @var \Vaimo\ComposerPatches\Package\PatchApplier
29
     */
30
    private $packagePatchApplier;
31
32
    /**
33
     * @var \Vaimo\ComposerPatches\Repository\PatchesApplier\QueueGenerator
34
     */
35
    private $queueGenerator;
36
37
    /**
38
     * @var \Vaimo\ComposerPatches\Managers\PatcherStateManager
39
     */
40
    private $patcherStateManager;
41
42
    /**
43
     * @var \Vaimo\ComposerPatches\Repository\StateGenerator
44
     */
45
    private $repositoryStateGenerator;
46
47
    /**
48
     * @var \Vaimo\ComposerPatches\Package\PatchApplier\InfoLogger
49
     */
50
    private $patchInfoLogger;
51
52
    /**
53
     * @var \Vaimo\ComposerPatches\Strategies\OutputStrategy
54
     */
55
    private $outputStrategy;
56
    
57
    /**
58
     * @var \Vaimo\ComposerPatches\Logger
59
     */
60
    private $logger;
61
62
    /**
63
     * @var \Vaimo\ComposerPatches\Patch\DefinitionList\Analyser
64
     */
65
    private $patchListAnalyser;
66
    
67
    /**
68
     * @var \Vaimo\ComposerPatches\Patch\DefinitionList\Transformer
69
     */
70
    private $patchListTransformer;
71
    
72
    /**
73
     * @var \Vaimo\ComposerPatches\Package\PatchApplier\StatusConfig
74
     */
75
    private $statusConfig;
76
    
77
    /**
78
     * @var \Vaimo\ComposerPatches\Utils\PackageUtils
79
     */
80
    private $packageUtils;
81
82
    /**
83
     * @var \Vaimo\ComposerPatches\Utils\DataUtils
84
     */
85
    private $dataUtils;
86
87
    /**
88
     * @var \Vaimo\ComposerPatches\Console\OutputGenerator
89
     */
90
    private $outputGenerator;
91
    
92
    /**
93
     * @param \Vaimo\ComposerPatches\Package\Collector $packageCollector
94
     * @param \Vaimo\ComposerPatches\Managers\RepositoryManager $repositoryManager
95
     * @param \Vaimo\ComposerPatches\Package\PatchApplier $patchApplier
96
     * @param \Vaimo\ComposerPatches\Repository\PatchesApplier\QueueGenerator $queueGenerator
97
     * @param \Vaimo\ComposerPatches\Managers\PatcherStateManager $patcherStateManager
98
     * @param \Vaimo\ComposerPatches\Package\PatchApplier\InfoLogger $patchInfoLogger
99
     * @param \Vaimo\ComposerPatches\Strategies\OutputStrategy $outputStrategy
100
     * @param \Vaimo\ComposerPatches\Logger $logger
101
     */
102
    public function __construct(
103
        \Vaimo\ComposerPatches\Package\Collector $packageCollector,
104
        \Vaimo\ComposerPatches\Managers\RepositoryManager $repositoryManager,
105
        \Vaimo\ComposerPatches\Package\PatchApplier $patchApplier,
106
        \Vaimo\ComposerPatches\Repository\PatchesApplier\QueueGenerator $queueGenerator,
107
        \Vaimo\ComposerPatches\Managers\PatcherStateManager $patcherStateManager,
108
        \Vaimo\ComposerPatches\Package\PatchApplier\InfoLogger $patchInfoLogger,
109
        \Vaimo\ComposerPatches\Strategies\OutputStrategy $outputStrategy,
110
        \Vaimo\ComposerPatches\Logger $logger
111
    ) {
112
        $this->packageCollector = $packageCollector;
113
        $this->repositoryManager = $repositoryManager;
114
        $this->packagePatchApplier = $patchApplier;
115
        $this->queueGenerator = $queueGenerator;
116
        $this->patcherStateManager = $patcherStateManager;
117
        $this->patchInfoLogger = $patchInfoLogger;
118
        $this->outputStrategy = $outputStrategy;
119
        $this->logger = $logger;
120
121
        $this->repositoryStateGenerator = new \Vaimo\ComposerPatches\Repository\StateGenerator(
122
            $this->packageCollector
123
        );
124
125
        $this->outputGenerator = new \Vaimo\ComposerPatches\Console\OutputGenerator($logger);
126
        
127
        $this->patchListAnalyser = new \Vaimo\ComposerPatches\Patch\DefinitionList\Analyser();
128
        $this->patchListTransformer = new \Vaimo\ComposerPatches\Patch\DefinitionList\Transformer();
129
        $this->statusConfig = new \Vaimo\ComposerPatches\Package\PatchApplier\StatusConfig();
130
        $this->packageUtils = new \Vaimo\ComposerPatches\Utils\PackageUtils();
131
        $this->dataUtils = new \Vaimo\ComposerPatches\Utils\DataUtils();
132
    }
133
134
    /**
135
     * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
136
     * @SuppressWarnings(PHPMD.NPathComplexity)
137
     * @SuppressWarnings(PHPMD.CyclomaticComplexity)
138
     *
139
     * @param Repository $repository
140
     * @param array $patches
141
     * @return bool
142
     * @throws \Vaimo\ComposerPatches\Exceptions\PackageNotFound
143
     * @throws \Vaimo\ComposerPatches\Exceptions\PackageResetException
144
     */
145
    public function apply(Repository $repository, array $patches)
146
    {
147
        $packages = $this->packageCollector->collect($repository);
148
149
        $packagesUpdated = false;
150
151
        $repositoryState = $this->repositoryStateGenerator->generate($repository);
152
        
153
        $applyQueue = $this->queueGenerator->generateApplyQueue($patches, $repositoryState);
154
        $removeQueue = $this->queueGenerator->generateRemovalQueue($applyQueue, $repositoryState);
155
        $resetQueue = $this->queueGenerator->generateResetQueue($applyQueue);
156
        
157
        $applyQueue = array_map('array_filter', $applyQueue);
158
        
159
        $patchQueueFootprints = $this->patchListTransformer->createSimplifiedList($applyQueue);
160
161
        $labels = array_diff_key($this->statusConfig->getLabels(), array('unknown' => true));
162
163
        $applyQueue = $this->updateStatusLabels($applyQueue, $labels);
164
        $removeQueue = $this->updateStatusLabels($removeQueue, $labels);
165
        
166
        foreach ($packages as $packageName => $package) {
167
            $hasPatches = !empty($applyQueue[$packageName]);
168
169
            $patchTargets = $hasPatches ?
170
                $this->patchListAnalyser->getAllTargets(array($applyQueue[$packageName]))
171
                : array($packageName);
172
173
            $itemsToReset = $this->dataUtils->extractItems($resetQueue, $patchTargets);
174
            
175
            $resetResult = array();
176
177
            foreach ($itemsToReset as $targetName) {
178
                $resetTarget = $packages[$targetName];
179
180
                $resetPatches = $this->packageUtils->resetAppliedPatches($resetTarget);
181
                $resetResult[$targetName] = is_array($resetPatches) ? $resetPatches : array();
182
183
                if (!$hasPatches && $resetPatches && !isset($patchQueueFootprints[$targetName])) {
184
                    $this->logger->writeRaw(
185
                        'Resetting patches for <info>%s</info> (%s)',
186
                        array($targetName, count($resetResult[$targetName]))
187
                    );
188
                }
189
190
                $this->repositoryManager->resetPackage($repository, $resetTarget);
191
            }
192
            
193
            $packagesUpdated = $packagesUpdated || (bool)array_filter($resetResult);
194
195
            if (!$hasPatches) {
196
                continue;
197
            }
198
            
199
            $changedTargets = $this->resolveChangedTargets($packages, $patchTargets, $patchQueueFootprints);
200
            
201
            if (empty($changedTargets)) {
202
                continue;
203
            }
204
205
            $queuedPatches = array_filter(
206
                $applyQueue[$packageName],
207
                function ($data) use ($changedTargets) {
208
                    return array_intersect($data[Patch::TARGETS], $changedTargets);
209
                }
210
            );
211
    
212
            $this->updatePackage(
213
                $package,
214
                $repository,
215
                $queuedPatches,
216
                $this->dataUtils->extractValue($removeQueue, $packageName, array())
217
            );
218
            
219
            $packagesUpdated = true;
220
        }
221
222
        return $packagesUpdated;
223
    }
224
    
225
    private function updatePackage(Package $package, Repository $repository, array $additions, array $removals)
226
    {
227
        $muteDepth = null;
228
229
        $packageName = $package->getName();
230
        
231
        if (!$this->shouldAllowOutput($additions, $removals)) {
232
            $muteDepth = $this->logger->mute();
233
        }
234
235
        $this->logger->writeRaw(
236
            'Applying patches for <info>%s</info> (%s)',
237
            array($packageName, count($additions))
238
        );
239
240
        $this->processQueues($package, $repository, $additions, $removals);
241
        
242
        $this->logger->writeNewLine();
243
244
        if ($muteDepth !== null) {
245
            $this->logger->unMute($muteDepth);
246
        }
247
    }
248
249
    private function updateStatusLabels(array $queue, array $labels)
250
    {
251
        foreach ($queue as $target => $group) {
252
            foreach ($group as $path => $item) {
253
                $status = isset($item[Patch::STATUS]) ? $item[Patch::STATUS] : 'unknown';
254
255
                if (!isset($labels[$status])) {
256
                    continue;
257
                }
258
259
                $queue[$target][$path][Patch::STATUS_LABEL] = $labels[$status];
260
            }
261
        }
262
263
        return $queue;
264
    }
265
    
266
    private function processQueues(Package $package, Repository $repository, $additions, $removals)
267
    {
268
        try {
269
            if ($removals) {
270
                $processIndentation = $this->logger->push('~');
271
272
                foreach ($removals as $item) {
273
                    $this->patchInfoLogger->outputPatchInfo($item);
274
                }
275
276
                $this->logger->reset($processIndentation);
277
            }
278
279
            $this->processPatchesForPackage($repository, $package, $additions);
280
        } catch (\Exception $exception) {
281
            $this->logger->unMute();
282
283
            throw $exception;
284
        }
285
    }
286
    
287
    private function resolveChangedTargets(array $packages, array $patchTargets, array $patchFootprints)
288
    {
289
        $changesMap = array();
290
291
        foreach ($patchTargets as $targetName) {
292
            $targetQueue = array();
293
294
            if (isset($patchFootprints[$targetName])) {
295
                $targetQueue = $patchFootprints[$targetName];
296
            }
297
298
            if (!isset($packages[$targetName])) {
299
                throw new \Vaimo\ComposerPatches\Exceptions\PackageNotFound(
300
                    sprintf(
301
                        'Unknown target "%s" found when checking patch changes for: %s',
302
                        $targetName,
303
                        implode(',', array_keys($targetQueue))
304
                    )
305
                );
306
            }
307
308
            $changesMap[$targetName] = $this->packageUtils->hasPatchChanges(
309
                $packages[$targetName],
310
                $targetQueue
311
            );
312
        }
313
314
        return array_keys(array_filter($changesMap));
315
    }
316
    
317
    private function processPatchesForPackage(Repository $repository, Package $package, array $patchesQueue)
318
    {
319
        $processIndentation = $this->logger->push('~');
320
321
        try {
322
            $appliedPatches = $this->packagePatchApplier->applyPatches($package, $patchesQueue);
323
324
            $this->patcherStateManager->registerAppliedPatches($repository, $appliedPatches);
325
326
            $this->logger->reset($processIndentation);
327
        } catch (\Vaimo\ComposerPatches\Exceptions\PatchFailureException $exception) {
328
            $this->logger->push();
329
            
330
            $this->logger->write('error', $exception->getMessage());
331
332
            $previousError = $exception->getPrevious();
333
            
334
            if ($previousError instanceof \Exception) {
0 ignored issues
show
introduced by
$previousError is always a sub-type of Exception.
Loading history...
335
                $this->outputGenerator->generateForException($previousError);
336
            }
337
338
            $failedPath = $exception->getFailedPatchPath();
339
340
            $paths = array_keys($patchesQueue);
341
            
342
            $failureIndex = array_search($failedPath, $paths);
343
344
            $appliedPatches = array();
345
346
            if (is_int($failureIndex)) {
347
                $appliedPaths = array_slice($paths, 0, $failureIndex);
348
                $appliedPatches = array_intersect_key($patchesQueue, array_flip($appliedPaths));
349
            }
350
351
            $this->patcherStateManager->registerAppliedPatches($repository, $appliedPatches);
352
353
            throw $exception;
354
        }
355
    }
356
    
357
    private function shouldAllowOutput(array $patches, array $removals)
358
    {
359
        return $this->outputStrategy->shouldAllowForPatches($patches)
360
            || $this->outputStrategy->shouldAllowForPatches($removals);
361
    }
362
}
363