These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | |||
3 | /* |
||
4 | * This file is part of the puli/manager package. |
||
5 | * |
||
6 | * (c) Bernhard Schussek <[email protected]> |
||
7 | * |
||
8 | * For the full copyright and license information, please view the LICENSE |
||
9 | * file that was distributed with this source code. |
||
10 | */ |
||
11 | |||
12 | namespace Puli\Manager\Conflict; |
||
13 | |||
14 | /** |
||
15 | * Detects configuration conflicts between packages. |
||
16 | * |
||
17 | * Packages may claim "tokens" for themselves. A token, in that sense, can be |
||
18 | * any integer or string. If packages claim the same token, a conflict is |
||
19 | * raised: |
||
20 | * |
||
21 | * ```php |
||
22 | * use Puli\Manager\Conflict\PackageConflictDetector; |
||
23 | * |
||
24 | * $detector = new PackageConflictDetector(); |
||
25 | * $detector->claim('/app/config', 'package1'); |
||
26 | * $detector->claim('/app/views', 'package2'); |
||
27 | * |
||
28 | * $conflicts = $detector->detectConflicts(array('/app/config', '/app/views')); |
||
29 | * // => array() |
||
30 | * |
||
31 | * $detector->claim('/app/config', 'package2'); |
||
32 | * |
||
33 | * $conflicts = $detector->detectConflicts(array('/app/config', '/app/views')); |
||
34 | * // => array(PackageConflict) |
||
35 | * ``` |
||
36 | * |
||
37 | * You can resolve conflicts by passing an {@link OverrideGraph} to the |
||
38 | * detector. The override graph has package names as nodes. When the conflict |
||
39 | * graph contains an edge from package A to package B, then package A is |
||
40 | * considered to be overridden by package B. Claims for the same resources will |
||
41 | * not result in conflicts for these packages: |
||
42 | * |
||
43 | * ```php |
||
44 | * use Puli\Manager\Conflict\OverrideGraph; |
||
45 | * use Puli\Manager\Conflict\PackageConflictDetector; |
||
46 | * |
||
47 | * $graph = new OverrideGraph(); |
||
48 | * $graph->addPackageName('package1'); |
||
49 | * $graph->addPackageName('package2'); |
||
50 | * |
||
51 | * // package1 is overridden by package2 |
||
52 | * $graph->addEdge('package1', 'package2'); |
||
53 | * |
||
54 | * $detector = new PackageConflictDetector($graph); |
||
55 | * $detector->claim('/app/config', 'package1'); |
||
56 | * $detector->claim('/app/config', 'package2'); |
||
57 | * |
||
58 | * // The conflict has been resolved |
||
59 | * $conflict s= $detector->detectConflict(array('/app/config')); |
||
60 | * // => array() |
||
61 | * ``` |
||
62 | * |
||
63 | * @since 1.0 |
||
64 | * |
||
65 | * @author Bernhard Schussek <[email protected]> |
||
66 | */ |
||
67 | class PackageConflictDetector |
||
68 | { |
||
69 | /** |
||
70 | * @var OverrideGraph |
||
71 | */ |
||
72 | private $overrideGraph; |
||
73 | |||
74 | /** |
||
75 | * @var bool[][] |
||
76 | */ |
||
77 | private $tokens = array(); |
||
78 | |||
79 | /** |
||
80 | * Creates a new conflict detector. |
||
81 | * |
||
82 | * @param OverrideGraph|null $overrideGraph The graph indicating which |
||
83 | * package is overridden by which |
||
84 | * other package. |
||
85 | */ |
||
86 | 73 | public function __construct(OverrideGraph $overrideGraph = null) |
|
87 | { |
||
88 | 73 | $this->overrideGraph = $overrideGraph ?: new OverrideGraph(); |
|
89 | 73 | } |
|
90 | |||
91 | /** |
||
92 | * Claims a token for a package. |
||
93 | * |
||
94 | * @param int|string $token The claimed token. Can be any integer or |
||
95 | * string. |
||
96 | * @param string $packageName The package name. |
||
97 | */ |
||
98 | 64 | public function claim($token, $packageName) |
|
99 | { |
||
100 | 64 | if (!isset($this->tokens[$token])) { |
|
101 | 64 | $this->tokens[$token] = array(); |
|
102 | } |
||
103 | |||
104 | 64 | $this->tokens[$token][$packageName] = true; |
|
105 | 64 | } |
|
106 | |||
107 | /** |
||
108 | * Releases a package's claim for a token. |
||
109 | * |
||
110 | * @param int|string $token The claimed token. Can be any integer or |
||
111 | * string. |
||
112 | * @param string $packageName The package name. |
||
113 | */ |
||
114 | 16 | public function release($token, $packageName) |
|
115 | { |
||
116 | 16 | unset($this->tokens[$token][$packageName]); |
|
117 | 16 | } |
|
118 | |||
119 | /** |
||
120 | * Checks the passed tokens for conflicts. |
||
121 | * |
||
122 | * If no tokens are passed, all tokens are checked. |
||
123 | * |
||
124 | * A conflict is returned for every token that is claimed by two packages |
||
125 | * that are not connected by an edge in the override graph. In other words, |
||
126 | * if two packages A and B claim the same token, an edge must exist from A |
||
127 | * to B (A is overridden by B) or from B to A (B is overridden by A). |
||
128 | * Otherwise a conflict is returned. |
||
129 | * |
||
130 | * @param int[]|string[]|null $tokens The tokens to check. If `null`, all |
||
0 ignored issues
–
show
Documentation
introduced
by
Loading history...
|
|||
131 | * claimed tokens are checked for |
||
132 | * conflicts. You are advised to pass |
||
133 | * tokens if possible to improve the |
||
134 | * performance of the conflict detection. |
||
135 | * |
||
136 | * @return PackageConflict[] The detected conflicts. |
||
137 | */ |
||
138 | 73 | public function detectConflicts(array $tokens = null) |
|
139 | { |
||
140 | 73 | $tokens = null === $tokens ? array_keys($this->tokens) : $tokens; |
|
141 | 73 | $conflicts = array(); |
|
142 | |||
143 | 73 | foreach ($tokens as $token) { |
|
144 | // Claim was released |
||
145 | 64 | if (!isset($this->tokens[$token])) { |
|
146 | continue; |
||
147 | } |
||
148 | |||
149 | 64 | $packageNames = array_keys($this->tokens[$token]); |
|
150 | |||
151 | // Token claimed by only one package |
||
152 | 64 | if (1 === count($packageNames)) { |
|
153 | 58 | continue; |
|
154 | } |
||
155 | |||
156 | 38 | $sortedNames = $this->overrideGraph->getSortedPackageNames($packageNames); |
|
157 | 38 | $conflictingNames = array(); |
|
158 | |||
159 | // An edge must exist between each package pair in the sorted set, |
||
160 | // otherwise the dependencies are not sufficiently defined |
||
161 | 38 | for ($i = 1, $l = count($sortedNames); $i < $l; ++$i) { |
|
162 | 38 | if (!$this->overrideGraph->hasEdge($sortedNames[$i - 1], $sortedNames[$i])) { |
|
163 | 26 | $conflictingNames[$sortedNames[$i - 1]] = true; |
|
164 | 26 | $conflictingNames[$sortedNames[$i]] = true; |
|
165 | } |
||
166 | } |
||
167 | |||
168 | 38 | if (count($conflictingNames) > 0) { |
|
169 | 38 | $conflicts[] = new PackageConflict($token, array_keys($conflictingNames)); |
|
170 | } |
||
171 | } |
||
172 | |||
173 | 73 | return $conflicts; |
|
174 | } |
||
175 | } |
||
176 |