Passed
Push — master ( bd4ad1...e3bb8d )
by Allan
03:57
created

PatchesApplier::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 29
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 15
nc 1
nop 8
dl 0
loc 29
rs 9.7666
c 0
b 0
f 0

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
class PatchesApplier
13
{
14
    /**
15
     * @var \Vaimo\ComposerPatches\Package\Collector
16
     */
17
    private $packageCollector;
18
19
    /**
20
     * @var \Vaimo\ComposerPatches\Managers\RepositoryManager
21
     */
22
    private $repositoryManager;
23
24
    /**
25
     * @var \Vaimo\ComposerPatches\Package\PatchApplier
26
     */
27
    private $packagePatchApplier;
28
29
    /**
30
     * @var \Vaimo\ComposerPatches\Repository\PatchesApplier\QueueGenerator
31
     */
32
    private $queueGenerator;
33
34
    /**
35
     * @var \Vaimo\ComposerPatches\Managers\PatcherStateManager
36
     */
37
    private $patcherStateManager;
38
39
    /**
40
     * @var \Vaimo\ComposerPatches\Repository\StateGenerator
41
     */
42
    private $repositoryStateGenerator;
43
44
    /**
45
     * @var \Vaimo\ComposerPatches\Package\PatchApplier\InfoLogger
46
     */
47
    private $patchInfoLogger;
48
49
    /**
50
     * @var \Vaimo\ComposerPatches\Strategies\OutputStrategy
51
     */
52
    private $outputStrategy;
53
    
54
    /**
55
     * @var \Vaimo\ComposerPatches\Logger
56
     */
57
    private $logger;
58
59
    /**
60
     * @var \Vaimo\ComposerPatches\Patch\DefinitionList\Analyser
61
     */
62
    private $patchListAnalyser;
63
    
64
    /**
65
     * @var \Vaimo\ComposerPatches\Patch\DefinitionList\Transformer
66
     */
67
    private $patchListTransformer;
68
    
69
    /**
70
     * @var \Vaimo\ComposerPatches\Package\PatchApplier\StatusConfig
71
     */
72
    private $statusConfig;
73
    
74
    /**
75
     * @var \Vaimo\ComposerPatches\Utils\PackageUtils
76
     */
77
    private $packageUtils;
78
79
    /**
80
     * \Vaimo\ComposerPatches\Utils\PatchListUtils
81
     */
82
    private $patchListUtils;
83
    
84
    /**
85
     * @param \Vaimo\ComposerPatches\Package\Collector $packageCollector
86
     * @param \Vaimo\ComposerPatches\Managers\RepositoryManager $repositoryManager
87
     * @param \Vaimo\ComposerPatches\Package\PatchApplier $patchApplier
88
     * @param \Vaimo\ComposerPatches\Repository\PatchesApplier\QueueGenerator $queueGenerator
89
     * @param \Vaimo\ComposerPatches\Managers\PatcherStateManager $patcherStateManager
90
     * @param \Vaimo\ComposerPatches\Package\PatchApplier\InfoLogger $patchInfoLogger
91
     * @param \Vaimo\ComposerPatches\Strategies\OutputStrategy $outputStrategy
92
     * @param \Vaimo\ComposerPatches\Logger $logger
93
     */
94
    public function __construct(
95
        \Vaimo\ComposerPatches\Package\Collector $packageCollector,
96
        \Vaimo\ComposerPatches\Managers\RepositoryManager $repositoryManager,
97
        \Vaimo\ComposerPatches\Package\PatchApplier $patchApplier,
98
        \Vaimo\ComposerPatches\Repository\PatchesApplier\QueueGenerator $queueGenerator,
99
        \Vaimo\ComposerPatches\Managers\PatcherStateManager $patcherStateManager,
100
        \Vaimo\ComposerPatches\Package\PatchApplier\InfoLogger $patchInfoLogger,
101
        \Vaimo\ComposerPatches\Strategies\OutputStrategy $outputStrategy,
102
        \Vaimo\ComposerPatches\Logger $logger
103
    ) {
104
        $this->packageCollector = $packageCollector;
105
        $this->repositoryManager = $repositoryManager;
106
        $this->packagePatchApplier = $patchApplier;
107
        $this->queueGenerator = $queueGenerator;
108
        $this->patcherStateManager = $patcherStateManager;
109
        $this->patchInfoLogger = $patchInfoLogger;
110
        $this->logger = $logger;
111
112
        $this->repositoryStateGenerator = new \Vaimo\ComposerPatches\Repository\StateGenerator(
113
            $this->packageCollector
114
        );
115
116
        $this->outputStrategy = $outputStrategy;
117
118
        $this->patchListAnalyser = new \Vaimo\ComposerPatches\Patch\DefinitionList\Analyser();
119
        $this->patchListTransformer = new \Vaimo\ComposerPatches\Patch\DefinitionList\Transformer();
120
        $this->statusConfig = new \Vaimo\ComposerPatches\Package\PatchApplier\StatusConfig();
121
        $this->packageUtils = new \Vaimo\ComposerPatches\Utils\PackageUtils();
122
        $this->patchListUtils = new \Vaimo\ComposerPatches\Utils\PatchListUtils();
123
    }
124
125
    /**
126
     * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
127
     * @SuppressWarnings(PHPMD.NPathComplexity)
128
     * @SuppressWarnings(PHPMD.CyclomaticComplexity)
129
     *
130
     * @param Repository $repository
131
     * @param array $patches
132
     * @return bool
133
     * @throws \Vaimo\ComposerPatches\Exceptions\PackageNotFound
134
     * @throws \Vaimo\ComposerPatches\Exceptions\PackageResetException
135
     */
136
    public function apply(Repository $repository, array $patches)
137
    {
138
        $packages = $this->packageCollector->collect($repository);
139
140
        $packagesUpdated = false;
141
142
        $repositoryState = $this->repositoryStateGenerator->generate($repository);
143
        
144
        $applyQueue = $this->queueGenerator->generateApplyQueue($patches, $repositoryState);
145
        $removeQueue = $this->queueGenerator->generateRemovalQueue($applyQueue, $repositoryState);
146
        $resetQueue = $this->queueGenerator->generateResetQueue($applyQueue);
147
        
148
        $applyQueue = array_map('array_filter', $applyQueue);
149
        
150
        $patchQueueFootprints = $this->patchListTransformer->createSimplifiedList($applyQueue);
151
152
        $labels = array_diff_key($this->statusConfig->getLabels(), array('unknown' => true));
153
154
        $applyQueue = $this->updateStatusLabels($applyQueue, $labels);
155
        $removeQueue = $this->updateStatusLabels($removeQueue, $labels);
156
        
157
        foreach ($packages as $packageName => $package) {
158
            $hasPatches = !empty($applyQueue[$packageName]);
159
160
            $patchTargets = $hasPatches ?
161
                $this->patchListAnalyser->getAllTargets(array($applyQueue[$packageName]))
162
                : array($packageName);
163
                
164
            $itemsToReset = array_intersect($resetQueue, $patchTargets);
165
166
            $resetResult = array();
167
168
            foreach ($itemsToReset as $targetName) {
169
                $resetTarget = $packages[$targetName];
170
171
                $resetPatches = $this->packageUtils->resetAppliedPatches($resetTarget);
172
                $resetResult[$targetName] = is_array($resetPatches) ? $resetPatches : array();
173
174
                if (!$hasPatches && $resetPatches && !isset($patchQueueFootprints[$targetName])) {
175
                    $this->logger->writeRaw(
176
                        'Resetting patches for <info>%s</info> (%s)',
177
                        array($targetName, count($resetResult[$targetName]))
178
                    );
179
                }
180
181
                $this->repositoryManager->resetPackage($repository, $resetTarget);
182
183
                $packagesUpdated = $packagesUpdated || (bool)$resetResult[$targetName];
184
            }
185
            
186
            $resetQueue = array_diff($resetQueue, $patchTargets);
187
188
            if (!$hasPatches) {
189
                continue;
190
            }
191
            
192
            $changedTargets = $this->resolveChangedTargets($packages, $patchTargets, $patchQueueFootprints);
193
            
194
            if (empty($changedTargets)) {
195
                continue;
196
            }
197
198
            $queuedPatches = array_filter(
199
                $applyQueue[$packageName],
200
                function ($data) use ($changedTargets) {
201
                    return array_intersect($data[Patch::TARGETS], $changedTargets);
202
                }
203
            );
204
            
205
            $muteDepth = null;
206
            
207
            $patchRemovals = isset($removeQueue[$packageName])
208
                ? $removeQueue[$packageName]
209
                : array();
210
            
211
            if (!$this->shouldAllowOutput($queuedPatches, $patchRemovals)) {
212
                $muteDepth = $this->logger->mute();
213
            }
214
215
            $this->logger->writeRaw(
216
                'Applying patches for <info>%s</info> (%s)',
217
                array($packageName, count($queuedPatches))
218
            );
219
            
220
            $this->processQueues($repository, $package, $queuedPatches, $patchRemovals);
221
222
            $packagesUpdated = true;
223
224
            $this->logger->writeNewLine();
225
226
            if ($muteDepth !== null) {
227
                $this->logger->unMute($muteDepth);
228
            }
229
        }
230
231
        return $packagesUpdated;
232
    }
233
234
    private function updateStatusLabels(array $queue, array $labels)
235
    {
236
        foreach ($queue as $target => $group) {
237
            foreach ($group as $path => $item) {
238
                $status = isset($item[Patch::STATUS]) ? $item[Patch::STATUS] : 'unknown';
239
240
                if (!isset($labels[$status])) {
241
                    continue;
242
                }
243
244
                $queue[$target][$path][Patch::STATUS_LABEL] = $labels[$status];
245
            }
246
        }
247
248
        return $queue;
249
    }
250
    
251
    private function processQueues(Repository $repository, Package $package, $additions, $removals)
252
    {
253
        try {
254
            if ($removals) {
255
                $processIndentation = $this->logger->push('~');
256
257
                foreach ($removals as $item) {
258
                    $this->patchInfoLogger->outputPatchInfo($item);
259
                }
260
261
                $this->logger->reset($processIndentation);
262
            }
263
264
            $this->processPatchesForPackage($repository, $package, $additions);
265
        } catch (\Exception $exception) {
266
            $this->logger->unMute();
267
268
            throw $exception;
269
        }
270
    }
271
    
272
    private function resolveChangedTargets(array $packages, array $patchTargets, array $patchFootprints)
273
    {
274
        $changesMap = array();
275
276
        foreach ($patchTargets as $targetName) {
277
            $targetQueue = array();
278
279
            if (isset($patchFootprints[$targetName])) {
280
                $targetQueue = $patchFootprints[$targetName];
281
            }
282
283
            if (!isset($packages[$targetName])) {
284
                throw new \Vaimo\ComposerPatches\Exceptions\PackageNotFound(
285
                    sprintf(
286
                        'Unknown target "%s" found when checking patch changes for: %s',
287
                        $targetName,
288
                        implode(',', array_keys($targetQueue))
289
                    )
290
                );
291
            }
292
293
            $changesMap[$targetName] = $this->packageUtils->hasPatchChanges(
294
                $packages[$targetName],
295
                $targetQueue
296
            );
297
        }
298
299
        return array_keys(array_filter($changesMap));
300
    }
301
    
302
    private function processPatchesForPackage(Repository $repository, Package $package, array $patchesQueue)
303
    {
304
        $processIndentation = $this->logger->push('~');
305
306
        try {
307
            $appliedPatches = $this->packagePatchApplier->applyPatches($package, $patchesQueue);
308
309
            $this->patcherStateManager->registerAppliedPatches($repository, $appliedPatches);
310
311
            $this->logger->reset($processIndentation);
312
        } catch (\Vaimo\ComposerPatches\Exceptions\PatchFailureException $exception) {
313
            $failedPath = $exception->getFailedPatchPath();
314
315
            $paths = array_keys($patchesQueue);
316
            $appliedPaths = array_slice($paths, 0, array_search($failedPath, $paths));
0 ignored issues
show
Bug introduced by
It seems like array_search($failedPath, $paths) can also be of type false and string; however, parameter $length of array_slice() does only seem to accept integer, 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

316
            $appliedPaths = array_slice($paths, 0, /** @scrutinizer ignore-type */ array_search($failedPath, $paths));
Loading history...
317
            $appliedPatches = array_intersect_key($patchesQueue, array_flip($appliedPaths));
318
319
            $this->patcherStateManager->registerAppliedPatches($repository, $appliedPatches);
320
321
            throw $exception;
322
        }
323
    }
324
    
325
    private function shouldAllowOutput(array $patches, array $removals)
326
    {
327
        return $this->outputStrategy->shouldAllowForPatches($patches)
328
            || $this->outputStrategy->shouldAllowForPatches($removals);
329
    }
330
}
331