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

MemoryWorkflowStore::checkExpression()   F

Complexity

Conditions 43
Paths 241

Size

Total Lines 197
Code Lines 117

Duplication

Lines 132
Ratio 67.01 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
c 3
b 0
f 0
dl 132
loc 197
rs 3.4533
cc 43
eloc 117
nc 241
nop 2

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * @link    https://github.com/old-town/old-town-workflow
4
 * @author  Malofeykin Andrey  <[email protected]>
5
 */
6
namespace OldTown\Workflow\Spi\Memory;
7
8
use OldTown\PropertySet\PropertySetInterface;
9
use OldTown\PropertySet\PropertySetManager;
10
use OldTown\Workflow\Exception\ArgumentNotNumericException;
11
use OldTown\Workflow\Exception\InvalidWorkflowEntryException;
12
use OldTown\Workflow\Exception\NotFoundWorkflowEntryException;
13
use OldTown\Workflow\Query\FieldExpression;
14
use OldTown\Workflow\Query\NestedExpression;
15
use OldTown\Workflow\Query\WorkflowExpressionQuery;
16
use OldTown\Workflow\Spi\SimpleStep;
17
use OldTown\Workflow\Spi\SimpleWorkflowEntry;
18
use OldTown\Workflow\Spi\StepInterface;
19
use OldTown\Workflow\Spi\WorkflowEntryInterface;
20
use OldTown\Workflow\Exception\StoreException;
21
use DateTime;
22
use OldTown\Workflow\Spi\WorkflowStoreInterface;
23
use SplObjectStorage;
24
use OldTown\Workflow\Exception\InvalidArgumentException;
25
26
/**
27
 * Class MemoryWorkflowStore
28
 *
29
 * @package OldTown\Workflow\Spi\Memory
30
 */
31
class MemoryWorkflowStore implements WorkflowStoreInterface
32
{
33
    /**
34
     * @var SimpleWorkflowEntry[]
35
     */
36
    protected static $entryCache = [];
37
38
    /**
39
     * @var SplObjectStorage[]|SimpleStep[]
40
     */
41
    protected static $currentStepsCache = [];
42
43
    /**
44
     * @var SplObjectStorage[]|SimpleStep[]
45
     */
46
    protected static $historyStepsCache = [];
47
48
    /**
49
     * @var array
50
     */
51
    protected static $propertySetCache = [];
52
53
    /**
54
     * @var int
55
     */
56
    protected static $globalEntryId = 1;
57
58
    /**
59
     * @var int
60
     */
61
    protected static $globalStepId = 1;
62
63
    /**
64
     * Вызывается один раз, при инициализации хранилища
65
     *
66
     * @param array $props
67
     * @throws StoreException
68
     */
69
    public function init(array $props = [])
70
    {
71
        // TODO: Implement init() method.
72
    }
73
74
    /**
75
     * Возвращает PropertySet that связанный с данным экземпляром workflow
76
     * @param integer $entryId id workflow
77
     * @return PropertySetInterface
78
     * @throws StoreException
79
     * @throws \OldTown\PropertySet\Exception\RuntimeException
80
     * @throws \OldTown\Workflow\Exception\ArgumentNotNumericException
81
     */
82 View Code Duplication
    public function getPropertySet($entryId)
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...
83
    {
84
        if (array_key_exists($entryId, static::$propertySetCache)) {
85
            return static::$propertySetCache[$entryId];
86
        }
87
88
        if (!is_numeric($entryId)) {
89
            $errMsg = sprintf('Аргумент должен быть числом. Актуальное значение %s', $entryId);
90
            throw new ArgumentNotNumericException($errMsg);
91
        }
92
        $entryId = (integer)$entryId;
93
94
        $ps = $this->createPropertySet();
95
        static::$propertySetCache[$entryId] = $ps;
96
97
        return static::$propertySetCache[$entryId];
98
    }
99
100
    /**
101
     * Создаем экземпляр PropertySet
102
     *
103
     * @return PropertySetInterface
104
     */
105
    protected function createPropertySet()
106
    {
107
        return PropertySetManager::getInstance('memory');
108
    }
109
110
    //~ Methods ////////////////////////////////////////////////////////////////
111
112
    /**
113
     * Устанавливает статус для сущности workflow с заданным id
114
     *
115
     * @param int $entryId
116
     * @param int $state
117
     *
118
     * @return $this
119
     * @throws StoreException
120
     * @throws NotFoundWorkflowEntryException
121
     * @throws ArgumentNotNumericException
122
     * @throws InvalidWorkflowEntryException
123
     */
124
    public function setEntryState($entryId, $state)
125
    {
126
        /** @var SimpleWorkflowEntry $theEntry */
127
        $theEntry = $this->findEntry($entryId);
128
        $theEntry->setState($state);
129
    }
130
131
    /**
132
     * Ищет сущность workflow с заданным id во внутреннем кеше
133
     *
134
     * @param int $entryId
135
     *
136
     * @return WorkflowEntryInterface
137
     * @throws NotFoundWorkflowEntryException
138
     * @throws ArgumentNotNumericException
139
     * @throws InvalidWorkflowEntryException
140
     */
141
    public function findEntry($entryId)
142
    {
143
        if (!is_numeric($entryId)) {
144
            $errMsg = sprintf('Аргумент должен быть числом. Актуальное значение %s', $entryId);
145
            throw new ArgumentNotNumericException($errMsg);
146
        }
147
148
        if (!array_key_exists($entryId, static::$entryCache)) {
149
            $errMsg = sprintf('Не найдена сущность workflow с id %s', $entryId);
150
            throw new NotFoundWorkflowEntryException($errMsg);
151
        }
152
153
        $entry = static::$entryCache[$entryId];
154
155
        if (!$entry instanceof WorkflowEntryInterface) {
156
            $errMsg = sprintf('Сущность workflow должна реализовывать интерфейс %s', WorkflowEntryInterface::class);
157
            throw new InvalidWorkflowEntryException($errMsg);
158
        }
159
160
161
        return $entry;
162
    }
163
164
    /**
165
     * Создает экземпляр workflow
166
     *
167
     * @param string $workflowName
168
     *
169
     * @return SimpleWorkflowEntry
170
     */
171
    public function createEntry($workflowName)
172
    {
173
        $id = static::$globalEntryId++;
174
        $entry = new SimpleWorkflowEntry($id, $workflowName, WorkflowEntryInterface::CREATED);
175
        static::$entryCache[$id] = $entry;
176
177
        return $entry;
178
    }
179
180
    /**
181
     * Создает новый шаг
182
     *
183
     * @param integer  $entryId
184
     * @param integer  $stepId
185
     * @param string   $owner
186
     * @param DateTime $startDate
187
     * @param DateTime $dueDate
188
     * @param string   $status
189
     * @param array    $previousIds
190
     *
191
     * @return SimpleStep
192
     */
193
    public function createCurrentStep($entryId, $stepId, $owner = null, DateTime $startDate, DateTime $dueDate = null, $status, array $previousIds = [])
194
    {
195
        $id = static::$globalStepId++;
196
        $step = new SimpleStep($id, $entryId, $stepId, 0, $owner, $startDate, $dueDate, null, $status, $previousIds, null);
197
198
        if (!array_key_exists($entryId, static::$currentStepsCache)) {
199
            $currentSteps = new SplObjectStorage();
200
            static::$currentStepsCache[$entryId] = $currentSteps;
201
        }
202
203
204
        static::$currentStepsCache[$entryId]->attach($step);
0 ignored issues
show
Bug introduced by
The method attach does only exist in SplObjectStorage, but not in OldTown\Workflow\Spi\SimpleStep.

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...
205
206
        return $step;
207
    }
208
209
    /**
210
     * Ищет текущий набор шагов для сущности workflow c заданным id
211
     *
212
     * @param Integer $entryId
213
     *
214
     * @return SimpleStep[]|SplObjectStorage
215
     * @throws ArgumentNotNumericException
216
     */
217 View Code Duplication
    public function findCurrentSteps($entryId)
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...
218
    {
219
        if (!is_numeric($entryId)) {
220
            $errMsg = sprintf('Аргумент должен быть числом. Актуальное значение %s', $entryId);
221
            throw new ArgumentNotNumericException($errMsg);
222
        }
223
        $entryId = (integer)$entryId;
224
225
        if (!array_key_exists($entryId, static::$currentStepsCache)) {
226
            $currentSteps = new SplObjectStorage();
227
            static::$currentStepsCache[$entryId] = $currentSteps;
228
        }
229
230
        return static::$currentStepsCache[$entryId];
0 ignored issues
show
Bug Best Practice introduced by
The return type of return static::$currentStepsCache[$entryId]; (SplObjectStorage|OldTown\Workflow\Spi\SimpleStep) is incompatible with the return type declared by the interface OldTown\Workflow\Spi\Wor...rface::findCurrentSteps of type OldTown\Workflow\Spi\StepInterface[].

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...
231
    }
232
233
    /**
234
     * Пометить текущий шаг как выполненный
235
     *
236
     * @param StepInterface $step
237
     * @param integer       $actionId
238
     * @param DateTime      $finishDate
239
     * @param string        $status
240
     * @param string        $caller
241
     *
242
     * @return null|SimpleStep
243
     *
244
     * @throws ArgumentNotNumericException
245
     */
246
    public function markFinished(StepInterface $step, $actionId, DateTime $finishDate, $status, $caller)
247
    {
248
        $entryId = $step->getEntryId();
249
        $currentSteps = $this->findCurrentSteps($entryId);
250
251
        foreach ($currentSteps as $theStep) {
0 ignored issues
show
Bug introduced by
The expression $currentSteps of type object<SplObjectStorage>...orkflow\Spi\SimpleStep> is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
252
            if ($theStep->getId() === $step->getId()) {
253
                $theStep->setStatus($status);
254
                $theStep->setActionId($actionId);
255
                $theStep->setFinishDate($finishDate);
256
                $theStep->setCaller($caller);
257
258
                return $theStep;
259
            }
260
        }
261
262
        return null;
263
    }
264
265
    /**
266
     * Сбрасывает внутренние кеш хранилища.
267
     */
268
    public static function reset()
269
    {
270
        static::$entryCache = [];
271
        static::$currentStepsCache = [];
272
        static::$historyStepsCache = [];
273
        static::$propertySetCache = [];
274
        static::$globalEntryId = 1;
275
        static::$globalStepId = 1;
276
    }
277
278
    /**
279
     * Перенос шага в историю
280
     *
281
     * @param StepInterface $step
282
     *
283
     * @return void
284
     *
285
     * @throws \OldTown\Workflow\Exception\ArgumentNotNumericException
286
     */
287
    public function moveToHistory(StepInterface $step)
288
    {
289
        $entryId = $step->getEntryId();
290
        $currentSteps = $this->findCurrentSteps($entryId);
291
292
        if (!array_key_exists($entryId, static::$historyStepsCache)) {
293
            $historySteps = new SplObjectStorage();
294
            static::$historyStepsCache[$entryId] = $historySteps;
295
        }
296
297
        foreach ($currentSteps as $currentStep) {
0 ignored issues
show
Bug introduced by
The expression $currentSteps of type object<SplObjectStorage>...orkflow\Spi\SimpleStep> is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
298
            if ($step->getId() === $currentStep->getId()) {
299
                $currentSteps->detach($currentStep);
0 ignored issues
show
Bug introduced by
The method detach does only exist in SplObjectStorage, but not in OldTown\Workflow\Spi\SimpleStep.

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...
300
                foreach (static::$historyStepsCache[$entryId] as $historyStep) {
0 ignored issues
show
Bug introduced by
The expression static::$historyStepsCache[$entryId] of type object<SplObjectStorage>...orkflow\Spi\SimpleStep> is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
301
                    /** @var StepInterface $historyStep */
302
                    if ($historyStep->getId() === $step->getId()) {
303
                        static::$historyStepsCache[$entryId]->detach($historyStep);
304
                    }
305
                }
306
307
                static::$historyStepsCache[$entryId]->attach($currentStep);
0 ignored issues
show
Bug introduced by
The method attach does only exist in SplObjectStorage, but not in OldTown\Workflow\Spi\SimpleStep.

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...
308
309
                break;
310
            }
311
        }
312
    }
313
314
    /**
315
     * Поиск по истории шагов
316
     *
317
     * @param $entryId
318
     *
319
     * @return SimpleStep[]|SplObjectStorage
320
     */
321
    public function findHistorySteps($entryId)
322
    {
323
        if (array_key_exists($entryId, static::$historyStepsCache)) {
324
            return static::$historyStepsCache[$entryId];
0 ignored issues
show
Bug Compatibility introduced by
The expression static::$historyStepsCache[$entryId]; of type SplObjectStorage|OldTown\Workflow\Spi\SimpleStep adds the type OldTown\Workflow\Spi\SimpleStep to the return on line 324 which is incompatible with the return type declared by the interface OldTown\Workflow\Spi\Wor...rface::findHistorySteps of type OldTown\Workflow\Spi\Ste...face[]|SplObjectStorage.
Loading history...
325
        }
326
327
        return new SplObjectStorage();
328
    }
329
330
    /**
331
     * Поиск в хранилище
332
     *
333
     * @param WorkflowExpressionQuery $query
334
     *
335
     * @return array
336
     *
337
     * @throws \OldTown\Workflow\Exception\InvalidArgumentException
338
     */
339
    public function query(WorkflowExpressionQuery $query)
340
    {
341
        $results = [];
342
343
        foreach (static::$entryCache as $entryId => $mapEntry) {
344
            if ($this->queryInternal($entryId, $query)) {
345
                $results[$entryId] = $entryId;
346
            }
347
        }
348
349
        return $results;
350
    }
351
352
    /**
353
     * Реализация поиска в харинилище
354
     *
355
     * @param integer                 $entryId
356
     * @param WorkflowExpressionQuery $query
357
     *
358
     * @return bool
359
     *
360
     * @throws \OldTown\Workflow\Exception\InvalidArgumentException
361
     */
362
    protected function queryInternal($entryId, WorkflowExpressionQuery $query)
363
    {
364
        $expression = $query->getExpression();
365
366
        if ($expression->isNested()) {
367
            return $this->checkNestedExpression($entryId, $expression);
0 ignored issues
show
Compatibility introduced by
$expression of type object<OldTown\Workflow\Query\AbstractExpression> is not a sub-type of object<OldTown\Workflow\Query\NestedExpression>. It seems like you assume a child class of the class OldTown\Workflow\Query\AbstractExpression to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
368
        } else {
369
            return $this->checkExpression($entryId, $expression);
0 ignored issues
show
Compatibility introduced by
$expression of type object<OldTown\Workflow\Query\AbstractExpression> is not a sub-type of object<OldTown\Workflow\Query\FieldExpression>. It seems like you assume a child class of the class OldTown\Workflow\Query\AbstractExpression to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
370
        }
371
    }
372
373
//
374
375
    /**
376
     * Проверка выражения
377
     *
378
     * @param integer         $entryId
379
     * @param FieldExpression $expression
380
     *
381
     * @return bool
382
     *
383
     * @throws InvalidArgumentException
384
     */
385
    private function checkExpression($entryId, FieldExpression $expression)
386
    {
387
        $value = $expression->getValue();
388
        $operator = $expression->getOperator();
389
        $field = $expression->getField();
390
        $context = $expression->getContext();
391
392
        $id = (integer)$entryId;
393
394
        if ($context === FieldExpression::ENTRY) {
395
            $theEntry = static::$entryCache[$id];
396
397
            if ($field === FieldExpression::NAME) {
398
                return $this->compareText($theEntry->getWorkflowName(), $value, $operator);
399
            }
400
401
            if ($field === FieldExpression::STATE) {
402
                if (!is_numeric($value)) {
403
                    $errMsg = 'unknown field';
404
                    throw new InvalidArgumentException($errMsg);
405
                }
406
407
                $valueInt = (integer)$value;
408
409
                return $this->compareLong($valueInt, $theEntry->getState(), $operator);
410
            }
411
412
            $errMsg = 'unknown field';
413
            throw new InvalidArgumentException($errMsg);
414
        }
415
416
        /** @var SplObjectStorage[]|SimpleStep[] $steps */
417
        $steps = [];
418
419
        if ($context === FieldExpression::CURRENT_STEPS) {
420
            $steps = array_key_exists($id, static::$currentStepsCache) ? static::$currentStepsCache[$id] : $steps;
421
        } elseif ($context === FieldExpression::HISTORY_STEPS) {
422
            $steps = array_key_exists($id, static::$historyStepsCache) ? static::$historyStepsCache[$id] : $steps;
423
        } else {
424
            $errMsg = 'unknown field context';
425
            throw new InvalidArgumentException($errMsg);
426
        }
427
428
        if (0 === count($steps)) {
429
            return false;
430
        }
431
432
        $expressionResult = false;
433
434
        switch ($field) {
435 View Code Duplication
            case FieldExpression::ACTION:
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...
436
                if (!is_numeric($value)) {
437
                    $errMsg = 'unknown field';
438
                    throw new InvalidArgumentException($errMsg);
439
                }
440
441
                $actionId = (integer)$value;
442
443
                foreach ($steps as $step) {
0 ignored issues
show
Bug introduced by
The expression $steps of type object<SplObjectStorage>...rkflow\Spi\SimpleStep>> is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
444
                    if ($this->compareLong($step->getActionId(), $actionId, $operator)) {
445
                        $expressionResult = true;
446
447
                        break;
448
                    }
449
                }
450
451
452
                break;
453
454 View Code Duplication
            case FieldExpression::CALLER:
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...
455
                $caller = $value;
456
                if ('string' !== gettype($caller)) {
457
                    $errMsg = 'unknown field';
458
                    throw new InvalidArgumentException($errMsg);
459
                }
460
461
                foreach ($steps as $step) {
0 ignored issues
show
Bug introduced by
The expression $steps of type object<SplObjectStorage>...rkflow\Spi\SimpleStep>> is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
462
                    if ($this->compareText($step->getCaller(), $caller, $operator)) {
463
                        $expressionResult = true;
464
465
                        break;
466
                    }
467
                }
468
469
                break;
470
471 View Code Duplication
            case FieldExpression::FINISH_DATE:
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...
472
                if ($value instanceof DateTime) {
473
                    $finishDate = $value;
474
                    foreach ($steps as $step) {
0 ignored issues
show
Bug introduced by
The expression $steps of type object<SplObjectStorage>...rkflow\Spi\SimpleStep>> is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
475
                        if ($this->compareDate($step->getFinishDate(), $finishDate, $operator)) {
476
                            $expressionResult = true;
477
478
                            break;
479
                        }
480
                    }
481
                } else {
482
                    $errMsg = 'unknown field';
483
                    throw new InvalidArgumentException($errMsg);
484
                }
485
486
                break;
487
488 View Code Duplication
            case FieldExpression::OWNER:
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...
489
                $owner = $value;
490
                if ('string' !== gettype($owner)) {
491
                    $errMsg = 'unknown field';
492
                    throw new InvalidArgumentException($errMsg);
493
                }
494
495
                foreach ($steps as $step) {
0 ignored issues
show
Bug introduced by
The expression $steps of type object<SplObjectStorage>...rkflow\Spi\SimpleStep>> is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
496
                    if ($this->compareText($step->getOwner(), $owner, $operator)) {
497
                        $expressionResult = true;
498
499
                        break;
500
                    }
501
                }
502
503
504
                break;
505
506 View Code Duplication
            case FieldExpression::START_DATE:
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...
507
                if ($value instanceof DateTime) {
508
                    $startDate = $value;
509
                    foreach ($steps as $step) {
0 ignored issues
show
Bug introduced by
The expression $steps of type object<SplObjectStorage>...rkflow\Spi\SimpleStep>> is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
510
                        if ($this->compareDate($step->getStartDate(), $startDate, $operator)) {
511
                            $expressionResult = true;
512
513
                            break;
514
                        }
515
                    }
516
                } else {
517
                    $errMsg = 'unknown field';
518
                    throw new InvalidArgumentException($errMsg);
519
                }
520
521
                break;
522
523 View Code Duplication
            case FieldExpression::STEP:
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...
524
                if (!is_numeric($value)) {
525
                    $errMsg = 'unknown field';
526
                    throw new InvalidArgumentException($errMsg);
527
                }
528
                $stepId = (integer)$value;
529
530
                foreach ($steps as $step) {
0 ignored issues
show
Bug introduced by
The expression $steps of type object<SplObjectStorage>...rkflow\Spi\SimpleStep>> is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
531
                    if ($this->compareLong($step->getStepId(), $stepId, $operator)) {
532
                        $expressionResult = true;
533
534
                        break;
535
                    }
536
                }
537
538
                break;
539
540 View Code Duplication
            case FieldExpression::STATUS:
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...
541
                $status = $value;
542
                if ('string' !== gettype($status)) {
543
                    $errMsg = 'unknown field';
544
                    throw new InvalidArgumentException($errMsg);
545
                }
546
547
                foreach ($steps as $step) {
0 ignored issues
show
Bug introduced by
The expression $steps of type object<SplObjectStorage>...rkflow\Spi\SimpleStep>> is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
548
                    if ($this->compareText($step->getStatus(), $status, $operator)) {
549
                        $expressionResult = true;
550
551
                        break;
552
                    }
553
                }
554
555
                break;
556
557 View Code Duplication
            case FieldExpression::DUE_DATE:
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...
558
                if ($value instanceof DateTime) {
559
                    $dueDate = $value;
560
                    foreach ($steps as $step) {
0 ignored issues
show
Bug introduced by
The expression $steps of type object<SplObjectStorage>...rkflow\Spi\SimpleStep>> is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
561
                        if ($this->compareDate($step->getDueDate(), $dueDate, $operator)) {
562
                            $expressionResult = true;
563
564
                            break;
565
                        }
566
                    }
567
                } else {
568
                    $errMsg = 'unknown field';
569
                    throw new InvalidArgumentException($errMsg);
570
                }
571
572
573
                break;
574
        }
575
576
        if ($expression->isNegate()) {
577
            return !$expressionResult;
578
        } else {
579
            return $expressionResult;
580
        }
581
    }
582
583
    /**
584
     * Проверка вложенных выражений
585
     *
586
     * @param integer          $entryId
587
     * @param NestedExpression $nestedExpression
588
     *
589
     * @return bool
590
     * @throws InvalidArgumentException
591
     */
592
    private function checkNestedExpression($entryId, NestedExpression $nestedExpression)
593
    {
594
        $expressions = $nestedExpression->getExpressions();
595
        foreach ($expressions as $expression) {
596
            if ($expression->isNested()) {
597
                $expressionResult = $this->checkNestedExpression($entryId, $expression);
0 ignored issues
show
Compatibility introduced by
$expression of type object<OldTown\Workflow\Query\AbstractExpression> is not a sub-type of object<OldTown\Workflow\Query\NestedExpression>. It seems like you assume a child class of the class OldTown\Workflow\Query\AbstractExpression to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
598
            } else {
599
                $expressionResult = $this->checkExpression($entryId, $expression);
0 ignored issues
show
Compatibility introduced by
$expression of type object<OldTown\Workflow\Query\AbstractExpression> is not a sub-type of object<OldTown\Workflow\Query\FieldExpression>. It seems like you assume a child class of the class OldTown\Workflow\Query\AbstractExpression to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
600
            }
601
602
            if (false === $expressionResult && $nestedExpression->getExpressionOperator() === NestedExpression::AND_OPERATOR) {
603
                return $nestedExpression->isNegate();
604
            }
605
            if (true === $expressionResult && $nestedExpression->getExpressionOperator() === NestedExpression::OR_OPERATOR) {
606
                return !$nestedExpression->isNegate();
607
            }
608
        }
609
610
611
        if ($nestedExpression->getExpressionOperator() === NestedExpression::AND_OPERATOR) {
612
            return !$nestedExpression->isNegate();
613
        } elseif ($nestedExpression->getExpressionOperator() === NestedExpression::OR_OPERATOR) {
614
            return $nestedExpression->isNegate();
615
        }
616
617
        $errMsg = 'unknown field';
618
        throw new InvalidArgumentException($errMsg);
619
    }
620
621
    /**
622
     * Сравнение дат
623
     *
624
     * @param DateTime $value1
625
     * @param DateTime $value2
626
     * @param integer  $operator
627
     *
628
     * @return bool
629
     * @throws InvalidArgumentException
630
     */
631
    private function compareDate(DateTime $value1, DateTime $value2, $operator)
632
    {
633
        $diff = $value1->format('U') - $value2->format('U');
634
        switch ($operator) {
635
            case FieldExpression::EQUALS:
636
                return $diff === 0;
637
            case FieldExpression::NOT_EQUALS:
638
                return $diff !== 0;
639
640
            case FieldExpression::GT:
641
                return $diff > 0;
642
643
            case FieldExpression::LT:
644
                return $diff < 0;
645
        }
646
647
        $errMsg = 'unknown field operator';
648
        throw new InvalidArgumentException($errMsg);
649
    }
650
651
    /**
652
     * Сравнивает целые числа
653
     *
654
     * @param string  $value1
655
     * @param string  $value2
656
     * @param integer $operator
657
     *
658
     * @return bool
659
     *
660
     * @throws InvalidArgumentException
661
     */
662
    private function compareLong($value1, $value2, $operator)
663
    {
664
        switch ($operator) {
665
            case FieldExpression::EQUALS:
666
                return $value1 === $value2;
667
668
            case FieldExpression::NOT_EQUALS:
669
                return $value1 !== $value2;
670
671
            case FieldExpression::GT:
672
                return $value1 > $value2;
673
674
            case FieldExpression::LT:
675
                return $value1 < $value2;
676
        }
677
678
        $errMsg = 'unknown field operator';
679
        throw new InvalidArgumentException($errMsg);
680
    }
681
682
    /**
683
     *
684
     * @todo поправить для юникода
685
     *
686
     * Сравнение строк
687
     *
688
     * @param $value1
689
     * @param $value2
690
     * @param $operator
691
     *
692
     * @return bool
693
     *
694
     * @throws \OldTown\Workflow\Exception\InvalidArgumentException
695
     */
696
    private function compareText($value1, $value2, $operator)
697
    {
698
        switch ($operator) {
699
            case FieldExpression::EQUALS:
700
                return $value1 === $value2;
701
702
            case FieldExpression::NOT_EQUALS:
703
                return $value1 !== $value2;
704
705
            case FieldExpression::GT:
706
                return strlen($value1) > strlen($value2);
707
708
            case FieldExpression::LT:
709
                return strlen($value1) < strlen($value2);
710
        }
711
712
        $errMsg = 'unknown field operator';
713
        throw new InvalidArgumentException($errMsg);
714
    }
715
}
716