PackageRequirementsResolver   A
last analyzed

Complexity

Total Complexity 14

Size/Duplication

Total Lines 136
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 48
c 2
b 0
f 0
dl 0
loc 136
ccs 50
cts 50
cp 1
rs 10
wmc 14

4 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 3 1
A resolve() 0 36 3
B resolveGraph() 0 56 9
A getDependents() 0 3 1
1
<?php
2
/**
3
 * Copyright MediaCT. All rights reserved.
4
 * https://www.mediact.nl
5
 */
6
7
namespace Mediact\DependencyGuard\Composer\Locker;
8
9
use Composer\Package\Locker;
10
use Composer\Package\PackageInterface;
11
use SplObjectStorage;
12
13
class PackageRequirementsResolver implements PackageRequirementsResolverInterface
14
{
15
    /** @var SplObjectStorage|PackageInterface[][][] */
16
    private $resolvedLockers;
17
18
    /**
19
     * Constructor.
20
     *
21
     * @param SplObjectStorage|null $resolvedLockers
22
     */
23 1
    public function __construct(SplObjectStorage $resolvedLockers = null)
24
    {
25 1
        $this->resolvedLockers = $resolvedLockers ?? new SplObjectStorage();
26 1
    }
27
28
    /**
29
     * Resolve the dependents graph for the given Composer locker.
30
     *
31
     * @param Locker $locker
32
     *
33
     * @return array|PackageInterface[][]
34
     */
35 3
    public function resolve(Locker $locker): array
36
    {
37 3
        if (!$this->resolvedLockers->contains($locker)) {
38 3
            $lockedPackages = $locker->getLockedRepository()->getPackages();
39
40 3
            $this->resolvedLockers->attach(
41 3
                $locker,
42 3
                array_map(
43
                    function (array $packages) use ($lockedPackages) : array {
44 2
                        return array_reduce(
45 2
                            $lockedPackages,
46
                            function (
47
                                array $carry,
48
                                PackageInterface $package
49
                            ) use ($packages) : array {
50 2
                                if (in_array(
51 2
                                    $package->getName(),
52 2
                                    $packages,
53 2
                                    true
54
                                )) {
55 2
                                    $carry[] = $package;
56
                                }
57
58 2
                                return $carry;
59 2
                            },
60 2
                            []
61
                        );
62 3
                    },
63 3
                    $this->resolveGraph(
64 3
                        ...$locker->getLockData()['packages'] ?? []
65
                    )
66
                )
67
            );
68
        }
69
70 3
        return $this->resolvedLockers->offsetGet($locker);
71
    }
72
73
    /**
74
     * Get the dependent packages for the given package, using the given locker.
75
     *
76
     * @param string $package
77
     * @param Locker $locker
78
     *
79
     * @return array|PackageInterface[]
80
     */
81 3
    public function getDependents(string $package, Locker $locker): array
82
    {
83 3
        return $this->resolve($locker)[$package] ?? [];
84
    }
85
86
    /**
87
     * Resolve the dependents graph for the given packages.
88
     *
89
     * @param array ...$packages
90
     *
91
     * @return string[]
92
     */
93 3
    private function resolveGraph(array ...$packages): array
94
    {
95 3
        $graph = array_reduce(
96 3
            $packages,
97
            function (array $carry, array $package): array {
98 2
                foreach (array_keys($package['require'] ?? []) as $link) {
99 2
                    if (!preg_match('/^[^\/]+\/[^\/]+$/', $link)) {
100
                        // Most likely a platform requirement.
101
                        // E.g.: php
102
                        // E.g.: ext-openssl
103 2
                        continue;
104
                    }
105
106 2
                    if (!array_key_exists($link, $carry)) {
107 2
                        $carry[$link] = [];
108
                    }
109
110 2
                    if (!in_array($package['name'], $carry[$link], true)) {
111 2
                        $carry[$link][] = $package['name'];
112
                    }
113
                }
114
115 2
                return $carry;
116 3
            },
117 3
            []
118
        );
119
120
        // While the graph keeps receiving updates, keep on resolving.
121 3
        for ($previousGraph = []; $graph !== $previousGraph;) {
122
            // Do not update the previous graph before the current iteration
123
            // has started.
124 2
            $previousGraph = $graph;
125
126
            // For each package and its dependents in the graph ...
127 2
            foreach ($graph as $package => $dependents) {
128
                // ... Update the dependents of the package with grandparents.
129 2
                $graph[$package] = array_reduce(
130
                    // Determine grandparents by looking up the parents of the
131
                    // available dependents.
132 2
                    $dependents,
133
                    function (array $carry, string $parent) use ($graph): array {
134 2
                        foreach ($graph[$parent] ?? [] as $grandparent) {
135 1
                            if (!in_array($grandparent, $carry, true)) {
136 1
                                $carry[] = $grandparent;
137
                            }
138
                        }
139
140 2
                        return $carry;
141 2
                    },
142
                    // Start out with the current list of dependents.
143 2
                    $dependents
144
                );
145
            }
146
        }
147
148 3
        return $graph;
149
    }
150
}
151