1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* @link https://github.com/old-town/old-town-workflow |
4
|
|
|
* @author Malofeykin Andrey <[email protected]> |
5
|
|
|
*/ |
6
|
|
|
namespace OldTown\Workflow\Loader; |
7
|
|
|
|
8
|
|
|
use DOMElement; |
9
|
|
|
use OldTown\Workflow\Exception\ArgumentNotNumericException; |
10
|
|
|
use OldTown\Workflow\Exception\InvalidDescriptorException; |
11
|
|
|
use OldTown\Workflow\Exception\InvalidParsingWorkflowException; |
12
|
|
|
use OldTown\Workflow\Exception\InvalidWorkflowDescriptorException; |
13
|
|
|
use OldTown\Workflow\Exception\InvalidWriteWorkflowException; |
14
|
|
|
use SplObjectStorage; |
15
|
|
|
use DOMDocument; |
16
|
|
|
|
17
|
|
|
/** |
18
|
|
|
* Class ConditionDescriptor |
19
|
|
|
* |
20
|
|
|
* @package OldTown\Workflow\Loader |
21
|
|
|
*/ |
22
|
|
|
class StepDescriptor extends AbstractDescriptor |
23
|
|
|
implements |
24
|
|
|
Traits\NameInterface, |
25
|
|
|
ValidateDescriptorInterface, |
26
|
|
|
WriteXmlInterface |
27
|
|
|
{ |
28
|
|
|
use Traits\NameTrait, Traits\IdTrait; |
29
|
|
|
|
30
|
|
|
/** |
31
|
|
|
* @var ActionDescriptor[]|SplObjectStorage |
32
|
|
|
*/ |
33
|
|
|
protected $actions; |
34
|
|
|
|
35
|
|
|
/** |
36
|
|
|
* Список id дейсвтия являющихся общими для всего workflow |
37
|
|
|
* |
38
|
|
|
* @var array |
39
|
|
|
*/ |
40
|
|
|
protected $commonActions = []; |
41
|
|
|
|
42
|
|
|
/** |
43
|
|
|
* @var FunctionDescriptor[]|SplObjectStorage |
44
|
|
|
*/ |
45
|
|
|
protected $postFunctions; |
46
|
|
|
|
47
|
|
|
/** |
48
|
|
|
* @var FunctionDescriptor[]|SplObjectStorage |
49
|
|
|
*/ |
50
|
|
|
protected $preFunctions; |
51
|
|
|
|
52
|
|
|
/** |
53
|
|
|
* @var PermissionDescriptor[]|SplObjectStorage |
54
|
|
|
*/ |
55
|
|
|
protected $permissions; |
56
|
|
|
|
57
|
|
|
/** |
58
|
|
|
* @var array |
59
|
|
|
*/ |
60
|
|
|
protected $metaAttributes = []; |
61
|
|
|
|
62
|
|
|
/** |
63
|
|
|
* Определяет есть ли действия для данного шага workflow |
64
|
|
|
* |
65
|
|
|
* @var bool |
66
|
|
|
*/ |
67
|
|
|
protected $hasActions = false; |
68
|
|
|
|
69
|
|
|
|
70
|
|
|
/** |
71
|
|
|
* @param DOMElement $element |
72
|
|
|
* @param AbstractDescriptor $parent |
73
|
|
|
* |
74
|
|
|
* @throws InvalidParsingWorkflowException |
75
|
|
|
*/ |
76
|
69 |
|
public function __construct(DOMElement $element = null, AbstractDescriptor $parent = null) |
77
|
|
|
{ |
78
|
69 |
|
$this->preFunctions = new SplObjectStorage(); |
79
|
69 |
|
$this->postFunctions = new SplObjectStorage(); |
80
|
69 |
|
$this->actions = new SplObjectStorage(); |
81
|
69 |
|
$this->permissions = new SplObjectStorage(); |
82
|
|
|
|
83
|
69 |
|
parent::__construct($element); |
84
|
|
|
|
85
|
69 |
|
if (null !== $parent) { |
86
|
64 |
|
$this->setParent($parent); |
87
|
64 |
|
} |
88
|
69 |
|
if (null !== $element) { |
89
|
65 |
|
$this->init($element); |
90
|
64 |
|
} |
91
|
68 |
|
} |
92
|
|
|
|
93
|
|
|
/** |
94
|
|
|
* @param DOMElement $step |
95
|
|
|
* |
96
|
|
|
* @return void |
97
|
|
|
* @throws InvalidParsingWorkflowException |
98
|
|
|
*/ |
99
|
65 |
|
protected function init(DOMElement $step) |
100
|
|
|
{ |
101
|
65 |
|
$this->parseId($step); |
102
|
65 |
|
$this->parseName($step); |
103
|
|
|
|
104
|
|
|
|
105
|
65 |
|
$metaElements = XmlUtil::getChildElements($step, 'meta'); |
106
|
65 |
|
$metaAttributes = []; |
107
|
65 |
View Code Duplication |
foreach ($metaElements as $meta) { |
|
|
|
|
108
|
1 |
|
$value = XmlUtil::getText($meta); |
109
|
1 |
|
$name = XmlUtil::getRequiredAttributeValue($meta, 'name'); |
110
|
|
|
|
111
|
1 |
|
$metaAttributes[$name] = $value; |
112
|
65 |
|
} |
113
|
65 |
|
$this->setMetaAttributes($metaAttributes); |
114
|
|
|
|
115
|
|
|
// set up pre-functions -- OPTIONAL |
116
|
65 |
|
$pre = XMLUtil::getChildElement($step, 'pre-functions'); |
117
|
65 |
View Code Duplication |
if (null !== $pre) { |
|
|
|
|
118
|
1 |
|
$preFunctions = XMLUtil::getChildElements($pre, 'function'); |
119
|
1 |
|
foreach ($preFunctions as $preFunction) { |
120
|
1 |
|
$functionDescriptor = DescriptorFactory::getFactory()->createFunctionDescriptor($preFunction); |
121
|
1 |
|
$functionDescriptor->setParent($this); |
122
|
1 |
|
$this->preFunctions->attach($functionDescriptor); |
123
|
1 |
|
} |
124
|
1 |
|
} |
125
|
|
|
|
126
|
|
|
// set up permissions - OPTIONAL |
127
|
65 |
|
$p = XMLUtil::getChildElement($step, 'external-permissions'); |
128
|
65 |
View Code Duplication |
if (null !== $p) { |
|
|
|
|
129
|
12 |
|
$permissions = XMLUtil::getChildElements($p, 'permission'); |
130
|
12 |
|
foreach ($permissions as $permission) { |
131
|
12 |
|
$permissionDescriptor = DescriptorFactory::getFactory()->createPermissionDescriptor($permission); |
132
|
12 |
|
$permissionDescriptor->setParent($this); |
133
|
12 |
|
$this->permissions->attach($permissionDescriptor); |
134
|
12 |
|
} |
135
|
12 |
|
} |
136
|
|
|
|
137
|
|
|
// set up actions - OPTIONAL |
138
|
65 |
|
$a = XMLUtil::getChildElement($step, 'actions'); |
139
|
65 |
|
if (null !== $a) { |
140
|
65 |
|
$this->hasActions = true; |
141
|
|
|
|
142
|
65 |
|
$actions = XMLUtil::getChildElements($a, 'action'); |
143
|
65 |
|
foreach ($actions as $action) { |
144
|
55 |
|
$actionDescriptor = DescriptorFactory::getFactory()->createActionDescriptor($action); |
145
|
55 |
|
$actionDescriptor->setParent($this); |
146
|
55 |
|
$this->actions->attach($actionDescriptor); |
147
|
65 |
|
} |
148
|
|
|
|
149
|
65 |
|
$commonActions = XMLUtil::getChildElements($a, 'common-action'); |
150
|
|
|
/** @var WorkflowDescriptor $workflowDescriptor */ |
151
|
65 |
|
$workflowDescriptor = $this->getParent(); |
152
|
65 |
|
if (!$workflowDescriptor instanceof WorkflowDescriptor) { |
153
|
1 |
|
$errMsg = 'Отсутствует Workflow Descriptor'; |
154
|
1 |
|
throw new InvalidParsingWorkflowException($errMsg); |
155
|
|
|
} |
156
|
64 |
|
foreach ($commonActions as $commonAction) { |
157
|
12 |
|
$actionId = XmlUtil::getRequiredAttributeValue($commonAction, 'id'); |
158
|
12 |
|
$commonActionReference = $workflowDescriptor->getCommonAction($actionId); |
159
|
|
|
|
160
|
12 |
|
if ($commonActionReference !== null) { |
161
|
6 |
|
$this->actions->attach($commonActionReference); |
162
|
6 |
|
} |
163
|
12 |
|
$this->commonActions[$actionId] = $actionId; |
164
|
64 |
|
} |
165
|
64 |
|
} |
166
|
|
|
|
167
|
|
|
// set up post-functions - OPTIONAL |
168
|
|
|
|
169
|
|
|
// set up post-functions - OPTIONAL |
170
|
64 |
|
$post = XMLUtil::getChildElement($step, 'post-functions'); |
171
|
64 |
View Code Duplication |
if (null !== $post) { |
|
|
|
|
172
|
2 |
|
$postFunctions = XMLUtil::getChildElements($post, 'function'); |
173
|
2 |
|
foreach ($postFunctions as $postFunction) { |
174
|
2 |
|
$functionDescriptor = DescriptorFactory::getFactory()->createFunctionDescriptor($postFunction); |
175
|
2 |
|
$functionDescriptor->setParent($this); |
176
|
2 |
|
$this->postFunctions->attach($functionDescriptor); |
177
|
2 |
|
} |
178
|
2 |
|
} |
179
|
64 |
|
} |
180
|
|
|
|
181
|
|
|
/** |
182
|
|
|
* Возвращает список id дейсвтий являющихся общим для всего workflow |
183
|
|
|
* |
184
|
|
|
* @return array |
185
|
|
|
*/ |
186
|
22 |
|
public function getCommonActions() |
187
|
|
|
{ |
188
|
22 |
|
return $this->commonActions; |
189
|
|
|
} |
190
|
|
|
|
191
|
|
|
|
192
|
|
|
/** |
193
|
|
|
* @return FunctionDescriptor[]|SplObjectStorage |
194
|
|
|
*/ |
195
|
26 |
|
public function getPostFunctions() |
196
|
|
|
{ |
197
|
26 |
|
return $this->postFunctions; |
198
|
|
|
} |
199
|
|
|
|
200
|
|
|
/** |
201
|
|
|
* @return FunctionDescriptor[]|SplObjectStorage |
202
|
|
|
*/ |
203
|
36 |
|
public function getPreFunctions() |
204
|
|
|
{ |
205
|
36 |
|
return $this->preFunctions; |
206
|
|
|
} |
207
|
|
|
|
208
|
|
|
/** |
209
|
|
|
* @return array |
210
|
|
|
*/ |
211
|
14 |
|
public function getMetaAttributes() |
212
|
|
|
{ |
213
|
14 |
|
return $this->metaAttributes; |
214
|
|
|
} |
215
|
|
|
|
216
|
|
|
/** |
217
|
|
|
* @param array $metaAttributes |
218
|
|
|
* |
219
|
|
|
* @return $this |
220
|
|
|
*/ |
221
|
65 |
|
public function setMetaAttributes(array $metaAttributes = []) |
222
|
|
|
{ |
223
|
65 |
|
$this->metaAttributes = $metaAttributes; |
224
|
|
|
|
225
|
65 |
|
return $this; |
226
|
|
|
} |
227
|
|
|
|
228
|
|
|
/** |
229
|
|
|
* @return PermissionDescriptor[]|SplObjectStorage |
230
|
|
|
*/ |
231
|
20 |
|
public function getPermissions() |
232
|
|
|
{ |
233
|
20 |
|
return $this->permissions; |
234
|
|
|
} |
235
|
|
|
|
236
|
|
|
/** |
237
|
|
|
* @return ActionDescriptor[]|SplObjectStorage |
238
|
|
|
*/ |
239
|
47 |
|
public function getActions() |
240
|
|
|
{ |
241
|
47 |
|
return $this->actions; |
242
|
|
|
} |
243
|
|
|
|
244
|
|
|
/** |
245
|
|
|
* @param integer $id |
246
|
|
|
* |
247
|
|
|
* @return ActionDescriptor|null |
248
|
|
|
*/ |
249
|
6 |
|
public function getAction($id) |
250
|
|
|
{ |
251
|
6 |
|
$id = (integer)$id; |
252
|
6 |
|
foreach ($this->actions as $action) { |
253
|
5 |
|
if ($id === $action->getId()) { |
254
|
4 |
|
return $action; |
255
|
|
|
} |
256
|
2 |
|
} |
257
|
2 |
|
return null; |
258
|
|
|
} |
259
|
|
|
|
260
|
|
|
/** |
261
|
|
|
* Удаляет действия для данного шага |
262
|
|
|
* |
263
|
|
|
* @return $this |
264
|
|
|
*/ |
265
|
1 |
|
public function removeActions() |
266
|
|
|
{ |
267
|
1 |
|
$this->commonActions = []; |
268
|
1 |
|
$this->actions = new SplObjectStorage(); |
269
|
1 |
|
$this->hasActions = false; |
270
|
1 |
|
return $this; |
271
|
|
|
} |
272
|
|
|
|
273
|
|
|
/** |
274
|
|
|
* @param integer $join |
275
|
|
|
* @return boolean |
276
|
|
|
* |
277
|
|
|
* @throws ArgumentNotNumericException |
278
|
|
|
*/ |
279
|
4 |
|
public function resultsInJoin($join) |
280
|
|
|
{ |
281
|
4 |
|
if (!is_numeric($join)) { |
282
|
1 |
|
$errMsg = 'Аргумент должен быть числом'; |
283
|
1 |
|
throw new ArgumentNotNumericException($errMsg); |
284
|
|
|
} |
285
|
|
|
|
286
|
3 |
|
$join = (integer)$join; |
287
|
|
|
|
288
|
3 |
|
$actions = $this->getActions(); |
289
|
|
|
|
290
|
3 |
|
foreach ($actions as $actionDescriptor) { |
291
|
3 |
|
if ($join === $actionDescriptor->getUnconditionalResult()->getJoin()) { |
292
|
1 |
|
return true; |
293
|
|
|
} |
294
|
|
|
|
295
|
2 |
|
$results = $actionDescriptor->getConditionalResults(); |
296
|
2 |
|
foreach ($results as $resultDescriptor) { |
297
|
2 |
|
if ($join === $resultDescriptor->getJoin()) { |
298
|
1 |
|
return true; |
299
|
|
|
} |
300
|
1 |
|
} |
301
|
1 |
|
} |
302
|
|
|
|
303
|
1 |
|
return false; |
304
|
|
|
} |
305
|
|
|
|
306
|
|
|
/** |
307
|
|
|
* Возвращает флаг определяющий есть ли действия у данного шага |
308
|
|
|
* |
309
|
|
|
* @return boolean |
310
|
|
|
*/ |
311
|
1 |
|
public function getFlagHasActions() |
312
|
|
|
{ |
313
|
1 |
|
return $this->hasActions; |
314
|
|
|
} |
315
|
|
|
|
316
|
|
|
|
317
|
|
|
/** |
318
|
|
|
* Валидация дескриптора |
319
|
|
|
* |
320
|
|
|
* @return void |
321
|
|
|
* @throws InvalidWorkflowDescriptorException |
322
|
|
|
*/ |
323
|
20 |
|
public function validate() |
324
|
|
|
{ |
325
|
20 |
|
$commonActions = $this->getCommonActions(); |
326
|
20 |
|
$actions = $this->getActions(); |
327
|
20 |
|
$hasActions = $this->hasActions; |
328
|
|
|
|
329
|
20 |
|
if ($hasActions && 0 === count($commonActions) && 0 === $actions->count()) { |
330
|
1 |
|
$stepName = (string)$this->getName(); |
331
|
1 |
|
$errMsg = sprintf('Шаг %s должен содержать одни действие или одно общее действие', $stepName); |
332
|
1 |
|
throw new InvalidWorkflowDescriptorException($errMsg); |
333
|
|
|
} |
334
|
|
|
|
335
|
19 |
|
if (-1 === $this->getId()) { |
336
|
1 |
|
$errMsg = 'В качестве id шага нельзя использовать -1, так как это зарезериврованное значение'; |
337
|
1 |
|
throw new InvalidWorkflowDescriptorException($errMsg); |
338
|
|
|
} |
339
|
|
|
|
340
|
18 |
|
$preFunctions = $this->getPreFunctions(); |
341
|
18 |
|
$postFunctions = $this->getPostFunctions(); |
342
|
18 |
|
$actions = $this->getActions(); |
343
|
18 |
|
$permissions = $this->getPermissions(); |
344
|
|
|
|
345
|
18 |
|
ValidationHelper::validate($preFunctions); |
346
|
18 |
|
ValidationHelper::validate($postFunctions); |
347
|
18 |
|
ValidationHelper::validate($actions); |
348
|
18 |
|
ValidationHelper::validate($permissions); |
349
|
|
|
|
350
|
18 |
|
$workflowDescriptor = $this->getParent(); |
351
|
18 |
|
if (!$workflowDescriptor instanceof WorkflowDescriptor) { |
352
|
1 |
|
$errMsg = sprintf('Родительский элемент для шага должен реализовывать %s', WorkflowDescriptor::class); |
353
|
1 |
|
throw new InvalidWorkflowDescriptorException($errMsg); |
354
|
|
|
} |
355
|
17 |
|
foreach ($commonActions as $actionId) { |
356
|
|
|
try { |
357
|
4 |
|
$commonActionReference = $workflowDescriptor->getCommonAction($actionId); |
358
|
|
|
|
359
|
4 |
|
if (null === $commonActionReference) { |
360
|
1 |
|
$stepName = (string)$this->getName(); |
361
|
1 |
|
$errMsg = sprintf('Common-action %s указанное для шага %s не существует', $actionId, $stepName); |
362
|
1 |
|
throw new InvalidWorkflowDescriptorException($errMsg); |
363
|
|
|
} |
364
|
4 |
|
} catch (\Exception $e) { |
365
|
1 |
|
$actionIdStr = (string)$actionId; |
366
|
1 |
|
$errMsg = sprintf('Некорректный id для common-action: id %s', $actionIdStr); |
367
|
1 |
|
throw new InvalidWorkflowDescriptorException($errMsg, $e->getCode(), $e); |
368
|
|
|
} |
369
|
17 |
|
} |
370
|
16 |
|
} |
371
|
|
|
|
372
|
|
|
|
373
|
|
|
/** |
374
|
|
|
* Создает DOMElement - эквивалентный состоянию дескриптора |
375
|
|
|
* |
376
|
|
|
* @param DOMDocument $dom |
377
|
|
|
* |
378
|
|
|
* @return DOMElement|null |
379
|
|
|
* @throws InvalidDescriptorException |
380
|
|
|
* @throws InvalidWriteWorkflowException |
381
|
|
|
*/ |
382
|
16 |
|
public function writeXml(DOMDocument $dom = null) |
383
|
|
|
{ |
384
|
16 |
|
if (null === $dom) { |
385
|
1 |
|
$errMsg = 'Не передан DOMDocument'; |
386
|
1 |
|
throw new InvalidWriteWorkflowException($errMsg); |
387
|
|
|
} |
388
|
15 |
|
$descriptor = $dom->createElement('step'); |
389
|
|
|
|
390
|
15 |
|
if (!$this->hasId()) { |
391
|
1 |
|
$errMsg = 'Отсутствует атрибут id'; |
392
|
1 |
|
throw new InvalidDescriptorException($errMsg); |
393
|
|
|
} |
394
|
14 |
|
$id = $this->getId(); |
395
|
14 |
|
$descriptor->setAttribute('id', $id); |
396
|
|
|
|
397
|
14 |
|
$name = (string)$this->getName(); |
398
|
14 |
|
$name = trim($name); |
399
|
14 |
|
if (strlen($name) > 0) { |
400
|
14 |
|
$nameEncode = XmlUtil::encode($name); |
401
|
14 |
|
$descriptor->setAttribute('name', $nameEncode); |
402
|
14 |
|
} |
403
|
|
|
|
404
|
|
|
|
405
|
14 |
|
$metaAttributes = $this->getMetaAttributes(); |
406
|
14 |
|
$baseMeta = $dom->createElement('meta'); |
407
|
14 |
View Code Duplication |
foreach ($metaAttributes as $metaAttributeName => $metaAttributeValue) { |
|
|
|
|
408
|
1 |
|
$metaAttributeNameEncode = XmlUtil::encode($metaAttributeName); |
409
|
1 |
|
$metaAttributeValueEnEncode = XmlUtil::encode($metaAttributeValue); |
410
|
|
|
|
411
|
1 |
|
$metaElement = clone $baseMeta; |
412
|
1 |
|
$metaElement->setAttribute('name', $metaAttributeNameEncode); |
413
|
1 |
|
$metaValueElement = $dom->createTextNode($metaAttributeValueEnEncode); |
414
|
1 |
|
$metaElement->appendChild($metaValueElement); |
415
|
|
|
|
416
|
1 |
|
$descriptor->appendChild($metaElement); |
417
|
14 |
|
} |
418
|
|
|
|
419
|
|
|
|
420
|
14 |
|
$preFunctions = $this->getPreFunctions(); |
421
|
14 |
View Code Duplication |
if ($preFunctions->count() > 0) { |
|
|
|
|
422
|
1 |
|
$preFunctionsElement = $dom->createElement('pre-functions'); |
423
|
1 |
|
foreach ($preFunctions as $function) { |
424
|
1 |
|
$functionElement = $function->writeXml($dom); |
425
|
1 |
|
$preFunctionsElement->appendChild($functionElement); |
426
|
1 |
|
} |
427
|
|
|
|
428
|
1 |
|
$descriptor->appendChild($preFunctionsElement); |
429
|
1 |
|
} |
430
|
|
|
|
431
|
|
|
|
432
|
14 |
|
$permissions = $this->getPermissions(); |
433
|
14 |
|
if ($permissions->count() > 0) { |
434
|
12 |
|
$permissionsElement = $dom->createElement('external-permissions'); |
435
|
12 |
|
foreach ($permissions as $permission) { |
436
|
12 |
|
$permissionElement = $permission->writeXml($dom); |
437
|
12 |
|
$permissionsElement->appendChild($permissionElement); |
438
|
12 |
|
} |
439
|
|
|
|
440
|
12 |
|
$descriptor->appendChild($permissionsElement); |
441
|
12 |
|
} |
442
|
|
|
|
443
|
14 |
|
$actions = $this->getActions(); |
444
|
14 |
|
$commonActions = $this->getCommonActions(); |
445
|
|
|
|
446
|
14 |
|
if ($actions->count() > 0 || count($commonActions) > 0) { |
447
|
14 |
|
$actionsElement = $dom->createElement('actions'); |
448
|
|
|
|
449
|
14 |
|
$commonActionElementBase = $dom->createElement('common-action'); |
450
|
14 |
|
foreach ($commonActions as $commonActionId) { |
451
|
3 |
|
$commonActionElement = clone $commonActionElementBase; |
452
|
3 |
|
$commonActionElement->setAttribute('id', $commonActionId); |
453
|
|
|
|
454
|
3 |
|
$actionsElement->appendChild($commonActionElement); |
455
|
14 |
|
} |
456
|
|
|
|
457
|
14 |
|
foreach ($actions as $action) { |
458
|
14 |
|
if (!$action->isCommon()) { |
459
|
13 |
|
$actionElement = $action->writeXml($dom); |
460
|
13 |
|
$actionsElement->appendChild($actionElement); |
461
|
13 |
|
} |
462
|
14 |
|
} |
463
|
|
|
|
464
|
14 |
|
$descriptor->appendChild($actionsElement); |
465
|
14 |
|
} |
466
|
|
|
|
467
|
14 |
|
$postFunctions = $this->getPostFunctions(); |
468
|
14 |
View Code Duplication |
if ($postFunctions->count() > 0) { |
|
|
|
|
469
|
1 |
|
$postFunctionsElement = $dom->createElement('post-functions'); |
470
|
1 |
|
foreach ($postFunctions as $function) { |
471
|
1 |
|
$functionElement = $function->writeXml($dom); |
472
|
1 |
|
$postFunctionsElement->appendChild($functionElement); |
473
|
1 |
|
} |
474
|
|
|
|
475
|
1 |
|
$descriptor->appendChild($postFunctionsElement); |
476
|
1 |
|
} |
477
|
|
|
|
478
|
|
|
|
479
|
14 |
|
return $descriptor; |
480
|
|
|
} |
481
|
|
|
} |
482
|
|
|
|
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.