1<?php 2 3namespace ILIAS\BackgroundTasks\Implementation\TaskManager; 4 5use ILIAS\BackgroundTasks\Bucket; 6use ILIAS\BackgroundTasks\Exceptions\Exception; 7use ILIAS\BackgroundTasks\Implementation\Bucket\State; 8use ILIAS\BackgroundTasks\Implementation\Tasks\UserInteraction\UserInteractionRequiredException; 9use ILIAS\BackgroundTasks\Implementation\Tasks\UserInteraction\UserInteractionSkippedException; 10use ILIAS\BackgroundTasks\Implementation\Values\ThunkValue; 11use ILIAS\BackgroundTasks\Observer; 12use ILIAS\BackgroundTasks\Persistence; 13use ILIAS\BackgroundTasks\Task; 14use ILIAS\BackgroundTasks\Task\UserInteraction\Option; 15use ILIAS\BackgroundTasks\TaskManager; 16use ILIAS\BackgroundTasks\Value; 17 18/** 19 * Class BasicTaskManager 20 * 21 * @package ILIAS\BackgroundTasks\Implementation 22 * 23 * @author Oskar Truffer <ot@studer-raimann.ch> 24 * 25 * Basic Task manager. Will execute tasks immediately. 26 * 27 * Some important infos: 28 * - The bucket and its tasks are not saved into the db upon execution 29 * - The percentage and current task are not updated during execution. 30 * - The bucket and its tasks inkl. percentage and current task are only saved into the DB 31 * when a user interaction occurs. 32 * 33 */ 34abstract class BasicTaskManager implements TaskManager 35{ 36 37 /** 38 * @var Persistence 39 */ 40 protected $persistence; 41 42 43 public function __construct(Persistence $persistence) 44 { 45 $this->persistence = $persistence; 46 } 47 48 49 /** 50 * @param Task $task 51 * @param Observer $observer 52 * 53 * @return Value 54 * @throws UserInteractionSkippedException|UserInteractionRequiredException|Exception 55 */ 56 public function executeTask(Task $task, Observer $observer) 57 { 58 $observer->notifyState(State::RUNNING); 59 /** @var Value[] $values */ 60 $values = $task->getInput(); 61 $final_values = []; 62 $replace_thunk_values = false; 63 foreach ($values as $value) { 64 if (is_a($value, ThunkValue::class)) { 65 $value = $this->executeTask($value->getParentTask(), $observer); 66 $replace_thunk_values = true; 67 } 68 $final_values[] = $value; 69 } 70 71 if ($replace_thunk_values) { 72 $task->setInput($final_values); 73 } 74 75 if (is_a($task, Task\Job::class)) { 76 /** @var Task\Job $job */ 77 $job = $task; 78 $observer->notifyCurrentTask($job); 79 $value = $job->run($final_values, $observer); 80 if (!$value->getType()->isExtensionOf($job->getOutputType())) { 81 throw new Exception("The job " . $job->getType() 82 . " did state to output a value of type " 83 . $job->getOutputType() . " but outputted a value of type " 84 . $value->getType()); 85 } 86 $observer->notifyPercentage($job, 100); 87 88 return $value; 89 } 90 91 if (is_a($task, Task\UserInteraction::class)) { 92 /** @var Task\UserInteraction $user_interaction */ 93 $user_interaction = $task; 94 95 if ($user_interaction->canBeSkipped($final_values)) { 96 if ($task->isFinal()) { 97 throw new UserInteractionSkippedException('Final interaction skipped'); 98 } 99 return $task->getSkippedValue($task->getInput()); 100 } 101 102 $observer->notifyCurrentTask($user_interaction); 103 $observer->notifyState(State::USER_INTERACTION); 104 throw new UserInteractionRequiredException("User interaction required."); 105 } 106 107 throw new Exception("You need to execute a Job or a UserInteraction."); 108 } 109 110 111 /** 112 * Continue a task with a given option. 113 * 114 * @param Bucket $bucket 115 * @param Option $option 116 * 117 * @return mixed 118 */ 119 public function continueTask(Bucket $bucket, Option $option) 120 { 121 // We do the user interaction 122 $bucket->userInteraction($option); 123 if ($bucket->getState() != State::FINISHED) { // The job is not done after the user interaction, so we continue to run it. 124 $this->run($bucket); 125 } else { 126 $this->persistence->deleteBucket($bucket); 127 } 128 } 129 130 131 /** 132 * @inheritdoc 133 */ 134 public function quitBucket(Bucket $bucket) 135 { 136 $this->persistence->deleteBucket($bucket); 137 } 138} 139