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 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 $userInteraction */
93            $userInteraction = $task;
94
95            if ($userInteraction->canBeSkipped($final_values)) {
96                $observer->notifyState(State::FINISHED);
97                throw new UserInteractionSkippedException("User interaction skipped.");
98            } else {
99                $observer->notifyCurrentTask($userInteraction);
100                $observer->notifyState(State::USER_INTERACTION);
101                throw new UserInteractionRequiredException("User interaction required.");
102            }
103        }
104
105        throw new Exception("You need to execute a Job or a UserInteraction.");
106    }
107
108
109    /**
110     * Continue a task with a given option.
111     *
112     * @param Bucket $bucket
113     * @param Option $option
114     *
115     * @return mixed
116     */
117    public function continueTask(Bucket $bucket, Option $option)
118    {
119        // We do the user interaction
120        $bucket->userInteraction($option);
121        if ($bucket->getState() != State::FINISHED) { // The job is not done after the user interaction, so we continue to run it.
122            $this->run($bucket);
123        } else {
124            $this->persistence->deleteBucket($bucket);
125        }
126    }
127
128
129    /**
130     * @inheritdoc
131     */
132    public function quitBucket(Bucket $bucket)
133    {
134        $this->persistence->deleteBucket($bucket);
135    }
136}
137