ILIAS  trunk Revision v11.0_alpha-3011-gc6b235a2e85
FlavourBuilder.php
Go to the documentation of this file.
1<?php
2
19declare(strict_types=1);
20
22
40
46{
47 public const VARIANT_NAME_MAX_LENGTH = 768;
48 private array $current_revision_cache = [];
49 private array $resources_cache = [];
50
51 public function __construct(
52 private readonly FlavourRepository $flavour_resource_repository,
53 private readonly Factory $flavour_machine_factory,
54 private readonly ResourceBuilder $resource_builder,
55 private readonly StorageHandlerFactory $storage_handler_factory,
56 private readonly StreamAccess $stream_access,
57 private readonly Subject $events
58 ) {
59 }
60
61 public function has(
62 ResourceIdentification $identification,
63 FlavourDefinition $definition
64 ): bool {
65 $this->checkDefinition($definition);
66 return $this->flavour_resource_repository->has(
67 $identification,
68 $this->getResource($identification)->getCurrentRevision()->getVersionNumber(),
69 $definition
70 );
71 }
72
76 public function get(
78 FlavourDefinition $definition,
79 bool $force_building = false
80 ): Flavour {
81 $this->checkDefinition($definition);
82 if (!$this->resource_builder->has($rid)) {
83 throw new ResourceNotFoundException($rid->serialize());
84 }
85 if ($this->has($rid, $definition)) {
86 return $this->read($rid, $definition, $force_building);
87 }
88
89 return $this->build($rid, $definition);
90 }
91
92 private function build(
94 FlavourDefinition $definition
95 ): Flavour {
96 $flavour = $this->new($definition, $rid);
97 $flavour = $this->runMachine($rid, $definition, $flavour);
98
99 if ($definition->persist()) {
100 $this->flavour_resource_repository->store($flavour);
101 $flavour = $this->populateFlavourWithExistingStreams($flavour);
102 }
103
104 return $flavour;
105 }
106
107 private function read(
109 FlavourDefinition $definition,
110 bool $force_building = false
111 ): Flavour {
112 $current_revision = $this->getResource($rid)->getCurrentRevision();
113 $flavour = $this->flavour_resource_repository->get(
114 $rid,
115 $current_revision->getVersionNumber(),
116 $definition
117 );
118
119 if ($force_building || !$this->hasFlavourStreams($flavour)) {
120 // ensure deletion of old streams
121 $storage = $this->getStorageHandler($flavour);
122 $storage->deleteFlavour($current_revision, $flavour);
123 // run Machine
124 return $this->runMachine($rid, $definition, $flavour);
125 }
126
127 return $this->populateFlavourWithExistingStreams($flavour);
128 }
129
130 private function new(FlavourDefinition $definition, ResourceIdentification $rid): Flavour
131 {
132 return new Flavour(
133 $definition,
134 $rid,
135 $this->getResource($rid)->getCurrentRevision()->getVersionNumber()
136 );
137 }
138
139 public function delete(
141 FlavourDefinition $definition
142 ): bool {
143 $current_revision = $this->getResource($rid)->getCurrentRevision();
144 $revision_number = $current_revision->getVersionNumber();
145
146 if ($this->flavour_resource_repository->has($rid, $revision_number, $definition)) {
147 $flavour = $this->flavour_resource_repository->get($rid, $revision_number, $definition);
148 $this->flavour_resource_repository->delete($flavour);
149 $storage = $this->getStorageHandler($flavour);
150 $storage->deleteFlavour($current_revision, $flavour);
151
152 return true;
153 }
154
155 return false;
156 }
157
158 // STREAMS
159
160 private function hasFlavourStreams(Flavour $flavour): bool
161 {
162 return $this->getStorageHandler($flavour)->hasFlavour(
163 $this->getCurrentRevision($flavour),
164 $flavour
165 );
166 }
167
168 private function storeFlavourStreams(Flavour $flavour, array $streams): void
169 {
170 $storable = new StorableFlavourDecorator($flavour);
171 $storable->setStreams($streams);
172
173 $this->getStorageHandler($flavour)->storeFlavour(
174 $this->getCurrentRevision($flavour),
175 $storable
176 );
177 }
178
180 {
181 $handler = $this->getStorageHandler($flavour);
182 $identification = $flavour->getResourceId();
183 $revision = $this->getCurrentRevision($flavour);
184 foreach (
185 $handler->getFlavourStreams(
186 $revision,
187 $flavour
188 ) as $index => $file_stream
189 ) {
190 $flavour = $this->stream_access->populateFlavour($flavour, $file_stream, $index);
191 }
192 return $flavour;
193 }
194
195 // DEFINITIONS AND MACHINES
196 private function checkDefinitionForMachine(FlavourDefinition $definition, FlavourMachine $machine): void
197 {
198 if (!$machine->canHandleDefinition($definition)) {
199 throw new \InvalidArgumentException("FlavourDefinition not supported by machine");
200 }
201 }
202
203 private function checkDefinition(FlavourDefinition $definition): void
204 {
205 if ($definition->getVariantName() === null) {
206 return;
207 }
208 if (strlen($definition->getVariantName()) > self::VARIANT_NAME_MAX_LENGTH) {
209 throw new \InvalidArgumentException("FlavourDefinition variant name too long");
210 }
211 }
212
213 public function testDefinition(
215 FlavourDefinition $definition
216 ): bool {
217 try {
218 $this->checkDefinition($definition);
219 $machine = $this->flavour_machine_factory->get($definition);
220 $this->checkDefinitionForMachine($definition, $machine);
221 } catch (\Throwable) {
222 return false;
223 }
224 if ($machine instanceof NullMachine) {
225 return false;
226 }
227 $engine = $machine->getEngine();
228 if (!$engine->isRunning()) {
229 return false;
230 }
231 $current_revision = $this->getResource($rid)->getCurrentRevision();
232 $suffix = $current_revision->getInformation()->getSuffix();
233 $size = $current_revision->getInformation()->getSize();
234 if ($size > $engine->getSizeLimitInBytes()) {
235 return false;
236 }
237
238 return $engine->supports($suffix);
239 }
240
241 // STREAMS GENERATION
242 protected function runMachine(
244 FlavourDefinition $definition,
245 Flavour $flavour
246 ): Flavour {
247 $revision = $this->getCurrentRevision($flavour);
248
249 // Get Orignal Stream of Resource/Revision
250 $handler = $this->getStorageHandler($flavour);
251 try {
252 $stream = $this->resource_builder->extractStream($revision);
253 $stream->rewind();
254 } catch (\Throwable) {
255 // error while reading file stream, cannot process
256 $this->events->notify(Event::FLAVOUR_BUILD_FAILED, new FlavourData($rid, $definition, $flavour, $t));
257 return $flavour;
258 }
259
260 // Get Machine
261 $machine = $this->flavour_machine_factory->get($definition);
262 $this->checkDefinitionForMachine($definition, $machine);
263
264 // Run Machine and get Streams
265 $storable_streams = [];
266 try {
267 foreach (
268 $machine->processStream(
269 $revision->getInformation(),
270 $stream,
271 $definition
272 ) as $result
273 ) {
274 $generated_stream = $result->getStream();
275 if ($result->isStoreable()) {
276 // Collect Streams to store persistently
277 $storable_streams[$result->getIndex()] = $generated_stream;
278 }
279
280 $cloned_stream = Streams::ofString((string) $generated_stream);
281
282 $flavour = $this->stream_access->populateFlavour(
283 $flavour,
284 $cloned_stream,
285 $result->getIndex()
286 );
287 }
288 } catch (\Throwable $t) {
289 // error while processing stream, cannot process
290 $this->events->notify(Event::FLAVOUR_BUILD_FAILED, new FlavourData($rid, $definition, $flavour, $t));
291 return $flavour;
292 }
293
294 // Store Streams persistently if needed
295 if ($definition->persist()) {
296 $this->storeFlavourStreams($flavour, $storable_streams);
297 }
298
299 $this->events->notify(Event::FLAVOUR_BUILD_SUCCESS, new FlavourData($rid, $definition, $flavour));
300
301 return $flavour;
302 }
303
304 // Helpers
305
306 private function getCurrentRevision(Flavour $flavour): Revision
307 {
308 $rid = $flavour->getResourceId()->serialize();
309 if (isset($this->current_revision_cache[$rid])) {
310 // return $this->current_revision_cache[$rid]; // we are currently not able to cache this seriously,
311 // since there might be situations where the revision changes in the meantime
312 }
313 return $this->current_revision_cache[$rid] = $this->getResourceOfFlavour($flavour)->getCurrentRevision();
314 }
315
317 {
318 $rid_string = $rid->serialize();
319 return $this->resources_cache[$rid_string] ?? ($this->resources_cache[$rid_string] = $this->resource_builder->get(
320 $rid
321 ));
322 }
323
325 {
326 return $this->getResource($flavour->getResourceID());
327 }
328
329 private function getStorageHandler(Flavour $flavour): StorageHandler
330 {
331 return $this->storage_handler_factory->getHandlerForResource($this->getResourceOfFlavour($flavour));
332 }
333}
Stream factory which enables the user to create streams without the knowledge of the concrete class.
Definition: Streams.php:32
build(ResourceIdentification $rid, FlavourDefinition $definition)
checkDefinitionForMachine(FlavourDefinition $definition, FlavourMachine $machine)
has(ResourceIdentification $identification, FlavourDefinition $definition)
testDefinition(ResourceIdentification $rid, FlavourDefinition $definition)
read(ResourceIdentification $rid, FlavourDefinition $definition, bool $force_building=false)
__construct(private readonly FlavourRepository $flavour_resource_repository, private readonly Factory $flavour_machine_factory, private readonly ResourceBuilder $resource_builder, private readonly StorageHandlerFactory $storage_handler_factory, private readonly StreamAccess $stream_access, private readonly Subject $events)
runMachine(ResourceIdentification $rid, FlavourDefinition $definition, Flavour $flavour)
getResource(ResourceIdentification $rid)
checkDefinition(FlavourDefinition $definition)
storeFlavourStreams(Flavour $flavour, array $streams)
persist()
Define whether the generated flavor and the respective streams should be persisted,...
getVariantName()
If a definition can be used in several variants (e.g.
canHandleDefinition(FlavourDefinition $definition)
Check if a corresponding configuration can be processed by this Machine.
has(string $class_name)
@ FLAVOUR_BUILD_FAILED
event string being used if a new Resource has been stored to the IRSS.
Definition: Event.php:52
try
handle Lrs Init
Definition: xapiproxy.php:86
$handler
Definition: oai.php:29