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