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\InvalidRoleException; |
19
|
|
|
use OldTown\Workflow\Exception\StoreException; |
20
|
|
|
use OldTown\Workflow\Exception\WorkflowException; |
21
|
|
|
use OldTown\Workflow\Loader\ActionDescriptor; |
22
|
|
|
use OldTown\Workflow\Loader\PermissionDescriptor; |
23
|
|
|
use OldTown\Workflow\Loader\WorkflowDescriptor; |
24
|
|
|
use OldTown\Workflow\Query\WorkflowExpressionQuery; |
25
|
|
|
use OldTown\Workflow\Spi\SimpleWorkflowEntry; |
26
|
|
|
use OldTown\Workflow\Spi\StepInterface; |
27
|
|
|
use OldTown\Workflow\Spi\WorkflowEntryInterface; |
28
|
|
|
use OldTown\Workflow\Spi\WorkflowStoreInterface; |
29
|
|
|
use OldTown\Workflow\TransientVars\TransientVarsInterface; |
30
|
|
|
use Psr\Log\LoggerInterface; |
31
|
|
|
use Traversable; |
32
|
|
|
use SplObjectStorage; |
33
|
|
|
use OldTown\Workflow\TransientVars\BaseTransientVars; |
34
|
|
|
use ReflectionClass; |
35
|
|
|
use ArrayObject; |
36
|
|
|
use OldTown\Workflow\Engine\EngineManagerInterface; |
37
|
|
|
use OldTown\Workflow\Engine\EngineManager; |
38
|
|
|
|
39
|
|
|
|
40
|
|
|
|
41
|
|
|
/** |
42
|
|
|
* Class AbstractWorkflow |
43
|
|
|
* |
44
|
|
|
* @package OldTown\Workflow |
45
|
|
|
*/ |
46
|
|
|
abstract class AbstractWorkflow implements WorkflowInterface |
47
|
|
|
{ |
48
|
|
|
/** |
49
|
|
|
* @var string |
50
|
|
|
*/ |
51
|
|
|
const CURRENT_STEPS = 'currentSteps'; |
52
|
|
|
|
53
|
|
|
/** |
54
|
|
|
* @var string |
55
|
|
|
*/ |
56
|
|
|
const HISTORY_STEPS = 'historySteps'; |
57
|
|
|
|
58
|
|
|
/** |
59
|
|
|
* @var WorkflowContextInterface |
60
|
|
|
*/ |
61
|
|
|
protected $context; |
62
|
|
|
|
63
|
|
|
/** |
64
|
|
|
* @var ConfigurationInterface |
65
|
|
|
*/ |
66
|
|
|
protected $configuration; |
67
|
|
|
|
68
|
|
|
|
69
|
|
|
/** |
70
|
|
|
* @var TypeResolverInterface |
71
|
|
|
*/ |
72
|
|
|
protected $typeResolver; |
73
|
|
|
|
74
|
|
|
/** |
75
|
|
|
* Логер |
76
|
|
|
* |
77
|
|
|
* @var LoggerInterface |
78
|
|
|
*/ |
79
|
|
|
protected $log; |
80
|
|
|
|
81
|
|
|
/** |
82
|
|
|
* Резолвер для создания провайдеров отвечающих за исполнение функций, проверку условий, выполнение валидаторов и т.д. |
83
|
|
|
* |
84
|
|
|
* @var string |
85
|
|
|
*/ |
86
|
|
|
protected $defaultTypeResolverClass = TypeResolver::class; |
87
|
|
|
|
88
|
|
|
/** |
89
|
|
|
* Карта переходов состояния процесса workflow |
90
|
|
|
* |
91
|
|
|
* @var null|array |
92
|
|
|
*/ |
93
|
|
|
protected $mapEntryState; |
94
|
|
|
|
95
|
|
|
/** |
96
|
|
|
* Менеджер движков |
97
|
|
|
* |
98
|
|
|
* @var EngineManagerInterface |
99
|
|
|
*/ |
100
|
|
|
protected $engineManager; |
101
|
|
|
|
102
|
|
|
/** |
103
|
|
|
* AbstractWorkflow constructor. |
104
|
|
|
* |
105
|
|
|
* @throws InternalWorkflowException |
106
|
|
|
*/ |
107
|
19 |
|
public function __construct() |
108
|
|
|
{ |
109
|
19 |
|
$this->initLoger(); |
110
|
19 |
|
$this->initMapEntryState(); |
111
|
19 |
|
} |
112
|
|
|
|
113
|
|
|
/** |
114
|
|
|
* Устанавливает менеджер движков |
115
|
|
|
* |
116
|
|
|
* @return EngineManagerInterface |
117
|
|
|
*/ |
118
|
19 |
|
public function getEngineManager() |
119
|
|
|
{ |
120
|
19 |
|
if ($this->engineManager) { |
121
|
19 |
|
return $this->engineManager; |
122
|
|
|
} |
123
|
19 |
|
$this->engineManager = new EngineManager($this); |
124
|
|
|
|
125
|
19 |
|
return $this->engineManager; |
126
|
2 |
|
} |
127
|
|
|
|
128
|
|
|
/** |
129
|
|
|
* Возвращает менеджер движков |
130
|
|
|
* |
131
|
|
|
* @param EngineManagerInterface $engineManager |
132
|
|
|
* |
133
|
|
|
* @return $this |
134
|
|
|
*/ |
135
|
|
|
public function setEngineManager(EngineManagerInterface $engineManager) |
136
|
|
|
{ |
137
|
|
|
$this->engineManager = $engineManager; |
138
|
|
|
|
139
|
|
|
return $this; |
140
|
|
|
} |
141
|
|
|
|
142
|
|
|
/** |
143
|
|
|
* Инициация карты переходов состояния процесса workflow |
144
|
|
|
*/ |
145
|
19 |
|
protected function initMapEntryState() |
146
|
|
|
{ |
147
|
19 |
|
$this->mapEntryState = [ |
148
|
19 |
|
WorkflowEntryInterface::COMPLETED => [ |
149
|
19 |
|
WorkflowEntryInterface::ACTIVATED => WorkflowEntryInterface::ACTIVATED |
150
|
19 |
|
], |
151
|
19 |
|
WorkflowEntryInterface::CREATED => [], |
152
|
19 |
|
WorkflowEntryInterface::ACTIVATED => [ |
153
|
19 |
|
WorkflowEntryInterface::CREATED => WorkflowEntryInterface::CREATED, |
154
|
19 |
|
WorkflowEntryInterface::SUSPENDED => WorkflowEntryInterface::SUSPENDED, |
155
|
|
|
|
156
|
19 |
|
], |
157
|
19 |
|
WorkflowEntryInterface::SUSPENDED => [ |
158
|
19 |
|
WorkflowEntryInterface::ACTIVATED => WorkflowEntryInterface::ACTIVATED |
159
|
19 |
|
], |
160
|
19 |
|
WorkflowEntryInterface::KILLED => [ |
161
|
19 |
|
WorkflowEntryInterface::SUSPENDED => WorkflowEntryInterface::SUSPENDED, |
162
|
19 |
|
WorkflowEntryInterface::ACTIVATED => WorkflowEntryInterface::ACTIVATED, |
163
|
19 |
|
WorkflowEntryInterface::CREATED => WorkflowEntryInterface::CREATED |
164
|
19 |
|
] |
165
|
19 |
|
]; |
166
|
19 |
|
} |
167
|
|
|
|
168
|
|
|
/** |
169
|
|
|
* Инициализация системы логирования |
170
|
|
|
* |
171
|
|
|
* @throws InternalWorkflowException |
172
|
|
|
*/ |
173
|
19 |
|
protected function initLoger() |
174
|
1 |
|
{ |
175
|
|
|
try { |
176
|
19 |
|
$this->log = LogFactory::getLog(); |
177
|
19 |
|
} catch (\Exception $e) { |
178
|
|
|
throw new InternalWorkflowException($e->getMessage(), $e->getCode(), $e); |
179
|
|
|
} |
180
|
19 |
|
} |
181
|
|
|
|
182
|
|
|
/** |
183
|
|
|
* Инициализация workflow. Workflow нужно иницаилизровать прежде, чем выполнять какие либо действия. |
184
|
|
|
* Workflow может быть инициализированно только один раз |
185
|
|
|
* |
186
|
|
|
* @param string $workflowName Имя workflow |
187
|
|
|
* @param integer $initialAction Имя первого шага, с которого начинается workflow |
188
|
|
|
* @param TransientVarsInterface $inputs Данные введеные пользователем |
189
|
|
|
* |
190
|
|
|
* @return integer |
191
|
|
|
* |
192
|
|
|
* @throws InternalWorkflowException |
193
|
|
|
* @throws InvalidActionException |
194
|
|
|
* @throws InvalidRoleException |
195
|
|
|
* @throws \OldTown\Workflow\Exception\ArgumentNotNumericException |
196
|
|
|
* @throws InvalidArgumentException |
197
|
|
|
* @throws WorkflowException |
198
|
|
|
*/ |
199
|
19 |
|
public function initialize($workflowName, $initialAction, TransientVarsInterface $inputs = null) |
200
|
|
|
{ |
201
|
|
|
try { |
202
|
19 |
|
$initialAction = (integer)$initialAction; |
203
|
|
|
|
204
|
19 |
|
$wf = $this->getConfiguration()->getWorkflow($workflowName); |
205
|
|
|
|
206
|
19 |
|
$store = $this->getPersistence(); |
207
|
|
|
|
208
|
19 |
|
$entry = $store->createEntry($workflowName); |
209
|
|
|
|
210
|
19 |
|
$ps = $store->getPropertySet($entry->getId()); |
211
|
|
|
|
212
|
|
|
|
213
|
19 |
|
if (null === $inputs) { |
214
|
|
|
$inputs = $this->transientVarsFactory(); |
215
|
|
|
} |
216
|
|
|
|
217
|
19 |
|
$engineManager = $this->getEngineManager(); |
218
|
|
|
|
219
|
19 |
|
$transientVars = $inputs; |
220
|
19 |
|
$inputs = clone $transientVars; |
221
|
|
|
|
222
|
19 |
|
$engineManager->getDataEngine()->populateTransientMap($entry, $transientVars, $wf->getRegisters(), $initialAction, new ArrayObject(), $ps); |
223
|
|
|
|
224
|
19 |
|
if (!$this->canInitializeInternal($workflowName, $initialAction, $transientVars, $ps)) { |
225
|
1 |
|
$this->context->setRollbackOnly(); |
226
|
1 |
|
$errMsg = 'You are restricted from initializing this workflow'; |
227
|
1 |
|
throw new InvalidRoleException($errMsg); |
228
|
|
|
} |
229
|
|
|
|
230
|
18 |
|
$action = $wf->getInitialAction($initialAction); |
231
|
|
|
|
232
|
18 |
|
if (null === $action) { |
233
|
|
|
$errMsg = sprintf('Invalid initial action id: %s', $initialAction); |
234
|
|
|
throw new InvalidActionException($errMsg); |
235
|
|
|
} |
236
|
|
|
|
237
|
18 |
|
$currentSteps = new SplObjectStorage(); |
238
|
18 |
|
$transitionManager = $engineManager->getTransitionEngine(); |
239
|
18 |
|
$transitionManager->transitionWorkflow($entry, $currentSteps, $store, $wf, $action, $transientVars, $inputs, $ps); |
240
|
|
|
|
241
|
16 |
|
$entryId = $entry->getId(); |
242
|
19 |
|
} catch (WorkflowException $e) { |
243
|
|
|
$this->context->setRollbackOnly(); |
244
|
|
|
throw new InternalWorkflowException($e->getMessage(), $e->getCode(), $e); |
245
|
|
|
} |
246
|
|
|
|
247
|
16 |
|
return $entryId; |
248
|
|
|
} |
249
|
|
|
|
250
|
|
|
|
251
|
|
|
/** |
252
|
|
|
* Проверка того что данные могут быть использованы в цикле |
253
|
|
|
* |
254
|
|
|
* @param $data |
255
|
|
|
* |
256
|
|
|
* @throws InvalidArgumentException |
257
|
|
|
*/ |
258
|
|
|
protected function validateIterateData($data) |
259
|
|
|
{ |
260
|
|
View Code Duplication |
if (!is_array($data) && !$data instanceof Traversable) { |
|
|
|
|
261
|
|
|
$errMsg = 'Data not iterate'; |
262
|
|
|
throw new InvalidArgumentException($errMsg); |
263
|
|
|
} |
264
|
|
|
} |
265
|
|
|
|
266
|
|
|
|
267
|
|
|
|
268
|
|
|
/** |
269
|
|
|
* @param $id |
270
|
|
|
* @param $inputs |
271
|
|
|
* |
272
|
|
|
* @return array |
273
|
|
|
* |
274
|
|
|
*/ |
275
|
|
|
public function getAvailableActions($id, TransientVarsInterface $inputs = null) |
276
|
|
|
{ |
277
|
|
|
try { |
278
|
|
|
$store = $this->getPersistence(); |
279
|
|
|
$entry = $store->findEntry($id); |
280
|
|
|
|
281
|
|
|
if (null === $entry) { |
282
|
|
|
$errMsg = sprintf( |
283
|
|
|
'Не существует экземпляра workflow c id %s', |
284
|
|
|
$id |
285
|
|
|
); |
286
|
|
|
throw new InvalidArgumentException($errMsg); |
287
|
|
|
} |
288
|
|
|
|
289
|
|
|
if (WorkflowEntryInterface::ACTIVATED === $entry->getState()) { |
290
|
|
|
return []; |
291
|
|
|
} |
292
|
|
|
|
293
|
|
|
$wf = $this->getConfiguration()->getWorkflow($entry->getWorkflowName()); |
294
|
|
|
|
295
|
|
|
$l = []; |
296
|
|
|
$ps = $store->getPropertySet($id); |
297
|
|
|
|
298
|
|
|
$transientVars = $inputs; |
299
|
|
|
if (null === $transientVars) { |
300
|
|
|
$transientVars = $this->transientVarsFactory(); |
301
|
|
|
} |
302
|
|
|
|
303
|
|
|
$currentSteps = $store->findCurrentSteps($id); |
304
|
|
|
|
305
|
|
|
$engineManager = $this->getEngineManager(); |
306
|
|
|
$engineManager->getDataEngine()->populateTransientMap($entry, $transientVars, $wf->getRegisters(), 0, $currentSteps, $ps); |
307
|
|
|
|
308
|
|
|
$globalActions = $wf->getGlobalActions(); |
309
|
|
|
|
310
|
|
|
$conditionsEngine = $engineManager->getConditionsEngine(); |
311
|
|
|
foreach ($globalActions as $action) { |
312
|
|
|
$restriction = $action->getRestriction(); |
313
|
|
|
$conditions = null; |
314
|
|
|
|
315
|
|
|
$transientVars['actionId'] = $action->getId(); |
316
|
|
|
|
317
|
|
|
if (null !== $restriction) { |
318
|
|
|
$conditions = $restriction->getConditionsDescriptor(); |
319
|
|
|
} |
320
|
|
|
|
321
|
|
|
$flag = $conditionsEngine->passesConditionsByDescriptor($wf->getGlobalConditions(), $transientVars, $ps, 0) && $conditionsEngine->passesConditionsByDescriptor($conditions, $transientVars, $ps, 0); |
322
|
|
|
if ($flag) { |
323
|
|
|
$l[] = $action->getId(); |
324
|
|
|
} |
325
|
|
|
} |
326
|
|
|
|
327
|
|
|
foreach ($currentSteps as $currentStep) { |
328
|
|
|
$availableActionsForStep = $this->getAvailableActionsForStep($wf, $currentStep, $transientVars, $ps); |
329
|
|
|
foreach ($availableActionsForStep as $actionId) { |
330
|
|
|
$l[] = $actionId; |
331
|
|
|
} |
332
|
|
|
} |
333
|
|
|
|
334
|
|
|
|
335
|
|
|
return array_unique($l); |
336
|
|
|
} catch (\Exception $e) { |
337
|
|
|
$errMsg = 'Ошибка проверки доступных действий'; |
338
|
|
|
$this->getLog()->error($errMsg, [$e]); |
339
|
|
|
} |
340
|
|
|
|
341
|
|
|
return []; |
342
|
|
|
} |
343
|
|
|
|
344
|
|
|
/** |
345
|
|
|
* Создает хранилище переменных |
346
|
|
|
* |
347
|
|
|
* @param $class |
348
|
|
|
* |
349
|
|
|
* @return TransientVarsInterface |
350
|
|
|
*/ |
351
|
|
|
protected function transientVarsFactory($class = BaseTransientVars::class) |
352
|
|
|
{ |
353
|
|
|
$r = new \ReflectionClass($class); |
354
|
|
|
return $r->newInstance(); |
355
|
|
|
} |
356
|
|
|
|
357
|
|
|
/** |
358
|
|
|
* |
359
|
|
|
* |
360
|
|
|
* Осуществляет переходл в новое состояние, для заданного процесса workflow |
361
|
|
|
* |
362
|
|
|
* @param integer $entryId id запущенного процесса workflow |
363
|
|
|
* @param integer $actionId id действия, доступного та текущем шаеге процессса workflow |
364
|
|
|
* @param TransientVarsInterface $inputs Входные данные для перехода |
365
|
|
|
* |
366
|
|
|
* @return void |
367
|
|
|
* |
368
|
|
|
* @throws WorkflowException |
369
|
|
|
* @throws InvalidActionException |
370
|
|
|
* @throws InvalidArgumentException |
371
|
|
|
* @throws InternalWorkflowException |
372
|
|
|
* @throws \OldTown\Workflow\Exception\ArgumentNotNumericException |
373
|
|
|
*/ |
374
|
8 |
|
public function doAction($entryId, $actionId, TransientVarsInterface $inputs = null) |
375
|
|
|
{ |
376
|
8 |
|
$actionId = (integer)$actionId; |
377
|
8 |
|
if (null === $inputs) { |
378
|
|
|
$inputs = $this->transientVarsFactory(); |
379
|
|
|
} |
380
|
8 |
|
$transientVars = $inputs; |
381
|
8 |
|
$inputs = clone $transientVars; |
382
|
|
|
|
383
|
8 |
|
$store = $this->getPersistence(); |
384
|
8 |
|
$entry = $store->findEntry($entryId); |
385
|
|
|
|
386
|
8 |
|
if (WorkflowEntryInterface::ACTIVATED !== $entry->getState()) { |
387
|
|
|
return; |
388
|
|
|
} |
389
|
|
|
|
390
|
8 |
|
$wf = $this->getConfiguration()->getWorkflow($entry->getWorkflowName()); |
391
|
|
|
|
392
|
8 |
|
$currentSteps = $store->findCurrentSteps($entryId); |
393
|
8 |
|
if (!$currentSteps instanceof SplObjectStorage) { |
394
|
|
|
$errMsg = 'Invalid currentSteps'; |
395
|
|
|
throw new InternalWorkflowException($errMsg); |
396
|
|
|
} |
397
|
|
|
|
398
|
8 |
|
$action = null; |
399
|
|
|
|
400
|
8 |
|
$ps = $store->getPropertySet($entryId); |
401
|
|
|
|
402
|
8 |
|
$engineManager = $this->getEngineManager(); |
403
|
8 |
|
$engineManager->getDataEngine()->populateTransientMap($entry, $transientVars, $wf->getRegisters(), $actionId, $currentSteps, $ps); |
404
|
|
|
|
405
|
|
|
|
406
|
8 |
|
$validAction = false; |
407
|
|
|
|
408
|
8 |
|
$transitionEngine = $engineManager->getTransitionEngine(); |
409
|
8 |
|
foreach ($wf->getGlobalActions() as $actionDesc) { |
410
|
|
View Code Duplication |
if ($actionId === $actionDesc->getId()) { |
|
|
|
|
411
|
|
|
$action = $actionDesc; |
412
|
|
|
|
413
|
|
|
if ($transitionEngine->isActionAvailable($action, $transientVars, $ps, 0)) { |
414
|
|
|
$validAction = true; |
415
|
|
|
} |
416
|
|
|
} |
417
|
8 |
|
} |
418
|
|
|
|
419
|
|
|
|
420
|
8 |
|
foreach ($currentSteps as $step) { |
421
|
8 |
|
$s = $wf->getStep($step->getStepId()); |
422
|
|
|
|
423
|
8 |
|
foreach ($s->getActions() as $actionDesc) { |
424
|
8 |
|
if (!$actionDesc instanceof ActionDescriptor) { |
425
|
|
|
$errMsg = 'Invalid action descriptor'; |
426
|
|
|
throw new InternalWorkflowException($errMsg); |
427
|
|
|
} |
428
|
|
|
|
429
|
8 |
View Code Duplication |
if ($actionId === $actionDesc->getId()) { |
|
|
|
|
430
|
8 |
|
$action = $actionDesc; |
431
|
|
|
|
432
|
8 |
|
if ($transitionEngine->isActionAvailable($action, $transientVars, $ps, $s->getId())) { |
433
|
6 |
|
$validAction = true; |
434
|
6 |
|
} |
435
|
8 |
|
} |
436
|
8 |
|
} |
437
|
8 |
|
} |
438
|
|
|
|
439
|
|
|
|
440
|
8 |
|
if (!$validAction) { |
441
|
2 |
|
$errMsg = sprintf( |
442
|
2 |
|
'Action %s is invalid', |
443
|
|
|
$actionId |
444
|
2 |
|
); |
445
|
2 |
|
throw new InvalidActionException($errMsg); |
446
|
|
|
} |
447
|
|
|
|
448
|
|
|
|
449
|
|
|
try { |
450
|
6 |
|
$transitionManager = $this->getEngineManager()->getTransitionEngine(); |
451
|
6 |
|
if ($transitionManager->transitionWorkflow($entry, $currentSteps, $store, $wf, $action, $transientVars, $inputs, $ps)) { |
452
|
|
|
$this->checkImplicitFinish($action, $entryId); |
453
|
|
|
} |
454
|
6 |
|
} catch (WorkflowException $e) { |
455
|
|
|
$this->context->setRollbackOnly(); |
456
|
|
|
/** @var WorkflowException $e*/ |
457
|
|
|
throw $e; |
458
|
|
|
} |
459
|
5 |
|
} |
460
|
|
|
|
461
|
|
|
/** |
462
|
|
|
* @param ActionDescriptor $action |
463
|
|
|
* @param $id |
464
|
|
|
* |
465
|
|
|
* @return void |
466
|
|
|
* |
467
|
|
|
* @throws \OldTown\Workflow\Exception\ArgumentNotNumericException |
468
|
|
|
* @throws InvalidArgumentException |
469
|
|
|
* @throws InternalWorkflowException |
470
|
|
|
*/ |
471
|
|
|
protected function checkImplicitFinish(ActionDescriptor $action, $id) |
472
|
|
|
{ |
473
|
|
|
$store = $this->getPersistence(); |
474
|
|
|
$entry = $store->findEntry($id); |
475
|
|
|
|
476
|
|
|
$wf = $this->getConfiguration()->getWorkflow($entry->getWorkflowName()); |
477
|
|
|
|
478
|
|
|
$currentSteps = $store->findCurrentSteps($id); |
479
|
|
|
|
480
|
|
|
$isCompleted = $wf->getGlobalActions()->count() === 0; |
481
|
|
|
|
482
|
|
|
foreach ($currentSteps as $step) { |
483
|
|
|
if ($isCompleted) { |
484
|
|
|
break; |
485
|
|
|
} |
486
|
|
|
|
487
|
|
|
$stepDes = $wf->getStep($step->getStepId()); |
488
|
|
|
|
489
|
|
|
if ($stepDes->getActions()->count() > 0) { |
490
|
|
|
$isCompleted = true; |
491
|
|
|
} |
492
|
|
|
} |
493
|
|
|
|
494
|
|
|
if ($isCompleted) { |
495
|
|
|
$entryEngine = $this->getEngineManager()->getEntryEngine(); |
496
|
|
|
$entryEngine->completeEntry($action, $id, $currentSteps, WorkflowEntryInterface::COMPLETED); |
497
|
|
|
} |
498
|
|
|
} |
499
|
|
|
|
500
|
|
|
|
501
|
|
|
|
502
|
|
|
/** |
503
|
|
|
* |
504
|
|
|
* Check if the state of the specified workflow instance can be changed to the new specified one. |
505
|
|
|
* |
506
|
|
|
* @param integer $id The workflow instance id. |
507
|
|
|
* @param integer $newState The new state id. |
508
|
|
|
* |
509
|
|
|
* @return boolean true if the state of the workflow can be modified, false otherwise. |
510
|
|
|
* |
511
|
|
|
* @throws InternalWorkflowException |
512
|
|
|
*/ |
513
|
16 |
|
public function canModifyEntryState($id, $newState) |
514
|
|
|
{ |
515
|
16 |
|
$store = $this->getPersistence(); |
516
|
16 |
|
$entry = $store->findEntry($id); |
517
|
|
|
|
518
|
16 |
|
$currentState = $entry->getState(); |
519
|
|
|
|
520
|
16 |
|
return array_key_exists($newState, $this->mapEntryState) && array_key_exists($currentState, $this->mapEntryState[$newState]); |
521
|
|
|
} |
522
|
|
|
|
523
|
|
|
|
524
|
|
|
/** |
525
|
|
|
* |
526
|
|
|
* Возвращает коллекцию объектов описывающие состояние для текущего экземпляра workflow |
527
|
|
|
* |
528
|
|
|
* @param integer $entryId id экземпляра workflow |
529
|
|
|
* |
530
|
|
|
* @return SplObjectStorage|StepInterface[] |
531
|
|
|
* |
532
|
|
|
* @throws InternalWorkflowException |
533
|
|
|
*/ |
534
|
|
|
public function getCurrentSteps($entryId) |
535
|
|
|
{ |
536
|
|
|
return $this->getStepFromStorage($entryId, static::CURRENT_STEPS); |
537
|
|
|
} |
538
|
|
|
|
539
|
|
|
/** |
540
|
|
|
* Возвращает информацию о том в какие шаги, были осуществленны переходы, для процесса workflow с заданным id |
541
|
|
|
* |
542
|
|
|
* @param integer $entryId уникальный идентификатор процесса workflow |
543
|
|
|
* |
544
|
|
|
* @return StepInterface[]|SplObjectStorage список шагов |
545
|
|
|
* |
546
|
|
|
* @throws InternalWorkflowException |
547
|
|
|
*/ |
548
|
|
|
public function getHistorySteps($entryId) |
549
|
|
|
{ |
550
|
|
|
return $this->getStepFromStorage($entryId, static::HISTORY_STEPS); |
551
|
|
|
} |
552
|
|
|
|
553
|
|
|
/** |
554
|
|
|
* Получение шагов информации о шагах процесса workflow |
555
|
|
|
* |
556
|
|
|
* @param $entryId |
557
|
|
|
* @param $type |
558
|
|
|
* |
559
|
|
|
* @return Spi\StepInterface[]|SplObjectStorage |
560
|
|
|
* |
561
|
|
|
* @throws InternalWorkflowException |
562
|
|
|
*/ |
563
|
|
|
protected function getStepFromStorage($entryId, $type) |
564
|
|
|
{ |
565
|
|
|
try { |
566
|
|
|
$store = $this->getPersistence(); |
567
|
|
|
|
568
|
|
|
if (static::CURRENT_STEPS === $type) { |
569
|
|
|
return $store->findCurrentSteps($entryId); |
570
|
|
|
} elseif (static::HISTORY_STEPS === $type) { |
571
|
|
|
return $store->findHistorySteps($entryId); |
572
|
|
|
} |
573
|
|
|
} catch (StoreException $e) { |
574
|
|
|
$errMsg = sprintf( |
575
|
|
|
'Ошибка при получение истории шагов для экземпляра workflow c id# %s', |
576
|
|
|
$entryId |
577
|
|
|
); |
578
|
|
|
$this->getLog()->error($errMsg, [$e]); |
579
|
|
|
} |
580
|
|
|
|
581
|
|
|
return new SplObjectStorage(); |
582
|
|
|
} |
583
|
|
|
|
584
|
|
|
|
585
|
|
|
|
586
|
|
|
/** |
587
|
|
|
* |
588
|
|
|
* |
589
|
|
|
* Modify the state of the specified workflow instance. |
590
|
|
|
* @param integer $id The workflow instance id. |
591
|
|
|
* @param integer $newState the new state to change the workflow instance to. |
592
|
|
|
* |
593
|
|
|
* @throws InvalidArgumentException |
594
|
|
|
* @throws InvalidEntryStateException |
595
|
|
|
* @throws InternalWorkflowException |
596
|
|
|
*/ |
597
|
16 |
|
public function changeEntryState($id, $newState) |
598
|
|
|
{ |
599
|
16 |
|
$store = $this->getPersistence(); |
600
|
16 |
|
$entry = $store->findEntry($id); |
601
|
|
|
|
602
|
16 |
|
if ($newState === $entry->getState()) { |
603
|
|
|
return; |
604
|
|
|
} |
605
|
|
|
|
606
|
16 |
|
if ($this->canModifyEntryState($id, $newState)) { |
607
|
16 |
|
if (WorkflowEntryInterface::KILLED === $newState || WorkflowEntryInterface::COMPLETED === $newState) { |
608
|
|
|
$currentSteps = $this->getCurrentSteps($id); |
609
|
|
|
|
610
|
|
|
if (count($currentSteps) > 0) { |
611
|
|
|
$entryEngine = $this->getEngineManager()->getEntryEngine(); |
612
|
|
|
$entryEngine->completeEntry(null, $id, $currentSteps, $newState); |
613
|
|
|
} |
614
|
|
|
} |
615
|
|
|
|
616
|
16 |
|
$store->setEntryState($id, $newState); |
617
|
16 |
|
} else { |
618
|
|
|
$errMsg = sprintf( |
619
|
|
|
'Не возможен переход в экземпляре workflow #%s. Текущее состояние %s, ожидаемое состояние %s', |
620
|
|
|
$id, |
621
|
|
|
$entry->getState(), |
622
|
|
|
$newState |
623
|
|
|
); |
624
|
|
|
|
625
|
|
|
throw new InvalidEntryStateException($errMsg); |
626
|
|
|
} |
627
|
|
|
|
628
|
16 |
|
$msg = sprintf( |
629
|
16 |
|
'%s : Новое состояние: %s', |
630
|
16 |
|
$entry->getId(), |
631
|
16 |
|
$entry->getState() |
632
|
16 |
|
); |
633
|
16 |
|
$this->getLog()->debug($msg); |
634
|
16 |
|
} |
635
|
|
|
|
636
|
|
|
|
637
|
|
|
|
638
|
|
|
|
639
|
|
|
|
640
|
|
|
/** |
641
|
|
|
* Проверяет имеет ли пользователь достаточно прав, что бы иниициировать вызываемый процесс |
642
|
|
|
* |
643
|
|
|
* @param string $workflowName имя workflow |
644
|
|
|
* @param integer $initialAction id начального состояния |
645
|
|
|
* @param TransientVarsInterface $inputs |
646
|
|
|
* |
647
|
|
|
* @return bool |
648
|
|
|
* |
649
|
|
|
* @throws InvalidArgumentException |
650
|
|
|
* @throws WorkflowException |
651
|
|
|
* @throws InternalWorkflowException |
652
|
|
|
* @throws \OldTown\Workflow\Exception\ArgumentNotNumericException |
653
|
|
|
*/ |
654
|
|
|
public function canInitialize($workflowName, $initialAction, TransientVarsInterface $inputs = null) |
655
|
|
|
{ |
656
|
|
|
$mockWorkflowName = $workflowName; |
657
|
|
|
$mockEntry = new SimpleWorkflowEntry(0, $mockWorkflowName, WorkflowEntryInterface::CREATED); |
658
|
|
|
|
659
|
|
|
try { |
660
|
|
|
$ps = PropertySetManager::getInstance('memory', []); |
661
|
|
|
if (!$ps instanceof PropertySetInterface) { |
662
|
|
|
$errMsg = 'Invalid create PropertySet'; |
663
|
|
|
throw new InternalWorkflowException($errMsg); |
664
|
|
|
} |
665
|
|
|
} catch (\Exception $e) { |
666
|
|
|
throw new InternalWorkflowException($e->getMessage(), $e->getCode(), $e); |
667
|
|
|
} |
668
|
|
|
|
669
|
|
|
|
670
|
|
|
|
671
|
|
|
|
672
|
|
|
if (null === $inputs) { |
673
|
|
|
$inputs = $this->transientVarsFactory(); |
674
|
|
|
} |
675
|
|
|
$transientVars = $inputs; |
676
|
|
|
|
677
|
|
|
try { |
678
|
|
|
$this->getEngineManager()->getDataEngine()->populateTransientMap($mockEntry, $transientVars, [], $initialAction, [], $ps); |
679
|
|
|
|
680
|
|
|
$result = $this->canInitializeInternal($workflowName, $initialAction, $transientVars, $ps); |
681
|
|
|
|
682
|
|
|
return $result; |
683
|
|
|
} catch (InvalidActionException $e) { |
684
|
|
|
$this->getLog()->error($e->getMessage(), [$e]); |
685
|
|
|
|
686
|
|
|
return false; |
687
|
|
|
} catch (WorkflowException $e) { |
688
|
|
|
$errMsg = sprintf( |
689
|
|
|
'Ошибка при проверки canInitialize: %s', |
690
|
|
|
$e->getMessage() |
691
|
|
|
); |
692
|
|
|
$this->getLog()->error($errMsg, [$e]); |
693
|
|
|
|
694
|
|
|
return false; |
695
|
|
|
} |
696
|
|
|
} |
697
|
|
|
|
698
|
|
|
|
699
|
|
|
/** |
700
|
|
|
* Проверяет имеет ли пользователь достаточно прав, что бы иниициировать вызываемый процесс |
701
|
|
|
* |
702
|
|
|
* @param string $workflowName имя workflow |
703
|
|
|
* @param integer $initialAction id начального состояния |
704
|
|
|
* @param TransientVarsInterface $transientVars |
705
|
|
|
* |
706
|
|
|
* @param PropertySetInterface $ps |
707
|
|
|
* |
708
|
|
|
* @return bool |
709
|
|
|
* |
710
|
|
|
* @throws \OldTown\Workflow\Exception\ArgumentNotNumericException |
711
|
|
|
* @throws InvalidActionException |
712
|
|
|
* @throws InternalWorkflowException |
713
|
|
|
* @throws WorkflowException |
714
|
|
|
*/ |
715
|
19 |
|
protected function canInitializeInternal($workflowName, $initialAction, TransientVarsInterface $transientVars, PropertySetInterface $ps) |
716
|
|
|
{ |
717
|
19 |
|
$wf = $this->getConfiguration()->getWorkflow($workflowName); |
718
|
|
|
|
719
|
19 |
|
$actionDescriptor = $wf->getInitialAction($initialAction); |
720
|
|
|
|
721
|
19 |
|
if (null === $actionDescriptor) { |
722
|
|
|
$errMsg = sprintf( |
723
|
|
|
'Некорректное инициирующие действие # %s', |
724
|
|
|
$initialAction |
725
|
|
|
); |
726
|
|
|
throw new InvalidActionException($errMsg); |
727
|
|
|
} |
728
|
|
|
|
729
|
19 |
|
$restriction = $actionDescriptor->getRestriction(); |
730
|
|
|
|
731
|
|
|
|
732
|
19 |
|
$conditions = null; |
733
|
19 |
|
if (null !== $restriction) { |
734
|
2 |
|
$conditions = $restriction->getConditionsDescriptor(); |
735
|
2 |
|
} |
736
|
|
|
|
737
|
19 |
|
$conditionsEngine = $this->getEngineManager()->getConditionsEngine(); |
738
|
19 |
|
$passesConditions = $conditionsEngine->passesConditionsByDescriptor($conditions, $transientVars, $ps, 0); |
739
|
|
|
|
740
|
19 |
|
return $passesConditions; |
741
|
|
|
} |
742
|
|
|
|
743
|
|
|
/** |
744
|
|
|
* Возвращает резолвер |
745
|
|
|
* |
746
|
|
|
* @return TypeResolverInterface |
747
|
|
|
*/ |
748
|
19 |
|
public function getResolver() |
749
|
|
|
{ |
750
|
19 |
|
if (null !== $this->typeResolver) { |
751
|
16 |
|
return $this->typeResolver; |
752
|
|
|
} |
753
|
|
|
|
754
|
19 |
|
$classResolver = $this->getDefaultTypeResolverClass(); |
755
|
19 |
|
$r = new ReflectionClass($classResolver); |
756
|
19 |
|
$resolver = $r->newInstance(); |
757
|
19 |
|
$this->typeResolver = $resolver; |
758
|
|
|
|
759
|
19 |
|
return $this->typeResolver; |
760
|
|
|
} |
761
|
|
|
|
762
|
|
|
/** |
763
|
|
|
* Возвращает хранилище состояния workflow |
764
|
|
|
* |
765
|
|
|
* @return WorkflowStoreInterface |
766
|
|
|
* |
767
|
|
|
* @throws InternalWorkflowException |
768
|
|
|
*/ |
769
|
19 |
|
protected function getPersistence() |
770
|
|
|
{ |
771
|
19 |
|
return $this->getConfiguration()->getWorkflowStore(); |
772
|
|
|
} |
773
|
|
|
|
774
|
|
|
/** |
775
|
|
|
* Получить конфигурацию workflow. Метод также проверяет была ли иницилазированн конфигурация, если нет, то |
776
|
|
|
* инициализирует ее. |
777
|
|
|
* |
778
|
|
|
* Если конфигурация не была установленна, то возвращает конфигурацию по умолчанию |
779
|
|
|
* |
780
|
|
|
* @return ConfigurationInterface|DefaultConfiguration Конфигурация которая была установленна |
781
|
|
|
* |
782
|
|
|
* @throws InternalWorkflowException |
783
|
|
|
*/ |
784
|
19 |
|
public function getConfiguration() |
785
|
|
|
{ |
786
|
19 |
|
$config = null !== $this->configuration ? $this->configuration : DefaultConfiguration::getInstance(); |
787
|
|
|
|
788
|
19 |
|
if (!$config->isInitialized()) { |
789
|
|
|
try { |
790
|
|
|
$config->load(null); |
791
|
|
|
} catch (FactoryException $e) { |
792
|
|
|
$errMsg = 'Ошибка при иницилазации конфигурации workflow'; |
793
|
|
|
$this->getLog()->critical($errMsg, ['exception' => $e]); |
794
|
|
|
throw new InternalWorkflowException($errMsg, $e->getCode(), $e); |
795
|
|
|
} |
796
|
|
|
} |
797
|
|
|
|
798
|
19 |
|
return $config; |
799
|
|
|
} |
800
|
|
|
|
801
|
|
|
/** |
802
|
|
|
* @return LoggerInterface |
803
|
|
|
*/ |
804
|
17 |
|
public function getLog() |
805
|
|
|
{ |
806
|
17 |
|
return $this->log; |
807
|
|
|
} |
808
|
|
|
|
809
|
|
|
/** |
810
|
|
|
* @param LoggerInterface $log |
811
|
|
|
* |
812
|
|
|
* @return $this |
813
|
|
|
* |
814
|
|
|
* @throws InternalWorkflowException |
815
|
|
|
*/ |
816
|
|
|
public function setLog($log) |
817
|
|
|
{ |
818
|
|
|
try { |
819
|
|
|
LogFactory::validLogger($log); |
820
|
|
|
} catch (\Exception $e) { |
821
|
|
|
$errMsg = 'Ошибка при валидации логера'; |
822
|
|
|
throw new InternalWorkflowException($errMsg, $e->getCode(), $e); |
823
|
|
|
} |
824
|
|
|
|
825
|
|
|
|
826
|
|
|
$this->log = $log; |
827
|
|
|
|
828
|
|
|
return $this; |
829
|
|
|
} |
830
|
|
|
|
831
|
|
|
|
832
|
|
|
/** |
833
|
|
|
* Get the workflow descriptor for the specified workflow name. |
834
|
|
|
* |
835
|
|
|
* @param string $workflowName The workflow name. |
836
|
|
|
* @return WorkflowDescriptor |
837
|
|
|
* |
838
|
|
|
* @throws InternalWorkflowException |
839
|
|
|
*/ |
840
|
|
|
public function getWorkflowDescriptor($workflowName) |
841
|
|
|
{ |
842
|
|
|
try { |
843
|
|
|
return $this->getConfiguration()->getWorkflow($workflowName); |
844
|
|
|
} catch (FactoryException $e) { |
845
|
|
|
$errMsg = 'Ошибка при загрузке workflow'; |
846
|
|
|
$this->getLog()->error($errMsg, ['exception' => $e]); |
847
|
|
|
throw new InternalWorkflowException($errMsg, $e->getCode(), $e); |
848
|
|
|
} |
849
|
|
|
} |
850
|
|
|
|
851
|
|
|
|
852
|
|
|
/** |
853
|
|
|
* Executes a special trigger-function using the context of the given workflow instance id. |
854
|
|
|
* Note that this method is exposed for Quartz trigger jobs, user code should never call it. |
855
|
|
|
* |
856
|
|
|
* @param integer $id The workflow instance id |
857
|
|
|
* @param integer $triggerId The id of the special trigger-function |
858
|
|
|
* |
859
|
|
|
* @throws InvalidArgumentException |
860
|
|
|
* @throws WorkflowException |
861
|
|
|
* @throws InternalWorkflowException |
862
|
|
|
* @throws \OldTown\Workflow\Exception\ArgumentNotNumericException |
863
|
|
|
*/ |
864
|
|
|
public function executeTriggerFunction($id, $triggerId) |
865
|
|
|
{ |
866
|
|
|
$store = $this->getPersistence(); |
867
|
|
|
$entry = $store->findEntry($id); |
868
|
|
|
|
869
|
|
|
if (null === $entry) { |
870
|
|
|
$errMsg = sprintf( |
871
|
|
|
'Ошибка при выполнение тригера # %s для несуществующего экземпляра workflow id# %s', |
872
|
|
|
$triggerId, |
873
|
|
|
$id |
874
|
|
|
); |
875
|
|
|
$this->getLog()->warning($errMsg); |
876
|
|
|
return; |
877
|
|
|
} |
878
|
|
|
|
879
|
|
|
$wf = $this->getConfiguration()->getWorkflow($entry->getWorkflowName()); |
880
|
|
|
|
881
|
|
|
$ps = $store->getPropertySet($id); |
882
|
|
|
$transientVars = $this->transientVarsFactory(); |
883
|
|
|
|
884
|
|
|
$this->getEngineManager()->getDataEngine()->populateTransientMap($entry, $transientVars, $wf->getRegisters(), null, $store->findCurrentSteps($id), $ps); |
885
|
|
|
|
886
|
|
|
$functionsEngine = $this->getEngineManager()->getFunctionsEngine(); |
887
|
|
|
$functionsEngine->executeFunction($wf->getTriggerFunction($triggerId), $transientVars, $ps); |
888
|
|
|
} |
889
|
|
|
|
890
|
|
|
|
891
|
|
|
/** |
892
|
|
|
* @param WorkflowDescriptor $wf |
893
|
|
|
* @param StepInterface $step |
894
|
|
|
* @param TransientVarsInterface $transientVars |
895
|
|
|
* @param PropertySetInterface $ps |
896
|
|
|
* |
897
|
|
|
* @return array |
898
|
|
|
* |
899
|
|
|
* @throws InternalWorkflowException |
900
|
|
|
* @throws WorkflowException |
901
|
|
|
* @throws \OldTown\Workflow\Exception\ArgumentNotNumericException |
902
|
|
|
*/ |
903
|
|
|
protected function getAvailableActionsForStep(WorkflowDescriptor $wf, StepInterface $step, TransientVarsInterface $transientVars, PropertySetInterface $ps) |
904
|
|
|
{ |
905
|
|
|
$l = []; |
906
|
|
|
$s = $wf->getStep($step->getStepId()); |
907
|
|
|
|
908
|
|
|
if (null === $s) { |
909
|
|
|
$errMsg = sprintf( |
910
|
|
|
'getAvailableActionsForStep вызван с не существующим id шага %s', |
911
|
|
|
$step->getStepId() |
912
|
|
|
); |
913
|
|
|
|
914
|
|
|
$this->getLog()->warning($errMsg); |
915
|
|
|
|
916
|
|
|
return $l; |
917
|
|
|
} |
918
|
|
|
|
919
|
|
|
$actions = $s->getActions(); |
920
|
|
|
|
921
|
|
|
if (null === $actions || 0 === $actions->count()) { |
922
|
|
|
return $l; |
923
|
|
|
} |
924
|
|
|
|
925
|
|
|
$conditionsEngine = $this->getEngineManager()->getConditionsEngine(); |
926
|
|
|
foreach ($actions as $action) { |
927
|
|
|
$restriction = $action->getRestriction(); |
928
|
|
|
$conditions = null; |
929
|
|
|
|
930
|
|
|
$transientVars['actionId'] = $action->getId(); |
931
|
|
|
|
932
|
|
|
|
933
|
|
|
if (null !== $restriction) { |
934
|
|
|
$conditions = $restriction->getConditionsDescriptor(); |
935
|
|
|
} |
936
|
|
|
|
937
|
|
|
$f = $conditionsEngine->passesConditionsByDescriptor($wf->getGlobalConditions(), $transientVars, $ps, $s->getId()) |
938
|
|
|
&& $conditionsEngine->passesConditionsByDescriptor($conditions, $transientVars, $ps, $s->getId()); |
939
|
|
|
if ($f) { |
940
|
|
|
$l[] = $action->getId(); |
941
|
|
|
} |
942
|
|
|
} |
943
|
|
|
|
944
|
|
|
return $l; |
945
|
|
|
} |
946
|
|
|
|
947
|
|
|
/** |
948
|
|
|
* @param ConfigurationInterface $configuration |
949
|
|
|
* |
950
|
|
|
* @return $this |
951
|
|
|
*/ |
952
|
19 |
|
public function setConfiguration(ConfigurationInterface $configuration) |
953
|
|
|
{ |
954
|
19 |
|
$this->configuration = $configuration; |
955
|
|
|
|
956
|
19 |
|
return $this; |
957
|
|
|
} |
958
|
|
|
|
959
|
|
|
/** |
960
|
|
|
* Возвращает состояние для текущего экземпляра workflow |
961
|
|
|
* |
962
|
|
|
* @param integer $id id экземпляра workflow |
963
|
|
|
* @return integer id текущего состояния |
964
|
|
|
* |
965
|
|
|
* @throws InternalWorkflowException |
966
|
|
|
*/ |
967
|
|
|
public function getEntryState($id) |
968
|
|
|
{ |
969
|
|
|
try { |
970
|
|
|
$store = $this->getPersistence(); |
971
|
|
|
|
972
|
|
|
return $store->findEntry($id)->getState(); |
973
|
|
|
} catch (StoreException $e) { |
974
|
|
|
$errMsg = sprintf( |
975
|
|
|
'Ошибка при получение состояния экземпляра workflow c id# %s', |
976
|
|
|
$id |
977
|
|
|
); |
978
|
|
|
$this->getLog()->error($errMsg, [$e]); |
979
|
|
|
} |
980
|
|
|
|
981
|
|
|
return WorkflowEntryInterface::UNKNOWN; |
982
|
|
|
} |
983
|
|
|
|
984
|
|
|
|
985
|
|
|
/** |
986
|
|
|
* Настройки хранилища |
987
|
|
|
* |
988
|
|
|
* @return array |
989
|
|
|
* |
990
|
|
|
* @throws InternalWorkflowException |
991
|
|
|
*/ |
992
|
|
|
public function getPersistenceProperties() |
993
|
|
|
{ |
994
|
|
|
return $this->getConfiguration()->getPersistenceArgs(); |
995
|
|
|
} |
996
|
|
|
|
997
|
|
|
|
998
|
|
|
/** |
999
|
|
|
* Get the PropertySet for the specified workflow instance id. |
1000
|
|
|
* @param integer $id The workflow instance id. |
1001
|
|
|
* |
1002
|
|
|
* @return PropertySetInterface |
1003
|
|
|
* @throws InternalWorkflowException |
1004
|
|
|
*/ |
1005
|
|
|
public function getPropertySet($id) |
1006
|
|
|
{ |
1007
|
|
|
$ps = null; |
1008
|
|
|
|
1009
|
|
|
try { |
1010
|
|
|
$ps = $this->getPersistence()->getPropertySet($id); |
1011
|
|
|
} catch (StoreException $e) { |
1012
|
|
|
$errMsg = sprintf( |
1013
|
|
|
'Ошибка при получение PropertySet для экземпляра workflow c id# %s', |
1014
|
|
|
$id |
1015
|
|
|
); |
1016
|
|
|
$this->getLog()->error($errMsg, [$e]); |
1017
|
|
|
} |
1018
|
|
|
|
1019
|
|
|
return $ps; |
1020
|
|
|
} |
1021
|
|
|
|
1022
|
|
|
/** |
1023
|
|
|
* @return string[] |
1024
|
|
|
* |
1025
|
|
|
* @throws InternalWorkflowException |
1026
|
|
|
*/ |
1027
|
|
|
public function getWorkflowNames() |
1028
|
|
|
{ |
1029
|
|
|
try { |
1030
|
|
|
return $this->getConfiguration()->getWorkflowNames(); |
1031
|
|
|
} catch (FactoryException $e) { |
1032
|
|
|
$errMsg = 'Ошибка при получение имен workflow'; |
1033
|
|
|
$this->getLog()->error($errMsg, [$e]); |
1034
|
|
|
} |
1035
|
|
|
|
1036
|
|
|
return []; |
1037
|
|
|
} |
1038
|
|
|
|
1039
|
|
|
/** |
1040
|
|
|
* @param TypeResolverInterface $typeResolver |
1041
|
|
|
* |
1042
|
|
|
* @return $this |
1043
|
|
|
*/ |
1044
|
|
|
public function setTypeResolver(TypeResolverInterface $typeResolver) |
1045
|
|
|
{ |
1046
|
|
|
$this->typeResolver = $typeResolver; |
1047
|
|
|
|
1048
|
|
|
return $this; |
1049
|
|
|
} |
1050
|
|
|
|
1051
|
|
|
|
1052
|
|
|
/** |
1053
|
|
|
* Get a collection (Strings) of currently defined permissions for the specified workflow instance. |
1054
|
|
|
* @param integer $id id the workflow instance id. |
1055
|
|
|
* @param TransientVarsInterface $inputs inputs The inputs to the workflow instance. |
1056
|
|
|
* |
1057
|
|
|
* @return array A List of permissions specified currently (a permission is a string name). |
1058
|
|
|
* |
1059
|
|
|
*/ |
1060
|
|
|
public function getSecurityPermissions($id, TransientVarsInterface $inputs = null) |
1061
|
|
|
{ |
1062
|
|
|
try { |
1063
|
|
|
$store = $this->getPersistence(); |
1064
|
|
|
$entry = $store->findEntry($id); |
1065
|
|
|
$wf = $this->getConfiguration()->getWorkflow($entry->getWorkflowName()); |
1066
|
|
|
|
1067
|
|
|
$ps = $store->getPropertySet($id); |
1068
|
|
|
|
1069
|
|
|
if (null === $inputs) { |
1070
|
|
|
$inputs = $this->transientVarsFactory(); |
1071
|
|
|
} |
1072
|
|
|
$transientVars = $inputs; |
1073
|
|
|
|
1074
|
|
|
$currentSteps = $store->findCurrentSteps($id); |
1075
|
|
|
|
1076
|
|
|
$engineManager = $this->getEngineManager(); |
1077
|
|
|
try { |
1078
|
|
|
$engineManager->getDataEngine()->populateTransientMap($entry, $transientVars, $wf->getRegisters(), null, $currentSteps, $ps); |
1079
|
|
|
} catch (\Exception $e) { |
1080
|
|
|
$errMsg = sprintf( |
1081
|
|
|
'Внутреннея ошибка: %s', |
1082
|
|
|
$e->getMessage() |
1083
|
|
|
); |
1084
|
|
|
throw new InternalWorkflowException($errMsg, $e->getCode(), $e); |
1085
|
|
|
} |
1086
|
|
|
|
1087
|
|
|
|
1088
|
|
|
$s = []; |
1089
|
|
|
|
1090
|
|
|
$conditionsEngine = $engineManager->getConditionsEngine(); |
1091
|
|
|
foreach ($currentSteps as $step) { |
1092
|
|
|
$stepId = $step->getStepId(); |
1093
|
|
|
|
1094
|
|
|
$xmlStep = $wf->getStep($stepId); |
1095
|
|
|
|
1096
|
|
|
$securities = $xmlStep->getPermissions(); |
1097
|
|
|
|
1098
|
|
|
foreach ($securities as $security) { |
1099
|
|
|
if (!$security instanceof PermissionDescriptor) { |
1100
|
|
|
$errMsg = 'Invalid PermissionDescriptor'; |
1101
|
|
|
throw new InternalWorkflowException($errMsg); |
1102
|
|
|
} |
1103
|
|
|
$conditionsDescriptor = $security->getRestriction()->getConditionsDescriptor(); |
1104
|
|
|
if (null !== $security->getRestriction() && $conditionsEngine->passesConditionsByDescriptor($conditionsDescriptor, $transientVars, $ps, $xmlStep->getId())) { |
1105
|
|
|
$s[$security->getName()] = $security->getName(); |
1106
|
|
|
} |
1107
|
|
|
} |
1108
|
|
|
} |
1109
|
|
|
|
1110
|
|
|
return $s; |
1111
|
|
|
} catch (\Exception $e) { |
1112
|
|
|
$errMsg = sprintf( |
1113
|
|
|
'Ошибка при получение информации о правах доступа для экземпляра workflow c id# %s', |
1114
|
|
|
$id |
1115
|
|
|
); |
1116
|
|
|
$this->getLog()->error($errMsg, [$e]); |
1117
|
|
|
} |
1118
|
|
|
|
1119
|
|
|
return []; |
1120
|
|
|
} |
1121
|
|
|
|
1122
|
|
|
|
1123
|
|
|
/** |
1124
|
|
|
* Get the name of the specified workflow instance. |
1125
|
|
|
* |
1126
|
|
|
* @param integer $id the workflow instance id. |
1127
|
|
|
* |
1128
|
|
|
* @return string |
1129
|
|
|
* |
1130
|
|
|
* @throws InternalWorkflowException |
1131
|
|
|
*/ |
1132
|
|
|
public function getWorkflowName($id) |
1133
|
|
|
{ |
1134
|
|
|
try { |
1135
|
|
|
$store = $this->getPersistence(); |
1136
|
|
|
$entry = $store->findEntry($id); |
1137
|
|
|
|
1138
|
|
|
if (null !== $entry) { |
1139
|
|
|
return $entry->getWorkflowName(); |
1140
|
|
|
} |
1141
|
|
|
} catch (FactoryException $e) { |
1142
|
|
|
$errMsg = sprintf( |
1143
|
|
|
'Ошибка при получение имен workflow для инстанса с id # %s', |
1144
|
|
|
$id |
1145
|
|
|
); |
1146
|
|
|
$this->getLog()->error($errMsg, [$e]); |
1147
|
|
|
} |
1148
|
|
|
|
1149
|
|
|
return null; |
1150
|
|
|
} |
1151
|
|
|
|
1152
|
|
|
/** |
1153
|
|
|
* Удаляет workflow |
1154
|
|
|
* |
1155
|
|
|
* @param string $workflowName |
1156
|
|
|
* |
1157
|
|
|
* @return bool |
1158
|
|
|
* |
1159
|
|
|
* @throws InternalWorkflowException |
1160
|
|
|
*/ |
1161
|
|
|
public function removeWorkflowDescriptor($workflowName) |
1162
|
|
|
{ |
1163
|
|
|
return $this->getConfiguration()->removeWorkflow($workflowName); |
1164
|
|
|
} |
1165
|
|
|
|
1166
|
|
|
/** |
1167
|
|
|
* @param $workflowName |
1168
|
|
|
* @param WorkflowDescriptor $descriptor |
1169
|
|
|
* @param $replace |
1170
|
|
|
* |
1171
|
|
|
* @return bool |
1172
|
|
|
* |
1173
|
|
|
* @throws InternalWorkflowException |
1174
|
|
|
*/ |
1175
|
|
|
public function saveWorkflowDescriptor($workflowName, WorkflowDescriptor $descriptor, $replace) |
1176
|
|
|
{ |
1177
|
|
|
$success = $this->getConfiguration()->saveWorkflow($workflowName, $descriptor, $replace); |
1178
|
|
|
|
1179
|
|
|
return $success; |
1180
|
|
|
} |
1181
|
|
|
|
1182
|
|
|
|
1183
|
|
|
/** |
1184
|
|
|
* Query the workflow store for matching instances |
1185
|
|
|
* |
1186
|
|
|
* @param WorkflowExpressionQuery $query |
1187
|
|
|
* |
1188
|
|
|
* @return array |
1189
|
|
|
* |
1190
|
|
|
* @throws InternalWorkflowException |
1191
|
|
|
*/ |
1192
|
|
|
public function query(WorkflowExpressionQuery $query) |
1193
|
|
|
{ |
1194
|
|
|
return $this->getPersistence()->query($query); |
1195
|
|
|
} |
1196
|
|
|
|
1197
|
|
|
/** |
1198
|
|
|
* @return string |
1199
|
|
|
*/ |
1200
|
19 |
|
public function getDefaultTypeResolverClass() |
1201
|
|
|
{ |
1202
|
19 |
|
return $this->defaultTypeResolverClass; |
1203
|
|
|
} |
1204
|
|
|
|
1205
|
|
|
/** |
1206
|
|
|
* @param string $defaultTypeResolverClass |
1207
|
|
|
* |
1208
|
|
|
* @return $this |
1209
|
|
|
*/ |
1210
|
|
|
public function setDefaultTypeResolverClass($defaultTypeResolverClass) |
1211
|
|
|
{ |
1212
|
|
|
$this->defaultTypeResolverClass = (string)$defaultTypeResolverClass; |
1213
|
|
|
|
1214
|
|
|
return $this; |
1215
|
|
|
} |
1216
|
|
|
|
1217
|
|
|
|
1218
|
|
|
/** |
1219
|
|
|
* @return WorkflowContextInterface |
1220
|
|
|
*/ |
1221
|
19 |
|
public function getContext() |
1222
|
|
|
{ |
1223
|
19 |
|
return $this->context; |
1224
|
|
|
} |
1225
|
|
|
} |
1226
|
|
|
|
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.