ILIAS  trunk Revision v11.0_alpha-3011-gc6b235a2e85
ObjectiveIterator.php
Go to the documentation of this file.
1<?php
2
19declare(strict_types=1);
20
21namespace ILIAS\Setup;
22
30class ObjectiveIterator implements \Iterator
31{
34
38 protected array $stack;
39
40 protected ?Objective $current = null;
41
45 protected array $returned;
46
50 protected array $failed;
51
55 protected array $reverse_dependencies;
56
57
59 {
61 $this->objective = $objective;
62 $this->rewind();
63 }
64
66 {
68 }
69
70 public function markAsFailed(Objective $objective): void
71 {
72 if (!isset($this->returned[$objective->getHash()])) {
73 throw new \LogicException(
74 "You may only mark objectives as failed that have been returned by this iterator."
75 );
76 }
77
78 $this->failed[$objective->getHash()] = true;
79 }
80
81 public function rewind(): void
82 {
83 $this->stack = [$this->objective];
84 $this->current = null;
85 $this->returned = [];
86 $this->failed = [];
87 $this->reverse_dependencies = [];
88 $this->next();
89 }
90
91 public function current(): \ILIAS\Setup\Objective
92 {
93 if ($this->current === null) {
94 throw new \LogicException(
95 "Iterator is finished or wasn't initialized correctly internally."
96 );
97 }
98 return $this->current;
99 }
100
101 public function key(): string
102 {
103 return $this->current()->getHash();
104 }
105
106 public function next(): void
107 {
108 if ($this->stack === []) {
109 $this->current = null;
110 return;
111 }
112
113 $cur = array_pop($this->stack);
114 $hash = $cur->getHash();
115
116 if (isset($this->returned[$hash]) || isset($this->failed[$hash])) {
117 $this->next();
118 return;
119 }
120
121 $preconditions = [];
122 $failed_preconditions = [];
123 foreach ($cur->getPreconditions($this->environment) as $p) {
124 $h = $p->getHash();
125 if (!isset($this->returned[$h]) || isset($this->failed[$h])) {
126 $preconditions[] = $p;
127 }
128
129 if (isset($this->failed[$h])) {
130 $failed_preconditions[] = $p;
131 }
132 }
133
134 // We only have preconditions left that we know to have failed.
135 if (
136 $preconditions !== [] &&
137 count($preconditions) === count($failed_preconditions)
138 ) {
139 $this->returned[$hash] = true;
140 $this->markAsFailed($cur);
141 if ($this->stack === []) {
142 // Since the current objective doesn't need to be achieved,
143 // we are fine and can simply stop here.
144 if ($cur instanceof Objective\Tentatively) {
145 return;
146 }
147 throw new UnachievableException(
148 "Objective '" . $cur->getLabel() . "' had failed preconditions:\n - " .
149 implode("\n - ", array_map(fn ($o) => $o->getLabel(), $failed_preconditions))
150 );
151 }
152 $this->next();
153 return;
154 }
155
156 // No preconditions open, we can proceed with the objective.
157 if ($preconditions === []) {
158 $this->returned[$hash] = true;
159 $this->current = $cur;
160 return;
161 }
162
163 $this->stack[] = $cur;
164 $this->detectDependencyCycles($hash, $hash);
165 foreach (array_reverse($preconditions) as $p) {
166 $this->stack[] = $p;
167 $this->setReverseDependency($p->getHash(), $hash);
168 }
169 $this->next();
170 }
171
172 public function valid(): bool
173 {
174 return $this->current !== null;
175 }
176
177 protected function detectDependencyCycles(string $cur, string $next): void
178 {
179 if (!isset($this->reverse_dependencies[$next])) {
180 return;
181 }
182 if (in_array($cur, $this->reverse_dependencies[$next])) {
183 throw new UnachievableException(
184 "The objectives contain a dependency cycle and won't all be achievable."
185 );
186 }
187 foreach ($this->reverse_dependencies[$next] as $d) {
188 $this->detectDependencyCycles($cur, $d);
189 }
190 }
191
192 protected function setReverseDependency(string $other, string $cur): void
193 {
194 if (!isset($this->reverse_dependencies[$other])) {
195 $this->reverse_dependencies[$other] = [];
196 }
197 $this->reverse_dependencies[$other][] = $cur;
198 }
199}
Tries to enumerate all preconditions for the given objective, where the ones that can be achieved (i....
setEnvironment(Environment $environment)
markAsFailed(Objective $objective)
setReverseDependency(string $other, string $cur)
detectDependencyCycles(string $cur, string $next)
__construct(Environment $environment, Objective $objective)
A wrapper around an objective that attempts to achieve the wrapped objective but won't stop the proce...
Definition: Tentatively.php:30
Signals that some goal won't be achievable by actions of the system ever.
An environment holds resources to be used in the setup process.
Definition: Environment.php:28
An objective is a desired state of the system that is supposed to be created by the setup.
Definition: Objective.php:31
getHash()
Get a hash for this objective.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
Interface Observer \BackgroundTasks Contains several chained tasks and infos about them.