1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* Copyright © Vaimo Group. All rights reserved. |
4
|
|
|
* See LICENSE_VAIMO.txt for license details. |
5
|
|
|
*/ |
6
|
|
|
namespace Vaimo\ComposerPatches\Utils; |
7
|
|
|
|
8
|
|
|
use Vaimo\ComposerPatches\Patch\Definition as Patch; |
9
|
|
|
|
10
|
|
|
class PatchListUtils |
11
|
|
|
{ |
12
|
|
|
public function createSimplifiedList(array $patches) |
13
|
|
|
{ |
14
|
|
|
$groups = $this->createTargetsList($patches); |
15
|
|
|
|
16
|
|
|
$result = array_map(function ($group) { |
17
|
|
|
$fingerprints = array_map(function ($item) { |
18
|
|
|
return sprintf( |
19
|
|
|
'%s, %s:%s', |
20
|
|
|
isset($item[Patch::LABEL]) ? $item[Patch::LABEL] : '{no label}', |
21
|
|
|
Patch::HASH, |
22
|
|
|
isset($item[Patch::HASH]) && $item[Patch::HASH] ? $item[Patch::HASH] : '{no hash}' |
23
|
|
|
); |
24
|
|
|
}, $group); |
25
|
|
|
|
26
|
|
|
$keys = array_map(function ($key, $item) { |
27
|
|
|
return sprintf('%s%s%s', $item[Patch::OWNER], Patch::SOURCE_INFO_SEPARATOR, $key); |
28
|
|
|
}, array_keys($group), $group); |
29
|
|
|
|
30
|
|
|
return array_combine($keys, $fingerprints); |
31
|
|
|
|
32
|
|
|
}, $groups); |
33
|
|
|
|
34
|
|
|
return $result; |
35
|
|
|
} |
36
|
|
|
|
37
|
|
|
public function createDetailedList(array $patches) |
38
|
|
|
{ |
39
|
|
|
$result = array(); |
40
|
|
|
|
41
|
|
|
$labelInfoMatcher = sprintf('/%s:(?P<hash>.*)/', Patch::HASH); |
42
|
|
|
|
43
|
|
|
foreach ($patches as $target => $group) { |
44
|
|
|
$result[$target] = array(); |
45
|
|
|
|
46
|
|
|
if (!is_array($group)) { |
47
|
|
|
continue; |
48
|
|
|
} |
49
|
|
|
|
50
|
|
|
foreach ($group as $sourceInfo => $label) { |
51
|
|
|
$sourceConfig = explode(Patch::SOURCE_INFO_SEPARATOR, $sourceInfo); |
52
|
|
|
|
53
|
|
|
$path = array_pop($sourceConfig); |
54
|
|
|
$owner = array_pop($sourceConfig); |
55
|
|
|
|
56
|
|
|
$labelConfig = explode(',', $label); |
57
|
|
|
|
58
|
|
|
preg_match($labelInfoMatcher, trim(end($labelConfig)), $matches); |
59
|
|
|
|
60
|
|
|
$result[$target][$path] = array( |
61
|
|
|
'path' => $path, |
62
|
|
|
'targets' => array($target), |
63
|
|
|
'source' => $path, |
64
|
|
|
'owner' => $owner ? $owner : Patch::OWNER_UNKNOWN, |
65
|
|
|
'label' => implode(',', array_slice($labelConfig, 0, -1)), |
66
|
|
|
'md5' => is_array($matches) && isset($matches['hash']) ? $matches['hash'] : null |
67
|
|
|
); |
68
|
|
|
} |
69
|
|
|
} |
70
|
|
|
|
71
|
|
|
return $result; |
72
|
|
|
} |
73
|
|
|
|
74
|
|
|
public function createTargetsList(array $patches) |
75
|
|
|
{ |
76
|
|
|
$result = array(); |
77
|
|
|
|
78
|
|
|
foreach ($patches as $originName => $patchGroup) { |
79
|
|
|
if (!is_array($patchGroup)) { |
80
|
|
|
continue; |
81
|
|
|
} |
82
|
|
|
|
83
|
|
|
foreach ($patchGroup as $patchPath => $patchInfo) { |
84
|
|
|
foreach ($patchInfo[Patch::TARGETS] as $target) { |
85
|
|
|
if (!isset($result[$target])) { |
86
|
|
|
$result[$target] = array(); |
87
|
|
|
} |
88
|
|
|
|
89
|
|
|
$path = (isset($patchInfo['url']) && $patchInfo['url']) ? $patchInfo['url'] : $patchPath; |
90
|
|
|
|
91
|
|
|
$result[$target][$path] = array_replace( |
92
|
|
|
$patchInfo, |
93
|
|
|
array(Patch::ORIGIN => $originName) |
94
|
|
|
); |
95
|
|
|
} |
96
|
|
|
} |
97
|
|
|
} |
98
|
|
|
|
99
|
|
|
return $result; |
100
|
|
|
} |
101
|
|
|
|
102
|
|
|
public function groupItemsByTarget(array $patchesList) |
103
|
|
|
{ |
104
|
|
|
$result = array(); |
105
|
|
|
|
106
|
|
|
foreach ($patchesList as $origin => $group) { |
107
|
|
|
if (!isset($result[$origin])) { |
108
|
|
|
$result[$origin] = array(); |
109
|
|
|
} |
110
|
|
|
|
111
|
|
|
foreach ($group as $path => $patch) { |
112
|
|
|
foreach ($patch[Patch::TARGETS] as $target) { |
113
|
|
|
$result[$target][$path] = array_replace( |
114
|
|
|
$patch, |
115
|
|
|
array(Patch::ORIGIN => $origin) |
116
|
|
|
); |
117
|
|
|
} |
118
|
|
|
} |
119
|
|
|
} |
120
|
|
|
|
121
|
|
|
return array_filter($result); |
122
|
|
|
} |
123
|
|
|
|
124
|
|
|
public function createOriginList(array $patchesList) |
125
|
|
|
{ |
126
|
|
|
$result = array(); |
127
|
|
|
|
128
|
|
|
foreach ($patchesList as $group) { |
129
|
|
|
foreach ($group as $path => $patch) { |
130
|
|
|
$origin = $patch[Patch::ORIGIN]; |
131
|
|
|
|
132
|
|
|
if (!isset($result[$origin])) { |
133
|
|
|
$result[$origin] = array(); |
134
|
|
|
} |
135
|
|
|
|
136
|
|
|
if (isset($result[$origin][$path])) { |
137
|
|
|
continue; |
138
|
|
|
} |
139
|
|
|
|
140
|
|
|
$result[$origin][$path] = array_diff_key($patch, array(Patch::ORIGIN => true)); |
141
|
|
|
} |
142
|
|
|
} |
143
|
|
|
|
144
|
|
|
return array_filter($result); |
145
|
|
|
} |
146
|
|
|
|
147
|
|
|
public function sanitizeFileSystem(array $patches) |
148
|
|
|
{ |
149
|
|
|
foreach ($patches as $patchGroup) { |
150
|
|
|
foreach ($patchGroup as $patchInfo) { |
151
|
|
|
if (!isset($patchInfo[Patch::TMP]) || !$patchInfo[Patch::TMP]) { |
152
|
|
|
continue; |
153
|
|
|
} |
154
|
|
|
|
155
|
|
|
unlink($patchInfo[Patch::PATH]); |
156
|
|
|
} |
157
|
|
|
} |
158
|
|
|
} |
159
|
|
|
|
160
|
|
|
public function createFlatList(array $patches) |
161
|
|
|
{ |
162
|
|
|
$result = array(); |
163
|
|
|
|
164
|
|
|
foreach ($patches as $patchGroup) { |
165
|
|
|
foreach ($patchGroup as $patchInfo) { |
166
|
|
|
$result[] = $patchInfo; |
167
|
|
|
} |
168
|
|
|
} |
169
|
|
|
|
170
|
|
|
return $result; |
171
|
|
|
} |
172
|
|
|
|
173
|
|
|
public function getAllTargets(array $patches) |
174
|
|
|
{ |
175
|
|
|
$targetList = array(); |
176
|
|
|
|
177
|
|
|
foreach ($patches as $patchGroup) { |
178
|
|
|
foreach ($patchGroup as $patchInfo) { |
179
|
|
|
$targetList = array_merge( |
180
|
|
|
$targetList, |
181
|
|
|
$patchInfo[Patch::TARGETS] |
182
|
|
|
); |
183
|
|
|
} |
184
|
|
|
} |
185
|
|
|
|
186
|
|
|
return array_values( |
187
|
|
|
array_unique($targetList) |
188
|
|
|
); |
189
|
|
|
} |
190
|
|
|
|
191
|
|
|
public function applyDefinitionFilter(array $patches, $filter, $key) |
192
|
|
|
{ |
193
|
|
|
foreach ($patches as &$packagePatches) { |
194
|
|
|
foreach ($packagePatches as &$patchInfo) { |
195
|
|
|
if (!isset($patchInfo[$key])) { |
196
|
|
|
$patchInfo = false; |
197
|
|
|
|
198
|
|
|
continue; |
199
|
|
|
} |
200
|
|
|
|
201
|
|
|
if ($this->shouldIncludePatch($patchInfo[$key], $filter)) { |
202
|
|
|
continue; |
203
|
|
|
} |
204
|
|
|
|
205
|
|
|
$patchInfo = false; |
206
|
|
|
} |
207
|
|
|
} |
208
|
|
|
|
209
|
|
|
return array_filter( |
210
|
|
|
array_map('array_filter', $patches) |
211
|
|
|
); |
212
|
|
|
} |
213
|
|
|
|
214
|
|
|
private function shouldIncludePatch($value, $filter) |
215
|
|
|
{ |
216
|
|
|
if (is_array($value) && preg_grep($filter, $value)) { |
217
|
|
|
return true; |
218
|
|
|
} |
219
|
|
|
|
220
|
|
|
if (is_string($value) && preg_match($filter, $value)) { |
221
|
|
|
return true; |
222
|
|
|
} |
223
|
|
|
|
224
|
|
|
if (is_bool($filter) && $value === $filter) { |
225
|
|
|
return true; |
226
|
|
|
} |
227
|
|
|
|
228
|
|
|
return false; |
229
|
|
|
} |
230
|
|
|
|
231
|
|
|
public function getRelatedPatches(array $patchesList, array $targets) |
232
|
|
|
{ |
233
|
|
|
$scanTargets = $targets; |
234
|
|
|
|
235
|
|
|
$targetsStack = array(); |
236
|
|
|
|
237
|
|
|
$result = array(); |
238
|
|
|
|
239
|
|
|
do { |
240
|
|
|
$targetsUpdates = array(); |
241
|
|
|
|
242
|
|
|
foreach ($patchesList as $owner => $patches) { |
243
|
|
|
foreach ($patches as $patchPath => $patchInfo) { |
244
|
|
|
if (!array_intersect($patchInfo[Patch::TARGETS], $scanTargets)) { |
245
|
|
|
continue; |
246
|
|
|
} |
247
|
|
|
|
248
|
|
|
if (!isset($result[$owner])) { |
249
|
|
|
$result[$owner] = array(); |
250
|
|
|
} |
251
|
|
|
|
252
|
|
|
$result[$owner][$patchPath] = $patchInfo; |
253
|
|
|
|
254
|
|
|
$targetsUpdates = array_merge($targetsUpdates, $patchInfo[Patch::TARGETS]); |
255
|
|
|
} |
256
|
|
|
} |
257
|
|
|
|
258
|
|
|
$targetsStack = array_unique( |
259
|
|
|
array_merge($targetsStack, $scanTargets) |
260
|
|
|
); |
261
|
|
|
|
262
|
|
|
$scanTargets = array_diff($targetsUpdates, $targetsStack, $scanTargets); |
263
|
|
|
} while ($scanTargets); |
|
|
|
|
264
|
|
|
|
265
|
|
|
return $result; |
266
|
|
|
} |
267
|
|
|
|
268
|
|
|
/** |
269
|
|
|
* @SuppressWarnings(PHPMD.BooleanArgumentFlag) |
270
|
|
|
* |
271
|
|
|
* @param array $patches |
272
|
|
|
* @param array|bool|string $update |
273
|
|
|
* @param bool $onlyNew |
274
|
|
|
* @return array |
275
|
|
|
*/ |
276
|
|
|
public function embedInfoToItems(array $patches, $update, $onlyNew = false) |
277
|
|
|
{ |
278
|
|
|
foreach ($patches as $target => $group) { |
279
|
|
|
foreach (array_keys($group) as $path) { |
280
|
|
|
$patches[$target][$path] = is_array($update) |
281
|
|
|
? array_replace( |
282
|
|
|
$patches[$target][$path], |
283
|
|
|
$onlyNew ? array_diff_key($update, array_filter($patches[$target][$path])) : $update |
284
|
|
|
) |
285
|
|
|
: $update; |
286
|
|
|
} |
287
|
|
|
} |
288
|
|
|
|
289
|
|
|
return $patches; |
290
|
|
|
} |
291
|
|
|
|
292
|
|
|
public function filterListByTargets(array $patches, array $targets) |
293
|
|
|
{ |
294
|
|
|
foreach ($patches as $target => $group) { |
295
|
|
|
foreach ($group as $path => $patch) { |
296
|
|
|
if (array_intersect($patch[Patch::TARGETS], $targets)) { |
297
|
|
|
continue; |
298
|
|
|
} |
299
|
|
|
|
300
|
|
|
unset($patches[$target][$path]); |
301
|
|
|
} |
302
|
|
|
} |
303
|
|
|
|
304
|
|
|
return array_filter($patches); |
305
|
|
|
} |
306
|
|
|
|
307
|
|
|
public function mergeLists(array $listA, array $listB) |
308
|
|
|
{ |
309
|
|
|
$result = array(); |
310
|
|
|
|
311
|
|
|
$keys = array_unique( |
312
|
|
|
array_merge(array_keys($listA), array_keys($listB)) |
313
|
|
|
); |
314
|
|
|
|
315
|
|
|
foreach ($keys as $key) { |
316
|
|
|
$result[$key] = array_replace( |
317
|
|
|
isset($listA[$key]) ? $listA[$key] : array(), |
318
|
|
|
isset($listB[$key]) ? $listB[$key] : array() |
319
|
|
|
); |
320
|
|
|
} |
321
|
|
|
|
322
|
|
|
return $result; |
323
|
|
|
} |
324
|
|
|
|
325
|
|
|
public function getAllPaths($patches) |
326
|
|
|
{ |
327
|
|
|
return array_reduce( |
328
|
|
|
$patches, |
329
|
|
|
function ($result, array $group) { |
330
|
|
|
return array_merge( |
331
|
|
|
$result, |
332
|
|
|
array_values( |
333
|
|
|
array_map(function (array $item) { |
334
|
|
|
return $item[Patch::PATH] ? $item[Patch::PATH] : $item[Patch::URL]; |
335
|
|
|
}, $group) |
336
|
|
|
) |
337
|
|
|
); |
338
|
|
|
}, |
339
|
|
|
array() |
340
|
|
|
); |
341
|
|
|
} |
342
|
|
|
|
343
|
|
|
public function diffListsByPath(array $listA, array $listB) |
344
|
|
|
{ |
345
|
|
|
$pathFlags = array_fill_keys($this->getAllPaths($listB), true); |
346
|
|
|
|
347
|
|
|
return array_map(function (array $group) use ($pathFlags) { |
348
|
|
|
return array_filter( |
349
|
|
|
$group, |
350
|
|
|
function (array $item) use ($pathFlags) { |
351
|
|
|
$path = $item[Patch::PATH] ? $item[Patch::PATH] : $item[Patch::URL]; |
352
|
|
|
|
353
|
|
|
return !isset($pathFlags[$path]); |
354
|
|
|
} |
355
|
|
|
); |
356
|
|
|
}, $listA); |
357
|
|
|
} |
358
|
|
|
|
359
|
|
|
public function intersectListsByPath(array $listA, array $listB) |
360
|
|
|
{ |
361
|
|
|
$pathFlags = array_fill_keys($this->getAllPaths($listB), true); |
362
|
|
|
|
363
|
|
|
return array_map(function (array $group) use ($pathFlags) { |
364
|
|
|
return array_filter( |
365
|
|
|
$group, |
366
|
|
|
function (array $item) use ($pathFlags) { |
367
|
|
|
$path = $item[Patch::PATH] ? $item[Patch::PATH] : $item[Patch::URL]; |
368
|
|
|
|
369
|
|
|
return isset($pathFlags[$path]); |
370
|
|
|
} |
371
|
|
|
); |
372
|
|
|
}, $listA); |
373
|
|
|
} |
374
|
|
|
|
375
|
|
|
public function diffListsByName(array $listA, array $listB) |
376
|
|
|
{ |
377
|
|
|
foreach ($listB as $target => $group) { |
378
|
|
|
if (!isset($listA[$target])) { |
379
|
|
|
$listA[$target] = array(); |
380
|
|
|
} |
381
|
|
|
|
382
|
|
|
$listA[$target] = array_diff_key($listA[$target], $group); |
383
|
|
|
} |
384
|
|
|
|
385
|
|
|
return $listA; |
386
|
|
|
} |
387
|
|
|
|
388
|
|
|
public function intersectListsByName(array $listA, array $listB) |
389
|
|
|
{ |
390
|
|
|
$result = array(); |
391
|
|
|
|
392
|
|
|
foreach ($listB as $target => $group) { |
393
|
|
|
if (!isset($listA[$target])) { |
394
|
|
|
continue; |
395
|
|
|
} |
396
|
|
|
|
397
|
|
|
$result[$target] = array_intersect_key($listA[$target], $group); |
398
|
|
|
} |
399
|
|
|
|
400
|
|
|
return $result; |
401
|
|
|
} |
402
|
|
|
|
403
|
|
|
public function updateStatuses(array $patches, $status) |
404
|
|
|
{ |
405
|
|
|
return array_map(function (array $group) use ($status) { |
406
|
|
|
return array_map(function (array $patch) use ($status) { |
407
|
|
|
return array_replace($patch, array( |
408
|
|
|
Patch::STATUS => $status |
409
|
|
|
)); |
410
|
|
|
}, $group); |
411
|
|
|
}, $patches); |
412
|
|
|
} |
413
|
|
|
|
414
|
|
|
public function extractValue($patches, array $keys) |
415
|
|
|
{ |
416
|
|
|
return array_reduce( |
417
|
|
|
$patches, |
418
|
|
|
function (array $result, array $items) use ($keys) { |
419
|
|
|
$values = array_values( |
420
|
|
|
array_map(function ($item) use ($keys) { |
421
|
|
|
foreach ($keys as $key) { |
422
|
|
|
if (!isset($item[$key])) { |
423
|
|
|
continue; |
424
|
|
|
} |
425
|
|
|
|
426
|
|
|
if (!$item[$key]) { |
427
|
|
|
continue; |
428
|
|
|
} |
429
|
|
|
|
430
|
|
|
return $item[$key]; |
431
|
|
|
} |
432
|
|
|
|
433
|
|
|
return null; |
434
|
|
|
}, $items) |
435
|
|
|
); |
436
|
|
|
|
437
|
|
|
return array_merge($result, $values); |
438
|
|
|
}, |
439
|
|
|
array() |
440
|
|
|
); |
441
|
|
|
} |
442
|
|
|
} |
443
|
|
|
|
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.