1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* @link https://github.com/old-town/old-town-workflow |
4
|
|
|
* @author Malofeykin Andrey <[email protected]> |
5
|
|
|
*/ |
6
|
|
|
namespace OldTown\Workflow\Engine; |
7
|
|
|
|
8
|
|
|
use OldTown\PropertySet\PropertySetInterface; |
9
|
|
|
use OldTown\Workflow\Exception\InternalWorkflowException; |
10
|
|
|
use OldTown\Workflow\Exception\InvalidInputException; |
11
|
|
|
use OldTown\Workflow\Exception\StoreException; |
12
|
|
|
use OldTown\Workflow\Exception\WorkflowException; |
13
|
|
|
use OldTown\Workflow\JoinNodes; |
14
|
|
|
use OldTown\Workflow\Loader\ActionDescriptor; |
15
|
|
|
use OldTown\Workflow\Loader\ResultDescriptor; |
16
|
|
|
use OldTown\Workflow\Loader\ValidatorDescriptor; |
17
|
|
|
use OldTown\Workflow\Loader\WorkflowDescriptor; |
18
|
|
|
use OldTown\Workflow\Spi\StepInterface; |
19
|
|
|
use OldTown\Workflow\Spi\WorkflowEntryInterface; |
20
|
|
|
use OldTown\Workflow\Spi\WorkflowStoreInterface; |
21
|
|
|
use OldTown\Workflow\TransientVars\TransientVarsInterface; |
22
|
|
|
use SplObjectStorage; |
23
|
|
|
use DateTime; |
24
|
|
|
use OldTown\Workflow\Exception\InvalidArgumentException; |
25
|
|
|
|
26
|
|
|
|
27
|
|
|
/** |
28
|
|
|
* Class Transition |
29
|
|
|
* |
30
|
|
|
* @package OldTown\Workflow |
31
|
|
|
*/ |
32
|
|
|
class Transition extends AbstractEngine implements TransitionInterface |
33
|
|
|
{ |
34
|
|
|
/** |
35
|
|
|
* |
36
|
|
|
* @var array |
37
|
|
|
*/ |
38
|
|
|
protected $stateCache = []; |
39
|
|
|
|
40
|
|
|
/** |
41
|
|
|
* Переход между двумя статусами |
42
|
|
|
* |
43
|
|
|
* @param WorkflowEntryInterface $entry |
44
|
|
|
* @param SplObjectStorage|StepInterface[] $currentSteps |
45
|
|
|
* @param WorkflowStoreInterface $store |
46
|
|
|
* @param WorkflowDescriptor $wf |
47
|
|
|
* @param ActionDescriptor $action |
48
|
|
|
* @param TransientVarsInterface $transientVars |
49
|
|
|
* @param TransientVarsInterface $inputs |
50
|
|
|
* @param PropertySetInterface $ps |
51
|
|
|
* |
52
|
|
|
* @return boolean |
53
|
|
|
* |
54
|
|
|
* @throws InternalWorkflowException |
55
|
|
|
*/ |
56
|
18 |
|
public function transitionWorkflow(WorkflowEntryInterface $entry, SplObjectStorage $currentSteps, WorkflowStoreInterface $store, WorkflowDescriptor $wf, ActionDescriptor $action, TransientVarsInterface $transientVars, TransientVarsInterface $inputs, PropertySetInterface $ps) |
57
|
|
|
{ |
58
|
|
|
try { |
59
|
18 |
|
$step = $this->getCurrentStep($wf, $action->getId(), $currentSteps, $transientVars, $ps); |
60
|
|
|
|
61
|
18 |
|
$validators = $action->getValidators(); |
62
|
18 |
|
if ($validators->count() > 0) { |
63
|
3 |
|
$this->verifyInputs($validators, $transientVars, $ps); |
64
|
2 |
|
} |
65
|
|
|
|
66
|
17 |
|
$workflowManager = $this->getWorkflowManager(); |
67
|
17 |
|
$engineManager = $workflowManager->getEngineManager(); |
68
|
17 |
|
$functionsEngine = $engineManager->getFunctionsEngine(); |
69
|
|
|
|
70
|
17 |
|
if (null !== $step) { |
71
|
6 |
|
$stepPostFunctions = $wf->getStep($step->getStepId())->getPostFunctions(); |
72
|
6 |
|
foreach ($stepPostFunctions as $function) { |
73
|
1 |
|
$functionsEngine->executeFunction($function, $transientVars, $ps); |
74
|
6 |
|
} |
75
|
6 |
|
} |
76
|
|
|
|
77
|
17 |
|
$preFunctions = $action->getPreFunctions(); |
78
|
17 |
|
foreach ($preFunctions as $preFunction) { |
79
|
7 |
|
$functionsEngine->executeFunction($preFunction, $transientVars, $ps); |
80
|
17 |
|
} |
81
|
|
|
|
82
|
17 |
|
$conditionalResults = $action->getConditionalResults(); |
83
|
17 |
|
$extraPreFunctions = null; |
84
|
17 |
|
$extraPostFunctions = null; |
85
|
|
|
|
86
|
17 |
|
$theResult = null; |
87
|
|
|
|
88
|
|
|
|
89
|
|
|
|
90
|
17 |
|
$currentStepId = null !== $step ? $step->getStepId() : -1; |
91
|
|
|
|
92
|
|
|
|
93
|
17 |
|
$conditionsEngine = $engineManager->getConditionsEngine(); |
94
|
17 |
|
$log = $workflowManager->getLog(); |
95
|
17 |
|
$context = $workflowManager->getContext(); |
96
|
|
|
|
97
|
17 |
|
foreach ($conditionalResults as $conditionalResult) { |
98
|
6 |
|
if ($conditionsEngine->passesConditionsWithType(null, $conditionalResult->getConditions(), $transientVars, $ps, $currentStepId)) { |
99
|
4 |
|
$theResult = $conditionalResult; |
100
|
|
|
|
101
|
4 |
|
$validatorsStorage = $conditionalResult->getValidators(); |
102
|
4 |
|
if ($validatorsStorage->count() > 0) { |
103
|
1 |
|
$this->verifyInputs($validatorsStorage, $transientVars, $ps); |
104
|
|
|
} |
105
|
|
|
|
106
|
3 |
|
$extraPreFunctions = $conditionalResult->getPreFunctions(); |
107
|
3 |
|
$extraPostFunctions = $conditionalResult->getPostFunctions(); |
108
|
|
|
|
109
|
3 |
|
break; |
110
|
|
|
} |
111
|
17 |
|
} |
112
|
|
|
|
113
|
|
|
|
114
|
16 |
|
if (null === $theResult) { |
115
|
15 |
|
$theResult = $action->getUnconditionalResult(); |
116
|
13 |
|
$this->verifyInputs($theResult->getValidators(), $transientVars, $ps); |
117
|
13 |
|
$extraPreFunctions = $theResult->getPreFunctions(); |
118
|
13 |
|
$extraPostFunctions = $theResult->getPostFunctions(); |
119
|
13 |
|
} |
120
|
|
|
|
121
|
16 |
|
$logMsg = sprintf('theResult=%s %s', $theResult->getStep(), $theResult->getStatus()); |
122
|
16 |
|
$log->debug($logMsg); |
123
|
|
|
|
124
|
|
|
|
125
|
16 |
|
if ($extraPreFunctions && $extraPreFunctions->count() > 0) { |
126
|
3 |
|
foreach ($extraPreFunctions as $function) { |
127
|
2 |
|
$functionsEngine->executeFunction($function, $transientVars, $ps); |
128
|
2 |
|
} |
129
|
2 |
|
} |
130
|
|
|
|
131
|
16 |
|
$split = $theResult->getSplit(); |
132
|
16 |
|
$join = $theResult->getJoin(); |
133
|
16 |
|
if (null !== $split && 0 !== $split) { |
134
|
|
|
$splitDesc = $wf->getSplit($split); |
135
|
|
|
$results = $splitDesc->getResults(); |
136
|
|
|
$splitPreFunctions = []; |
137
|
|
|
$splitPostFunctions = []; |
138
|
|
|
|
139
|
|
|
foreach ($results as $resultDescriptor) { |
140
|
|
|
if ($resultDescriptor->getValidators()->count() > 0) { |
141
|
|
|
$this->verifyInputs($resultDescriptor->getValidators(), $transientVars, $ps); |
142
|
|
|
} |
143
|
|
|
|
144
|
|
|
foreach ($resultDescriptor->getPreFunctions() as $function) { |
145
|
|
|
$splitPreFunctions[] = $function; |
146
|
|
|
} |
147
|
|
|
foreach ($resultDescriptor->getPostFunctions() as $function) { |
148
|
|
|
$splitPostFunctions[] = $function; |
149
|
|
|
} |
150
|
|
|
} |
151
|
|
|
|
152
|
|
|
foreach ($splitPreFunctions as $function) { |
153
|
|
|
$functionsEngine->executeFunction($function, $transientVars, $ps); |
154
|
|
|
} |
155
|
|
|
|
156
|
|
|
if (!$action->isFinish()) { |
157
|
|
|
$moveFirst = true; |
158
|
|
|
|
159
|
|
|
foreach ($results as $resultDescriptor) { |
160
|
|
|
$moveToHistoryStep = null; |
161
|
|
|
|
162
|
1 |
|
if ($moveFirst) { |
163
|
|
|
$moveToHistoryStep = $step; |
164
|
|
|
} |
165
|
|
|
|
166
|
|
|
$previousIds = []; |
167
|
|
|
|
168
|
|
|
if (null !== $step) { |
169
|
|
|
$previousIds[] = $step->getStepId(); |
170
|
|
|
} |
171
|
|
|
|
172
|
|
|
$this->createNewCurrentStep($resultDescriptor, $entry, $store, $action->getId(), $moveToHistoryStep, $previousIds, $transientVars, $ps); |
173
|
|
|
$moveFirst = false; |
174
|
|
|
} |
175
|
|
|
} |
176
|
|
|
|
177
|
|
|
|
178
|
|
|
foreach ($splitPostFunctions as $function) { |
179
|
|
|
$functionsEngine->executeFunction($function, $transientVars, $ps); |
180
|
|
|
} |
181
|
16 |
|
} elseif (null !== $join && 0 !== $join) { |
182
|
|
|
$joinDesc = $wf->getJoin($join); |
183
|
|
|
$oldStatus = $theResult->getOldStatus(); |
184
|
|
|
$caller = $context->getCaller(); |
185
|
|
|
if (null !== $step) { |
186
|
|
|
$step = $store->markFinished($step, $action->getId(), new DateTime(), $oldStatus, $caller); |
187
|
|
|
} else { |
188
|
|
|
$errMsg = 'Invalid step'; |
189
|
|
|
throw new InternalWorkflowException($errMsg); |
190
|
|
|
} |
191
|
|
|
|
192
|
|
|
|
193
|
|
|
$store->moveToHistory($step); |
194
|
|
|
|
195
|
|
|
/** @var StepInterface[] $joinSteps */ |
196
|
1 |
|
$joinSteps = []; |
197
|
|
|
$joinSteps[] = $step; |
198
|
|
|
|
199
|
|
|
$joinSteps = $this->buildJoinsSteps($currentSteps, $step, $wf, $join, $joinSteps); |
200
|
|
|
|
201
|
|
|
$historySteps = $store->findHistorySteps($entry->getId()); |
202
|
|
|
|
203
|
|
|
$joinSteps = $this->buildJoinsSteps($historySteps, $step, $wf, $join, $joinSteps); |
204
|
|
|
|
205
|
|
|
|
206
|
|
|
$jn = new JoinNodes($joinSteps); |
207
|
|
|
$transientVars['jn'] = $jn; |
208
|
|
|
|
209
|
|
|
|
210
|
|
|
if ($conditionsEngine->passesConditionsWithType(null, $joinDesc->getConditions(), $transientVars, $ps, 0)) { |
211
|
|
|
$joinResult = $joinDesc->getResult(); |
212
|
|
|
|
213
|
|
|
$joinResultValidators = $joinResult->getValidators(); |
214
|
|
|
if ($joinResultValidators->count() > 0) { |
215
|
|
|
$this->verifyInputs($joinResultValidators, $transientVars, $ps); |
216
|
|
|
} |
217
|
|
|
|
218
|
|
|
foreach ($joinResult->getPreFunctions() as $function) { |
219
|
|
|
$functionsEngine->executeFunction($function, $transientVars, $ps); |
220
|
|
|
} |
221
|
|
|
|
222
|
|
|
$previousIds = []; |
223
|
|
|
$i = 1; |
224
|
|
|
|
225
|
|
|
foreach ($joinSteps as $currentJoinStep) { |
226
|
|
|
if (!$historySteps->contains($currentJoinStep) && $currentJoinStep->getId() !== $step->getId()) { |
227
|
|
|
$store->moveToHistory($step); |
228
|
|
|
} |
229
|
|
|
|
230
|
|
|
$previousIds[$i] = $currentJoinStep->getId(); |
231
|
|
|
} |
232
|
|
|
|
233
|
|
View Code Duplication |
if (!$action->isFinish()) { |
|
|
|
|
234
|
|
|
$previousIds[0] = $step->getId(); |
235
|
|
|
$theResult = $joinDesc->getResult(); |
236
|
|
|
|
237
|
|
|
$this->createNewCurrentStep($theResult, $entry, $store, $action->getId(), null, $previousIds, $transientVars, $ps); |
238
|
|
|
} |
239
|
|
|
|
240
|
|
|
foreach ($joinResult->getPostFunctions() as $function) { |
241
|
|
|
$functionsEngine->executeFunction($function, $transientVars, $ps); |
242
|
|
|
} |
243
|
|
|
} |
244
|
|
View Code Duplication |
} else { |
|
|
|
|
245
|
16 |
|
$previousIds = []; |
246
|
|
|
|
247
|
16 |
|
if (null !== $step) { |
248
|
5 |
|
$previousIds[] = $step->getId(); |
249
|
5 |
|
} |
250
|
|
|
|
251
|
16 |
|
if (!$action->isFinish()) { |
252
|
16 |
|
$this->createNewCurrentStep($theResult, $entry, $store, $action->getId(), $step, $previousIds, $transientVars, $ps); |
253
|
16 |
|
} |
254
|
|
|
} |
255
|
|
|
|
256
|
16 |
|
if ($extraPostFunctions && $extraPostFunctions->count() > 0) { |
257
|
2 |
|
foreach ($extraPostFunctions as $function) { |
258
|
2 |
|
$functionsEngine->executeFunction($function, $transientVars, $ps); |
259
|
2 |
|
} |
260
|
2 |
|
} |
261
|
|
|
|
262
|
16 |
|
if (WorkflowEntryInterface::COMPLETED !== $entry->getState() && null !== $wf->getInitialAction($action->getId())) { |
263
|
16 |
|
$workflowManager->changeEntryState($entry->getId(), WorkflowEntryInterface::ACTIVATED); |
264
|
16 |
|
} |
265
|
|
|
|
266
|
16 |
|
if ($action->isFinish()) { |
267
|
|
|
$entryEngine = $engineManager->getEntryEngine(); |
268
|
|
|
$entryEngine->completeEntry($action, $entry->getId(), $workflowManager->getCurrentSteps($entry->getId()), WorkflowEntryInterface::COMPLETED); |
269
|
|
|
return true; |
270
|
|
|
} |
271
|
|
|
|
272
|
16 |
|
$availableAutoActions = $this->getAvailableAutoActions($entry->getId(), $inputs); |
273
|
|
|
|
274
|
16 |
|
if (count($availableAutoActions) > 0) { |
275
|
|
|
$workflowManager->doAction($entry->getId(), $availableAutoActions[0], $inputs); |
276
|
|
|
} |
277
|
|
|
|
278
|
16 |
|
return false; |
279
|
3 |
|
} catch (\Exception $e) { |
280
|
3 |
|
throw new InternalWorkflowException($e->getMessage(), $e->getCode(), $e); |
281
|
|
|
} |
282
|
|
|
} |
283
|
|
|
|
284
|
|
|
|
285
|
|
|
/** |
286
|
|
|
* @param ResultDescriptor $theResult |
287
|
|
|
* @param WorkflowEntryInterface $entry |
288
|
|
|
* @param WorkflowStoreInterface $store |
289
|
|
|
* @param integer $actionId |
290
|
|
|
* @param StepInterface $currentStep |
291
|
|
|
* @param array $previousIds |
292
|
|
|
* @param TransientVarsInterface $transientVars |
293
|
|
|
* @param PropertySetInterface $ps |
294
|
|
|
* |
295
|
|
|
* @return StepInterface |
296
|
|
|
* |
297
|
|
|
* @throws InternalWorkflowException |
298
|
|
|
* @throws StoreException |
299
|
|
|
* @throws \OldTown\Workflow\Exception\ArgumentNotNumericException |
300
|
|
|
* @throws WorkflowException |
301
|
|
|
*/ |
302
|
16 |
|
protected function createNewCurrentStep( |
303
|
|
|
ResultDescriptor $theResult, |
304
|
|
|
WorkflowEntryInterface $entry, |
305
|
|
|
WorkflowStoreInterface $store, |
306
|
|
|
$actionId, |
307
|
|
|
StepInterface $currentStep = null, |
308
|
|
|
array $previousIds = [], |
309
|
|
|
TransientVarsInterface $transientVars, |
310
|
|
|
PropertySetInterface $ps |
311
|
|
|
) { |
312
|
16 |
|
$workflowManager = $this->getWorkflowManager(); |
313
|
16 |
|
$context = $workflowManager->getContext(); |
314
|
|
|
|
315
|
|
|
|
316
|
|
|
try { |
317
|
16 |
|
$nextStep = $theResult->getStep(); |
318
|
|
|
|
319
|
16 |
|
if (-1 === $nextStep) { |
320
|
|
|
if (null !== $currentStep) { |
321
|
|
|
$nextStep = $currentStep->getStepId(); |
322
|
|
|
} else { |
323
|
|
|
$errMsg = 'Неверный аргумент. Новый шаг является таким же как текущий. Но текущий шаг не указан'; |
324
|
|
|
throw new StoreException($errMsg); |
325
|
|
|
} |
326
|
|
|
} |
327
|
|
|
|
328
|
16 |
|
$owner = $theResult->getOwner(); |
329
|
|
|
|
330
|
16 |
|
$logMsg = sprintf( |
331
|
16 |
|
'Результат: stepId=%s, status=%s, owner=%s, actionId=%s, currentStep=%s', |
332
|
16 |
|
$nextStep, |
333
|
16 |
|
$theResult->getStatus(), |
334
|
16 |
|
$owner, |
335
|
16 |
|
$actionId, |
336
|
16 |
|
null !== $currentStep ? $currentStep->getId() : 0 |
337
|
16 |
|
); |
338
|
|
|
|
339
|
16 |
|
$log = $workflowManager->getLog(); |
340
|
|
|
|
341
|
|
|
|
342
|
16 |
|
$log->debug($logMsg); |
343
|
|
|
|
344
|
16 |
|
$variableResolver = $workflowManager->getConfiguration()->getVariableResolver(); |
345
|
|
|
|
346
|
16 |
|
if (null !== $owner) { |
347
|
|
|
$o = $variableResolver->translateVariables($owner, $transientVars, $ps); |
348
|
|
|
$owner = null !== $o ? (string)$o : null; |
349
|
|
|
} |
350
|
|
|
|
351
|
|
|
|
352
|
16 |
|
$oldStatus = $theResult->getOldStatus(); |
353
|
16 |
|
$oldStatus = (string)$variableResolver->translateVariables($oldStatus, $transientVars, $ps); |
354
|
|
|
|
355
|
16 |
|
$status = $theResult->getStatus(); |
356
|
16 |
|
$status = (string)$variableResolver->translateVariables($status, $transientVars, $ps); |
357
|
|
|
|
358
|
|
|
|
359
|
16 |
|
if (null !== $currentStep) { |
360
|
5 |
|
$store->markFinished($currentStep, $actionId, new DateTime(), $oldStatus, $context->getCaller()); |
361
|
5 |
|
$store->moveToHistory($currentStep); |
362
|
5 |
|
} |
363
|
|
|
|
364
|
16 |
|
$startDate = new DateTime(); |
365
|
16 |
|
$dueDate = null; |
366
|
|
|
|
367
|
16 |
|
$theResultDueDate = (string)$theResult->getDueDate(); |
368
|
16 |
|
$theResultDueDate = trim($theResultDueDate); |
369
|
16 |
|
if (strlen($theResultDueDate) > 0) { |
370
|
|
|
$dueDateObject = $variableResolver->translateVariables($theResultDueDate, $transientVars, $ps); |
371
|
|
|
|
372
|
|
|
if ($dueDateObject instanceof DateTime) { |
373
|
|
|
$dueDate = $dueDateObject; |
374
|
|
|
} elseif (is_string($dueDateObject)) { |
375
|
|
|
$dueDate = new DateTime($dueDate); |
376
|
|
|
} elseif (is_numeric($dueDateObject)) { |
377
|
|
|
$dueDate = DateTime::createFromFormat('U', $dueDateObject); |
378
|
|
|
if (false === $dueDate) { |
379
|
|
|
$errMsg = 'Invalid due date conversion'; |
380
|
|
|
throw new InternalWorkflowException($errMsg); |
381
|
|
|
} |
382
|
|
|
} |
383
|
|
|
} |
384
|
|
|
|
385
|
16 |
|
$newStep = $store->createCurrentStep($entry->getId(), $nextStep, $owner, $startDate, $dueDate, $status, $previousIds); |
386
|
16 |
|
$transientVars['createdStep'] = $newStep; |
387
|
|
|
|
388
|
16 |
|
if (null === $currentStep && 0 === count($previousIds)) { |
389
|
16 |
|
$currentSteps = []; |
390
|
16 |
|
$currentSteps[] = $newStep; |
391
|
16 |
|
$transientVars['currentSteps'] = $currentSteps; |
392
|
16 |
|
} |
393
|
|
|
|
394
|
16 |
|
if (! $transientVars->offsetExists('descriptor')) { |
395
|
|
|
$errMsg = 'Ошибка при получение дескриптора workflow из transientVars'; |
396
|
|
|
throw new InternalWorkflowException($errMsg); |
397
|
|
|
} |
398
|
|
|
|
399
|
|
|
/** @var WorkflowDescriptor $descriptor */ |
400
|
16 |
|
$descriptor = $transientVars['descriptor']; |
401
|
16 |
|
$step = $descriptor->getStep($nextStep); |
402
|
|
|
|
403
|
16 |
|
if (null === $step) { |
404
|
|
|
$errMsg = sprintf('Шаг #%s не найден', $nextStep); |
405
|
|
|
throw new WorkflowException($errMsg); |
406
|
|
|
} |
407
|
|
|
|
408
|
16 |
|
$preFunctions = $step->getPreFunctions(); |
409
|
|
|
|
410
|
16 |
|
$functionsEngine = $workflowManager->getEngineManager()->getFunctionsEngine(); |
411
|
16 |
|
foreach ($preFunctions as $function) { |
412
|
|
|
$functionsEngine->executeFunction($function, $transientVars, $ps); |
413
|
16 |
|
} |
414
|
16 |
|
} catch (WorkflowException $e) { |
415
|
|
|
$context->setRollbackOnly(); |
416
|
|
|
/** @var WorkflowException $e */ |
417
|
|
|
throw $e; |
418
|
|
|
} |
419
|
16 |
|
} |
420
|
|
|
|
421
|
|
|
|
422
|
|
|
/** |
423
|
|
|
* |
424
|
|
|
* Возвращает текущий шаг |
425
|
|
|
* |
426
|
|
|
* @param WorkflowDescriptor $wfDesc |
427
|
|
|
* @param integer $actionId |
428
|
|
|
* @param StepInterface[]|SplObjectStorage $currentSteps |
429
|
|
|
* @param TransientVarsInterface $transientVars |
430
|
|
|
* @param PropertySetInterface $ps |
431
|
|
|
* |
432
|
|
|
* @return StepInterface |
433
|
|
|
* |
434
|
|
|
* @throws \OldTown\Workflow\Exception\ArgumentNotNumericException |
435
|
|
|
* @throws InternalWorkflowException |
436
|
|
|
*/ |
437
|
18 |
|
protected function getCurrentStep(WorkflowDescriptor $wfDesc, $actionId, SplObjectStorage $currentSteps, TransientVarsInterface $transientVars, PropertySetInterface $ps) |
438
|
|
|
{ |
439
|
18 |
|
if (1 === $currentSteps->count()) { |
440
|
6 |
|
$currentSteps->rewind(); |
441
|
6 |
|
return $currentSteps->current(); |
442
|
|
|
} |
443
|
|
|
|
444
|
|
|
|
445
|
18 |
|
foreach ($currentSteps as $step) { |
446
|
|
|
$stepId = $step->getId(); |
447
|
|
|
$action = $wfDesc->getStep($stepId)->getAction($actionId); |
448
|
|
|
|
449
|
|
|
if ($this->isActionAvailable($action, $transientVars, $ps, $stepId)) { |
450
|
|
|
return $step; |
451
|
|
|
} |
452
|
18 |
|
} |
453
|
|
|
|
454
|
18 |
|
return null; |
455
|
|
|
} |
456
|
|
|
|
457
|
|
|
|
458
|
|
|
/** |
459
|
|
|
* @param $validatorsStorage |
460
|
|
|
* @param TransientVarsInterface $transientVars |
461
|
|
|
* @param PropertySetInterface $ps |
462
|
|
|
* |
463
|
|
|
* @throws InvalidInputException |
464
|
|
|
* @throws WorkflowException |
465
|
|
|
*/ |
466
|
18 |
|
protected function verifyInputs($validatorsStorage, TransientVarsInterface $transientVars, PropertySetInterface $ps) |
467
|
|
|
{ |
468
|
15 |
|
$workflowManager = $this->getWorkflowManager(); |
469
|
15 |
|
$engineManager = $workflowManager->getEngineManager(); |
470
|
15 |
|
$argsEngine = $engineManager->getArgsEngine(); |
471
|
15 |
|
$dataEngine = $engineManager->getDataEngine(); |
472
|
|
|
|
473
|
|
|
|
474
|
15 |
|
$validators = $dataEngine->convertDataInArray($validatorsStorage); |
475
|
|
|
|
476
|
|
|
|
477
|
15 |
|
$resolver = $workflowManager->getResolver(); |
478
|
|
|
|
479
|
|
|
/** @var ValidatorDescriptor[] $validators */ |
480
|
15 |
|
foreach ($validators as $input) { |
481
|
6 |
|
if (null !== $input) { |
482
|
6 |
|
$type = $input->getType(); |
483
|
6 |
|
$argsOriginal = $input->getArgs(); |
484
|
|
|
|
485
|
6 |
|
$args = $argsEngine->prepareArgs($argsOriginal, $transientVars, $ps); |
486
|
|
|
|
487
|
|
|
|
488
|
18 |
|
$validator = $resolver->getValidator($type, $args); |
489
|
|
|
|
490
|
6 |
|
if (null === $validator) { |
491
|
|
|
$workflowManager->getContext()->setRollbackOnly(); |
492
|
|
|
$errMsg = 'Ошибка при загрузке валидатора'; |
493
|
|
|
throw new WorkflowException($errMsg); |
494
|
|
|
} |
495
|
|
|
|
496
|
|
|
try { |
497
|
6 |
|
$validator->validate($transientVars, $args, $ps); |
498
|
6 |
|
} catch (InvalidInputException $e) { |
499
|
|
|
/** @var InvalidInputException $e*/ |
500
|
|
|
throw $e; |
501
|
2 |
|
} catch (\Exception $e) { |
502
|
2 |
|
$workflowManager->getContext()->setRollbackOnly(); |
503
|
|
|
|
504
|
2 |
|
if ($e instanceof WorkflowException) { |
505
|
|
|
/** @var WorkflowException $e*/ |
506
|
|
|
throw $e; |
507
|
|
|
} |
508
|
|
|
|
509
|
2 |
|
throw new WorkflowException($e->getMessage(), $e->getCode(), $e); |
510
|
|
|
} |
511
|
4 |
|
} |
512
|
13 |
|
} |
513
|
13 |
|
} |
514
|
|
|
|
515
|
|
|
|
516
|
|
|
/** |
517
|
|
|
* Подготавливает данные о шагах используемых в объеденение |
518
|
|
|
* |
519
|
|
|
* @param StepInterface[]|SplObjectStorage $steps |
520
|
|
|
* @param StepInterface $step |
521
|
|
|
* @param WorkflowDescriptor $wf |
522
|
|
|
* @param integer $join |
523
|
|
|
* |
524
|
|
|
* @param array $joinSteps |
525
|
|
|
* |
526
|
|
|
* @return array |
527
|
|
|
* |
528
|
|
|
* @throws \OldTown\Workflow\Exception\ArgumentNotNumericException |
529
|
|
|
*/ |
530
|
|
|
protected function buildJoinsSteps($steps, StepInterface $step, WorkflowDescriptor $wf, $join, array $joinSteps = []) |
531
|
|
|
{ |
532
|
|
|
foreach ($steps as $currentStep) { |
533
|
|
|
if ($currentStep->getId() !== $step->getId()) { |
534
|
|
|
$stepDesc = $wf->getStep($currentStep->getStepId()); |
535
|
|
|
|
536
|
|
|
if ($stepDesc->resultsInJoin($join)) { |
537
|
|
|
$joinSteps[] = $currentStep; |
538
|
|
|
} |
539
|
|
|
} |
540
|
|
|
} |
541
|
|
|
|
542
|
|
|
return $joinSteps; |
543
|
|
|
} |
544
|
|
|
|
545
|
|
|
|
546
|
|
|
/** |
547
|
|
|
* @param $id |
548
|
|
|
* @param TransientVarsInterface $inputs |
549
|
|
|
* |
550
|
|
|
* @return array |
551
|
|
|
*/ |
552
|
16 |
|
protected function getAvailableAutoActions($id, TransientVarsInterface $inputs) |
553
|
|
|
{ |
554
|
16 |
|
$workflowManager = $this->getWorkflowManager(); |
555
|
|
|
try { |
556
|
16 |
|
$configurations = $workflowManager->getConfiguration(); |
557
|
16 |
|
$store = $configurations->getWorkflowStore(); |
558
|
|
|
|
559
|
16 |
|
$entry = $store->findEntry($id); |
560
|
|
|
|
561
|
16 |
|
if (null === $entry) { |
562
|
|
|
$errMsg = sprintf( |
563
|
|
|
'Нет сущности workflow c id %s', |
564
|
|
|
$id |
565
|
|
|
); |
566
|
|
|
throw new InvalidArgumentException($errMsg); |
567
|
|
|
} |
568
|
|
|
|
569
|
|
|
|
570
|
16 |
|
if (WorkflowEntryInterface::ACTIVATED !== $entry->getState()) { |
571
|
|
|
$logMsg = sprintf('--> состояние %s', $entry->getState()); |
572
|
|
|
$workflowManager->getLog()->debug($logMsg); |
573
|
|
|
return [0]; |
574
|
|
|
} |
575
|
|
|
|
576
|
16 |
|
$wf = $configurations->getWorkflow($entry->getWorkflowName()); |
577
|
|
|
|
578
|
16 |
|
$l = []; |
579
|
16 |
|
$ps = $store->getPropertySet($id); |
580
|
16 |
|
$transientVars = $inputs; |
581
|
16 |
|
$currentSteps = $store->findCurrentSteps($id); |
582
|
|
|
|
583
|
16 |
|
$workflowManager->getEngineManager()->getDataEngine()->populateTransientMap($entry, $transientVars, $wf->getRegisters(), 0, $currentSteps, $ps); |
584
|
|
|
|
585
|
16 |
|
$globalActions = $wf->getGlobalActions(); |
586
|
|
|
|
587
|
16 |
|
$l = $this->buildListIdsAvailableActions($globalActions, $transientVars, $ps, $l); |
588
|
|
|
|
589
|
16 |
|
foreach ($currentSteps as $step) { |
590
|
16 |
|
$availableAutoActionsForStep = $this->getAvailableAutoActionsForStep($wf, $step, $transientVars, $ps); |
591
|
|
|
foreach ($availableAutoActionsForStep as $v) { |
592
|
|
|
$l[] = $v; |
593
|
|
|
} |
594
|
|
|
} |
595
|
|
|
|
596
|
|
|
$l = array_unique($l); |
597
|
|
|
|
598
|
|
|
return $l; |
599
|
16 |
|
} catch (\Exception $e) { |
600
|
16 |
|
$errMsg = 'Ошибка при проверке доступных действий'; |
601
|
16 |
|
$workflowManager->getLog()->error($errMsg, [$e]); |
602
|
|
|
} |
603
|
|
|
|
604
|
16 |
|
return []; |
605
|
|
|
} |
606
|
|
|
|
607
|
|
|
|
608
|
|
|
/** |
609
|
|
|
* @param WorkflowDescriptor $wf |
610
|
|
|
* @param StepInterface $step |
611
|
|
|
* @param TransientVarsInterface $transientVars |
612
|
|
|
* @param PropertySetInterface $ps |
613
|
|
|
* |
614
|
|
|
* @return array |
615
|
|
|
* |
616
|
|
|
* @throws \OldTown\Workflow\Exception\ArgumentNotNumericException |
617
|
|
|
* @throws InternalWorkflowException |
618
|
|
|
* @throws WorkflowException |
619
|
|
|
*/ |
620
|
16 |
|
protected function getAvailableAutoActionsForStep(WorkflowDescriptor $wf, StepInterface $step, TransientVarsInterface $transientVars, PropertySetInterface $ps) |
621
|
|
|
{ |
622
|
16 |
|
$l = []; |
623
|
16 |
|
$s = $wf->getStep($step->getStepId()); |
624
|
|
|
|
625
|
16 |
|
if (null === $s) { |
626
|
|
|
$msg = sprintf('getAvailableAutoActionsForStep вызвана с несуществующим id %s', $step->getStepId()); |
627
|
|
|
$this->getWorkflowManager()->getLog()->debug($msg); |
628
|
|
|
return $l; |
629
|
|
|
} |
630
|
|
|
|
631
|
|
|
|
632
|
16 |
|
$actions = $s->getActions(); |
633
|
16 |
|
if (null === $actions || 0 === $actions->count()) { |
634
|
|
|
return $l; |
635
|
|
|
} |
636
|
|
|
|
637
|
16 |
|
$l = $this->buildListIdsAvailableActions($actions, $transientVars, $ps, $l); |
638
|
|
|
|
639
|
|
|
return $l; |
640
|
|
|
} |
641
|
|
|
|
642
|
|
|
|
643
|
|
|
/** |
644
|
|
|
* Подготавливает список id действий в workflow |
645
|
|
|
* |
646
|
|
|
* @param ActionDescriptor[]|SplObjectStorage $actions |
647
|
|
|
* @param TransientVarsInterface $transientVars |
648
|
|
|
* @param PropertySetInterface $ps |
649
|
|
|
* @param array $storage |
650
|
|
|
* |
651
|
|
|
* @return array |
652
|
|
|
* |
653
|
|
|
* @throws InternalWorkflowException |
654
|
|
|
* @throws WorkflowException |
655
|
|
|
*/ |
656
|
16 |
|
protected function buildListIdsAvailableActions($actions, TransientVarsInterface $transientVars, PropertySetInterface $ps, array $storage = []) |
657
|
|
|
{ |
658
|
16 |
|
foreach ($actions as $action) { |
659
|
16 |
|
if ($action instanceof ActionDescriptor) { |
660
|
16 |
|
$errMsg = sprintf('Invalid workflow action. Action not implement %s', ActionDescriptor::class); |
661
|
16 |
|
throw new InternalWorkflowException($errMsg); |
662
|
|
|
} |
663
|
|
|
$transientVars['actionId'] = $action->getId(); |
664
|
|
|
|
665
|
|
|
if ($action->getAutoExecute() && $this->isActionAvailable($action, $transientVars, $ps, 0)) { |
666
|
|
|
$storage[] = $action->getId(); |
667
|
|
|
} |
668
|
16 |
|
} |
669
|
|
|
|
670
|
16 |
|
return $storage; |
671
|
|
|
} |
672
|
|
|
|
673
|
|
|
|
674
|
|
|
/** |
675
|
|
|
* |
676
|
|
|
* @param ActionDescriptor|null $action |
677
|
|
|
* @param TransientVarsInterface $transientVars |
678
|
|
|
* @param PropertySetInterface $ps |
679
|
|
|
* @param $stepId |
680
|
|
|
* |
681
|
|
|
* @return boolean |
682
|
|
|
* |
683
|
|
|
* @throws InternalWorkflowException |
684
|
|
|
*/ |
685
|
8 |
|
public function isActionAvailable(ActionDescriptor $action = null, TransientVarsInterface $transientVars, PropertySetInterface $ps, $stepId) |
686
|
|
|
{ |
687
|
8 |
|
if (null === $action) { |
688
|
|
|
return false; |
689
|
|
|
} |
690
|
|
|
|
691
|
8 |
|
$result = null; |
692
|
8 |
|
$actionHash = spl_object_hash($action); |
693
|
|
|
|
694
|
8 |
|
$result = array_key_exists($actionHash, $this->stateCache) ? $this->stateCache[$actionHash] : $result; |
695
|
|
|
|
696
|
8 |
|
$wf = $this->getWorkflowDescriptorForAction($action); |
697
|
|
|
|
698
|
8 |
|
$conditionsEngine = $this->getWorkflowManager()->getEngineManager()->getConditionsEngine(); |
699
|
8 |
|
if (null === $result) { |
700
|
8 |
|
$restriction = $action->getRestriction(); |
701
|
8 |
|
$conditions = null; |
702
|
|
|
|
703
|
8 |
|
if (null !== $restriction) { |
704
|
5 |
|
$conditions = $restriction->getConditionsDescriptor(); |
705
|
5 |
|
} |
706
|
|
|
|
707
|
8 |
|
$result = $conditionsEngine->passesConditionsByDescriptor($wf->getGlobalConditions(), $transientVars, $ps, $stepId) |
708
|
8 |
|
&& $conditionsEngine->passesConditionsByDescriptor($conditions, $transientVars, $ps, $stepId); |
709
|
|
|
|
710
|
8 |
|
$this->stateCache[$actionHash] = $result; |
711
|
8 |
|
} |
712
|
|
|
|
713
|
|
|
|
714
|
8 |
|
$result = (boolean)$result; |
715
|
|
|
|
716
|
8 |
|
return $result; |
717
|
|
|
} |
718
|
|
|
|
719
|
|
|
|
720
|
|
|
|
721
|
|
|
/** |
722
|
|
|
* |
723
|
|
|
* По дейсвтию получаем дексрипторв workflow |
724
|
|
|
* |
725
|
|
|
* @param ActionDescriptor $action |
726
|
|
|
* |
727
|
|
|
* @return WorkflowDescriptor |
728
|
|
|
* |
729
|
|
|
* @throws InternalWorkflowException |
730
|
|
|
*/ |
731
|
8 |
|
private function getWorkflowDescriptorForAction(ActionDescriptor $action) |
732
|
|
|
{ |
733
|
8 |
|
$objWfd = $action; |
734
|
|
|
|
735
|
8 |
|
$count = 0; |
736
|
8 |
|
while (!$objWfd instanceof WorkflowDescriptor || null === $objWfd) { |
737
|
8 |
|
$objWfd = $objWfd->getParent(); |
738
|
|
|
|
739
|
8 |
|
$count++; |
740
|
8 |
|
if ($count > 10) { |
741
|
|
|
$errMsg = 'Ошибка при получение WorkflowDescriptor'; |
742
|
|
|
throw new InternalWorkflowException($errMsg); |
743
|
|
|
} |
744
|
8 |
|
} |
745
|
|
|
|
746
|
8 |
|
return $objWfd; |
747
|
|
|
} |
748
|
|
|
} |
749
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.