GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — master ( 138d30...5011e7 )
by Андрей
07:49
created

WorkflowDescriptor::getStep()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 15
Code Lines 9

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 15
rs 9.2
cc 4
eloc 9
nc 4
nop 1
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 OldTown\Workflow\Exception\ArgumentNotNumericException;
9
use OldTown\Workflow\Exception\InternalWorkflowException;
10
use OldTown\Workflow\Exception\InvalidArgumentException;
11
use OldTown\Workflow\Exception\InvalidDescriptorException;
12
use OldTown\Workflow\Exception\InvalidDtdSchemaException;
13
use OldTown\Workflow\Exception\InvalidWorkflowDescriptorException;
14
use DOMElement;
15
use OldTown\Workflow\Exception\InvalidWriteWorkflowException;
16
use OldTown\Workflow\Exception\RuntimeException;
17
use SplObjectStorage;
18
use DOMDocument;
19
use DOMImplementation;
20
use \LibXMLError;
21
22
/**
23
 * Interface WorkflowDescriptor
24
 *
25
 * @package OldTown\Workflow\Loader
26
 */
27
class WorkflowDescriptor extends AbstractDescriptor implements WriteXmlInterface
28
{
29
    /**
30
     *
31
     *
32
     * @var string
33
     */
34
    const DOCUMENT_TYPE_QUALIFIED_NAME = 'workflow';
35
36
    /**
37
     *
38
     *
39
     * @var string
40
     */
41
    const DOCUMENT_TYPE_PUBLIC_ID = '-//OpenSymphony Group//DTD OSWorkflow 2.8//EN';
42
43
    /**
44
     *
45
     *
46
     * @var string
47
     */
48
    const DOCUMENT_TYPE_SYSTEM_ID = 'http://www.opensymphony.com/osworkflow/workflow_2_8.dtd';
49
50
51
52
    /**
53
     * @var ConditionsDescriptor|null
54
     */
55
    protected $globalConditions;
56
57
    /**
58
     * @var ActionDescriptor[]|SplObjectStorage
59
     */
60
    protected $globalActions;
61
62
    /**
63
     * @var SplObjectStorage|ActionDescriptor[]
64
     */
65
    protected $initialActions;
66
67
    /**
68
     * @var JoinDescriptor[]|SplObjectStorage
69
     */
70
    protected $joins;
71
72
    /**
73
     * @var RegisterDescriptor[]|SplObjectStorage
74
     */
75
    protected $registers;
76
77
    /**
78
     * @var SplitDescriptor[]|SplObjectStorage
79
     */
80
    protected $splits = [];
81
82
    /**
83
     * @var StepDescriptor[]|SplObjectStorage
84
     */
85
    protected $steps;
86
87
    /**
88
     * @var ActionDescriptor[]
89
     */
90
    protected $commonActions = [];
91
92
    /**
93
     * @var ActionDescriptor[]|SplObjectStorage
94
     */
95
    protected $commonActionsList = [];
96
97
    /**
98
     * @var array
99
     */
100
    protected $metaAttributes = [];
101
102
    /**
103
     * @var array
104
     */
105
    protected $timerFunctions = [];
106
107
    /**
108
     * Имя workflow
109
     *
110
     * @var string|null
111
     */
112
    protected $workflowName;
113
114
    /**
115
     * @param DOMElement $element
116
     * @throws InvalidArgumentException
117
     * @throws RuntimeException
118
     * @throws InternalWorkflowException
119
     * @throws \OldTown\Workflow\Exception\ArgumentNotNumericException
120
     */
121
    public function __construct(DOMElement $element = null)
122
    {
123
        $this->registers = new SplObjectStorage();
124
        $this->initialActions = new SplObjectStorage();
125
        $this->globalActions = new SplObjectStorage();
126
        $this->steps = new SplObjectStorage();
127
        $this->joins = new SplObjectStorage();
128
        $this->splits = new SplObjectStorage();
129
130
        parent::__construct($element);
131
132
        if (null !== $element) {
133
            $this->init($element);
134
        }
135
    }
136
137
    /**
138
     * Возвращает имя workflow
139
     *
140
     * @return null|string
141
     */
142
    public function getName()
143
    {
144
        return $this->workflowName;
145
    }
146
147
148
    /**
149
     * Устанавливает имя workflow
150
     *
151
     * @param string $workflowName
152
     *
153
     * @return $this
154
     */
155
    public function setName($workflowName)
156
    {
157
        $this->workflowName = (string)$workflowName;
158
159
        return $this;
160
    }
161
162
    /**
163
     * Валидация workflow
164
     *
165
     * @throws InvalidWorkflowDescriptorException
166
     * @throws InternalWorkflowException
167
     * @throws InvalidDescriptorException
168
     * @throws InvalidWriteWorkflowException
169
     * @throws  \OldTown\Workflow\Exception\InvalidDtdSchemaException
170
     * @return void
171
     */
172
    public function validate()
173
    {
174
        $registers = $this->getRegisters();
175
        $triggerFunctions = $this->getTriggerFunctions();
176
        $globalActions = $this->getGlobalActions();
177
        $initialActions = $this->getInitialActions();
178
        $commonActions = $this->getCommonActions();
179
        $steps = $this->getSteps();
180
        $splits = $this->getSplits();
181
        $joins = $this->getJoins();
182
183
184
        ValidationHelper::validate($registers);
185
        ValidationHelper::validate($triggerFunctions);
186
        ValidationHelper::validate($globalActions);
187
        ValidationHelper::validate($initialActions);
188
        ValidationHelper::validate($commonActions);
189
        ValidationHelper::validate($steps);
190
        ValidationHelper::validate($splits);
191
        ValidationHelper::validate($joins);
192
193
194
        $actions = [];
195
196
        foreach ($globalActions as $action) {
197
            $actionId = $action->getId();
198
            if (array_key_exists($actionId, $actions)) {
199
                $errMsg = sprintf(
200
                    'Ошибка валидация. Действие с id %s уже существует',
201
                    $actionId
202
                );
203
                throw new InvalidWorkflowDescriptorException($errMsg);
204
            }
205
            $actions[$actionId] = $actionId;
206
        }
207
208
        foreach ($steps as $step) {
209
            $j = $step->getActions();
210
211
            foreach ($j as $action) {
212
                if (!$action->isCommon()) {
213
                    $actionId = $action->getId();
214
                    if (array_key_exists($actionId, $actions)) {
215
                        $errMsg = sprintf(
216
                            'Действие с id %s найденное у шага %s является дубликатом',
217
                            $actionId,
218
                            $step->getId()
219
                        );
220
                        throw new InvalidWorkflowDescriptorException($errMsg);
221
                    }
222
                    $actions[$actionId] = $actionId;
223
                }
224
            }
225
        }
226
227
228
        foreach ($commonActions as $action) {
229
            $actionId = $action->getId();
230
            if (array_key_exists($actionId, $actions)) {
231
                $errMsg = sprintf(
232
                    'common-action  с id %s является дубликатом',
233
                    $actionId
234
                );
235
                throw new InvalidWorkflowDescriptorException($errMsg);
236
            }
237
        }
238
239
240
        $this->validateDtd();
241
    }
242
243
    /**
244
     * Валидация схемы документа
245
     *
246
     * @return void
247
     * @throws InternalWorkflowException
248
     * @throws InvalidDescriptorException
249
     * @throws InvalidWriteWorkflowException
250
     * @throws  \OldTown\Workflow\Exception\InvalidDtdSchemaException
251
     */
252
    private function validateDtd()
253
    {
254
        $libxmlUseInternalErrors = libxml_use_internal_errors(true);
255
        $dom = $this->writeXml();
256
257
        $secureDtdEntityResolver = new SecureDtdEntityResolver();
258
        $dtd = $secureDtdEntityResolver->resolveEntity($dom->doctype);
259
260
261
        $systemId = 'data://text/plain;base64,'.base64_encode($dtd);
262
263
        $creator = new DOMImplementation;
264
        $doctype = $creator->createDocumentType(
265
            static::DOCUMENT_TYPE_QUALIFIED_NAME,
266
            static::DOCUMENT_TYPE_PUBLIC_ID,
267
            $systemId
268
        );
269
        $new = $creator->createDocument('', '', $doctype);
270
271
        $oldNode = $dom->getElementsByTagName(static::DOCUMENT_TYPE_QUALIFIED_NAME)->item(0);
272
        $newNode = $new->importNode($oldNode, true);
273
        $new->appendChild($newNode);
274
275
        if (!$new->validate()) {
276
            /** @var LibXMLError[] $errors */
277
            $errors = libxml_get_errors();
278
            $errMsgStack = [];
279
            foreach ($errors as $error) {
280
                $errMsgStack[] = $error->message;
281
            }
282
            $errMsg = implode(" \n", $errMsgStack);
283
284
            libxml_use_internal_errors($libxmlUseInternalErrors);
285
            throw new InvalidDtdSchemaException($errMsg);
286
        };
287
        libxml_use_internal_errors($libxmlUseInternalErrors);
288
    }
289
290
    /**
291
     * @param DOMElement $root
292
     * @throws InvalidArgumentException
293
     * @throws RuntimeException
294
     * @throws InternalWorkflowException
295
     * @throws \OldTown\Workflow\Exception\ArgumentNotNumericException
296
     */
297
    protected function init(DOMElement $root)
298
    {
299
        $metaElements = XmlUtil::getChildElements($root, 'meta');
300 View Code Duplication
        foreach ($metaElements as $meta) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
301
            $value = XmlUtil::getText($meta);
302
            $name = XmlUtil::getRequiredAttributeValue($meta, 'name');
303
304
            $this->metaAttributes[$name] = $value;
305
        }
306
307
        // handle registers - OPTIONAL
308
        $r = XmlUtil::getChildElement($root, 'registers');
309 View Code Duplication
        if (null !== $r) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
310
            $registers = XMLUtil::getChildElements($r, 'register');
311
312
            foreach ($registers as $register) {
313
                $registerDescriptor = DescriptorFactory::getFactory()->createRegisterDescriptor($register);
314
                $registerDescriptor->setParent($this);
315
                $this->registers->attach($registerDescriptor);
316
            }
317
        }
318
319
        // handle global-conditions - OPTIONAL
320
        $globalConditionsElement = XMLUtil::getChildElement($root, 'global-conditions');
321
        if ($globalConditionsElement !== null) {
322
            $globalConditions = XMLUtil::getChildElement($globalConditionsElement, 'conditions');
323
324
            $conditionsDescriptor = DescriptorFactory::getFactory()->createConditionsDescriptor($globalConditions);
325
            $conditionsDescriptor->setParent($this);
326
            $this->globalConditions = $conditionsDescriptor;
327
        }
328
329
        // handle initial-steps - REQUIRED
330
        $initialActionsElement = XMLUtil::getChildElement($root, 'initial-actions');
331
        $initialActions = XMLUtil::getChildElements($initialActionsElement, 'action');
0 ignored issues
show
Bug introduced by
It seems like $initialActionsElement defined by \OldTown\Workflow\Loader...oot, 'initial-actions') on line 330 can be null; however, OldTown\Workflow\Loader\...til::getChildElements() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
332
333
        foreach ($initialActions as $initialAction) {
334
            $actionDescriptor = DescriptorFactory::getFactory()->createActionDescriptor($initialAction);
335
            $actionDescriptor->setParent($this);
336
            $this->addInitialAction($actionDescriptor);
337
        }
338
339
        // handle global-actions - OPTIONAL
340
        $globalActionsElement = XMLUtil::getChildElement($root, 'global-actions');
341
342 View Code Duplication
        if (null !== $globalActionsElement) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
343
            $globalActions = XMLUtil::getChildElements($globalActionsElement, 'action');
344
345
            foreach ($globalActions as $globalAction) {
346
                $actionDescriptor = DescriptorFactory::getFactory()->createActionDescriptor($globalAction);
347
                $actionDescriptor->setParent($this);
348
                $this->addGlobalAction($actionDescriptor);
349
            }
350
        }
351
352
353
        // handle common-actions - OPTIONAL
354
        //   - Store actions in HashMap for now. When parsing Steps, we'll resolve
355
        //      any common actions into local references.
356
        $commonActionsElement = XMLUtil::getChildElement($root, 'common-actions');
357
358 View Code Duplication
        if (null !== $commonActionsElement) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
359
            $commonActions = XMLUtil::getChildElements($commonActionsElement, 'action');
360
361
            foreach ($commonActions as $commonAction) {
362
                $actionDescriptor = DescriptorFactory::getFactory()->createActionDescriptor($commonAction);
363
                $actionDescriptor->setParent($this);
364
                $this->addCommonAction($actionDescriptor);
365
            }
366
        }
367
368
369
        // handle timer-functions - OPTIONAL
370
        $timerFunctionsElement = XMLUtil::getChildElement($root, 'trigger-functions');
371
372
        if (null !== $timerFunctionsElement) {
373
            $timerFunctions = XMLUtil::getChildElements($timerFunctionsElement, 'trigger-function');
374
375
            foreach ($timerFunctions as $timerFunction) {
376
                $functionElement = XmlUtil::getRequiredChildElement($timerFunction, 'function');
377
                $id = XmlUtil::getRequiredAttributeValue($timerFunction, 'id');
378
379
                $function = DescriptorFactory::getFactory()->createFunctionDescriptor($functionElement);
380
                $function->setParent($this);
381
382
                $this->setTriggerFunction($id, $function);
383
            }
384
        }
385
386
        // handle steps - REQUIRED
387
        $stepsElement = XMLUtil::getChildElement($root, 'steps');
388
        $steps = XMLUtil::getChildElements($stepsElement, 'step');
0 ignored issues
show
Bug introduced by
It seems like $stepsElement defined by \OldTown\Workflow\Loader...Element($root, 'steps') on line 387 can be null; however, OldTown\Workflow\Loader\...til::getChildElements() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
389
390
        foreach ($steps as $step) {
391
            $stepDescriptor = DescriptorFactory::getFactory()->createStepDescriptor($step, $this);
392
            $this->addStep($stepDescriptor);
393
        }
394
395
396
        // handle splits - OPTIONAL:
397
        $splitsElement = XMLUtil::getChildElement($root, 'splits');
398 View Code Duplication
        if (null !== $splitsElement) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
399
            $split = XMLUtil::getChildElements($splitsElement, 'split');
400
            foreach ($split as $s) {
401
                $splitDescriptor = DescriptorFactory::getFactory()->createSplitDescriptor($s);
402
                $splitDescriptor->setParent($this);
403
                $this->addSplit($splitDescriptor);
404
            }
405
        }
406
407
408
        // handle joins - OPTIONAL:
409
        $joinsElement = XMLUtil::getChildElement($root, 'joins');
410 View Code Duplication
        if (null !== $joinsElement) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
411
            $join = XMLUtil::getChildElements($joinsElement, 'join');
412
            foreach ($join as $s) {
413
                $joinDescriptor = DescriptorFactory::getFactory()->createJoinDescriptor($s);
414
                $joinDescriptor->setParent($this);
415
                $this->addJoin($joinDescriptor);
416
            }
417
        }
418
    }
419
420
    /**
421
     * @return null|ConditionsDescriptor
422
     */
423
    public function getGlobalConditions()
424
    {
425
        return $this->globalConditions;
426
    }
427
428
429
    /**
430
     * Добавляет новый переход между действиями
431
     *
432
     * @param ActionDescriptor $descriptor
433
     * @return $this
434
     *
435
     * @throws InvalidArgumentException
436
     * @throws RuntimeException
437
     */
438
    public function addCommonAction(ActionDescriptor $descriptor)
439
    {
440
        $descriptor->setCommon(true);
441
        $this->addAction($this->commonActions, $descriptor);
442
        $this->addAction($this->commonActionsList, $descriptor);
443
444
        return $this;
445
    }
446
447
    /**
448
     * @param                  $actionsCollectionOrMap
449
     * @param ActionDescriptor $descriptor
450
     *
451
     * @return $this
452
     * @throws InvalidArgumentException
453
     * @throws RuntimeException
454
     */
455
    private function addAction(&$actionsCollectionOrMap, ActionDescriptor $descriptor)
456
    {
457
        $descriptorId = $descriptor->getId();
458
        $action = $this->getAction($descriptorId);
459
        if (null !== $action) {
460
            $errMsg = sprintf('Действие с id %s уже существует', $descriptorId);
461
            throw new InvalidArgumentException($errMsg);
462
        }
463
464
        if ($actionsCollectionOrMap instanceof SplObjectStorage) {
465
            $actionsCollectionOrMap->attach($descriptor);
466
        } elseif (is_array($actionsCollectionOrMap)) {
467
            $actionsCollectionOrMap[$descriptorId] = $descriptor;
468
        }
469
470
        return $this;
471
    }
472
473
    /**
474
     * Возвращает шаг по его id
475
     *
476
     * @param integer $id
477
     * @return StepDescriptor|null
478
     * @throws \OldTown\Workflow\Exception\ArgumentNotNumericException
479
     */
480
    public function getStep($id)
481
    {
482
        if (!is_numeric($id)) {
483
            $errMsg = 'Аргумент должен быть числом';
484
            throw new ArgumentNotNumericException($errMsg);
485
        }
486
        $id = (integer)$id;
487
488
        foreach ($this->getSteps() as $step) {
489
            if ($id === $step->getId()) {
490
                return $step;
491
            }
492
        }
493
        return null;
494
    }
495
496
    /**
497
     * @param integer $id
498
     * @return ActionDescriptor|null
499
     */
500
    public function getAction($id)
501
    {
502
        $id = (integer)$id;
503
504
        foreach ($this->getGlobalActions() as $actionDescriptor) {
505
            if ($id === $actionDescriptor->getId()) {
506
                return $actionDescriptor;
507
            }
508
        }
509
510
        foreach ($this->getSteps() as $stepDescriptor) {
511
            $actionDescriptor = $stepDescriptor->getAction($id);
512
            if (null !== $actionDescriptor) {
513
                return $actionDescriptor;
514
            }
515
        }
516
517
        foreach ($this->getInitialActions() as $actionDescriptor) {
518
            if ($id === $actionDescriptor->getId()) {
519
                return $actionDescriptor;
520
            }
521
        }
522
523
        return null;
524
    }
525
526
    /**
527
     * @return ActionDescriptor[]|SplObjectStorage
528
     */
529
    public function getGlobalActions()
530
    {
531
        return $this->globalActions;
532
    }
533
534
    /**
535
     * @return StepDescriptor[]|SplObjectStorage
536
     */
537
    public function getSteps()
538
    {
539
        return $this->steps;
540
    }
541
542
    /**
543
     * @return ActionDescriptor[]|SplObjectStorage
544
     */
545
    public function getInitialActions()
546
    {
547
        return $this->initialActions;
548
    }
549
550
    /**
551
     * @return ActionDescriptor[]
552
     */
553
    public function getCommonActions()
554
    {
555
        return $this->commonActions;
556
    }
557
558
    /**
559
     * @param $id
560
     *
561
     * @return ActionDescriptor|null
562
     */
563
    public function getCommonAction($id)
564
    {
565
        $id = (integer)$id;
566
        if (array_key_exists($id, $this->commonActions)) {
567
            return $this->commonActions[$id];
568
        }
569
        return null;
570
    }
571
572
573
    /**
574
     * @param $id
575
     *
576
     * @return ActionDescriptor|null
577
     * @throws \OldTown\Workflow\Exception\ArgumentNotNumericException
578
     */
579 View Code Duplication
    public function getInitialAction($id)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
580
    {
581
        if (!is_numeric($id)) {
582
            $errMsg = 'Аргумент должен быть числом';
583
            throw new ArgumentNotNumericException($errMsg);
584
        }
585
        $id = (integer)$id;
586
587
        $initialActions = $this->getInitialActions();
588
        foreach ($initialActions as $actionDescriptor) {
589
            if ($id === $actionDescriptor->getId()) {
590
                return $actionDescriptor;
591
            }
592
        }
593
594
        return null;
595
    }
596
597
    /**
598
     * @return JoinDescriptor[]|SplObjectStorage
599
     */
600
    public function getJoins()
601
    {
602
        return $this->joins;
603
    }
604
605
606
    /**
607
     * @param $id
608
     *
609
     * @return JoinDescriptor|null
610
     * @throws \OldTown\Workflow\Exception\ArgumentNotNumericException
611
     */
612 View Code Duplication
    public function getJoin($id)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
613
    {
614
        if (!is_numeric($id)) {
615
            $errMsg = 'Аргумент должен быть числом';
616
            throw new ArgumentNotNumericException($errMsg);
617
        }
618
        $id = (integer)$id;
619
620
        $joins = $this->getJoins();
621
        foreach ($joins as $joinDescriptor) {
622
            if ($id === $joinDescriptor->getId()) {
623
                return $joinDescriptor;
624
            }
625
        }
626
627
        return null;
628
    }
629
630
    /**
631
     * @return array
632
     */
633
    public function getMetaAttributes()
634
    {
635
        return $this->metaAttributes;
636
    }
637
638
    /**
639
     * @return RegisterDescriptor[]|SplObjectStorage
640
     */
641
    public function getRegisters()
642
    {
643
        return $this->registers;
644
    }
645
646
    /**
647
     * @return SplitDescriptor[]|SplObjectStorage
648
     */
649
    public function getSplits()
650
    {
651
        return $this->splits;
652
    }
653
654
655
656
    /**
657
     * @param $id
658
     *
659
     * @return SplitDescriptor|null
660
     * @throws \OldTown\Workflow\Exception\ArgumentNotNumericException
661
     */
662 View Code Duplication
    public function getSplit($id)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
663
    {
664
        if (!is_numeric($id)) {
665
            $errMsg = 'Аргумент должен быть числом';
666
            throw new ArgumentNotNumericException($errMsg);
667
        }
668
        $id = (integer)$id;
669
670
        $splits = $this->getSplits();
671
        foreach ($splits as $splitDescriptor) {
672
            if ($id === $splitDescriptor->getId()) {
673
                return $splitDescriptor;
674
            }
675
        }
676
677
        return null;
678
    }
679
680
    /**
681
     * @param integer  $id
682
     * @param FunctionDescriptor $descriptor
683
     * @return $this
684
     *
685
     * @throws \OldTown\Workflow\Exception\ArgumentNotNumericException
686
     */
687
    public function setTriggerFunction($id, FunctionDescriptor $descriptor)
688
    {
689
        if (!is_numeric($id)) {
690
            $errMsg = 'Аргумент должен быть числом';
691
            throw new ArgumentNotNumericException($errMsg);
692
        }
693
        $id = (integer)$id;
694
        $this->timerFunctions[$id] = $descriptor;
695
696
        return $this;
697
    }
698
699
    /**
700
     * @param integer  $id
701
     * @return FunctionDescriptor
702
     * @throws \OldTown\Workflow\Exception\ArgumentNotNumericException
703
     */
704
    public function getTriggerFunction($id)
705
    {
706
        if (!is_numeric($id)) {
707
            $errMsg = 'Аргумент должен быть числом';
708
            throw new ArgumentNotNumericException($errMsg);
709
        }
710
        $id = (integer)$id;
711
712
        if (!array_key_exists($id, $this->timerFunctions)) {
713
            $errMsg = sprintf('Не найдена trigger-function с id %s', $id);
714
            throw new ArgumentNotNumericException($errMsg);
715
        }
716
717
        $this->timerFunctions[$id];
718
719
        return $this->timerFunctions[$id];
720
    }
721
722
    /**
723
     * @return FunctionDescriptor[]
724
     */
725
    public function getTriggerFunctions()
726
    {
727
        return $this->timerFunctions;
728
    }
729
730
    /**
731
     * @param ActionDescriptor $descriptor
732
     * @return $this
733
     * @throws InvalidArgumentException
734
     * @throws RuntimeException
735
     */
736
    public function addGlobalAction(ActionDescriptor $descriptor)
737
    {
738
        $this->addAction($this->globalActions, $descriptor);
739
        return $this;
740
    }
741
742
    /**
743
     * @param ActionDescriptor $descriptor
744
     * @return $this
745
     * @throws InvalidArgumentException
746
     * @throws RuntimeException
747
     */
748
    public function addInitialAction(ActionDescriptor $descriptor)
749
    {
750
        $this->addAction($this->initialActions, $descriptor);
751
        return $this;
752
    }
753
754
755
    /**
756
     * @param JoinDescriptor $descriptor
757
     * @return $this
758
     * @throws InvalidArgumentException
759
     * @throws \OldTown\Workflow\Exception\ArgumentNotNumericException
760
     */
761
    public function addJoin(JoinDescriptor $descriptor)
762
    {
763
        $id = $descriptor->getId();
764
        if (null !== $this->getJoin($id)) {
765
            $errMsg = sprintf('Объеденение с id %s уже существует', $id);
766
            throw new InvalidArgumentException($errMsg);
767
        }
768
769
        $this->getJoins()->attach($descriptor);
770
        return $this;
771
    }
772
773
774
    /**
775
     * @param SplitDescriptor $descriptor
776
     * @return $this
777
     * @throws InvalidArgumentException
778
     * @throws \OldTown\Workflow\Exception\ArgumentNotNumericException
779
     */
780
    public function addSplit(SplitDescriptor $descriptor)
781
    {
782
        $id = $descriptor->getId();
783
        if (null !== $this->getSplit($id)) {
784
            $errMsg = sprintf('Ветвление с id %s уже существует', $id);
785
            throw new InvalidArgumentException($errMsg);
786
        }
787
788
        $this->getSplits()->attach($descriptor);
789
        return $this;
790
    }
791
792
    /**
793
     * @param StepDescriptor $descriptor
794
     * @return $this
795
     *
796
     * @throws \OldTown\Workflow\Exception\InvalidArgumentException
797
     * @throws ArgumentNotNumericException
798
     */
799
    public function addStep(StepDescriptor $descriptor)
800
    {
801
        $id = $descriptor->getId();
802
        if (null !== $this->getStep($id)) {
803
            $errMsg = sprintf('Шаг с id %s уже существует', $id);
804
            throw new InvalidArgumentException($errMsg);
805
        }
806
807
        $this->getSteps()->attach($descriptor);
808
        return $this;
809
    }
810
811
    /**
812
     * Удаление action по id
813
     *
814
     * @param $id
815
     *
816
     * @return bool
817
     */
818
    public function removeActionActionById($id)
819
    {
820
        $descriptor = $this->getAction($id);
821
822
        $result = $this->removeAction($descriptor);
0 ignored issues
show
Bug introduced by
It seems like $descriptor defined by $this->getAction($id) on line 820 can be null; however, OldTown\Workflow\Loader\...criptor::removeAction() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
823
824
        return $result;
825
    }
826
827
    /**
828
     * @param ActionDescriptor $actionToRemove
829
     * @return boolean
830
     */
831
    public function removeAction(ActionDescriptor $actionToRemove)
832
    {
833
        $resultRemove = false;
834
835
        $actionToRemoveId = $actionToRemove->getId();
836
        $globalActions = $this->getGlobalActions();
837
        foreach ($globalActions as $actionDescriptor) {
838
            if ($actionToRemoveId === $actionDescriptor->getId()) {
839
                $globalActions->detach($actionDescriptor);
840
841
                $resultRemove = true;
842
            }
843
        }
844
845
        $steps = $this->getSteps();
846
        foreach ($steps as $stepDescriptor) {
847
            $actionDescriptor = $stepDescriptor->getAction($actionToRemoveId);
848
849
            if (null !== $actionDescriptor) {
850
                $stepDescriptor->getActions()->detach($actionDescriptor);
851
852
                $resultRemove = true;
853
            }
854
        }
855
856
        return $resultRemove;
857
    }
858
859
860
    /**
861
     * Создает DOMElement - эквивалентный состоянию дескриптора
862
     *
863
     * @param DOMDocument|null $dom
864
     *
865
     * @return DOMDocument
866
     * @throws InternalWorkflowException
867
     * @throws InvalidDescriptorException
868
     * @throws InvalidWriteWorkflowException
869
     *
870
     */
871
    public function writeXml(DOMDocument $dom = null)
872
    {
873
        if (null === $dom) {
874
            $imp = new DOMImplementation();
875
            $dtd  = $imp->createDocumentType(
876
                static::DOCUMENT_TYPE_QUALIFIED_NAME,
877
                static::DOCUMENT_TYPE_PUBLIC_ID,
878
                static::DOCUMENT_TYPE_SYSTEM_ID
879
            );
880
            $dom = $imp->createDocument('', '', $dtd);
881
            $dom->encoding = 'UTF-8';
882
            $dom->xmlVersion = '1.0';
883
            $dom->formatOutput = true;
884
        }
885
886
        $descriptor = $dom->createElement('workflow');
887
888
        $metaAttributes = $this->getMetaAttributes();
889
        $metaElementBase = $dom->createElement('meta');
890 View Code Duplication
        foreach ($metaAttributes as $metaAttributeName => $metaAttributeValue) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
891
            $metaAttributeNameEncode = XmlUtil::encode($metaAttributeName);
892
            $metaAttributeValueEnEncode = XmlUtil::encode($metaAttributeValue);
893
894
            $metaElement = clone $metaElementBase;
895
            $metaElement->setAttribute('name', $metaAttributeNameEncode);
896
            $metaValueElement = $dom->createTextNode($metaAttributeValueEnEncode);
897
            $metaElement->appendChild($metaValueElement);
898
899
            $descriptor->appendChild($metaElement);
900
        }
901
902
        $registers = $this->getRegisters();
903
        if ($registers->count() > 0) {
904
            $registersElement = $dom->createElement('registers');
905
            foreach ($registers as $register) {
906
                $registerElement = $register->writeXml($dom);
907
                $registersElement->appendChild($registerElement);
908
            }
909
910
            $descriptor->appendChild($registersElement);
911
        }
912
913
914
        $timerFunctions = $this->getTriggerFunctions();
915
        if (count($timerFunctions) > 0) {
916
            $timerFunctionsElement = $dom->createElement('trigger-functions');
917
            $timerFunctionElementBase = $dom->createElement('trigger-function');
918
            foreach ($timerFunctions as $timerFunctionId => $timerFunction) {
919
                $timerFunctionElement = clone $timerFunctionElementBase;
920
                $timerFunctionElement->setAttribute('id', $timerFunctionId);
921
922
                $functionElement = $timerFunction->writeXml($dom);
923
924
                $timerFunctionElement->appendChild($functionElement);
925
926
                $timerFunctionsElement->appendChild($timerFunctionElement);
927
            }
928
            $descriptor->appendChild($timerFunctionsElement);
929
        }
930
931
932
        $globalConditions = $this->getGlobalConditions();
933
        if (null !== $globalConditions) {
934
            $globalConditionsElement = $dom->createElement('global-conditions');
935
            $globalConditionElement = $globalConditions->writeXml($dom);
936
            $globalConditionsElement->appendChild($globalConditionElement);
937
            $descriptor->appendChild($globalConditionsElement);
938
        }
939
940
941
        $initialActionsElement = $dom->createElement('initial-actions');
942
        $initialActions = $this->getInitialActions();
943
        foreach ($initialActions as $initialAction) {
944
            $initialActionElement = $initialAction->writeXml($dom);
945
            $initialActionsElement->appendChild($initialActionElement);
946
        }
947
        $descriptor->appendChild($initialActionsElement);
948
949
950
        $globalActions = $this->getGlobalActions();
951
        if ($globalActions->count() > 0) {
952
            $globalActionsElement = $dom->createElement('global-actions');
953
            foreach ($globalActions as $globalAction) {
954
                $globalActionElement = $globalAction->writeXml($dom);
955
956
                $globalActionsElement->appendChild($globalActionElement);
957
            }
958
959
            $descriptor->appendChild($globalActionsElement);
960
        }
961
962
        $commonActions = $this->getCommonActions();
963
        if (count($commonActions) > 0) {
964
            $commonActionsElement = $dom->createElement('common-actions');
965
            foreach ($commonActions as $commonAction) {
966
                $commonActionElement = $commonAction->writeXml($dom);
967
                $commonActionsElement->appendChild($commonActionElement);
968
            }
969
970
            $descriptor->appendChild($commonActionsElement);
971
        }
972
973
974
        $stepsElement = $dom->createElement('steps');
975
        $steps = $this->getSteps();
976
        foreach ($steps as $step) {
977
            $stepElement = $step->writeXml($dom);
978
            $stepsElement->appendChild($stepElement);
979
        }
980
981
        $descriptor->appendChild($stepsElement);
982
983
        $splits = $this->getSplits();
984
        if ($splits->count() > 0) {
985
            $splitsElement = $dom->createElement('splits');
986
            foreach ($splits as $split) {
987
                $splitElement = $split->writeXml($dom);
988
                $splitsElement->appendChild($splitElement);
989
            }
990
991
            $descriptor->appendChild($splitsElement);
992
        }
993
994
        $joins = $this->getJoins();
995
        if ($joins->count() > 0) {
996
            $joinsElement = $dom->createElement('joins');
997
            foreach ($joins as $join) {
998
                $joinElement = $join->writeXml($dom);
999
                $joinsElement->appendChild($joinElement);
1000
            }
1001
1002
            $descriptor->appendChild($joinsElement);
1003
        }
1004
1005
        $dom->appendChild($descriptor);
1006
        return $dom;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $dom; (DOMDocument) is incompatible with the return type declared by the interface OldTown\Workflow\Loader\...eXmlInterface::writeXml of type DOMElement.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
1007
    }
1008
}
1009