ILIAS  trunk Revision v11.0_alpha-3011-gc6b235a2e85
Reader.php
Go to the documentation of this file.
1<?php
2
19declare(strict_types=1);
20
22
24
25class Reader
26{
27 protected array $defines;
28 protected array $implements;
29 protected array $uses;
30 protected ?array $uses_temp = [];
31 protected array $contributes;
32 protected array $seeks;
33 protected ?array $seeks_temp = [];
34 protected array $provides;
35 protected array $pulls;
36 protected ?array $pulls_temp = [];
37 protected array $internal_out;
38 protected array $internal_in;
39 protected ?array $internal_in_temp = [];
40
44 public function read(Component $component): OfComponent
45 {
46 $this->defines = [];
47 $this->implements = [];
48 $this->uses = [];
49 $this->contributes = [];
50 $this->seeks = [];
51 $this->provides = [];
52 $this->pulls = [];
53 $this->internal_out = [];
54 $this->internal_in = [];
55
56 $probes = [
57 new SetProbe(fn($n, $v) => $this->addDefine($n, $v)),
58 new SetProbe(fn($n, $v) => $this->cacheImplement($n, $v)),
59 new GetProbe(fn($n) => $this->addUse($n)),
60 new SetProbe(fn($n, $v) => $this->cacheContribute($n, $v)),
61 new GetProbe(fn($n) => $this->addSeek($n)),
62 new SetProbe(fn($n, $v) => $this->cacheProvide($n, $v)),
63 new GetProbe(fn($n) => $this->addPull($n)),
64 new SetGetProbe(fn($n, $v) => $this->cacheInternalOut($n, $v), fn($n) => $this->addInternalIn($n))
65 ];
66 $component->init(...$probes);
67
68 $this->resolveDependencies();
69 return $this->compileResult($component);
70 }
71
72 protected function addDefine($_, $name)
73 {
74 if (!is_string($name) || !interface_exists($name)) {
75 throw new \LogicException(
76 "Only push interface-names into \$define."
77 );
78 }
79 $d = new Define(new Name($name));
80 $this->defines[] = $d;
81 }
82
83 protected function addImplement(int $i, string $name, $value)
84 {
85 if (!is_callable($value)) {
86 throw new \LogicException(
87 "\$implements must be set with a callable."
88 );
89 }
90
91 $this->populateTempArrays();
92 $impl = $value();
93 $dependencies = $this->flushTempArrays();
94
95 if (!$impl instanceof $name) {
96 throw new \LogicException(
97 "Implementation for $name does not implement the correct interface."
98 );
99 }
100
101 $aux = [
102 "class" => get_class($impl),
103 "position" => $i
104 ];
105 $d = new Out(OutType::IMPLEMENT, $name, $aux, $dependencies);
106 $this->implements[$i] = $d;
107 }
108
109 protected function cacheImplement(string $name, $value)
110 {
111 $this->implements[] = [$name, $value];
112 }
113
114 protected function addUse(string $name)
115 {
116 if (empty($this->uses_temp)) {
117 throw new \LogicException(
118 "\$use is only allowed when defining other dependencies"
119 );
120 }
121
122 if (array_key_exists($name, $this->uses)) {
123 $d = $this->uses[$name];
124 } else {
125 $d = new In(InType::USE, $name);
126 }
127
128 $this->uses_temp[0][$name] = $d;
129
130 return $this->createMock($name);
131 }
132
133 protected function addContribute(int $i, string $name, $value)
134 {
135 if (!is_callable($value)) {
136 throw new \LogicException(
137 "\$implements must be set with a callable."
138 );
139 }
140
141 $this->populateTempArrays();
142 $impl = $value();
143 $dependencies = $this->flushTempArrays();
144
145 if (!$impl instanceof $name) {
146 throw new \LogicException(
147 "Contribution for $name does not implement the correct interface."
148 );
149 }
150
151 $aux = [
152 "position" => $i
153 ];
154 if ($impl instanceof \ILIAS\Component\EntryPoint) {
155 $aux["entry_point_name"] = $impl->getName();
156 }
157 $d = new Out(OutType::CONTRIBUTE, $name, $aux, $dependencies);
158 $this->contributes[$i] = $d;
159 }
160
161 protected function cacheContribute(string $name, $value)
162 {
163 $this->contributes[] = [$name, $value];
164 }
165
166 protected function addSeek(string $name)
167 {
168 if (empty($this->seeks_temp)) {
169 throw new \LogicException(
170 "\$seek is only allowed when defining other dependencies"
171 );
172 }
173
174 if (array_key_exists($name, $this->seeks)) {
175 $d = $this->seeks[$name];
176 } else {
177 $d = new In(InType::SEEK, $name);
178 }
179
180 $this->seeks_temp[0][$name] = $d;
181
182 return [];
183 }
184
185 protected function addProvide(int $i, string $name, $value)
186 {
187 if (!is_callable($value)) {
188 throw new \LogicException(
189 "\$implements must be set with a callable."
190 );
191 }
192
193 $this->populateTempArrays();
194 $impl = $value();
195 $dependencies = $this->flushTempArrays();
196
197 if (!$impl instanceof $name) {
198 throw new \LogicException(
199 "Provision for $name does not implement the correct interface."
200 );
201 }
202
203 $d = new Out(OutType::PROVIDE, $name, null, $dependencies);
204 $this->provides[$i] = $d;
205 }
206
207 protected function cacheProvide(string $name, $value)
208 {
209 $this->provides[] = [$name, $value];
210 }
211
212 protected function addPull(string $name)
213 {
214 if (empty($this->pulls_temp)) {
215 throw new \LogicException(
216 "\$pull is only allowed when defining other dependencies"
217 );
218 }
219
220 if (array_key_exists($name, $this->pulls)) {
221 $d = $this->pulls[$name];
222 } else {
223 $d = new In(InType::PULL, $name);
224 }
225
226 $this->pulls_temp[0][$name] = $d;
227
228 return $this->createMock($name);
229 }
230
231 protected function addInternalOut(string $name, $value)
232 {
233 if (!is_callable($value)) {
234 throw new \LogicException(
235 "\$internal must be set with a callable."
236 );
237 }
238
239 $this->populateTempArrays();
240 $impl = $value();
241 $dependencies = $this->flushTempArrays();
242
243 $d = new Out(OutType::INTERNAL, $name, null, $dependencies);
244 $this->internal_out[$name] = [$d, $impl];
245 }
246
247 protected function cacheInternalOut(string $name, $value)
248 {
249 $this->internal_out[$name] = [$name, $value];
250 }
251
252 protected function resolveInternalOut(string $name)
253 {
254 if (!array_key_exists($name, $this->internal_out)) {
255 throw new \LogicException(
256 "Cannot resolve dependency \$internal[$name]. It either does not exist or is defined circular."
257 );
258 }
259
260 if (!$this->internal_out[$name][0] instanceof Out) {
261 $values = $this->internal_out[$name];
262 unset($this->internal_out[$name]);
263 $this->addInternalOut(...$values);
264 }
265 }
266
267 protected function addInternalIn(string $name)
268 {
269 if (is_null($this->internal_in_temp)) {
270 throw new \LogicException(
271 "getting from \$internal is only allowed when defining other dependencies"
272 );
273 }
274
275 if (array_key_exists($name, $this->internal_in)) {
276 $d = $this->internal_in[$name];
277 } else {
278 $d = new In(InType::INTERNAL, $name);
279 }
280
281 $this->internal_in_temp[0][$name] = $d;
282
283 $this->resolveInternalOut($name);
284
285 return $this->internal_out[$name][1];
286 }
287
288 protected function populateTempArrays(): void
289 {
290 array_unshift($this->uses_temp, []);
291 array_unshift($this->seeks_temp, []);
292 array_unshift($this->pulls_temp, []);
293 array_unshift($this->internal_in_temp, []);
294 }
295
296 protected function flushTempArrays(): array
297 {
298 $uses_temp = array_shift($this->uses_temp);
299 $seeks_temp = array_shift($this->seeks_temp);
300 $pulls_temp = array_shift($this->pulls_temp);
301 $internal_in_temp = array_shift($this->internal_in_temp);
302
303
304 $this->uses = array_merge($this->uses, $uses_temp);
305 $this->seeks = array_merge($this->seeks, $seeks_temp);
306 $this->pulls = array_merge($this->pulls, $pulls_temp);
307 $this->internal_in = array_merge($this->internal_in, $internal_in_temp);
308
309 $dependencies = array_merge(...array_map("array_values", [$uses_temp, $seeks_temp, $pulls_temp, $internal_in_temp]));
310
311 return $dependencies;
312 }
313
314 protected function resolveDependencies(): void
315 {
316 foreach ($this->implements as $i => $v) {
317 $this->addImplement($i, ...$v);
318 }
319 foreach ($this->contributes as $i => $v) {
320 $this->addContribute($i, ...$v);
321 }
322 foreach ($this->provides as $i => $v) {
323 $this->addProvide($i, ...$v);
324 }
325 foreach (array_keys($this->internal_out) as $i) {
326 $this->resolveInternalOut($i);
327 }
328 }
329
330 protected function compileResult(Component $component): OfComponent
331 {
332 return new OfComponent(
333 $component,
334 ...array_merge(
335 ...array_map(
336 "array_values",
337 [
338 $this->defines,
339 $this->implements,
340 $this->uses,
341 $this->contributes,
342 $this->seeks,
343 $this->provides,
344 $this->pulls,
345 array_map(fn($a) => $a[0], $this->internal_out),
346 $this->internal_in
347 ]
348 )
349 )
350 );
351 }
352
353 protected function createMock(string $class_name): object
354 {
355 $mock_builder = new \PHPUnit\Framework\MockObject\MockBuilder(new class ('dummy') extends \PHPUnit\Framework\TestCase {
356 public function dummy()
357 {
358 }
359 }, $class_name);
360 return $mock_builder
361 ->disableOriginalConstructor()
362 ->disableOriginalClone()
363 ->disableArgumentCloning()
364 ->disallowMockingUnknownTypes()
365 ->getMock();
366 }
367}
A dependency where the component needs something from the world.
Definition: In.php:27
A dependency where the component gives something to the world.
Definition: Out.php:27
cacheInternalOut(string $name, $value)
Definition: Reader.php:247
read(Component $component)
Definition: Reader.php:44
addInternalOut(string $name, $value)
Definition: Reader.php:231
compileResult(Component $component)
Definition: Reader.php:330
cacheImplement(string $name, $value)
Definition: Reader.php:109
addProvide(int $i, string $name, $value)
Definition: Reader.php:185
cacheProvide(string $name, $value)
Definition: Reader.php:207
cacheContribute(string $name, $value)
Definition: Reader.php:161
addContribute(int $i, string $name, $value)
Definition: Reader.php:133
addImplement(int $i, string $name, $value)
Definition: Reader.php:83
createMock(string $class_name)
Definition: Reader.php:353
init(array|\ArrayAccess &$define, array|\ArrayAccess &$implement, array|\ArrayAccess &$use, array|\ArrayAccess &$contribute, array|\ArrayAccess &$seek, array|\ArrayAccess &$provide, array|\ArrayAccess &$pull, array|\ArrayAccess &$internal,)
An entrypoint is where the programm execution starts.
Definition: EntryPoint.php:28
Interface Observer \BackgroundTasks Contains several chained tasks and infos about them.
$a
thx to https://mlocati.github.io/php-cs-fixer-configurator for the examples