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 — dev ( 40bda2...5b7120 )
by Андрей
04:01
created

Dispatcher::onRunWorkflowHandler()   B

Complexity

Conditions 4
Paths 6

Size

Total Lines 40
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 40
rs 8.5806
cc 4
eloc 26
nc 6
nop 1
1
<?php
2
/**
3
 * @link https://github.com/old-town/workflow-zf2-dispatch
4
 * @author  Malofeykin Andrey  <[email protected]>
5
 */
6
namespace OldTown\Workflow\ZF2\Dispatch\Dispatcher;
7
8
use OldTown\Workflow\TransientVars\TransientVarsInterface;
9
use Zend\EventManager\EventManagerAwareTrait;
10
use OldTown\Workflow\ZF2\ServiceEngine\Workflow as WorkflowService;
11
use OldTown\Workflow\ZF2\Dispatch\Metadata\ReaderInterface;
12
use ReflectionClass;
13
use OldTown\Workflow\ZF2\Dispatch\Metadata\Target\Dispatch\MetadataInterface;
14
use Zend\Mvc\Controller\AbstractController;
15
use Traversable;
16
use Zend\Stdlib\ArrayUtils;
17
use Zend\Validator\ValidatorPluginManager;
18
use Zend\Validator\ValidatorChain;
19
use Zend\Validator\ValidatorInterface;
20
use OldTown\Workflow\TransientVars\BaseTransientVars;
21
use OldTown\Workflow\ZF2\ServiceEngine\Workflow\TransitionResultInterface;
22
23
24
/**
25
 * Class Dispatcher
26
 *
27
 * @package OldTown\Workflow\ZF2\Dispatch\Dispatcher
28
 */
29
class Dispatcher implements DispatcherInterface
30
{
31
    use EventManagerAwareTrait;
32
33
    /**
34
     * @var string
35
     */
36
    const WORKFLOW_DISPATCH_EVENT = 'workflowDispatchEvent';
37
38
    /**
39
     * Имя класса события
40
     *
41
     * @var string
42
     */
43
    protected $workflowDispatchEventClassName = WorkflowDispatchEvent::class;
44
45
    /**
46
     * Имя класса события
47
     *
48
     * @var string
49
     */
50
    protected $transientVarsClassName = BaseTransientVars::class;
51
52
    /**
53
     * @var WorkflowService
54
     */
55
    protected $workflowService;
56
57
    /**
58
     * @var ReaderInterface
59
     */
60
    protected $metadataReader;
61
62
    /**
63
     * @var ValidatorPluginManager
64
     */
65
    protected $validatorManager;
66
67
    /**
68
     * @param array $options
69
     */
70
    public function __construct(array $options = [])
71
    {
72
        call_user_func_array([$this, 'init'], $options);
73
    }
74
75
    /**
76
     * @param WorkflowService        $workflowService
77
     * @param ReaderInterface        $metadataReader
78
     * @param ValidatorPluginManager $validatorManager
79
     */
80
    protected function init(WorkflowService $workflowService, ReaderInterface $metadataReader, ValidatorPluginManager $validatorManager)
81
    {
82
        $this->setWorkflowService($workflowService);
83
        $this->setMetadataReader($metadataReader);
84
        $this->setValidatorManager($validatorManager);
85
    }
86
87
    /**
88
     * Диспетчирезация работы с workflow
89
     *
90
     * @param WorkflowDispatchEventInterface $event
91
     *
92
     * @return void
93
     *
94
     * @throws Exception\RunWorkflowParamException
95
     */
96
    public function dispatch(WorkflowDispatchEventInterface $event)
97
    {
98
        $event->getMvcEvent()->setParam(static::WORKFLOW_DISPATCH_EVENT, $event);
99
        $event->setTarget($this);
100
101
        $metadataResult = $this->getEventManager()->trigger(WorkflowDispatchEventInterface::LOAD_METADATA_EVENT, $event, function ($test) {
102
            return ($test instanceof MetadataInterface);
103
        });
104
        $metadata = $metadataResult->last();
105
106
        if (!$metadata instanceof MetadataInterface) {
107
            return;
108
        }
109
110
        $event->setMetadata($metadata);
111
112
        $prepareData = [];
113
        if ($metadata->isFlagRunPrepareData()) {
114
            $prepareDataResults = $this->getEventManager()->trigger(WorkflowDispatchEventInterface::PREPARE_DATA_EVENT, $event);
115
            foreach ($prepareDataResults as $prepareDataResult) {
116
                if (is_array($prepareDataResult) || $prepareDataResult instanceof Traversable) {
117
                    $prepareData = ArrayUtils::merge($prepareData, $prepareDataResult);
0 ignored issues
show
Bug introduced by
It seems like $prepareDataResult defined by $prepareDataResult on line 115 can also be of type object<Traversable>; however, Zend\Stdlib\ArrayUtils::merge() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
118
                }
119
            }
120
        }
121
        $event->setPrepareData($prepareData);
122
123
124
        $flagRunWorkflow = $metadata->isWorkflowDispatch();
125
        if (true === $flagRunWorkflow) {
126
            $dispatchConditionsResults = $this->getEventManager()->trigger(WorkflowDispatchEventInterface::CHECK_RUN_WORKFLOW_EVENT, $event);
127
            foreach ($dispatchConditionsResults as $dispatchConditionsResult) {
128
                if (false === $dispatchConditionsResult) {
129
                    $flagRunWorkflow = false;
130
                    break;
131
                }
132
            }
133
        }
134
135
        if (true === $flagRunWorkflow) {
136
            $runWorkflowParamResults = $this->getEventManager()->trigger(WorkflowDispatchEventInterface::METADATA_WORKFLOW_TO_RUN_EVENT, $event, function ($result) {
137
                return ($result instanceof RunWorkflowParamInterface);
138
            });
139
            $runWorkflowParamResult = $runWorkflowParamResults->last();
140
141
            if (!$runWorkflowParamResult instanceof RunWorkflowParamInterface) {
142
                $errMsg = 'There is no evidence to launch workflow';
143
                throw new Exception\RunWorkflowParamException($errMsg);
144
            }
145
            $event->setRunWorkflowParam($runWorkflowParamResult);
146
147
148
            $runWorkflowResults = $this->getEventManager()->trigger(WorkflowDispatchEventInterface::RUN_WORKFLOW_EVENT, $event, function ($result) {
149
                return ($result instanceof TransitionResultInterface);
150
            });
151
152
            $workflowResult = $runWorkflowResults->last();
153
            if ($workflowResult instanceof TransitionResultInterface) {
154
                $event->setWorkflowResult($workflowResult);
155
            }
156
        }
157
    }
158
159
    /**
160
     * Добавление подписчиков по умолчанию
161
     *
162
     */
163
    public function attachDefaultListeners()
164
    {
165
        $em = $this->getEventManager();
166
        $em->attach(WorkflowDispatchEventInterface::LOAD_METADATA_EVENT, [$this, 'onLoadMetadataHandler']);
167
        $em->attach(WorkflowDispatchEventInterface::PREPARE_DATA_EVENT, [$this, 'onPrepareDataHandler']);
168
        $em->attach(WorkflowDispatchEventInterface::CHECK_RUN_WORKFLOW_EVENT, [$this, 'onCheckRunWorkflowHandler']);
169
        $em->attach(WorkflowDispatchEventInterface::RUN_WORKFLOW_EVENT, [$this, 'onRunWorkflowHandler']);
170
    }
171
172
    /**
173
     * Получение метаданных
174
     *
175
     * @param WorkflowDispatchEventInterface $e
176
     *
177
     * @return MetadataInterface|null
178
     */
179
    public function onLoadMetadataHandler(WorkflowDispatchEventInterface $e)
180
    {
181
        $mvcEvent = $e->getMvcEvent();
182
        $controller = $mvcEvent->getTarget();
183
        if (!$controller instanceof AbstractController) {
184
            return null;
185
        }
186
187
        $routeMatch = $mvcEvent->getRouteMatch();
188
        if (!$routeMatch) {
189
            return null;
190
        }
191
192
        $action = $routeMatch->getParam('action', 'not-found');
193
        $actionMethod = AbstractController::getMethodFromAction($action);
194
195
        if (!method_exists($controller, $actionMethod)) {
196
            return null;
197
        }
198
199
        $controllerClassName = get_class($controller);
200
        $metadata = $this->getMetadataReader()->loadMetadataForAction($controllerClassName, $actionMethod);
201
202
        return $metadata;
203
    }
204
205
    /**
206
     * @param WorkflowDispatchEventInterface $e
207
     *
208
     * @return mixed|null
209
     *
210
     * @throws Exception\PrepareDataException
211
     */
212
    public function onPrepareDataHandler(WorkflowDispatchEventInterface $e)
213
    {
214
        $metadata = $e->getMetadata();
215
216
        $type = $metadata->getPrepareDataMethod();
217
        $handler = $metadata->getPrepareDataHandler();
218
219
        $prepareDataResult = null;
0 ignored issues
show
Unused Code introduced by
$prepareDataResult is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
220
221
        if ('method' === $type) {
222
            $mvcEvent = $e->getMvcEvent();
223
            $controller = $mvcEvent->getTarget();
224
            if (!$controller instanceof AbstractController) {
225
                $errMsg = sprintf('Controller not implement %s', AbstractController::class);
226
                throw new Exception\PrepareDataException($errMsg);
227
            }
228
            $callback = [$controller, $handler];
229
            if (!is_callable($callback)) {
230
                $errMsg = sprintf('Invalid handler"%s"', $handler);
231
                throw new Exception\PrepareDataException($errMsg);
232
            }
233
            $prepareDataResult = call_user_func($callback, $e);
234
            if (null === $prepareDataResult) {
235
                $prepareDataResult = [];
236
            }
237
        } else {
238
            $errMsg = sprintf('Preparing data for Workflow will fail. Unknown handler type %s.', $type);
239
            throw new Exception\PrepareDataException($errMsg);
240
        }
241
242
        if (!is_array($prepareDataResult) && !$prepareDataResult instanceof Traversable) {
243
            $errMsg = 'Data preparation The results should be an array or Traversable';
244
            throw new Exception\PrepareDataException($errMsg);
245
        }
246
247
248
        return $prepareDataResult;
249
    }
250
251
    /**
252
     * Проверка, на то нужно ли запускать workflow
253
     *
254
     * @param WorkflowDispatchEventInterface $e
255
     *
256
     * @return boolean|null
257
     *
258
     * @throws \Zend\Validator\Exception\InvalidArgumentException
259
     * @throws  Exception\PrepareDataException
260
     */
261
    public function onCheckRunWorkflowHandler(WorkflowDispatchEventInterface $e)
262
    {
263
        $metadata = $e->getMetadata();
0 ignored issues
show
Bug introduced by
The method getMetadata does only exist in OldTown\Workflow\ZF2\Dis...wDispatchEventInterface, but not in Exception.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
264
        if (!$metadata->getFlagHasConditions()) {
265
            return null;
266
        }
267
268
        try {
269
            $conditions = $metadata->getConditions();
270
271
            $validatorManager = $this->getValidatorManager();
272
273
            /** @var ValidatorChain $validatorChains */
274
            $validatorChains = $validatorManager->get(ValidatorChain::class);
275
        } catch (\Exception $e) {
276
            throw new Exception\PrepareDataException($e->getMessage(), $e->getCode(), $e);
277
        }
278
279
280
281
        $mvcEvent = $e->getMvcEvent();
0 ignored issues
show
Bug introduced by
The method getMvcEvent does only exist in OldTown\Workflow\ZF2\Dis...wDispatchEventInterface, but not in Exception.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
282
        $controller = $mvcEvent->getTarget();
283
        if (!$controller instanceof AbstractController) {
284
            $controller = null;
285
        }
286
287
288
        foreach ($conditions as $condition) {
289
            try {
290
                $type = $condition->getType();
291
                $handler = $condition->getHandler();
292
                switch ($type) {
293
                    case 'method': {
294
                        if (null === $controller) {
295
                            $errMsg = 'Controller not specified';
296
                            throw new Exception\CheckRunWorkflowEventException($errMsg);
297
                        }
298
                        $callback = [$controller, $handler];
299
300
                        /** @var ValidatorInterface $callbackValidator */
301
                        $callbackValidator = $validatorManager->get('callback', $callback);
302
303
                        $validatorChains->attach($callbackValidator);
304
305
                        break;
306
                    }
307
                    case 'service': {
308
                        $validatorParams = $condition->getParams();
309
                        /** @var ValidatorInterface $validator */
310
                        $validator = $validatorManager->get($handler, $validatorParams);
311
312
                        $validatorChains->attach($validator);
313
314
                        break;
315
                    }
316
                    default: {
317
                        $errMsg = sprintf('Preparing data for Workflow will fail. Unknown handler type %s.', $type);
318
                        throw new Exception\PrepareDataException($errMsg);
319
                    }
320
                }
321
            } catch (\Exception $e) {
322
                throw new Exception\PrepareDataException($e->getMessage(), $e->getCode(), $e);
323
            }
324
        }
325
326
        return $validatorChains->isValid($e);
327
    }
328
329
    /**
330
     * Запуск workflow
331
     *
332
     * @param WorkflowDispatchEventInterface $e
333
     *
334
     * @return TransitionResultInterface
335
     *
336
     *
337
     * @throws Exception\InvalidArgumentException
338
     * @throws  Exception\WorkflowDispatchEventException
339
     * @throws \OldTown\Workflow\ZF2\ServiceEngine\Exception\InvalidInitializeWorkflowEntryException
340
     * @throws \OldTown\Workflow\ZF2\ServiceEngine\Exception\DoActionException
341
     */
342
    public function onRunWorkflowHandler(WorkflowDispatchEventInterface $e)
343
    {
344
        $runWorkflowParam = $e->getRunWorkflowParam();
345
        $runWorkflowParam->valid();
346
347
        $transientVars = $this->factoryTransientVars();
348
349
        $prepareData = $e->getPrepareData();
350
        foreach ($prepareData as $key => $value) {
351
            $transientVars[$key] = $value;
352
        }
353
354
        $runWorkflowParam = $e->getRunWorkflowParam();
355
        $runWorkflowParam->valid();
356
357
        $workflowManagerName = $runWorkflowParam->getManagerName();
358
        $workflowActionName = $runWorkflowParam->getActionName();
359
360
        $workflowActivity = $runWorkflowParam->getRunType();
361
        $result = null;
0 ignored issues
show
Unused Code introduced by
$result is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
362
        switch ($runWorkflowParam->getRunType()) {
363
            case RunWorkflowParamInterface::WORKFLOW_RUN_INITIALIZE: {
364
                $workflowName = $runWorkflowParam->getWorkflowName();
365
                $result = $this->getWorkflowService()->initialize($workflowManagerName, $workflowName, $workflowActionName, $transientVars);
366
                break;
367
            }
368
            case RunWorkflowParamInterface::WORKFLOW_RUN_TYPE_DO_ACTION: {
369
                $entryId = $runWorkflowParam->getEntryId();
370
371
                $result = $this->getWorkflowService()->doAction($workflowManagerName, $entryId, $workflowActionName, $transientVars);
372
                break;
373
            }
374
            default: {
375
                $errMsg = sprintf('Invalid activity %s', $workflowActivity);
376
                throw new Exception\InvalidArgumentException($errMsg);
377
            }
378
        }
379
380
        return $result;
381
    }
382
383
    /**
384
     *
385
     * @return TransientVarsInterface
386
     *
387
     * @throws Exception\WorkflowDispatchEventException
388
     */
389
    public function factoryTransientVars()
390
    {
391
        $className = $this->getTransientVarsClassName();
392
393
        return $this->factoryClassName($className, TransientVarsInterface::class);
394
    }
395
396
    /**
397
     * Фабрика для создания событий
398
     *
399
     * @return WorkflowDispatchEventInterface
400
     *
401
     * @throws Exception\WorkflowDispatchEventException
402
     */
403
    public function workflowDispatchEventFactory()
404
    {
405
        $className = $this->getWorkflowDispatchEventClassName();
406
407
        return $this->factoryClassName($className, WorkflowDispatchEventInterface::class);
408
    }
409
410
    /**
411
     * Создает экземпляр класса и проверяет то что созданный объект имплементирует заданный интерфейс
412
     *
413
     * @param $className
414
     * @param $interface
415
     *
416
     * @return mixed
417
     *
418
     * @throws  Exception\WorkflowDispatchEventException
419
     */
420
    protected function factoryClassName($className, $interface)
421
    {
422
        $r = new ReflectionClass($className);
423
424
        $instance = $r->newInstance();
425
426
        if (!$instance instanceof $interface) {
427
            $errMsg = sprintf('Class %s not implement %s', $className, $interface);
428
            throw new Exception\WorkflowDispatchEventException($errMsg);
429
        }
430
431
        return $instance;
432
    }
433
434
    /**
435
     * @return WorkflowService
436
     */
437
    public function getWorkflowService()
438
    {
439
        return $this->workflowService;
440
    }
441
442
    /**
443
     * @param WorkflowService $workflowService
444
     *
445
     * @return $this
446
     */
447
    public function setWorkflowService(WorkflowService $workflowService)
448
    {
449
        $this->workflowService = $workflowService;
450
451
        return $this;
452
    }
453
454
    /**
455
     * @return ReaderInterface
456
     */
457
    public function getMetadataReader()
458
    {
459
        return $this->metadataReader;
460
    }
461
462
    /**
463
     * @param ReaderInterface $metadataReader
464
     *
465
     * @return $this
466
     */
467
    public function setMetadataReader(ReaderInterface $metadataReader)
468
    {
469
        $this->metadataReader = $metadataReader;
470
471
        return $this;
472
    }
473
474
    /**
475
     * @return ValidatorPluginManager
476
     */
477
    public function getValidatorManager()
478
    {
479
        return $this->validatorManager;
480
    }
481
482
    /**
483
     * @param ValidatorPluginManager $validatorManager
484
     *
485
     * @return $this
486
     */
487
    public function setValidatorManager(ValidatorPluginManager $validatorManager)
488
    {
489
        $this->validatorManager = $validatorManager;
490
491
        return $this;
492
    }
493
494
    /**
495
     * @return string
496
     */
497
    public function getWorkflowDispatchEventClassName()
498
    {
499
        return $this->workflowDispatchEventClassName;
500
    }
501
502
    /**
503
     * @param string $workflowDispatchEventClassName
504
     *
505
     * @return $this
506
     */
507
    public function setWorkflowDispatchEventClassName($workflowDispatchEventClassName)
508
    {
509
        $this->workflowDispatchEventClassName = (string)$workflowDispatchEventClassName;
510
511
        return $this;
512
    }
513
514
    /**
515
     * @return string
516
     */
517
    public function getTransientVarsClassName()
518
    {
519
        return $this->transientVarsClassName;
520
    }
521
522
    /**
523
     * @param string $transientVarsClassName
524
     *
525
     * @return $this
526
     */
527
    public function setTransientVarsClassName($transientVarsClassName)
528
    {
529
        $this->transientVarsClassName = (string)$transientVarsClassName;
530
531
        return $this;
532
    }
533
}
534