ILIAS  trunk Revision v11.0_alpha-3011-gc6b235a2e85
Resolver.php
Go to the documentation of this file.
1<?php
2
19declare(strict_types=1);
20
22
24{
53 public function resolveDependencies(array $disambiguation, OfComponent ...$components): array
54 {
55 foreach ($components as $component) {
56 foreach ($component->getInDependencies() as $d) {
57 switch ($d->getType()) {
58 case InType::PULL:
59 $this->resolvePull($component, $d, $components);
60 break;
61 case InType::SEEK:
62 $this->resolveSeek($d, $components);
63 break;
64 case InType::USE:
65 $this->resolveUse($component, $disambiguation, $d, $components);
66 break;
67 }
68 }
69 }
70
71 $cycles = iterator_to_array($this->findCycles(...$components));
72 if (!empty($cycles)) {
73 throw new \LogicException(
74 "Detected Cycles in Dependency Tree: " .
75 join("\n", array_map(
76 fn($cycle) => join(
77 " <- ",
78 array_map(
79 fn($v) => "{$v[0]->getComponentName()} ({$v[1]})",
80 $cycle
81 )
82 ),
83 $cycles
84 ))
85 );
86 }
87
88 return $components;
89 }
90
91 protected function resolvePull(OfComponent $component, In $in, array &$others): void
92 {
93 $candidate = null;
94
95 foreach ($others as $other) {
96 if ($other->offsetExists("PROVIDE: " . $in->getName())) {
97 if (!is_null($candidate)) {
98 throw new \LogicException(
99 "Dependency {$in->getName()} is provided (at least) twice."
100 );
101 }
102 // For PROVIDEd dependencies, there only ever is one implementation.
103 $candidate = $other["PROVIDE: " . $in->getName()][0];
104 }
105 }
106
107 if (is_null($candidate)) {
108 throw new \LogicException("Could not resolve dependency for {$component->getComponentName()}: " . (string) $in);
109 }
110
111 $in->addResolution($candidate);
112 }
113
114 protected function resolveSeek(In $in, array &$others): void
115 {
116 foreach ($others as $other) {
117 if ($other->offsetExists("CONTRIBUTE: " . $in->getName())) {
118 // For CONTRIBUTEd, we just use all contributions.
119 foreach ($other["CONTRIBUTE: " . $in->getName()] as $o) {
120 $in->addResolution($o);
121 }
122 }
123 }
124 }
125
126 protected function resolveUse(OfComponent $component, array &$disambiguation, In $in, array &$others): void
127 {
128 $candidates = [];
129
130 foreach ($others as $other) {
131 if ($other->offsetExists("IMPLEMENT: " . $in->getName())) {
132 // For IMPLEMENTed dependencies, we need to make choice.
133 $candidates[] = $other["IMPLEMENT: " . $in->getName()];
134 }
135 }
136
137 $candidates = array_merge(...$candidates);
138
139 if (empty($candidates)) {
140 throw new \LogicException(
141 "Could not resolve dependency for {$component->getComponentName()}: " . (string) $in
142 );
143 }
144
145 if (count($candidates) === 1) {
146 $in->addResolution($candidates[0]);
147 return;
148 }
149
150 $preferred_class = $this->disambiguate($component, $disambiguation, $in);
151 if (is_null($preferred_class)) {
152 throw new \LogicException(
153 "Dependency {$in->getName()} is provided (at least) twice, " .
154 "no disambiguation for {$component->getComponentName()}."
155 );
156 }
157 foreach ($candidates as $candidate) {
158 if ($candidate->aux["class"] === $preferred_class) {
159 $in->addResolution($candidate);
160 return;
161 }
162 }
163 throw new \LogicException(
164 "Dependency $preferred_class for service {$in->getName()} " .
165 "for {$component->getComponentName()} could not be located."
166 );
167 }
168
169 protected function disambiguate(OfComponent $component, array &$disambiguation, In $in): ?string
170 {
171 $service_name = (string) $in->getName();
172 foreach ([$component->getComponentName(), "*"] as $c) {
173 if (isset($disambiguation[$c]) && isset($disambiguation[$c][$service_name])) {
174 return $disambiguation[$c][$service_name];
175 }
176 }
177 return null;
178 }
179
183 protected function findCycles(OfComponent ...$components): \Generator
184 {
185 foreach ($components as $component) {
186 foreach ($component->getInDependencies() as $in) {
187 foreach ($this->findCyclesWith([], $component, $in) as $cycle) {
188 yield $cycle;
189 }
190 }
191 }
192 }
193
194 protected function findCyclesWith(array $visited, OfComponent $component, In $in): \Generator
195 {
196 if (!empty($visited) && $visited[0][0] === $component && $visited[0][1] == $in) {
197 yield $visited;
198 return;
199 }
200
201 array_push($visited, [$component, $in]);
202 foreach ($in->getResolvedBy() as $out) {
203 $other = $out->getComponent();
204 array_push($visited, [$component, $out]);
205 foreach ($out->getDependencies() as $next) {
206 yield from $this->findCyclesWith($visited, $out->getComponent(), $next);
207 }
208 array_pop($visited);
209 }
210 array_pop($visited);
211 }
212}
$out
Definition: buildRTE.php:24
$components
A dependency where the component needs something from the world.
Definition: In.php:27
addResolution(Out $other)
Definition: In.php:63
resolveSeek(In $in, array &$others)
Definition: Resolver.php:114
disambiguate(OfComponent $component, array &$disambiguation, In $in)
Definition: Resolver.php:169
findCyclesWith(array $visited, OfComponent $component, In $in)
Definition: Resolver.php:194
resolvePull(OfComponent $component, In $in, array &$others)
Definition: Resolver.php:91
resolveDependencies(array $disambiguation, OfComponent ... $components)
Resolves dependencies of all components.
Definition: Resolver.php:53
resolveUse(OfComponent $component, array &$disambiguation, In $in, array &$others)
Definition: Resolver.php:126
findCycles(OfComponent ... $components)
Definition: Resolver.php:183
$c
Definition: deliver.php:25