ILIAS  trunk Revision v11.0_alpha-1753-gb21ca8c4367
All Data Structures Namespaces Files Functions Variables Enumerations Enumerator Modules Pages
FlavourBuilder.php
Go to the documentation of this file.
1 <?php
2 
19 declare(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 
324  private function getResourceOfFlavour(Flavour $flavour): StorableResource
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 }
checkDefinition(FlavourDefinition $definition)
build(ResourceIdentification $rid, FlavourDefinition $definition)
canHandleDefinition(FlavourDefinition $definition)
Check if a corresponding configuration can be processed by this Machine.
storeFlavourStreams(Flavour $flavour, array $streams)
read(ResourceIdentification $rid, FlavourDefinition $definition, bool $force_building=false)
persist()
Define whether the generated flavor and the respective streams should be persisted, or whether they should only be generated and used in-memory.
getVariantName()
If a definition can be used in several variants (e.g.
getResource(ResourceIdentification $rid)
checkDefinitionForMachine(FlavourDefinition $definition, FlavourMachine $machine)
has(ResourceIdentification $identification, FlavourDefinition $definition)
while($session_entry=$r->fetchRow(ilDBConstants::FETCHMODE_ASSOC)) return null
event string being used if a new Resource has been stored to the IRSS.
Definition: Event.php:52
static ofString(string $string)
Creates a new stream with an initial value.
Definition: Streams.php:41
$handler
Definition: oai.php:30
runMachine(ResourceIdentification $rid, FlavourDefinition $definition, Flavour $flavour)
testDefinition(ResourceIdentification $rid, FlavourDefinition $definition)
__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)