1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* @link https://github.com/old-town/old-town-workflow |
4
|
|
|
* @author Malofeykin Andrey <[email protected]> |
5
|
|
|
*/ |
6
|
|
|
namespace OldTown\Workflow; |
7
|
|
|
|
8
|
|
|
use OldTown\Log\LogFactory; |
9
|
|
|
use OldTown\PropertySet\PropertySetInterface; |
10
|
|
|
use OldTown\PropertySet\PropertySetManager; |
11
|
|
|
use OldTown\Workflow\Config\ConfigurationInterface; |
12
|
|
|
use OldTown\Workflow\Config\DefaultConfiguration; |
13
|
|
|
use OldTown\Workflow\Exception\FactoryException; |
14
|
|
|
use OldTown\Workflow\Exception\InternalWorkflowException; |
15
|
|
|
use OldTown\Workflow\Exception\InvalidActionException; |
16
|
|
|
use OldTown\Workflow\Exception\InvalidArgumentException; |
17
|
|
|
use OldTown\Workflow\Exception\InvalidEntryStateException; |
18
|
|
|
use OldTown\Workflow\Exception\InvalidInputException; |
19
|
|
|
use OldTown\Workflow\Exception\InvalidRoleException; |
20
|
|
|
use OldTown\Workflow\Exception\StoreException; |
21
|
|
|
use OldTown\Workflow\Exception\WorkflowException; |
22
|
|
|
use OldTown\Workflow\Loader\ActionDescriptor; |
23
|
|
|
use OldTown\Workflow\Loader\ConditionDescriptor; |
24
|
|
|
use OldTown\Workflow\Loader\ConditionsDescriptor; |
25
|
|
|
use OldTown\Workflow\Loader\FunctionDescriptor; |
26
|
|
|
use OldTown\Workflow\Loader\PermissionDescriptor; |
27
|
|
|
use OldTown\Workflow\Loader\RegisterDescriptor; |
28
|
|
|
use OldTown\Workflow\Loader\ResultDescriptor; |
29
|
|
|
use OldTown\Workflow\Loader\ValidatorDescriptor; |
30
|
|
|
use OldTown\Workflow\Loader\WorkflowDescriptor; |
31
|
|
|
use OldTown\Workflow\Query\WorkflowExpressionQuery; |
32
|
|
|
use OldTown\Workflow\Spi\SimpleWorkflowEntry; |
33
|
|
|
use OldTown\Workflow\Spi\StepInterface; |
34
|
|
|
use OldTown\Workflow\Spi\WorkflowEntryInterface; |
35
|
|
|
use OldTown\Workflow\Spi\WorkflowStoreInterface; |
36
|
|
|
use OldTown\Workflow\TransientVars\TransientVarsInterface; |
37
|
|
|
use Psr\Log\LoggerInterface; |
38
|
|
|
use Traversable; |
39
|
|
|
use SplObjectStorage; |
40
|
|
|
use DateTime; |
41
|
|
|
use OldTown\Workflow\TransientVars\BaseTransientVars; |
42
|
|
|
use ReflectionClass; |
43
|
|
|
use ArrayObject; |
44
|
|
|
|
45
|
|
|
|
46
|
|
|
/** |
47
|
|
|
* Class AbstractWorkflow |
48
|
|
|
* |
49
|
|
|
* @package OldTown\Workflow |
50
|
|
|
*/ |
51
|
|
|
abstract class AbstractWorkflow implements WorkflowInterface |
52
|
|
|
{ |
53
|
|
|
/** |
54
|
|
|
* @var string |
55
|
|
|
*/ |
56
|
|
|
const CURRENT_STEPS = 'currentSteps'; |
57
|
|
|
|
58
|
|
|
/** |
59
|
|
|
* @var string |
60
|
|
|
*/ |
61
|
|
|
const HISTORY_STEPS = 'historySteps'; |
62
|
|
|
|
63
|
|
|
/** |
64
|
|
|
* @var WorkflowContextInterface |
65
|
|
|
*/ |
66
|
|
|
protected $context; |
67
|
|
|
|
68
|
|
|
/** |
69
|
|
|
* @var ConfigurationInterface |
70
|
|
|
*/ |
71
|
|
|
protected $configuration; |
72
|
|
|
|
73
|
|
|
/** |
74
|
|
|
* |
75
|
|
|
* @var array |
76
|
|
|
*/ |
77
|
|
|
protected $stateCache = []; |
78
|
|
|
|
79
|
|
|
/** |
80
|
|
|
* @var TypeResolverInterface |
81
|
|
|
*/ |
82
|
|
|
protected $typeResolver; |
83
|
|
|
|
84
|
|
|
/** |
85
|
|
|
* Логер |
86
|
|
|
* |
87
|
|
|
* @var LoggerInterface |
88
|
|
|
*/ |
89
|
|
|
protected $log; |
90
|
|
|
|
91
|
|
|
/** |
92
|
|
|
* Резолвер для создания провайдеров отвечающих за исполнение функций, проверку условий, выполнение валидаторов и т.д. |
93
|
|
|
* |
94
|
|
|
* @var string |
95
|
|
|
*/ |
96
|
|
|
protected $defaultTypeResolverClass = TypeResolver::class; |
97
|
|
|
|
98
|
|
|
/** |
99
|
|
|
* AbstractWorkflow constructor. |
100
|
|
|
* |
101
|
|
|
* @throws InternalWorkflowException |
102
|
|
|
*/ |
103
|
|
|
public function __construct() |
104
|
|
|
{ |
105
|
|
|
$this->initLoger(); |
106
|
|
|
} |
107
|
|
|
|
108
|
|
|
/** |
109
|
|
|
* Инициализация системы логирования |
110
|
|
|
* |
111
|
|
|
* @throws InternalWorkflowException |
112
|
|
|
*/ |
113
|
|
|
protected function initLoger() |
114
|
|
|
{ |
115
|
|
|
try { |
116
|
|
|
$this->log = LogFactory::getLog(); |
117
|
|
|
} catch (\Exception $e) { |
118
|
|
|
throw new InternalWorkflowException($e->getMessage(), $e->getCode(), $e); |
119
|
|
|
} |
120
|
|
|
} |
121
|
|
|
|
122
|
|
|
/** |
123
|
|
|
* Инициализация workflow. Workflow нужно иницаилизровать прежде, чем выполнять какие либо действия. |
124
|
|
|
* Workflow может быть инициализированно только один раз |
125
|
|
|
* |
126
|
|
|
* @param string $workflowName Имя workflow |
127
|
|
|
* @param integer $initialAction Имя первого шага, с которого начинается workflow |
128
|
|
|
* @param TransientVarsInterface $inputs Данные введеные пользователем |
129
|
|
|
* |
130
|
|
|
* @return integer |
131
|
|
|
* |
132
|
|
|
* @throws InternalWorkflowException |
133
|
|
|
* @throws InvalidActionException |
134
|
|
|
* @throws InvalidRoleException |
135
|
|
|
* @throws \OldTown\Workflow\Exception\ArgumentNotNumericException |
136
|
|
|
* @throws InvalidArgumentException |
137
|
|
|
* @throws WorkflowException |
138
|
|
|
*/ |
139
|
|
|
public function initialize($workflowName, $initialAction, TransientVarsInterface $inputs = null) |
140
|
|
|
{ |
141
|
|
|
try { |
142
|
|
|
$initialAction = (integer)$initialAction; |
143
|
|
|
|
144
|
|
|
$wf = $this->getConfiguration()->getWorkflow($workflowName); |
145
|
|
|
|
146
|
|
|
$store = $this->getPersistence(); |
147
|
|
|
|
148
|
|
|
$entry = $store->createEntry($workflowName); |
149
|
|
|
|
150
|
|
|
$ps = $store->getPropertySet($entry->getId()); |
151
|
|
|
|
152
|
|
|
|
153
|
|
|
if (null === $inputs) { |
154
|
|
|
$inputs = $this->transientVarsFactory(); |
155
|
|
|
} |
156
|
|
|
$transientVars = $inputs; |
157
|
|
|
$inputs = clone $transientVars; |
158
|
|
|
|
159
|
|
|
$this->populateTransientMap($entry, $transientVars, $wf->getRegisters(), $initialAction, new ArrayObject(), $ps); |
160
|
|
|
|
161
|
|
|
if (!$this->canInitializeInternal($workflowName, $initialAction, $transientVars, $ps)) { |
162
|
|
|
$this->context->setRollbackOnly(); |
163
|
|
|
$errMsg = 'You are restricted from initializing this workflow'; |
164
|
|
|
throw new InvalidRoleException($errMsg); |
165
|
|
|
} |
166
|
|
|
|
167
|
|
|
$action = $wf->getInitialAction($initialAction); |
168
|
|
|
|
169
|
|
|
if (null === $action) { |
170
|
|
|
$errMsg = sprintf('Invalid initial action id: %s', $initialAction); |
171
|
|
|
throw new InvalidActionException($errMsg); |
172
|
|
|
} |
173
|
|
|
|
174
|
|
|
$currentSteps = new SplObjectStorage(); |
175
|
|
|
$this->transitionWorkflow($entry, $currentSteps, $store, $wf, $action, $transientVars, $inputs, $ps); |
176
|
|
|
|
177
|
|
|
$entryId = $entry->getId(); |
178
|
|
|
} catch (WorkflowException $e) { |
179
|
|
|
$this->context->setRollbackOnly(); |
180
|
|
|
throw new InternalWorkflowException($e->getMessage(), $e->getCode(), $e); |
181
|
|
|
} |
182
|
|
|
|
183
|
|
|
|
184
|
|
|
// now clone the memory PS to the real PS |
185
|
|
|
//PropertySetManager.clone(ps, store.getPropertySet(entryId)); |
|
|
|
|
186
|
|
|
return $entryId; |
187
|
|
|
} |
188
|
|
|
|
189
|
|
|
/** |
190
|
|
|
* @param WorkflowEntryInterface $entry |
191
|
|
|
* @param TransientVarsInterface $transientVars |
192
|
|
|
* @param array|Traversable|RegisterDescriptor[]|SplObjectStorage $registersStorage |
193
|
|
|
* @param integer $actionId |
194
|
|
|
* @param array|Traversable $currentSteps |
195
|
|
|
* @param PropertySetInterface $ps |
196
|
|
|
* |
197
|
|
|
* |
198
|
|
|
* @return TransientVarsInterface |
199
|
|
|
* |
200
|
|
|
* @throws InvalidArgumentException |
201
|
|
|
* @throws WorkflowException |
202
|
|
|
* @throws InternalWorkflowException |
203
|
|
|
*/ |
204
|
|
|
protected function populateTransientMap(WorkflowEntryInterface $entry, TransientVarsInterface $transientVars, $registersStorage, $actionId = null, $currentSteps, PropertySetInterface $ps) |
205
|
|
|
{ |
206
|
|
|
$this->validateIterateData($currentSteps); |
207
|
|
|
|
208
|
|
|
$registers = $this->convertDataInArray($registersStorage); |
209
|
|
|
|
210
|
|
|
|
211
|
|
|
/** @var RegisterDescriptor[] $registers */ |
212
|
|
|
|
213
|
|
|
$transientVars['context'] = $this->context; |
214
|
|
|
$transientVars['entry'] = $entry; |
215
|
|
|
$transientVars['entryId'] = $entry->getId(); |
216
|
|
|
$transientVars['store'] = $this->getPersistence(); |
217
|
|
|
$transientVars['configuration'] = $this->getConfiguration(); |
218
|
|
|
$transientVars['descriptor'] = $this->getConfiguration()->getWorkflow($entry->getWorkflowName()); |
219
|
|
|
|
220
|
|
|
if (null !== $actionId) { |
221
|
|
|
$transientVars['actionId'] = $actionId; |
222
|
|
|
} |
223
|
|
|
|
224
|
|
|
$transientVars['currentSteps'] = $currentSteps; |
225
|
|
|
|
226
|
|
|
|
227
|
|
|
foreach ($registers as $register) { |
228
|
|
|
$args = $register->getArgs(); |
229
|
|
|
$type = $register->getType(); |
230
|
|
|
|
231
|
|
|
try { |
232
|
|
|
$r = $this->getResolver()->getRegister($type, $args); |
233
|
|
|
} catch (\Exception $e) { |
234
|
|
|
$errMsg = 'Ошибка при инициализации register'; |
235
|
|
|
$this->context->setRollbackOnly(); |
236
|
|
|
throw new WorkflowException($errMsg, $e->getCode(), $e); |
237
|
|
|
} |
238
|
|
|
|
239
|
|
|
$variableName = $register->getVariableName(); |
240
|
|
|
try { |
241
|
|
|
$value = $r->registerVariable($this->context, $entry, $args, $ps); |
242
|
|
|
|
243
|
|
|
$transientVars[$variableName] = $value; |
244
|
|
|
} catch (\Exception $e) { |
245
|
|
|
$this->context->setRollbackOnly(); |
246
|
|
|
|
247
|
|
|
$errMsg = sprintf( |
248
|
|
|
'При получение значения переменной %s из registry %s произошла ошибка', |
249
|
|
|
$variableName, |
250
|
|
|
get_class($r) |
251
|
|
|
); |
252
|
|
|
|
253
|
|
|
throw new WorkflowException($errMsg, $e->getCode(), $e); |
254
|
|
|
} |
255
|
|
|
} |
256
|
|
|
|
257
|
|
|
return $transientVars; |
258
|
|
|
} |
259
|
|
|
|
260
|
|
|
/** |
261
|
|
|
* Проверка того что данные могут быть использованы в цикле |
262
|
|
|
* |
263
|
|
|
* @param $data |
264
|
|
|
* |
265
|
|
|
* @throws InvalidArgumentException |
266
|
|
|
*/ |
267
|
|
|
protected function validateIterateData($data) |
268
|
|
|
{ |
269
|
|
|
if (!is_array($data) && !$data instanceof Traversable) { |
270
|
|
|
$errMsg = 'Data not iterate'; |
271
|
|
|
throw new InvalidArgumentException($errMsg); |
272
|
|
|
} |
273
|
|
|
} |
274
|
|
|
|
275
|
|
|
/** |
276
|
|
|
* Преобразование данных в массив |
277
|
|
|
* |
278
|
|
|
* @param $data |
279
|
|
|
* |
280
|
|
|
* @return array |
281
|
|
|
* |
282
|
|
|
* @throws InvalidArgumentException |
283
|
|
|
*/ |
284
|
|
|
protected function convertDataInArray($data) |
285
|
|
|
{ |
286
|
|
|
$result = []; |
287
|
|
|
if ($data instanceof Traversable) { |
288
|
|
|
foreach ($data as $k => $v) { |
289
|
|
|
$result[$k] = $v; |
290
|
|
|
} |
291
|
|
|
} elseif (is_array($data)) { |
292
|
|
|
$result = $data; |
293
|
|
|
} else { |
294
|
|
|
$errMsg = 'Data must be an array or an interface to implement Traversable'; |
295
|
|
|
throw new InvalidArgumentException($errMsg); |
296
|
|
|
} |
297
|
|
|
|
298
|
|
|
return $result; |
299
|
|
|
} |
300
|
|
|
|
301
|
|
|
|
302
|
|
|
/** |
303
|
|
|
* Переход между двумя статусами |
304
|
|
|
* |
305
|
|
|
* @param WorkflowEntryInterface $entry |
306
|
|
|
* @param SplObjectStorage|StepInterface[] $currentSteps |
307
|
|
|
* @param WorkflowStoreInterface $store |
308
|
|
|
* @param WorkflowDescriptor $wf |
309
|
|
|
* @param ActionDescriptor $action |
310
|
|
|
* @param TransientVarsInterface $transientVars |
311
|
|
|
* @param TransientVarsInterface $inputs |
312
|
|
|
* @param PropertySetInterface $ps |
313
|
|
|
* |
314
|
|
|
* @return boolean |
315
|
|
|
* |
316
|
|
|
* @throws InternalWorkflowException |
317
|
|
|
*/ |
318
|
|
|
protected function transitionWorkflow(WorkflowEntryInterface $entry, SplObjectStorage $currentSteps, WorkflowStoreInterface $store, WorkflowDescriptor $wf, ActionDescriptor $action, TransientVarsInterface $transientVars, TransientVarsInterface $inputs, PropertySetInterface $ps) |
319
|
|
|
{ |
320
|
|
|
try { |
321
|
|
|
$step = $this->getCurrentStep($wf, $action->getId(), $currentSteps, $transientVars, $ps); |
322
|
|
|
|
323
|
|
|
$validators = $action->getValidators(); |
324
|
|
|
if ($validators->count() > 0) { |
325
|
|
|
$this->verifyInputs($validators, $transientVars, $ps); |
326
|
|
|
} |
327
|
|
|
|
328
|
|
|
|
329
|
|
|
if (null !== $step) { |
330
|
|
|
$stepPostFunctions = $wf->getStep($step->getStepId())->getPostFunctions(); |
331
|
|
|
foreach ($stepPostFunctions as $function) { |
332
|
|
|
$this->executeFunction($function, $transientVars, $ps); |
333
|
|
|
} |
334
|
|
|
} |
335
|
|
|
|
336
|
|
|
$preFunctions = $action->getPreFunctions(); |
337
|
|
|
foreach ($preFunctions as $preFunction) { |
338
|
|
|
$this->executeFunction($preFunction, $transientVars, $ps); |
339
|
|
|
} |
340
|
|
|
|
341
|
|
|
$conditionalResults = $action->getConditionalResults(); |
342
|
|
|
$extraPreFunctions = null; |
343
|
|
|
$extraPostFunctions = null; |
344
|
|
|
|
345
|
|
|
$theResult = null; |
346
|
|
|
|
347
|
|
|
|
348
|
|
|
$currentStepId = null !== $step ? $step->getStepId() : -1; |
349
|
|
|
foreach ($conditionalResults as $conditionalResult) { |
350
|
|
|
if ($this->passesConditionsWithType(null, $conditionalResult->getConditions(), $transientVars, $ps, $currentStepId)) { |
351
|
|
|
$theResult = $conditionalResult; |
352
|
|
|
|
353
|
|
|
$validatorsStorage = $conditionalResult->getValidators(); |
354
|
|
|
if ($validatorsStorage->count() > 0) { |
355
|
|
|
$this->verifyInputs($validatorsStorage, $transientVars, $ps); |
356
|
|
|
} |
357
|
|
|
|
358
|
|
|
$extraPreFunctions = $conditionalResult->getPreFunctions(); |
359
|
|
|
$extraPostFunctions = $conditionalResult->getPostFunctions(); |
360
|
|
|
|
361
|
|
|
break; |
362
|
|
|
} |
363
|
|
|
} |
364
|
|
|
|
365
|
|
|
|
366
|
|
|
if (null === $theResult) { |
367
|
|
|
$theResult = $action->getUnconditionalResult(); |
368
|
|
|
$this->verifyInputs($theResult->getValidators(), $transientVars, $ps); |
369
|
|
|
$extraPreFunctions = $theResult->getPreFunctions(); |
370
|
|
|
$extraPostFunctions = $theResult->getPostFunctions(); |
371
|
|
|
} |
372
|
|
|
|
373
|
|
|
$logMsg = sprintf('theResult=%s %s', $theResult->getStep(), $theResult->getStatus()); |
374
|
|
|
$this->getLog()->debug($logMsg); |
375
|
|
|
|
376
|
|
|
|
377
|
|
|
if ($extraPreFunctions && $extraPreFunctions->count() > 0) { |
378
|
|
|
foreach ($extraPreFunctions as $function) { |
379
|
|
|
$this->executeFunction($function, $transientVars, $ps); |
380
|
|
|
} |
381
|
|
|
} |
382
|
|
|
|
383
|
|
|
$split = $theResult->getSplit(); |
384
|
|
|
$join = $theResult->getJoin(); |
385
|
|
|
if (null !== $split && 0 !== $split) { |
386
|
|
|
$splitDesc = $wf->getSplit($split); |
387
|
|
|
$results = $splitDesc->getResults(); |
388
|
|
|
$splitPreFunctions = []; |
389
|
|
|
$splitPostFunctions = []; |
390
|
|
|
|
391
|
|
|
foreach ($results as $resultDescriptor) { |
392
|
|
|
if ($resultDescriptor->getValidators()->count() > 0) { |
393
|
|
|
$this->verifyInputs($resultDescriptor->getValidators(), $transientVars, $ps); |
394
|
|
|
} |
395
|
|
|
|
396
|
|
|
foreach ($resultDescriptor->getPreFunctions() as $function) { |
397
|
|
|
$splitPreFunctions[] = $function; |
398
|
|
|
} |
399
|
|
|
foreach ($resultDescriptor->getPostFunctions() as $function) { |
400
|
|
|
$splitPostFunctions[] = $function; |
401
|
|
|
} |
402
|
|
|
} |
403
|
|
|
|
404
|
|
|
foreach ($splitPreFunctions as $function) { |
405
|
|
|
$this->executeFunction($function, $transientVars, $ps); |
406
|
|
|
} |
407
|
|
|
|
408
|
|
|
if (!$action->isFinish()) { |
409
|
|
|
$moveFirst = true; |
410
|
|
|
|
411
|
|
|
//??????????????????? |
|
|
|
|
412
|
|
|
// $theResults = []; |
413
|
|
|
// foreach ($results as $result) { |
414
|
|
|
// $theResults[] = $result; |
415
|
|
|
// } |
416
|
|
|
|
417
|
|
|
foreach ($results as $resultDescriptor) { |
418
|
|
|
$moveToHistoryStep = null; |
419
|
|
|
|
420
|
|
|
if ($moveFirst) { |
421
|
|
|
$moveToHistoryStep = $step; |
422
|
|
|
} |
423
|
|
|
|
424
|
|
|
$previousIds = []; |
425
|
|
|
|
426
|
|
|
if (null !== $step) { |
427
|
|
|
$previousIds[] = $step->getStepId(); |
428
|
|
|
} |
429
|
|
|
|
430
|
|
|
$this->createNewCurrentStep($resultDescriptor, $entry, $store, $action->getId(), $moveToHistoryStep, $previousIds, $transientVars, $ps); |
431
|
|
|
$moveFirst = false; |
432
|
|
|
} |
433
|
|
|
} |
434
|
|
|
|
435
|
|
|
|
436
|
|
|
foreach ($splitPostFunctions as $function) { |
437
|
|
|
$this->executeFunction($function, $transientVars, $ps); |
438
|
|
|
} |
439
|
|
|
} elseif (null !== $join && 0 !== $join) { |
440
|
|
|
$joinDesc = $wf->getJoin($join); |
441
|
|
|
$oldStatus = $theResult->getOldStatus(); |
442
|
|
|
$caller = $this->context->getCaller(); |
443
|
|
|
if (null !== $step) { |
444
|
|
|
$step = $store->markFinished($step, $action->getId(), new DateTime(), $oldStatus, $caller); |
445
|
|
|
} else { |
446
|
|
|
$errMsg = 'Invalid step'; |
447
|
|
|
throw new InternalWorkflowException($errMsg); |
448
|
|
|
} |
449
|
|
|
|
450
|
|
|
|
451
|
|
|
$store->moveToHistory($step); |
452
|
|
|
|
453
|
|
|
/** @var StepInterface[] $joinSteps */ |
454
|
|
|
$joinSteps = []; |
455
|
|
|
$joinSteps[] = $step; |
456
|
|
|
|
457
|
|
View Code Duplication |
foreach ($currentSteps as $currentStep) { |
|
|
|
|
458
|
|
|
if ($currentStep->getId() !== $step->getId()) { |
459
|
|
|
$stepDesc = $wf->getStep($currentStep->getStepId()); |
460
|
|
|
|
461
|
|
|
if ($stepDesc->resultsInJoin($join)) { |
462
|
|
|
$joinSteps[] = $currentStep; |
463
|
|
|
} |
464
|
|
|
} |
465
|
|
|
} |
466
|
|
|
|
467
|
|
|
$historySteps = $store->findHistorySteps($entry->getId()); |
468
|
|
|
|
469
|
|
View Code Duplication |
foreach ($historySteps as $historyStep) { |
|
|
|
|
470
|
|
|
if ($historyStep->getId() !== $step->getId()) { |
471
|
|
|
$stepDesc = $wf->getStep($historyStep->getStepId()); |
472
|
|
|
|
473
|
|
|
if ($stepDesc->resultsInJoin($join)) { |
474
|
|
|
$joinSteps[] = $currentSteps; |
475
|
|
|
} |
476
|
|
|
} |
477
|
|
|
} |
478
|
|
|
|
479
|
|
|
$jn = new JoinNodes($joinSteps); |
480
|
|
|
$transientVars['jn'] = $jn; |
481
|
|
|
|
482
|
|
|
|
483
|
|
|
if ($this->passesConditionsWithType(null, $joinDesc->getConditions(), $transientVars, $ps, 0)) { |
484
|
|
|
$joinResult = $joinDesc->getResult(); |
485
|
|
|
|
486
|
|
|
$joinResultValidators = $joinResult->getValidators(); |
487
|
|
|
if ($joinResultValidators->count() > 0) { |
488
|
|
|
$this->verifyInputs($joinResultValidators, $transientVars, $ps); |
489
|
|
|
} |
490
|
|
|
|
491
|
|
|
foreach ($joinResult->getPreFunctions() as $function) { |
492
|
|
|
$this->executeFunction($function, $transientVars, $ps); |
493
|
|
|
} |
494
|
|
|
|
495
|
|
|
$previousIds = []; |
496
|
|
|
$i = 1; |
497
|
|
|
|
498
|
|
|
foreach ($joinSteps as $currentJoinStep) { |
499
|
|
|
if (!$historySteps->contains($currentJoinStep) && $currentJoinStep->getId() !== $step->getId()) { |
500
|
|
|
$store->moveToHistory($step); |
501
|
|
|
} |
502
|
|
|
|
503
|
|
|
$previousIds[$i] = $currentJoinStep->getId(); |
504
|
|
|
} |
505
|
|
|
|
506
|
|
|
if (!$action->isFinish()) { |
507
|
|
|
$previousIds[0] = $step->getId(); |
508
|
|
|
$theResult = $joinDesc->getResult(); |
509
|
|
|
|
510
|
|
|
$this->createNewCurrentStep($theResult, $entry, $store, $action->getId(), null, $previousIds, $transientVars, $ps); |
511
|
|
|
} |
512
|
|
|
|
513
|
|
|
foreach ($joinResult->getPostFunctions() as $function) { |
514
|
|
|
$this->executeFunction($function, $transientVars, $ps); |
515
|
|
|
} |
516
|
|
|
} |
517
|
|
|
} else { |
518
|
|
|
$previousIds = []; |
519
|
|
|
|
520
|
|
|
if (null !== $step) { |
521
|
|
|
$previousIds[] = $step->getId(); |
522
|
|
|
} |
523
|
|
|
|
524
|
|
|
if (!$action->isFinish()) { |
525
|
|
|
$this->createNewCurrentStep($theResult, $entry, $store, $action->getId(), $step, $previousIds, $transientVars, $ps); |
526
|
|
|
} |
527
|
|
|
} |
528
|
|
|
|
529
|
|
|
if ($extraPostFunctions && $extraPostFunctions->count() > 0) { |
530
|
|
|
foreach ($extraPostFunctions as $function) { |
531
|
|
|
$this->executeFunction($function, $transientVars, $ps); |
532
|
|
|
} |
533
|
|
|
} |
534
|
|
|
|
535
|
|
|
if (WorkflowEntryInterface::COMPLETED !== $entry->getState() && null !== $wf->getInitialAction($action->getId())) { |
536
|
|
|
$this->changeEntryState($entry->getId(), WorkflowEntryInterface::ACTIVATED); |
537
|
|
|
} |
538
|
|
|
|
539
|
|
|
if ($action->isFinish()) { |
540
|
|
|
$this->completeEntry($action, $entry->getId(), $this->getCurrentSteps($entry->getId()), WorkflowEntryInterface::COMPLETED); |
541
|
|
|
return true; |
542
|
|
|
} |
543
|
|
|
|
544
|
|
|
$availableAutoActions = $this->getAvailableAutoActions($entry->getId(), $inputs); |
545
|
|
|
|
546
|
|
|
if (count($availableAutoActions) > 0) { |
547
|
|
|
$this->doAction($entry->getId(), $availableAutoActions[0], $inputs); |
548
|
|
|
} |
549
|
|
|
|
550
|
|
|
return false; |
551
|
|
|
} catch (\Exception $e) { |
552
|
|
|
throw new InternalWorkflowException($e->getMessage(), $e->getCode(), $e); |
553
|
|
|
} |
554
|
|
|
} |
555
|
|
|
|
556
|
|
|
|
557
|
|
|
/** |
558
|
|
|
* @param $id |
559
|
|
|
* @param TransientVarsInterface $inputs |
560
|
|
|
* |
561
|
|
|
* @return array |
562
|
|
|
*/ |
563
|
|
|
protected function getAvailableAutoActions($id, TransientVarsInterface $inputs) |
564
|
|
|
{ |
565
|
|
|
try { |
566
|
|
|
$store = $this->getPersistence(); |
567
|
|
|
$entry = $store->findEntry($id); |
568
|
|
|
|
569
|
|
|
if (null === $entry) { |
570
|
|
|
$errMsg = sprintf( |
571
|
|
|
'Нет сущности workflow c id %s', |
572
|
|
|
$id |
573
|
|
|
); |
574
|
|
|
throw new InvalidArgumentException($errMsg); |
575
|
|
|
} |
576
|
|
|
|
577
|
|
|
|
578
|
|
|
if (WorkflowEntryInterface::ACTIVATED !== $entry->getState()) { |
579
|
|
|
$logMsg = sprintf('--> состояние %s', $entry->getState()); |
580
|
|
|
$this->getLog()->debug($logMsg); |
581
|
|
|
return [0]; |
582
|
|
|
} |
583
|
|
|
|
584
|
|
|
$wf = $this->getConfiguration()->getWorkflow($entry->getWorkflowName()); |
585
|
|
|
|
586
|
|
View Code Duplication |
if (null === $wf) { |
|
|
|
|
587
|
|
|
$errMsg = sprintf( |
588
|
|
|
'Нет workflow c именем %s', |
589
|
|
|
$entry->getWorkflowName() |
590
|
|
|
); |
591
|
|
|
throw new InvalidArgumentException($errMsg); |
592
|
|
|
} |
593
|
|
|
|
594
|
|
|
$l = []; |
595
|
|
|
$ps = $store->getPropertySet($id); |
596
|
|
|
$transientVars = $inputs; |
597
|
|
|
$currentSteps = $store->findCurrentSteps($id); |
598
|
|
|
|
599
|
|
|
$this->populateTransientMap($entry, $transientVars, $wf->getRegisters(), 0, $currentSteps, $ps); |
600
|
|
|
|
601
|
|
|
$globalActions = $wf->getGlobalActions(); |
602
|
|
|
|
603
|
|
View Code Duplication |
foreach ($globalActions as $action) { |
|
|
|
|
604
|
|
|
$transientVars['actionId'] = $action->getId(); |
605
|
|
|
|
606
|
|
|
if ($action->getAutoExecute() && $this->isActionAvailable($action, $transientVars, $ps, 0)) { |
607
|
|
|
$l[] = $action->getId(); |
608
|
|
|
} |
609
|
|
|
} |
610
|
|
|
|
611
|
|
View Code Duplication |
foreach ($currentSteps as $step) { |
|
|
|
|
612
|
|
|
$availableAutoActionsForStep = $this->getAvailableAutoActionsForStep($wf, $step, $transientVars, $ps); |
613
|
|
|
foreach ($availableAutoActionsForStep as $v) { |
614
|
|
|
$l[] = $v; |
615
|
|
|
} |
616
|
|
|
//$l = array_merge($l, $availableAutoActionsForStep); |
|
|
|
|
617
|
|
|
} |
618
|
|
|
|
619
|
|
|
$l = array_unique($l); |
620
|
|
|
|
621
|
|
|
return $l; |
622
|
|
|
} catch (\Exception $e) { |
623
|
|
|
$errMsg = 'Ошибка при проверке доступных действий'; |
624
|
|
|
$this->getLog()->error($errMsg, [$e]); |
625
|
|
|
} |
626
|
|
|
|
627
|
|
|
return []; |
628
|
|
|
} |
629
|
|
|
|
630
|
|
|
|
631
|
|
|
/** |
632
|
|
|
* @param WorkflowDescriptor $wf |
633
|
|
|
* @param StepInterface $step |
634
|
|
|
* @param TransientVarsInterface $transientVars |
635
|
|
|
* @param PropertySetInterface $ps |
636
|
|
|
* |
637
|
|
|
* @return array |
638
|
|
|
* |
639
|
|
|
* @throws \OldTown\Workflow\Exception\ArgumentNotNumericException |
640
|
|
|
* @throws InternalWorkflowException |
641
|
|
|
* @throws WorkflowException |
642
|
|
|
*/ |
643
|
|
|
protected function getAvailableAutoActionsForStep(WorkflowDescriptor $wf, StepInterface $step, TransientVarsInterface $transientVars, PropertySetInterface $ps) |
644
|
|
|
{ |
645
|
|
|
$l = []; |
646
|
|
|
$s = $wf->getStep($step->getStepId()); |
647
|
|
|
|
648
|
|
|
if (null === $s) { |
649
|
|
|
$msg = sprintf('getAvailableAutoActionsForStep вызвана с несуществующим id %s', $step->getStepId()); |
650
|
|
|
$this->getLog()->debug($msg); |
651
|
|
|
return $l; |
652
|
|
|
} |
653
|
|
|
|
654
|
|
|
|
655
|
|
|
$actions = $s->getActions(); |
656
|
|
|
if (null === $actions || 0 === $actions->count()) { |
657
|
|
|
return $l; |
658
|
|
|
} |
659
|
|
|
|
660
|
|
View Code Duplication |
foreach ($actions as $action) { |
|
|
|
|
661
|
|
|
$transientVars['actionId'] = $action->getId(); |
662
|
|
|
|
663
|
|
|
if ($action->getAutoExecute() && $this->isActionAvailable($action, $transientVars, $ps, 0)) { |
664
|
|
|
$l[] = $action->getId(); |
665
|
|
|
} |
666
|
|
|
} |
667
|
|
|
|
668
|
|
|
return $l; |
669
|
|
|
} |
670
|
|
|
|
671
|
|
|
/** |
672
|
|
|
* @param ActionDescriptor $action |
673
|
|
|
* @param $id |
674
|
|
|
* @param array|Traversable $currentSteps |
675
|
|
|
* @param $state |
676
|
|
|
* |
677
|
|
|
* @return void |
678
|
|
|
* |
679
|
|
|
* @throws InvalidArgumentException |
680
|
|
|
* @throws InternalWorkflowException |
681
|
|
|
*/ |
682
|
|
|
protected function completeEntry(ActionDescriptor $action = null, $id, $currentSteps, $state) |
683
|
|
|
{ |
684
|
|
|
$this->validateIterateData($currentSteps); |
685
|
|
|
|
686
|
|
|
|
687
|
|
|
$this->getPersistence()->setEntryState($id, $state); |
688
|
|
|
|
689
|
|
|
$oldStatus = null !== $action ? $action->getUnconditionalResult()->getOldStatus() : 'Finished'; |
690
|
|
|
$actionIdValue = null !== $action ? $action->getId() : -1; |
691
|
|
|
foreach ($currentSteps as $step) { |
692
|
|
|
$this->getPersistence()->markFinished($step, $actionIdValue, new DateTime(), $oldStatus, $this->context->getCaller()); |
693
|
|
|
$this->getPersistence()->moveToHistory($step); |
694
|
|
|
} |
695
|
|
|
} |
696
|
|
|
/** |
697
|
|
|
* @param ResultDescriptor $theResult |
698
|
|
|
* @param WorkflowEntryInterface $entry |
699
|
|
|
* @param WorkflowStoreInterface $store |
700
|
|
|
* @param integer $actionId |
701
|
|
|
* @param StepInterface $currentStep |
702
|
|
|
* @param array $previousIds |
703
|
|
|
* @param TransientVarsInterface $transientVars |
704
|
|
|
* @param PropertySetInterface $ps |
705
|
|
|
* |
706
|
|
|
* @return StepInterface |
707
|
|
|
* |
708
|
|
|
* @throws InternalWorkflowException |
709
|
|
|
* @throws StoreException |
710
|
|
|
* @throws \OldTown\Workflow\Exception\ArgumentNotNumericException |
711
|
|
|
* @throws WorkflowException |
712
|
|
|
*/ |
713
|
|
|
protected function createNewCurrentStep( |
714
|
|
|
ResultDescriptor $theResult, |
715
|
|
|
WorkflowEntryInterface $entry, |
716
|
|
|
WorkflowStoreInterface $store, |
717
|
|
|
$actionId, |
718
|
|
|
StepInterface $currentStep = null, |
719
|
|
|
array $previousIds = [], |
720
|
|
|
TransientVarsInterface $transientVars, |
721
|
|
|
PropertySetInterface $ps |
722
|
|
|
) { |
723
|
|
|
try { |
724
|
|
|
$nextStep = $theResult->getStep(); |
725
|
|
|
|
726
|
|
|
if (-1 === $nextStep) { |
727
|
|
|
if (null !== $currentStep) { |
728
|
|
|
$nextStep = $currentStep->getStepId(); |
729
|
|
|
} else { |
730
|
|
|
$errMsg = 'Неверный аргумент. Новый шаг является таким же как текущий. Но текущий шаг не указан'; |
731
|
|
|
throw new StoreException($errMsg); |
732
|
|
|
} |
733
|
|
|
} |
734
|
|
|
|
735
|
|
|
$owner = $theResult->getOwner(); |
736
|
|
|
|
737
|
|
|
$logMsg = sprintf( |
738
|
|
|
'Результат: stepId=%s, status=%s, owner=%s, actionId=%s, currentStep=%s', |
739
|
|
|
$nextStep, |
740
|
|
|
$theResult->getStatus(), |
741
|
|
|
$owner, |
742
|
|
|
$actionId, |
743
|
|
|
null !== $currentStep ? $currentStep->getId() : 0 |
744
|
|
|
); |
745
|
|
|
$this->getLog()->debug($logMsg); |
746
|
|
|
|
747
|
|
|
$variableResolver = $this->getConfiguration()->getVariableResolver(); |
748
|
|
|
|
749
|
|
|
if (null !== $owner) { |
750
|
|
|
$o = $variableResolver->translateVariables($owner, $transientVars, $ps); |
751
|
|
|
$owner = null !== $o ? (string)$o : null; |
752
|
|
|
} |
753
|
|
|
|
754
|
|
|
|
755
|
|
|
$oldStatus = $theResult->getOldStatus(); |
756
|
|
|
$oldStatus = (string)$variableResolver->translateVariables($oldStatus, $transientVars, $ps); |
757
|
|
|
|
758
|
|
|
$status = $theResult->getStatus(); |
759
|
|
|
$status = (string)$variableResolver->translateVariables($status, $transientVars, $ps); |
760
|
|
|
|
761
|
|
|
|
762
|
|
|
if (null !== $currentStep) { |
763
|
|
|
$store->markFinished($currentStep, $actionId, new DateTime(), $oldStatus, $this->context->getCaller()); |
764
|
|
|
$store->moveToHistory($currentStep); |
765
|
|
|
} |
766
|
|
|
|
767
|
|
|
$startDate = new DateTime(); |
768
|
|
|
$dueDate = null; |
769
|
|
|
|
770
|
|
|
$theResultDueDate = (string)$theResult->getDueDate(); |
771
|
|
|
$theResultDueDate = trim($theResultDueDate); |
772
|
|
|
if (strlen($theResultDueDate) > 0) { |
773
|
|
|
$dueDateObject = $variableResolver->translateVariables($theResultDueDate, $transientVars, $ps); |
774
|
|
|
|
775
|
|
|
if ($dueDateObject instanceof DateTime) { |
776
|
|
|
$dueDate = $dueDateObject; |
777
|
|
|
} elseif (is_string($dueDateObject)) { |
778
|
|
|
$dueDate = new DateTime($dueDate); |
779
|
|
|
} elseif (is_numeric($dueDateObject)) { |
780
|
|
|
$dueDate = DateTime::createFromFormat('U', $dueDateObject); |
781
|
|
|
if (false === $dueDate) { |
782
|
|
|
$errMsg = 'Invalid due date conversion'; |
783
|
|
|
throw new Exception\InternalWorkflowException($errMsg); |
784
|
|
|
} |
785
|
|
|
} |
786
|
|
|
} |
787
|
|
|
|
788
|
|
|
$newStep = $store->createCurrentStep($entry->getId(), $nextStep, $owner, $startDate, $dueDate, $status, $previousIds); |
789
|
|
|
$transientVars['createdStep'] = $newStep; |
790
|
|
|
|
791
|
|
|
if (null === $currentStep && 0 === count($previousIds)) { |
792
|
|
|
$currentSteps = []; |
793
|
|
|
$currentSteps[] = $newStep; |
794
|
|
|
$transientVars['currentSteps'] = $currentSteps; |
795
|
|
|
} |
796
|
|
|
|
797
|
|
|
if (! $transientVars->offsetExists('descriptor')) { |
798
|
|
|
$errMsg = 'Ошибка при получение дескриптора workflow из transientVars'; |
799
|
|
|
throw new InternalWorkflowException($errMsg); |
800
|
|
|
} |
801
|
|
|
|
802
|
|
|
/** @var WorkflowDescriptor $descriptor */ |
803
|
|
|
$descriptor = $transientVars['descriptor']; |
804
|
|
|
$step = $descriptor->getStep($nextStep); |
805
|
|
|
|
806
|
|
|
if (null === $step) { |
807
|
|
|
$errMsg = sprintf('Шаг #%s не найден', $nextStep); |
808
|
|
|
throw new WorkflowException($errMsg); |
809
|
|
|
} |
810
|
|
|
|
811
|
|
|
$preFunctions = $step->getPreFunctions(); |
812
|
|
|
|
813
|
|
|
foreach ($preFunctions as $function) { |
814
|
|
|
$this->executeFunction($function, $transientVars, $ps); |
815
|
|
|
} |
816
|
|
|
} catch (WorkflowException $e) { |
817
|
|
|
$this->context->setRollbackOnly(); |
818
|
|
|
/** @var WorkflowException $e */ |
819
|
|
|
throw $e; |
820
|
|
|
} |
821
|
|
|
} |
822
|
|
|
|
823
|
|
|
/** |
824
|
|
|
* Создает хранилище переменных |
825
|
|
|
* |
826
|
|
|
* @param $class |
827
|
|
|
* |
828
|
|
|
* @return TransientVarsInterface |
829
|
|
|
*/ |
830
|
|
|
protected function transientVarsFactory($class = BaseTransientVars::class) |
831
|
|
|
{ |
832
|
|
|
$r = new \ReflectionClass($class); |
833
|
|
|
return $r->newInstance(); |
834
|
|
|
} |
835
|
|
|
|
836
|
|
|
/** |
837
|
|
|
* |
838
|
|
|
* |
839
|
|
|
* Осуществляет переходл в новое состояние, для заданного процесса workflow |
840
|
|
|
* |
841
|
|
|
* @param integer $entryId id запущенного процесса workflow |
842
|
|
|
* @param integer $actionId id действия, доступного та текущем шаеге процессса workflow |
843
|
|
|
* @param TransientVarsInterface $inputs Входные данные для перехода |
844
|
|
|
* |
845
|
|
|
* @return void |
846
|
|
|
* |
847
|
|
|
* @throws WorkflowException |
848
|
|
|
* @throws InvalidActionException |
849
|
|
|
* @throws InvalidArgumentException |
850
|
|
|
* @throws InternalWorkflowException |
851
|
|
|
* @throws \OldTown\Workflow\Exception\ArgumentNotNumericException |
852
|
|
|
*/ |
853
|
|
|
public function doAction($entryId, $actionId, TransientVarsInterface $inputs = null) |
854
|
|
|
{ |
855
|
|
|
$actionId = (integer)$actionId; |
856
|
|
|
if (null === $inputs) { |
857
|
|
|
$inputs = $this->transientVarsFactory(); |
858
|
|
|
} |
859
|
|
|
$transientVars = $inputs; |
860
|
|
|
$inputs = clone $transientVars; |
861
|
|
|
|
862
|
|
|
$store = $this->getPersistence(); |
863
|
|
|
$entry = $store->findEntry($entryId); |
864
|
|
|
|
865
|
|
|
if (WorkflowEntryInterface::ACTIVATED !== $entry->getState()) { |
866
|
|
|
return; |
867
|
|
|
} |
868
|
|
|
|
869
|
|
|
$wf = $this->getConfiguration()->getWorkflow($entry->getWorkflowName()); |
870
|
|
|
|
871
|
|
|
$currentSteps = $store->findCurrentSteps($entryId); |
872
|
|
|
$action = null; |
873
|
|
|
|
874
|
|
|
$ps = $store->getPropertySet($entryId); |
875
|
|
|
|
876
|
|
|
$this->populateTransientMap($entry, $transientVars, $wf->getRegisters(), $actionId, $currentSteps, $ps); |
877
|
|
|
|
878
|
|
|
|
879
|
|
|
$validAction = false; |
880
|
|
|
|
881
|
|
|
foreach ($wf->getGlobalActions() as $actionDesc) { |
882
|
|
|
if ($actionId === $actionDesc->getId()) { |
883
|
|
|
$action = $actionDesc; |
884
|
|
|
|
885
|
|
|
if ($this->isActionAvailable($action, $transientVars, $ps, 0)) { |
886
|
|
|
$validAction = true; |
887
|
|
|
} |
888
|
|
|
} |
889
|
|
|
} |
890
|
|
|
|
891
|
|
|
|
892
|
|
|
foreach ($currentSteps as $step) { |
893
|
|
|
$s = $wf->getStep($step->getStepId()); |
894
|
|
|
|
895
|
|
|
foreach ($s->getActions() as $actionDesc) { |
896
|
|
|
if (!$actionDesc instanceof ActionDescriptor) { |
897
|
|
|
$errMsg = 'Invalid action descriptor'; |
898
|
|
|
throw new InternalWorkflowException($errMsg); |
899
|
|
|
} |
900
|
|
|
|
901
|
|
|
if ($actionId === $actionDesc->getId()) { |
902
|
|
|
$action = $actionDesc; |
903
|
|
|
|
904
|
|
|
if ($this->isActionAvailable($action, $transientVars, $ps, $s->getId())) { |
905
|
|
|
$validAction = true; |
906
|
|
|
} |
907
|
|
|
} |
908
|
|
|
} |
909
|
|
|
} |
910
|
|
|
|
911
|
|
|
|
912
|
|
|
if (!$validAction) { |
913
|
|
|
$errMsg = sprintf( |
914
|
|
|
'Action %s is invalid', |
915
|
|
|
$actionId |
916
|
|
|
); |
917
|
|
|
throw new InvalidActionException($errMsg); |
918
|
|
|
} |
919
|
|
|
|
920
|
|
|
|
921
|
|
|
try { |
922
|
|
|
if ($this->transitionWorkflow($entry, $currentSteps, $store, $wf, $action, $transientVars, $inputs, $ps)) { |
|
|
|
|
923
|
|
|
$this->checkImplicitFinish($action, $entryId); |
924
|
|
|
} |
925
|
|
|
} catch (WorkflowException $e) { |
926
|
|
|
$this->context->setRollbackOnly(); |
927
|
|
|
/** @var WorkflowException $e*/ |
928
|
|
|
throw $e; |
929
|
|
|
} |
930
|
|
|
} |
931
|
|
|
|
932
|
|
|
/** |
933
|
|
|
* @param ActionDescriptor $action |
934
|
|
|
* @param $id |
935
|
|
|
* |
936
|
|
|
* @return void |
937
|
|
|
* |
938
|
|
|
* @throws \OldTown\Workflow\Exception\ArgumentNotNumericException |
939
|
|
|
* @throws InvalidArgumentException |
940
|
|
|
* @throws InternalWorkflowException |
941
|
|
|
*/ |
942
|
|
|
protected function checkImplicitFinish(ActionDescriptor $action, $id) |
943
|
|
|
{ |
944
|
|
|
$store = $this->getPersistence(); |
945
|
|
|
$entry = $store->findEntry($id); |
946
|
|
|
|
947
|
|
|
$wf = $this->getConfiguration()->getWorkflow($entry->getWorkflowName()); |
948
|
|
|
|
949
|
|
|
$currentSteps = $store->findCurrentSteps($id); |
950
|
|
|
|
951
|
|
|
$isCompleted = $wf->getGlobalActions()->count() === 0; |
952
|
|
|
|
953
|
|
|
foreach ($currentSteps as $step) { |
954
|
|
|
if ($isCompleted) { |
955
|
|
|
break; |
956
|
|
|
} |
957
|
|
|
|
958
|
|
|
$stepDes = $wf->getStep($step->getStepId()); |
959
|
|
|
|
960
|
|
|
if ($stepDes->getActions()->count() > 0) { |
961
|
|
|
$isCompleted = true; |
962
|
|
|
} |
963
|
|
|
} |
964
|
|
|
|
965
|
|
|
if ($isCompleted) { |
966
|
|
|
$this->completeEntry($action, $id, $currentSteps, WorkflowEntryInterface::COMPLETED); |
967
|
|
|
} |
968
|
|
|
} |
969
|
|
|
|
970
|
|
|
/** |
971
|
|
|
* |
972
|
|
|
* Check if the state of the specified workflow instance can be changed to the new specified one. |
973
|
|
|
* |
974
|
|
|
* @param integer $id The workflow instance id. |
975
|
|
|
* @param integer $newState The new state id. |
976
|
|
|
* |
977
|
|
|
* @return boolean true if the state of the workflow can be modified, false otherwise. |
978
|
|
|
* |
979
|
|
|
* @throws InternalWorkflowException |
980
|
|
|
*/ |
981
|
|
|
public function canModifyEntryState($id, $newState) |
982
|
|
|
{ |
983
|
|
|
$store = $this->getPersistence(); |
984
|
|
|
$entry = $store->findEntry($id); |
985
|
|
|
|
986
|
|
|
$currentState = $entry->getState(); |
987
|
|
|
|
988
|
|
|
$result = false; |
989
|
|
|
try { |
990
|
|
|
switch ($newState) { |
991
|
|
|
case WorkflowEntryInterface::COMPLETED: { |
992
|
|
|
if (WorkflowEntryInterface::ACTIVATED === $currentState) { |
993
|
|
|
$result = true; |
994
|
|
|
} |
995
|
|
|
break; |
996
|
|
|
} |
997
|
|
|
|
998
|
|
|
//@TODO Разобраться с бизнес логикой. Может быть нужно добавить break |
999
|
|
|
/** @noinspection PhpMissingBreakStatementInspection */ |
1000
|
|
|
case WorkflowEntryInterface::CREATED: { |
1001
|
|
|
$result = false; |
1002
|
|
|
} |
1003
|
|
View Code Duplication |
case WorkflowEntryInterface::ACTIVATED: { |
|
|
|
|
1004
|
|
|
if (WorkflowEntryInterface::CREATED === $currentState || WorkflowEntryInterface::SUSPENDED === $currentState) { |
1005
|
|
|
$result = true; |
1006
|
|
|
} |
1007
|
|
|
break; |
1008
|
|
|
} |
1009
|
|
|
case WorkflowEntryInterface::SUSPENDED: { |
1010
|
|
|
if (WorkflowEntryInterface::ACTIVATED === $currentState) { |
1011
|
|
|
$result = true; |
1012
|
|
|
} |
1013
|
|
|
break; |
1014
|
|
|
} |
1015
|
|
View Code Duplication |
case WorkflowEntryInterface::KILLED: { |
|
|
|
|
1016
|
|
|
if (WorkflowEntryInterface::CREATED === $currentState || WorkflowEntryInterface::ACTIVATED === $currentState || WorkflowEntryInterface::SUSPENDED === $currentState) { |
1017
|
|
|
$result = true; |
1018
|
|
|
} |
1019
|
|
|
break; |
1020
|
|
|
} |
1021
|
|
|
default: { |
1022
|
|
|
$result = false; |
1023
|
|
|
break; |
1024
|
|
|
} |
1025
|
|
|
|
1026
|
|
|
} |
1027
|
|
|
|
1028
|
|
|
return $result; |
1029
|
|
|
} catch (StoreException $e) { |
|
|
|
|
1030
|
|
|
$errMsg = sprintf( |
1031
|
|
|
'Ошибка проверки изменения состояния для инстанса #%s', |
1032
|
|
|
$id |
1033
|
|
|
); |
1034
|
|
|
$this->getLog()->error($errMsg, [$e]); |
1035
|
|
|
} |
1036
|
|
|
|
1037
|
|
|
return false; |
1038
|
|
|
} |
1039
|
|
|
|
1040
|
|
|
|
1041
|
|
|
/** |
1042
|
|
|
* |
1043
|
|
|
* Возвращает коллекцию объектов описывающие состояние для текущего экземпляра workflow |
1044
|
|
|
* |
1045
|
|
|
* @param integer $entryId id экземпляра workflow |
1046
|
|
|
* |
1047
|
|
|
* @return SplObjectStorage|StepInterface[] |
1048
|
|
|
* |
1049
|
|
|
* @throws InternalWorkflowException |
1050
|
|
|
*/ |
1051
|
|
|
public function getCurrentSteps($entryId) |
1052
|
|
|
{ |
1053
|
|
|
return $this->getStepFromStorage($entryId, static::CURRENT_STEPS); |
1054
|
|
|
} |
1055
|
|
|
|
1056
|
|
|
/** |
1057
|
|
|
* Возвращает информацию о том в какие шаги, были осуществленны переходы, для процесса workflow с заданным id |
1058
|
|
|
* |
1059
|
|
|
* @param integer $entryId уникальный идентификатор процесса workflow |
1060
|
|
|
* |
1061
|
|
|
* @return StepInterface[]|SplObjectStorage список шагов |
1062
|
|
|
* |
1063
|
|
|
* @throws InternalWorkflowException |
1064
|
|
|
*/ |
1065
|
|
|
public function getHistorySteps($entryId) |
1066
|
|
|
{ |
1067
|
|
|
return $this->getStepFromStorage($entryId, static::HISTORY_STEPS); |
1068
|
|
|
} |
1069
|
|
|
|
1070
|
|
|
/** |
1071
|
|
|
* Получение шагов информации о шагах процесса workflow |
1072
|
|
|
* |
1073
|
|
|
* @param $entryId |
1074
|
|
|
* @param $type |
1075
|
|
|
* |
1076
|
|
|
* @return Spi\StepInterface[]|SplObjectStorage |
1077
|
|
|
* |
1078
|
|
|
* @throws InternalWorkflowException |
1079
|
|
|
*/ |
1080
|
|
|
protected function getStepFromStorage($entryId, $type) |
1081
|
|
|
{ |
1082
|
|
|
try { |
1083
|
|
|
$store = $this->getPersistence(); |
1084
|
|
|
|
1085
|
|
|
if (static::CURRENT_STEPS === $type) { |
1086
|
|
|
return $store->findCurrentSteps($entryId); |
1087
|
|
|
} elseif (static::HISTORY_STEPS === $type) { |
1088
|
|
|
return $store->findHistorySteps($entryId); |
1089
|
|
|
} |
1090
|
|
|
} catch (StoreException $e) { |
1091
|
|
|
$errMsg = sprintf( |
1092
|
|
|
'Ошибка при получение истории шагов для экземпляра workflow c id# %s', |
1093
|
|
|
$entryId |
1094
|
|
|
); |
1095
|
|
|
$this->getLog()->error($errMsg, [$e]); |
1096
|
|
|
} |
1097
|
|
|
|
1098
|
|
|
return new SplObjectStorage(); |
1099
|
|
|
} |
1100
|
|
|
|
1101
|
|
|
|
1102
|
|
|
|
1103
|
|
|
/** |
1104
|
|
|
* |
1105
|
|
|
* |
1106
|
|
|
* Modify the state of the specified workflow instance. |
1107
|
|
|
* @param integer $id The workflow instance id. |
1108
|
|
|
* @param integer $newState the new state to change the workflow instance to. |
1109
|
|
|
* |
1110
|
|
|
* @throws InvalidArgumentException |
1111
|
|
|
* @throws InvalidEntryStateException |
1112
|
|
|
* @throws InternalWorkflowException |
1113
|
|
|
*/ |
1114
|
|
|
public function changeEntryState($id, $newState) |
1115
|
|
|
{ |
1116
|
|
|
$store = $this->getPersistence(); |
1117
|
|
|
$entry = $store->findEntry($id); |
1118
|
|
|
|
1119
|
|
|
if ($newState === $entry->getState()) { |
1120
|
|
|
return; |
1121
|
|
|
} |
1122
|
|
|
|
1123
|
|
|
if ($this->canModifyEntryState($id, $newState)) { |
1124
|
|
|
if (WorkflowEntryInterface::KILLED === $newState || WorkflowEntryInterface::COMPLETED === $newState) { |
1125
|
|
|
$currentSteps = $this->getCurrentSteps($id); |
1126
|
|
|
|
1127
|
|
|
if (count($currentSteps) > 0) { |
1128
|
|
|
$this->completeEntry(null, $id, $currentSteps, $newState); |
1129
|
|
|
} |
1130
|
|
|
} |
1131
|
|
|
|
1132
|
|
|
$store->setEntryState($id, $newState); |
1133
|
|
|
} else { |
1134
|
|
|
$errMsg = sprintf( |
1135
|
|
|
'Не возможен переход в экземпляре workflow #%s. Текущее состояние %s, ожидаемое состояние %s', |
1136
|
|
|
$id, |
1137
|
|
|
$entry->getState(), |
1138
|
|
|
$newState |
1139
|
|
|
); |
1140
|
|
|
|
1141
|
|
|
throw new InvalidEntryStateException($errMsg); |
1142
|
|
|
} |
1143
|
|
|
|
1144
|
|
|
$msg = sprintf( |
1145
|
|
|
'%s : Новое состояние: %s', |
1146
|
|
|
$entry->getId(), |
1147
|
|
|
$entry->getState() |
1148
|
|
|
); |
1149
|
|
|
$this->getLog()->debug($msg); |
1150
|
|
|
} |
1151
|
|
|
|
1152
|
|
|
|
1153
|
|
|
/** |
1154
|
|
|
* @param FunctionDescriptor $function |
1155
|
|
|
* @param TransientVarsInterface $transientVars |
1156
|
|
|
* @param PropertySetInterface $ps |
1157
|
|
|
* |
1158
|
|
|
* @throws WorkflowException |
1159
|
|
|
* @throws InternalWorkflowException |
1160
|
|
|
*/ |
1161
|
|
|
protected function executeFunction(FunctionDescriptor $function, TransientVarsInterface $transientVars, PropertySetInterface $ps) |
1162
|
|
|
{ |
1163
|
|
|
if (null !== $function) { |
1164
|
|
|
$type = $function->getType(); |
1165
|
|
|
|
1166
|
|
|
$argsOriginal = $function->getArgs(); |
1167
|
|
|
$args = $this->prepareArgs($argsOriginal, $transientVars, $ps); |
1168
|
|
|
|
1169
|
|
|
$provider = $this->getResolver()->getFunction($type, $args); |
1170
|
|
|
|
1171
|
|
|
if (null === $provider) { |
1172
|
|
|
$this->context->setRollbackOnly(); |
1173
|
|
|
$errMsg = 'Не загружен провайдер для функции'; |
1174
|
|
|
throw new WorkflowException($errMsg); |
1175
|
|
|
} |
1176
|
|
|
|
1177
|
|
|
try { |
1178
|
|
|
$provider->execute($transientVars, $args, $ps); |
1179
|
|
|
} catch (WorkflowException $e) { |
1180
|
|
|
$this->context->setRollbackOnly(); |
1181
|
|
|
/** @var WorkflowException $e*/ |
1182
|
|
|
throw $e; |
1183
|
|
|
} |
1184
|
|
|
} |
1185
|
|
|
} |
1186
|
|
|
|
1187
|
|
|
|
1188
|
|
|
/** |
1189
|
|
|
* @param $validatorsStorage |
1190
|
|
|
* @param TransientVarsInterface $transientVars |
1191
|
|
|
* @param PropertySetInterface $ps |
1192
|
|
|
* |
1193
|
|
|
* @throws WorkflowException |
1194
|
|
|
* @throws InvalidArgumentException |
1195
|
|
|
* @throws InternalWorkflowException |
1196
|
|
|
* @throws InvalidInputException |
1197
|
|
|
*/ |
1198
|
|
|
protected function verifyInputs($validatorsStorage, TransientVarsInterface $transientVars, PropertySetInterface $ps) |
1199
|
|
|
{ |
1200
|
|
|
$validators = $this->convertDataInArray($validatorsStorage); |
1201
|
|
|
|
1202
|
|
|
/** @var ValidatorDescriptor[] $validators */ |
1203
|
|
|
foreach ($validators as $input) { |
1204
|
|
|
if (null !== $input) { |
1205
|
|
|
$type = $input->getType(); |
1206
|
|
|
$argsOriginal = $input->getArgs(); |
1207
|
|
|
|
1208
|
|
|
$args = $this->prepareArgs($argsOriginal, $transientVars, $ps); |
1209
|
|
|
|
1210
|
|
|
|
1211
|
|
|
$validator = $this->getResolver()->getValidator($type, $args); |
1212
|
|
|
|
1213
|
|
|
if (null === $validator) { |
1214
|
|
|
$this->context->setRollbackOnly(); |
1215
|
|
|
$errMsg = 'Ошибка при загрузке валидатора'; |
1216
|
|
|
throw new WorkflowException($errMsg); |
1217
|
|
|
} |
1218
|
|
|
|
1219
|
|
|
try { |
1220
|
|
|
$validator->validate($transientVars, $args, $ps); |
1221
|
|
|
} catch (InvalidInputException $e) { |
1222
|
|
|
/** @var InvalidInputException $e*/ |
1223
|
|
|
throw $e; |
1224
|
|
|
} catch (\Exception $e) { |
1225
|
|
|
$this->context->setRollbackOnly(); |
1226
|
|
|
|
1227
|
|
|
if ($e instanceof WorkflowException) { |
1228
|
|
|
/** @var WorkflowException $e*/ |
1229
|
|
|
throw $e; |
1230
|
|
|
} |
1231
|
|
|
|
1232
|
|
|
throw new WorkflowException($e->getMessage(), $e->getCode(), $e); |
1233
|
|
|
} |
1234
|
|
|
} |
1235
|
|
|
} |
1236
|
|
|
} |
1237
|
|
|
|
1238
|
|
|
|
1239
|
|
|
/** |
1240
|
|
|
* Возвращает текущий шаг |
1241
|
|
|
* |
1242
|
|
|
* @param WorkflowDescriptor $wfDesc |
1243
|
|
|
* @param integer $actionId |
1244
|
|
|
* @param StepInterface[]|SplObjectStorage $currentSteps |
1245
|
|
|
* @param TransientVarsInterface $transientVars |
1246
|
|
|
* @param PropertySetInterface $ps |
1247
|
|
|
* |
1248
|
|
|
* @return StepInterface |
1249
|
|
|
* |
1250
|
|
|
* @throws InternalWorkflowException |
1251
|
|
|
* @throws \OldTown\Workflow\Exception\ArgumentNotNumericException |
1252
|
|
|
* @throws WorkflowException |
1253
|
|
|
*/ |
1254
|
|
|
protected function getCurrentStep(WorkflowDescriptor $wfDesc, $actionId, SplObjectStorage $currentSteps, TransientVarsInterface $transientVars, PropertySetInterface $ps) |
1255
|
|
|
{ |
1256
|
|
|
if (1 === $currentSteps->count()) { |
1257
|
|
|
$currentSteps->rewind(); |
1258
|
|
|
return $currentSteps->current(); |
1259
|
|
|
} |
1260
|
|
|
|
1261
|
|
|
|
1262
|
|
|
foreach ($currentSteps as $step) { |
1263
|
|
|
$stepId = $step->getId(); |
1264
|
|
|
$action = $wfDesc->getStep($stepId)->getAction($actionId); |
1265
|
|
|
|
1266
|
|
|
if ($this->isActionAvailable($action, $transientVars, $ps, $stepId)) { |
1267
|
|
|
return $step; |
1268
|
|
|
} |
1269
|
|
|
} |
1270
|
|
|
|
1271
|
|
|
return null; |
1272
|
|
|
} |
1273
|
|
|
|
1274
|
|
|
/** |
1275
|
|
|
* @param ActionDescriptor|null $action |
1276
|
|
|
* @param TransientVarsInterface $transientVars |
1277
|
|
|
* @param PropertySetInterface $ps |
1278
|
|
|
* @param $stepId |
1279
|
|
|
* |
1280
|
|
|
* @return boolean |
1281
|
|
|
* |
1282
|
|
|
* @throws InternalWorkflowException |
1283
|
|
|
* @throws InternalWorkflowException |
1284
|
|
|
* @throws WorkflowException |
1285
|
|
|
*/ |
1286
|
|
|
protected function isActionAvailable(ActionDescriptor $action = null, TransientVarsInterface $transientVars, PropertySetInterface $ps, $stepId) |
1287
|
|
|
{ |
1288
|
|
|
if (null === $action) { |
1289
|
|
|
return false; |
1290
|
|
|
} |
1291
|
|
|
|
1292
|
|
|
$result = null; |
1293
|
|
|
$actionHash = spl_object_hash($action); |
1294
|
|
|
|
1295
|
|
|
$result = array_key_exists($actionHash, $this->stateCache) ? $this->stateCache[$actionHash] : $result; |
1296
|
|
|
|
1297
|
|
|
$wf = $this->getWorkflowDescriptorForAction($action); |
1298
|
|
|
|
1299
|
|
|
|
1300
|
|
|
if (null === $result) { |
1301
|
|
|
$restriction = $action->getRestriction(); |
1302
|
|
|
$conditions = null; |
1303
|
|
|
|
1304
|
|
|
if (null !== $restriction) { |
1305
|
|
|
$conditions = $restriction->getConditionsDescriptor(); |
1306
|
|
|
} |
1307
|
|
|
|
1308
|
|
|
$result = $this->passesConditionsByDescriptor($wf->getGlobalConditions(), $transientVars, $ps, $stepId) |
1309
|
|
|
&& $this->passesConditionsByDescriptor($conditions, $transientVars, $ps, $stepId); |
1310
|
|
|
|
1311
|
|
|
$this->stateCache[$actionHash] = $result; |
1312
|
|
|
} |
1313
|
|
|
|
1314
|
|
|
|
1315
|
|
|
$result = (boolean)$result; |
1316
|
|
|
|
1317
|
|
|
return $result; |
1318
|
|
|
} |
1319
|
|
|
|
1320
|
|
|
/** |
1321
|
|
|
* По дейсвтию получаем дексрипторв workflow |
1322
|
|
|
* |
1323
|
|
|
* @param ActionDescriptor $action |
1324
|
|
|
* |
1325
|
|
|
* @return WorkflowDescriptor |
1326
|
|
|
* |
1327
|
|
|
* @throws InternalWorkflowException |
1328
|
|
|
*/ |
1329
|
|
|
private function getWorkflowDescriptorForAction(ActionDescriptor $action) |
1330
|
|
|
{ |
1331
|
|
|
$objWfd = $action; |
1332
|
|
|
|
1333
|
|
|
$count = 0; |
1334
|
|
|
while (!$objWfd instanceof WorkflowDescriptor || null === $objWfd) { |
1335
|
|
|
$objWfd = $objWfd->getParent(); |
1336
|
|
|
|
1337
|
|
|
$count++; |
1338
|
|
|
if ($count > 10) { |
1339
|
|
|
$errMsg = 'Ошибка при получение WorkflowDescriptor'; |
1340
|
|
|
throw new InternalWorkflowException($errMsg); |
1341
|
|
|
} |
1342
|
|
|
} |
1343
|
|
|
|
1344
|
|
|
return $objWfd; |
1345
|
|
|
} |
1346
|
|
|
|
1347
|
|
|
|
1348
|
|
|
/** |
1349
|
|
|
* Проверяет имеет ли пользователь достаточно прав, что бы иниициировать вызываемый процесс |
1350
|
|
|
* |
1351
|
|
|
* @param string $workflowName имя workflow |
1352
|
|
|
* @param integer $initialAction id начального состояния |
1353
|
|
|
* @param TransientVarsInterface $inputs |
1354
|
|
|
* |
1355
|
|
|
* @return bool |
1356
|
|
|
* |
1357
|
|
|
* @throws InvalidArgumentException |
1358
|
|
|
* @throws WorkflowException |
1359
|
|
|
* @throws InternalWorkflowException |
1360
|
|
|
* @throws \OldTown\Workflow\Exception\ArgumentNotNumericException |
1361
|
|
|
*/ |
1362
|
|
|
public function canInitialize($workflowName, $initialAction, TransientVarsInterface $inputs = null) |
1363
|
|
|
{ |
1364
|
|
|
$mockWorkflowName = $workflowName; |
1365
|
|
|
$mockEntry = new SimpleWorkflowEntry(0, $mockWorkflowName, WorkflowEntryInterface::CREATED); |
1366
|
|
|
|
1367
|
|
|
try { |
1368
|
|
|
$ps = PropertySetManager::getInstance('memory', null); |
|
|
|
|
1369
|
|
|
if (!$ps instanceof PropertySetInterface) { |
1370
|
|
|
$errMsg = 'Invalid create PropertySet'; |
1371
|
|
|
throw new InternalWorkflowException($errMsg); |
1372
|
|
|
} |
1373
|
|
|
} catch (\Exception $e) { |
1374
|
|
|
throw new InternalWorkflowException($e->getMessage(), $e->getCode(), $e); |
1375
|
|
|
} |
1376
|
|
|
|
1377
|
|
|
|
1378
|
|
|
|
1379
|
|
|
|
1380
|
|
|
if (null === $inputs) { |
1381
|
|
|
$inputs = $this->transientVarsFactory(); |
1382
|
|
|
} |
1383
|
|
|
$transientVars = $inputs; |
1384
|
|
|
|
1385
|
|
|
try { |
1386
|
|
|
$this->populateTransientMap($mockEntry, $transientVars, [], $initialAction, [], $ps); |
1387
|
|
|
|
1388
|
|
|
$result = $this->canInitializeInternal($workflowName, $initialAction, $transientVars, $ps); |
1389
|
|
|
|
1390
|
|
|
return $result; |
1391
|
|
|
} catch (InvalidActionException $e) { |
1392
|
|
|
$this->getLog()->error($e->getMessage(), [$e]); |
1393
|
|
|
|
1394
|
|
|
return false; |
1395
|
|
|
} catch (WorkflowException $e) { |
1396
|
|
|
$errMsg = sprintf( |
1397
|
|
|
'Ошибка при проверки canInitialize: %s', |
1398
|
|
|
$e->getMessage() |
1399
|
|
|
); |
1400
|
|
|
$this->getLog()->error($errMsg, [$e]); |
1401
|
|
|
|
1402
|
|
|
return false; |
1403
|
|
|
} |
1404
|
|
|
} |
1405
|
|
|
|
1406
|
|
|
|
1407
|
|
|
/** |
1408
|
|
|
* Проверяет имеет ли пользователь достаточно прав, что бы иниициировать вызываемый процесс |
1409
|
|
|
* |
1410
|
|
|
* @param string $workflowName имя workflow |
1411
|
|
|
* @param integer $initialAction id начального состояния |
1412
|
|
|
* @param TransientVarsInterface $transientVars |
1413
|
|
|
* |
1414
|
|
|
* @param PropertySetInterface $ps |
1415
|
|
|
* |
1416
|
|
|
* @return bool |
1417
|
|
|
* |
1418
|
|
|
* @throws \OldTown\Workflow\Exception\ArgumentNotNumericException |
1419
|
|
|
* @throws InvalidActionException |
1420
|
|
|
* @throws InternalWorkflowException |
1421
|
|
|
* @throws WorkflowException |
1422
|
|
|
*/ |
1423
|
|
|
protected function canInitializeInternal($workflowName, $initialAction, TransientVarsInterface $transientVars, PropertySetInterface $ps) |
1424
|
|
|
{ |
1425
|
|
|
$wf = $this->getConfiguration()->getWorkflow($workflowName); |
1426
|
|
|
|
1427
|
|
|
$actionDescriptor = $wf->getInitialAction($initialAction); |
1428
|
|
|
|
1429
|
|
|
if (null === $actionDescriptor) { |
1430
|
|
|
$errMsg = sprintf( |
1431
|
|
|
'Некорректное инициирующие действие # %s', |
1432
|
|
|
$initialAction |
1433
|
|
|
); |
1434
|
|
|
throw new InvalidActionException($errMsg); |
1435
|
|
|
} |
1436
|
|
|
|
1437
|
|
|
$restriction = $actionDescriptor->getRestriction(); |
1438
|
|
|
|
1439
|
|
|
|
1440
|
|
|
$conditions = null; |
1441
|
|
|
if (null !== $restriction) { |
1442
|
|
|
$conditions = $restriction->getConditionsDescriptor(); |
1443
|
|
|
} |
1444
|
|
|
|
1445
|
|
|
$passesConditions = $this->passesConditionsByDescriptor($conditions, $transientVars, $ps, 0); |
1446
|
|
|
|
1447
|
|
|
return $passesConditions; |
1448
|
|
|
} |
1449
|
|
|
|
1450
|
|
|
/** |
1451
|
|
|
* Возвращает резолвер |
1452
|
|
|
* |
1453
|
|
|
* @return TypeResolverInterface |
1454
|
|
|
*/ |
1455
|
|
|
public function getResolver() |
1456
|
|
|
{ |
1457
|
|
|
if (null !== $this->typeResolver) { |
1458
|
|
|
return $this->typeResolver; |
1459
|
|
|
} |
1460
|
|
|
|
1461
|
|
|
$classResolver = $this->getDefaultTypeResolverClass(); |
1462
|
|
|
$r = new ReflectionClass($classResolver); |
1463
|
|
|
$resolver = $r->newInstance(); |
1464
|
|
|
$this->typeResolver = $resolver; |
1465
|
|
|
|
1466
|
|
|
return $this->typeResolver; |
1467
|
|
|
} |
1468
|
|
|
|
1469
|
|
|
/** |
1470
|
|
|
* Возвращает хранилище состояния workflow |
1471
|
|
|
* |
1472
|
|
|
* @return WorkflowStoreInterface |
1473
|
|
|
* |
1474
|
|
|
* @throws InternalWorkflowException |
1475
|
|
|
*/ |
1476
|
|
|
protected function getPersistence() |
1477
|
|
|
{ |
1478
|
|
|
return $this->getConfiguration()->getWorkflowStore(); |
1479
|
|
|
} |
1480
|
|
|
|
1481
|
|
|
/** |
1482
|
|
|
* Получить конфигурацию workflow. Метод также проверяет была ли иницилазированн конфигурация, если нет, то |
1483
|
|
|
* инициализирует ее. |
1484
|
|
|
* |
1485
|
|
|
* Если конфигурация не была установленна, то возвращает конфигурацию по умолчанию |
1486
|
|
|
* |
1487
|
|
|
* @return ConfigurationInterface|DefaultConfiguration Конфигурация которая была установленна |
1488
|
|
|
* |
1489
|
|
|
* @throws InternalWorkflowException |
1490
|
|
|
*/ |
1491
|
|
|
public function getConfiguration() |
1492
|
|
|
{ |
1493
|
|
|
$config = null !== $this->configuration ? $this->configuration : DefaultConfiguration::getInstance(); |
1494
|
|
|
|
1495
|
|
|
if (!$config->isInitialized()) { |
1496
|
|
|
try { |
1497
|
|
|
$config->load(null); |
1498
|
|
|
} catch (FactoryException $e) { |
1499
|
|
|
$errMsg = 'Ошибка при иницилазации конфигурации workflow'; |
1500
|
|
|
$this->getLog()->critical($errMsg, ['exception' => $e]); |
1501
|
|
|
throw new InternalWorkflowException($errMsg, $e->getCode(), $e); |
1502
|
|
|
} |
1503
|
|
|
} |
1504
|
|
|
|
1505
|
|
|
return $config; |
1506
|
|
|
} |
1507
|
|
|
|
1508
|
|
|
/** |
1509
|
|
|
* @return LoggerInterface |
1510
|
|
|
*/ |
1511
|
|
|
public function getLog() |
1512
|
|
|
{ |
1513
|
|
|
return $this->log; |
1514
|
|
|
} |
1515
|
|
|
|
1516
|
|
|
/** |
1517
|
|
|
* @param LoggerInterface $log |
1518
|
|
|
* |
1519
|
|
|
* @return $this |
1520
|
|
|
* |
1521
|
|
|
* @throws InternalWorkflowException |
1522
|
|
|
*/ |
1523
|
|
|
public function setLog($log) |
1524
|
|
|
{ |
1525
|
|
|
try { |
1526
|
|
|
LogFactory::validLogger($log); |
1527
|
|
|
} catch (\Exception $e) { |
1528
|
|
|
$errMsg = 'Ошибка при валидации логера'; |
1529
|
|
|
throw new InternalWorkflowException($errMsg, $e->getCode(), $e); |
1530
|
|
|
} |
1531
|
|
|
|
1532
|
|
|
|
1533
|
|
|
$this->log = $log; |
1534
|
|
|
|
1535
|
|
|
return $this; |
1536
|
|
|
} |
1537
|
|
|
|
1538
|
|
|
|
1539
|
|
|
/** |
1540
|
|
|
* Get the workflow descriptor for the specified workflow name. |
1541
|
|
|
* |
1542
|
|
|
* @param string $workflowName The workflow name. |
1543
|
|
|
* @return WorkflowDescriptor |
1544
|
|
|
* |
1545
|
|
|
* @throws InternalWorkflowException |
1546
|
|
|
*/ |
1547
|
|
|
public function getWorkflowDescriptor($workflowName) |
1548
|
|
|
{ |
1549
|
|
|
try { |
1550
|
|
|
return $this->getConfiguration()->getWorkflow($workflowName); |
1551
|
|
|
} catch (FactoryException $e) { |
1552
|
|
|
$errMsg = 'Ошибка при загрузке workflow'; |
1553
|
|
|
$this->getLog()->error($errMsg, ['exception' => $e]); |
1554
|
|
|
throw new InternalWorkflowException($errMsg, $e->getCode(), $e); |
1555
|
|
|
} |
1556
|
|
|
} |
1557
|
|
|
|
1558
|
|
|
|
1559
|
|
|
/** |
1560
|
|
|
* Executes a special trigger-function using the context of the given workflow instance id. |
1561
|
|
|
* Note that this method is exposed for Quartz trigger jobs, user code should never call it. |
1562
|
|
|
* |
1563
|
|
|
* @param integer $id The workflow instance id |
1564
|
|
|
* @param integer $triggerId The id of the special trigger-function |
1565
|
|
|
* |
1566
|
|
|
* @throws InvalidArgumentException |
1567
|
|
|
* @throws WorkflowException |
1568
|
|
|
* @throws InternalWorkflowException |
1569
|
|
|
* @throws \OldTown\Workflow\Exception\ArgumentNotNumericException |
1570
|
|
|
*/ |
1571
|
|
|
public function executeTriggerFunction($id, $triggerId) |
1572
|
|
|
{ |
1573
|
|
|
$store = $this->getPersistence(); |
1574
|
|
|
$entry = $store->findEntry($id); |
1575
|
|
|
|
1576
|
|
|
if (null === $entry) { |
1577
|
|
|
$errMsg = sprintf( |
1578
|
|
|
'Ошибка при выполнение тригера # %s для несуществующего экземпляра workflow id# %s', |
1579
|
|
|
$triggerId, |
1580
|
|
|
$id |
1581
|
|
|
); |
1582
|
|
|
$this->getLog()->warning($errMsg); |
1583
|
|
|
return; |
1584
|
|
|
} |
1585
|
|
|
|
1586
|
|
|
$wf = $this->getConfiguration()->getWorkflow($entry->getWorkflowName()); |
1587
|
|
|
|
1588
|
|
|
$ps = $store->getPropertySet($id); |
1589
|
|
|
$transientVars = $this->transientVarsFactory(); |
1590
|
|
|
|
1591
|
|
|
$this->populateTransientMap($entry, $transientVars, $wf->getRegisters(), null, $store->findCurrentSteps($id), $ps); |
1592
|
|
|
|
1593
|
|
|
$this->executeFunction($wf->getTriggerFunction($triggerId), $transientVars, $ps); |
1594
|
|
|
} |
1595
|
|
|
|
1596
|
|
|
/** |
1597
|
|
|
* @param $id |
1598
|
|
|
* @param $inputs |
1599
|
|
|
* |
1600
|
|
|
* @return array |
1601
|
|
|
* |
1602
|
|
|
*/ |
1603
|
|
|
public function getAvailableActions($id, TransientVarsInterface $inputs = null) |
1604
|
|
|
{ |
1605
|
|
|
try { |
1606
|
|
|
$store = $this->getPersistence(); |
1607
|
|
|
$entry = $store->findEntry($id); |
1608
|
|
|
|
1609
|
|
|
if (null === $entry) { |
1610
|
|
|
$errMsg = sprintf( |
1611
|
|
|
'Не существует экземпляра workflow c id %s', |
1612
|
|
|
$id |
1613
|
|
|
); |
1614
|
|
|
throw new InvalidArgumentException($errMsg); |
1615
|
|
|
} |
1616
|
|
|
|
1617
|
|
|
if (WorkflowEntryInterface::ACTIVATED === $entry->getState()) { |
1618
|
|
|
return []; |
1619
|
|
|
} |
1620
|
|
|
|
1621
|
|
|
$wf = $this->getConfiguration()->getWorkflow($entry->getWorkflowName()); |
1622
|
|
|
|
1623
|
|
View Code Duplication |
if (null === $wf) { |
|
|
|
|
1624
|
|
|
$errMsg = sprintf( |
1625
|
|
|
'Не существует workflow c именем %s', |
1626
|
|
|
$entry->getWorkflowName() |
1627
|
|
|
); |
1628
|
|
|
throw new InvalidArgumentException($errMsg); |
1629
|
|
|
} |
1630
|
|
|
|
1631
|
|
|
$l = []; |
1632
|
|
|
$ps = $store->getPropertySet($id); |
1633
|
|
|
|
1634
|
|
|
$transientVars = $inputs; |
1635
|
|
|
if (null === $transientVars) { |
1636
|
|
|
$transientVars = $this->transientVarsFactory(); |
1637
|
|
|
} |
1638
|
|
|
|
1639
|
|
|
$currentSteps = $store->findCurrentSteps($id); |
1640
|
|
|
|
1641
|
|
|
$this->populateTransientMap($entry, $transientVars, $wf->getRegisters(), 0, $currentSteps, $ps); |
1642
|
|
|
|
1643
|
|
|
$globalActions = $wf->getGlobalActions(); |
1644
|
|
|
|
1645
|
|
|
foreach ($globalActions as $action) { |
1646
|
|
|
$restriction = $action->getRestriction(); |
1647
|
|
|
$conditions = null; |
1648
|
|
|
|
1649
|
|
|
$transientVars['actionId'] = $action->getId(); |
1650
|
|
|
|
1651
|
|
|
if (null !== $restriction) { |
1652
|
|
|
$conditions = $restriction->getConditionsDescriptor(); |
1653
|
|
|
} |
1654
|
|
|
|
1655
|
|
|
$flag = $this->passesConditionsByDescriptor($wf->getGlobalConditions(), $transientVars, $ps, 0) && $this->passesConditionsByDescriptor($conditions, $transientVars, $ps, 0); |
1656
|
|
|
if ($flag) { |
1657
|
|
|
$l[] = $action->getId(); |
1658
|
|
|
} |
1659
|
|
|
} |
1660
|
|
|
|
1661
|
|
|
|
1662
|
|
View Code Duplication |
foreach ($currentSteps as $step) { |
|
|
|
|
1663
|
|
|
$data = $this->getAvailableActionsForStep($wf, $step, $transientVars, $ps); |
1664
|
|
|
foreach ($data as $v) { |
1665
|
|
|
$l[] = $v; |
1666
|
|
|
} |
1667
|
|
|
} |
1668
|
|
|
return array_unique($l); |
1669
|
|
|
} catch (\Exception $e) { |
1670
|
|
|
$errMsg = 'Ошибка проверки доступных действий'; |
1671
|
|
|
$this->getLog()->error($errMsg, [$e]); |
1672
|
|
|
} |
1673
|
|
|
|
1674
|
|
|
return []; |
1675
|
|
|
} |
1676
|
|
|
|
1677
|
|
|
/** |
1678
|
|
|
* @param WorkflowDescriptor $wf |
1679
|
|
|
* @param StepInterface $step |
1680
|
|
|
* @param TransientVarsInterface $transientVars |
1681
|
|
|
* @param PropertySetInterface $ps |
1682
|
|
|
* |
1683
|
|
|
* @return array |
1684
|
|
|
* |
1685
|
|
|
* @throws InternalWorkflowException |
1686
|
|
|
* @throws WorkflowException |
1687
|
|
|
* @throws \OldTown\Workflow\Exception\ArgumentNotNumericException |
1688
|
|
|
*/ |
1689
|
|
|
protected function getAvailableActionsForStep(WorkflowDescriptor $wf, StepInterface $step, TransientVarsInterface $transientVars, PropertySetInterface $ps) |
1690
|
|
|
{ |
1691
|
|
|
$l = []; |
1692
|
|
|
$s = $wf->getStep($step->getStepId()); |
1693
|
|
|
|
1694
|
|
|
if (null === $s) { |
1695
|
|
|
$errMsg = sprintf( |
1696
|
|
|
'getAvailableActionsForStep вызван с не существующим id шага %s', |
1697
|
|
|
$step->getStepId() |
1698
|
|
|
); |
1699
|
|
|
|
1700
|
|
|
$this->getLog()->warning($errMsg); |
1701
|
|
|
|
1702
|
|
|
return $l; |
1703
|
|
|
} |
1704
|
|
|
|
1705
|
|
|
$actions = $s->getActions(); |
1706
|
|
|
|
1707
|
|
|
if (null === $actions || 0 === $actions->count()) { |
1708
|
|
|
return $l; |
1709
|
|
|
} |
1710
|
|
|
|
1711
|
|
|
foreach ($actions as $action) { |
1712
|
|
|
$restriction = $action->getRestriction(); |
1713
|
|
|
$conditions = null; |
1714
|
|
|
|
1715
|
|
|
$transientVars['actionId'] = $action->getId(); |
1716
|
|
|
|
1717
|
|
|
|
1718
|
|
|
if (null !== $restriction) { |
1719
|
|
|
$conditions = $restriction->getConditionsDescriptor(); |
1720
|
|
|
} |
1721
|
|
|
|
1722
|
|
|
$f = $this->passesConditionsByDescriptor($wf->getGlobalConditions(), $transientVars, $ps, $s->getId()) |
1723
|
|
|
&& $this->passesConditionsByDescriptor($conditions, $transientVars, $ps, $s->getId()); |
1724
|
|
|
if ($f) { |
1725
|
|
|
$l[] = $action->getId(); |
1726
|
|
|
} |
1727
|
|
|
} |
1728
|
|
|
|
1729
|
|
|
return $l; |
1730
|
|
|
} |
1731
|
|
|
|
1732
|
|
|
/** |
1733
|
|
|
* @param ConfigurationInterface $configuration |
1734
|
|
|
* |
1735
|
|
|
* @return $this |
1736
|
|
|
*/ |
1737
|
|
|
public function setConfiguration(ConfigurationInterface $configuration) |
1738
|
|
|
{ |
1739
|
|
|
$this->configuration = $configuration; |
1740
|
|
|
|
1741
|
|
|
return $this; |
1742
|
|
|
} |
1743
|
|
|
|
1744
|
|
|
/** |
1745
|
|
|
* Возвращает состояние для текущего экземпляра workflow |
1746
|
|
|
* |
1747
|
|
|
* @param integer $id id экземпляра workflow |
1748
|
|
|
* @return integer id текущего состояния |
1749
|
|
|
* |
1750
|
|
|
* @throws InternalWorkflowException |
1751
|
|
|
*/ |
1752
|
|
|
public function getEntryState($id) |
1753
|
|
|
{ |
1754
|
|
|
try { |
1755
|
|
|
$store = $this->getPersistence(); |
1756
|
|
|
|
1757
|
|
|
return $store->findEntry($id)->getState(); |
1758
|
|
|
} catch (StoreException $e) { |
1759
|
|
|
$errMsg = sprintf( |
1760
|
|
|
'Ошибка при получение состояния экземпляра workflow c id# %s', |
1761
|
|
|
$id |
1762
|
|
|
); |
1763
|
|
|
$this->getLog()->error($errMsg, [$e]); |
1764
|
|
|
} |
1765
|
|
|
|
1766
|
|
|
return WorkflowEntryInterface::UNKNOWN; |
1767
|
|
|
} |
1768
|
|
|
|
1769
|
|
|
|
1770
|
|
|
/** |
1771
|
|
|
* Настройки хранилища |
1772
|
|
|
* |
1773
|
|
|
* @return array |
1774
|
|
|
* |
1775
|
|
|
* @throws InternalWorkflowException |
1776
|
|
|
*/ |
1777
|
|
|
public function getPersistenceProperties() |
1778
|
|
|
{ |
1779
|
|
|
return $this->getConfiguration()->getPersistenceArgs(); |
1780
|
|
|
} |
1781
|
|
|
|
1782
|
|
|
|
1783
|
|
|
/** |
1784
|
|
|
* Get the PropertySet for the specified workflow instance id. |
1785
|
|
|
* @param integer $id The workflow instance id. |
1786
|
|
|
* |
1787
|
|
|
* @return PropertySetInterface |
1788
|
|
|
* @throws InternalWorkflowException |
1789
|
|
|
*/ |
1790
|
|
|
public function getPropertySet($id) |
1791
|
|
|
{ |
1792
|
|
|
$ps = null; |
1793
|
|
|
|
1794
|
|
|
try { |
1795
|
|
|
$ps = $this->getPersistence()->getPropertySet($id); |
1796
|
|
|
} catch (StoreException $e) { |
1797
|
|
|
$errMsg = sprintf( |
1798
|
|
|
'Ошибка при получение PropertySet для экземпляра workflow c id# %s', |
1799
|
|
|
$id |
1800
|
|
|
); |
1801
|
|
|
$this->getLog()->error($errMsg, [$e]); |
1802
|
|
|
} |
1803
|
|
|
|
1804
|
|
|
return $ps; |
1805
|
|
|
} |
1806
|
|
|
|
1807
|
|
|
/** |
1808
|
|
|
* @return string[] |
1809
|
|
|
* |
1810
|
|
|
* @throws InternalWorkflowException |
1811
|
|
|
*/ |
1812
|
|
|
public function getWorkflowNames() |
1813
|
|
|
{ |
1814
|
|
|
try { |
1815
|
|
|
return $this->getConfiguration()->getWorkflowNames(); |
1816
|
|
|
} catch (FactoryException $e) { |
1817
|
|
|
$errMsg = 'Ошибка при получение имен workflow'; |
1818
|
|
|
$this->getLog()->error($errMsg, [$e]); |
1819
|
|
|
} |
1820
|
|
|
|
1821
|
|
|
return []; |
1822
|
|
|
} |
1823
|
|
|
|
1824
|
|
|
/** |
1825
|
|
|
* @param TypeResolverInterface $typeResolver |
1826
|
|
|
* |
1827
|
|
|
* @return $this |
1828
|
|
|
*/ |
1829
|
|
|
public function setTypeResolver(TypeResolverInterface $typeResolver) |
1830
|
|
|
{ |
1831
|
|
|
$this->typeResolver = $typeResolver; |
1832
|
|
|
|
1833
|
|
|
return $this; |
1834
|
|
|
} |
1835
|
|
|
|
1836
|
|
|
|
1837
|
|
|
/** |
1838
|
|
|
* Get a collection (Strings) of currently defined permissions for the specified workflow instance. |
1839
|
|
|
* @param integer $id id the workflow instance id. |
1840
|
|
|
* @param TransientVarsInterface $inputs inputs The inputs to the workflow instance. |
1841
|
|
|
* |
1842
|
|
|
* @return array A List of permissions specified currently (a permission is a string name). |
1843
|
|
|
* |
1844
|
|
|
*/ |
1845
|
|
|
public function getSecurityPermissions($id, TransientVarsInterface $inputs = null) |
1846
|
|
|
{ |
1847
|
|
|
try { |
1848
|
|
|
$store = $this->getPersistence(); |
1849
|
|
|
$entry = $store->findEntry($id); |
1850
|
|
|
$wf = $this->getConfiguration()->getWorkflow($entry->getWorkflowName()); |
1851
|
|
|
|
1852
|
|
|
$ps = $store->getPropertySet($id); |
1853
|
|
|
|
1854
|
|
|
if (null === $inputs) { |
1855
|
|
|
$inputs = $this->transientVarsFactory(); |
1856
|
|
|
} |
1857
|
|
|
$transientVars = $inputs; |
1858
|
|
|
|
1859
|
|
|
$currentSteps = $store->findCurrentSteps($id); |
1860
|
|
|
|
1861
|
|
|
try { |
1862
|
|
|
$this->populateTransientMap($entry, $transientVars, $wf->getRegisters(), null, $currentSteps, $ps); |
1863
|
|
|
} catch (\Exception $e) { |
1864
|
|
|
$errMsg = sprintf( |
1865
|
|
|
'Внутреннея ошибка: %s', |
1866
|
|
|
$e->getMessage() |
1867
|
|
|
); |
1868
|
|
|
throw new InternalWorkflowException($errMsg, $e->getCode(), $e); |
1869
|
|
|
} |
1870
|
|
|
|
1871
|
|
|
|
1872
|
|
|
$s = []; |
1873
|
|
|
|
1874
|
|
|
foreach ($currentSteps as $step) { |
1875
|
|
|
$stepId = $step->getStepId(); |
1876
|
|
|
|
1877
|
|
|
$xmlStep = $wf->getStep($stepId); |
1878
|
|
|
|
1879
|
|
|
$securities = $xmlStep->getPermissions(); |
1880
|
|
|
|
1881
|
|
|
foreach ($securities as $security) { |
1882
|
|
|
if (!$security instanceof PermissionDescriptor) { |
1883
|
|
|
$errMsg = 'Invalid PermissionDescriptor'; |
1884
|
|
|
throw new InternalWorkflowException($errMsg); |
1885
|
|
|
} |
1886
|
|
|
$conditionsDescriptor = $security->getRestriction()->getConditionsDescriptor(); |
1887
|
|
|
if (null !== $security->getRestriction() && $this->passesConditionsByDescriptor($conditionsDescriptor, $transientVars, $ps, $xmlStep->getId())) { |
1888
|
|
|
$s[$security->getName()] = $security->getName(); |
1889
|
|
|
} |
1890
|
|
|
} |
1891
|
|
|
} |
1892
|
|
|
|
1893
|
|
|
return $s; |
1894
|
|
|
} catch (\Exception $e) { |
1895
|
|
|
$errMsg = sprintf( |
1896
|
|
|
'Ошибка при получение информации о правах доступа для экземпляра workflow c id# %s', |
1897
|
|
|
$id |
1898
|
|
|
); |
1899
|
|
|
$this->getLog()->error($errMsg, [$e]); |
1900
|
|
|
} |
1901
|
|
|
|
1902
|
|
|
return []; |
1903
|
|
|
} |
1904
|
|
|
|
1905
|
|
|
|
1906
|
|
|
/** |
1907
|
|
|
* Get the name of the specified workflow instance. |
1908
|
|
|
* |
1909
|
|
|
* @param integer $id the workflow instance id. |
1910
|
|
|
* |
1911
|
|
|
* @return string |
1912
|
|
|
* |
1913
|
|
|
* @throws InternalWorkflowException |
1914
|
|
|
*/ |
1915
|
|
|
public function getWorkflowName($id) |
1916
|
|
|
{ |
1917
|
|
|
try { |
1918
|
|
|
$store = $this->getPersistence(); |
1919
|
|
|
$entry = $store->findEntry($id); |
1920
|
|
|
|
1921
|
|
|
if (null !== $entry) { |
1922
|
|
|
return $entry->getWorkflowName(); |
1923
|
|
|
} |
1924
|
|
|
} catch (FactoryException $e) { |
1925
|
|
|
$errMsg = sprintf( |
1926
|
|
|
'Ошибка при получение имен workflow для инстанса с id # %s', |
1927
|
|
|
$id |
1928
|
|
|
); |
1929
|
|
|
$this->getLog()->error($errMsg, [$e]); |
1930
|
|
|
} |
1931
|
|
|
|
1932
|
|
|
return null; |
1933
|
|
|
} |
1934
|
|
|
|
1935
|
|
|
/** |
1936
|
|
|
* Удаляет workflow |
1937
|
|
|
* |
1938
|
|
|
* @param string $workflowName |
1939
|
|
|
* |
1940
|
|
|
* @return bool |
1941
|
|
|
* |
1942
|
|
|
* @throws InternalWorkflowException |
1943
|
|
|
*/ |
1944
|
|
|
public function removeWorkflowDescriptor($workflowName) |
1945
|
|
|
{ |
1946
|
|
|
return $this->getConfiguration()->removeWorkflow($workflowName); |
1947
|
|
|
} |
1948
|
|
|
|
1949
|
|
|
/** |
1950
|
|
|
* @param $workflowName |
1951
|
|
|
* @param WorkflowDescriptor $descriptor |
1952
|
|
|
* @param $replace |
1953
|
|
|
* |
1954
|
|
|
* @return bool |
1955
|
|
|
* |
1956
|
|
|
* @throws InternalWorkflowException |
1957
|
|
|
*/ |
1958
|
|
|
public function saveWorkflowDescriptor($workflowName, WorkflowDescriptor $descriptor, $replace) |
1959
|
|
|
{ |
1960
|
|
|
$success = $this->getConfiguration()->saveWorkflow($workflowName, $descriptor, $replace); |
1961
|
|
|
|
1962
|
|
|
return $success; |
1963
|
|
|
} |
1964
|
|
|
|
1965
|
|
|
|
1966
|
|
|
/** |
1967
|
|
|
* Query the workflow store for matching instances |
1968
|
|
|
* |
1969
|
|
|
* @param WorkflowExpressionQuery $query |
1970
|
|
|
* |
1971
|
|
|
* @return array |
1972
|
|
|
* |
1973
|
|
|
* @throws InternalWorkflowException |
1974
|
|
|
*/ |
1975
|
|
|
public function query(WorkflowExpressionQuery $query) |
1976
|
|
|
{ |
1977
|
|
|
return $this->getPersistence()->query($query); |
1978
|
|
|
} |
1979
|
|
|
|
1980
|
|
|
/** |
1981
|
|
|
* @return string |
1982
|
|
|
*/ |
1983
|
|
|
public function getDefaultTypeResolverClass() |
1984
|
|
|
{ |
1985
|
|
|
return $this->defaultTypeResolverClass; |
1986
|
|
|
} |
1987
|
|
|
|
1988
|
|
|
/** |
1989
|
|
|
* @param string $defaultTypeResolverClass |
1990
|
|
|
* |
1991
|
|
|
* @return $this |
1992
|
|
|
*/ |
1993
|
|
|
public function setDefaultTypeResolverClass($defaultTypeResolverClass) |
1994
|
|
|
{ |
1995
|
|
|
$this->defaultTypeResolverClass = (string)$defaultTypeResolverClass; |
1996
|
|
|
|
1997
|
|
|
return $this; |
1998
|
|
|
} |
1999
|
|
|
|
2000
|
|
|
|
2001
|
|
|
/** |
2002
|
|
|
* @param ConditionsDescriptor $descriptor |
2003
|
|
|
* @param TransientVarsInterface $transientVars |
2004
|
|
|
* @param PropertySetInterface $ps |
2005
|
|
|
* @param $currentStepId |
2006
|
|
|
* |
2007
|
|
|
* @return bool |
2008
|
|
|
* |
2009
|
|
|
* @throws InternalWorkflowException |
2010
|
|
|
* @throws WorkflowException |
2011
|
|
|
*/ |
2012
|
|
|
protected function passesConditionsByDescriptor(ConditionsDescriptor $descriptor = null, TransientVarsInterface $transientVars, PropertySetInterface $ps, $currentStepId) |
2013
|
|
|
{ |
2014
|
|
|
if (null === $descriptor) { |
2015
|
|
|
return true; |
2016
|
|
|
} |
2017
|
|
|
|
2018
|
|
|
$type = $descriptor->getType(); |
2019
|
|
|
$conditions = $descriptor->getConditions(); |
2020
|
|
|
if (!$conditions instanceof SplObjectStorage) { |
2021
|
|
|
$errMsg = 'Invalid conditions'; |
2022
|
|
|
throw new InternalWorkflowException($errMsg); |
2023
|
|
|
} |
2024
|
|
|
$passesConditions = $this->passesConditionsWithType($type, $conditions, $transientVars, $ps, $currentStepId); |
2025
|
|
|
|
2026
|
|
|
return $passesConditions; |
2027
|
|
|
} |
2028
|
|
|
|
2029
|
|
|
/** |
2030
|
|
|
* @param string $conditionType |
2031
|
|
|
* @param SplObjectStorage $conditions |
2032
|
|
|
* @param TransientVarsInterface $transientVars |
2033
|
|
|
* @param PropertySetInterface $ps |
2034
|
|
|
* @param integer $currentStepId |
2035
|
|
|
* |
2036
|
|
|
* @return bool |
2037
|
|
|
* |
2038
|
|
|
* @throws InternalWorkflowException |
2039
|
|
|
* @throws InternalWorkflowException |
2040
|
|
|
* @throws WorkflowException |
2041
|
|
|
* |
2042
|
|
|
*/ |
2043
|
|
|
protected function passesConditionsWithType($conditionType, SplObjectStorage $conditions = null, TransientVarsInterface $transientVars, PropertySetInterface $ps, $currentStepId) |
2044
|
|
|
{ |
2045
|
|
|
if (null === $conditions) { |
2046
|
|
|
return true; |
2047
|
|
|
} |
2048
|
|
|
|
2049
|
|
|
if (0 === $conditions->count()) { |
2050
|
|
|
return true; |
2051
|
|
|
} |
2052
|
|
|
|
2053
|
|
|
$and = strtoupper($conditionType) === 'AND'; |
2054
|
|
|
$or = !$and; |
2055
|
|
|
|
2056
|
|
|
foreach ($conditions as $descriptor) { |
2057
|
|
|
if ($descriptor instanceof ConditionsDescriptor) { |
2058
|
|
|
$descriptorConditions = $descriptor->getConditions(); |
2059
|
|
|
if (!$descriptorConditions instanceof SplObjectStorage) { |
2060
|
|
|
$errMsg = 'Invalid conditions container'; |
2061
|
|
|
throw new InternalWorkflowException($errMsg); |
2062
|
|
|
} |
2063
|
|
|
|
2064
|
|
|
$result = $this->passesConditionsWithType($descriptor->getType(), $descriptorConditions, $transientVars, $ps, $currentStepId); |
2065
|
|
|
} elseif ($descriptor instanceof ConditionDescriptor) { |
2066
|
|
|
$result = $this->passesCondition($descriptor, $transientVars, $ps, $currentStepId); |
2067
|
|
|
} else { |
2068
|
|
|
$errMsg = 'Invalid condition descriptor'; |
2069
|
|
|
throw new Exception\InternalWorkflowException($errMsg); |
2070
|
|
|
} |
2071
|
|
|
|
2072
|
|
|
if ($and && !$result) { |
2073
|
|
|
return false; |
2074
|
|
|
} elseif ($or && $result) { |
2075
|
|
|
return true; |
2076
|
|
|
} |
2077
|
|
|
} |
2078
|
|
|
|
2079
|
|
|
if ($and) { |
2080
|
|
|
return true; |
2081
|
|
|
} |
2082
|
|
|
|
2083
|
|
|
return false; |
2084
|
|
|
} |
2085
|
|
|
|
2086
|
|
|
/** |
2087
|
|
|
* @param ConditionDescriptor $conditionDesc |
2088
|
|
|
* @param TransientVarsInterface $transientVars |
2089
|
|
|
* @param PropertySetInterface $ps |
2090
|
|
|
* @param integer $currentStepId |
2091
|
|
|
* |
2092
|
|
|
* @return boolean |
2093
|
|
|
* |
2094
|
|
|
* @throws WorkflowException |
2095
|
|
|
* @throws InternalWorkflowException |
2096
|
|
|
*/ |
2097
|
|
|
protected function passesCondition(ConditionDescriptor $conditionDesc, TransientVarsInterface $transientVars, PropertySetInterface $ps, $currentStepId) |
2098
|
|
|
{ |
2099
|
|
|
$type = $conditionDesc->getType(); |
2100
|
|
|
|
2101
|
|
|
$argsOriginal = $conditionDesc->getArgs(); |
2102
|
|
|
|
2103
|
|
|
|
2104
|
|
|
$args = $this->prepareArgs($argsOriginal, $transientVars, $ps); |
2105
|
|
|
|
2106
|
|
|
if (-1 !== $currentStepId) { |
2107
|
|
|
$stepId = array_key_exists('stepId', $args) ? (integer)$args['stepId'] : null; |
2108
|
|
|
|
2109
|
|
|
if (null !== $stepId && -1 === $stepId) { |
2110
|
|
|
$args['stepId'] = $currentStepId; |
2111
|
|
|
} |
2112
|
|
|
} |
2113
|
|
|
|
2114
|
|
|
$condition = $this->getResolver()->getCondition($type, $args); |
2115
|
|
|
|
2116
|
|
|
if (null === $condition) { |
2117
|
|
|
$this->context->setRollbackOnly(); |
2118
|
|
|
$errMsg = 'Огибка при загрузки условия'; |
2119
|
|
|
throw new WorkflowException($errMsg); |
2120
|
|
|
} |
2121
|
|
|
|
2122
|
|
|
try { |
2123
|
|
|
$passed = $condition->passesCondition($transientVars, $args, $ps); |
2124
|
|
|
|
2125
|
|
|
if ($conditionDesc->isNegate()) { |
2126
|
|
|
$passed = !$passed; |
2127
|
|
|
} |
2128
|
|
|
} catch (\Exception $e) { |
2129
|
|
|
$this->context->setRollbackOnly(); |
2130
|
|
|
|
2131
|
|
|
$errMsg = sprintf( |
2132
|
|
|
'Ошбика при выполнение условия %s', |
2133
|
|
|
get_class($condition) |
2134
|
|
|
); |
2135
|
|
|
|
2136
|
|
|
throw new WorkflowException($errMsg, $e->getCode(), $e); |
2137
|
|
|
} |
2138
|
|
|
|
2139
|
|
|
return $passed; |
2140
|
|
|
} |
2141
|
|
|
|
2142
|
|
|
/** |
2143
|
|
|
* Подготавливает аргументы. |
2144
|
|
|
* |
2145
|
|
|
* @param array $argsOriginal |
2146
|
|
|
* |
2147
|
|
|
* @param TransientVarsInterface $transientVars |
2148
|
|
|
* @param PropertySetInterface $ps |
2149
|
|
|
* |
2150
|
|
|
* @return array |
2151
|
|
|
* |
2152
|
|
|
* @throws InternalWorkflowException |
2153
|
|
|
*/ |
2154
|
|
|
protected function prepareArgs(array $argsOriginal = [], TransientVarsInterface $transientVars, PropertySetInterface $ps) |
2155
|
|
|
{ |
2156
|
|
|
$args = []; |
2157
|
|
|
foreach ($argsOriginal as $key => $value) { |
2158
|
|
|
$translateValue = $this->getConfiguration()->getVariableResolver()->translateVariables($value, $transientVars, $ps); |
2159
|
|
|
$args[$key] = $translateValue; |
2160
|
|
|
} |
2161
|
|
|
|
2162
|
|
|
return $args; |
2163
|
|
|
} |
2164
|
|
|
} |
2165
|
|
|
|
Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.
The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.
This check looks for comments that seem to be mostly valid code and reports them.