1<?php 2 3namespace ILIAS\BackgroundTasks\Implementation\Tasks; 4 5use ILIAS\BackgroundTasks\Exceptions\InvalidArgumentException; 6use ILIAS\BackgroundTasks\Implementation\Tasks\UserInteraction\UserInteractionOption; 7use ILIAS\BackgroundTasks\Implementation\Values\PrimitiveValueWrapperFactory; 8use ILIAS\BackgroundTasks\Implementation\Values\ScalarValues\BasicScalarValueFactory; 9use ILIAS\BackgroundTasks\Implementation\Values\ThunkValue; 10use ILIAS\BackgroundTasks\Task; 11use ILIAS\BackgroundTasks\Value; 12 13/** 14 * Class AbstractTask 15 * 16 * @package ILIAS\BackgroundTasks\Implementation\Tasks 17 * 18 * @author Oskar Truffer <ot@studer-raimann.ch> 19 */ 20abstract class AbstractTask implements Task 21{ 22 use BasicScalarValueFactory; 23 const MAIN_REMOVE = 'bt_main_remove'; 24 const MAIN_ABORT = 'bt_main_abort'; 25 /** 26 * @var Value[] 27 */ 28 protected $input = []; 29 /** 30 * @var Value 31 */ 32 protected $output; 33 34 35 /** 36 * @param $values (Value|Task)[] 37 * 38 * @return void 39 */ 40 public function setInput(array $values) 41 { 42 $this->input = $this->getValues($values); 43 $this->checkTypes($this->input); 44 } 45 46 47 protected function checkTypes($values) 48 { 49 $expectedTypes = $this->getInputTypes(); 50 51 for ($i = 0; $i < count($expectedTypes); $i++) { 52 $expectedType = $expectedTypes[$i]; 53 $givenType = $this->extractType($values[$i]); 54 if (!$givenType->isExtensionOf($expectedType)) { 55 throw new InvalidArgumentException("Types did not match when setting input for " 56 . get_called_class() 57 . ". Expected type $expectedType given type $givenType."); 58 } 59 } 60 } 61 62 63 /** 64 * @param $value Value 65 * 66 * @return mixed 67 * @throws InvalidArgumentException 68 */ 69 protected function extractType($value) 70 { 71 if (is_a($value, Value::class)) { 72 return $value->getType(); 73 } 74 if (is_a($value, Task::class)) { 75 ; 76 } 77 78 return $value->getOutputType(); 79 80 throw new InvalidArgumentException("Input values must be tasks or Values (extend BT\\Task or BT\\Value)."); 81 } 82 83 84 /** 85 * @return Value Returns a thunk value (yet to be calculated). It's used for task composition 86 * and type checks. 87 * 88 */ 89 public function getOutput() 90 { 91 $thunk = new ThunkValue($this->getOutputType()); 92 $thunk->setParentTask($this); 93 94 return $thunk; 95 } 96 97 98 /** 99 * @param $values (Value|Task)[] 100 * 101 * @return Value[] 102 */ 103 private function getValues($values) 104 { 105 $inputs = []; 106 107 foreach ($values as $value) { 108 if ($value instanceof Task) { 109 $inputs[] = $value->getOutput(); 110 } elseif ($value instanceof Value) { 111 $inputs[] = $value; 112 } else { 113 $inputs[] = $this->wrapScalar($value); 114 } 115 } 116 117 return $inputs; 118 } 119 120 121 /** 122 * @return Value[] 123 */ 124 public function getInput() 125 { 126 return $this->input; 127 } 128 129 130 /** 131 * @return string 132 */ 133 public function getType() 134 { 135 return get_called_class(); 136 } 137 138 139 /** 140 * Unfold the task. If task A has dependency B and B' and B has dependency C, the resulting 141 * list will be [A, B, C, B']. 142 * 143 * @return Task[] 144 */ 145 public function unfoldTask() 146 { 147 $list = [$this]; 148 foreach ($this->getInput() as $input) { 149 if (is_a($input, ThunkValue::class)) { 150 $list = array_merge($list, $input->getParentTask()->unfoldTask()); 151 } 152 } 153 154 return $list; 155 } 156 157 158 /** 159 * @inheritdoc 160 */ 161 public function getRemoveOption() 162 { 163 return new UserInteractionOption('remove', self::MAIN_REMOVE); 164 } 165 166 167 /** 168 * @inheritdoc 169 */ 170 public function getAbortOption() 171 { 172 return new UserInteractionOption('abort', self::MAIN_ABORT); 173 } 174} 175