Test Failed
Push — master ( 6bac61...6350a6 )
by Kirill
03:02
created

SystemManager::extract()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
nc 4
nop 2
dl 0
loc 21
rs 9.584
c 0
b 0
f 0
1
<?php
2
/**
3
 * This file is part of Railt package.
4
 *
5
 * For the full copyright and license information, please view the LICENSE
6
 * file that was distributed with this source code.
7
 */
8
declare(strict_types=1);
9
10
namespace Railt\SDL\Frontend;
11
12
use Railt\Io\Readable;
13
use Railt\Parser\Ast\RuleInterface;
14
use Railt\SDL\Compiler\Process\DeferredInterface;
15
use Railt\SDL\Compiler\Process\Emittable;
16
use Railt\SDL\Compiler\Process\Pipeline;
17
use Railt\SDL\Frontend\IR\Prototype;
18
use Railt\SDL\Frontend\System\DefinitionSystem;
19
use Railt\SDL\Frontend\System\SystemInterface;
20
21
/**
22
 * Class SystemManager
23
 */
24
class SystemManager
25
{
26
    /**
27
     * @var string[]
28
     */
29
    private const DEFAULT_SYSTEMS = [
30
        DefinitionSystem::class
31
    ];
32
33
    /**
34
     * @var array|SystemInterface[]
35
     */
36
    private $systems = [];
37
38
    /**
39
     * @var Pipeline
40
     */
41
    private $pipeline;
42
43
    /**
44
     * SystemManager constructor.
45
     */
46
    public function __construct()
47
    {
48
        $this->pipeline = new Pipeline();
49
50
        $this->bootDefaultSystems();
51
    }
52
53
    /**
54
     * @return void
55
     */
56
    private function bootDefaultSystems(): void
57
    {
58
        foreach (self::DEFAULT_SYSTEMS as $system) {
59
            $this->systems[] = new $system($this);
60
        }
61
    }
62
63
    /**
64
     * @param SystemInterface $system
65
     * @return SystemManager
66
     */
67
    public function addSystem(SystemInterface $system): SystemManager
68
    {
69
        $this->systems[] = $system;
70
71
        return $this;
72
    }
73
74
    /**
75
     * @param Readable $readable
76
     * @param RuleInterface $ast
77
     * @return \Generator
78
     */
79
    public function run(Readable $readable, RuleInterface $ast): \Generator
80
    {
81
        foreach ($ast as $child) {
82
            yield from $this->extract($readable, $this->apply($readable, $child));
83
            yield from $this->extract($readable, $this->runDeferred());
84
        }
85
    }
86
87
    /**
88
     * @return \Generator
89
     */
90
    private function runDeferred(): \Generator
91
    {
92
        /** @var Emittable $deferred */
93
        foreach ($this->pipeline as $deferred) {
94
            $result = $deferred->emit();
95
96
            if (\is_iterable($result)) {
97
                yield from $result;
98
            } else {
99
                yield $result;
100
            }
101
        }
102
    }
103
104
    /**
105
     * Applies additional result analyse.
106
     *
107
     * @param Readable $readable
108
     * @param \Generator $result
109
     * @return \Generator
110
     */
111
    private function extract(Readable $readable, \Generator $result): \Generator
112
    {
113
        while ($result->valid()) {
114
            [$key, $value] = [$result->key(), $result->current()];
0 ignored issues
show
Bug introduced by
The variable $key does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
Bug introduced by
The variable $value does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
115
116
            switch (true) {
117
                case \is_callable($value):
118
                    $result->send($this->deferred((int)$key, $value));
119
                    break;
120
121
                case $value instanceof Prototype:
122
                    yield $key => $value->create($readable, 0);
123
                    $result->next();
124
                    break;
125
126
                default:
127
                    yield $key => $value;
128
                    $result->next();
129
            }
130
        }
131
    }
132
133
    /**
134
     * @param int $priority
135
     * @param callable $value
136
     * @return Emittable|DeferredInterface
137
     */
138
    private function deferred(int $priority, callable $value): Emittable
139
    {
140
        $callback = $value instanceof \Closure ? $value : \Closure::fromCallable($value);
141
142
        return $this->pipeline->on($priority)->then($callback);
0 ignored issues
show
Bug introduced by
The method then does only exist in Railt\SDL\Compiler\Process\DeferredInterface, but not in Railt\SDL\Compiler\Process\Emittable.

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...
143
    }
144
145
    /**
146
     * Applies systems to all AST nodes.
147
     *
148
     * @param Readable $readable
149
     * @param RuleInterface $ast
150
     * @return \Generator
151
     */
152
    private function apply(Readable $readable, RuleInterface $ast): \Generator
153
    {
154
        foreach ($this->systems as $system) {
155
            if ($system->match($ast)) {
156
                $result = $system->apply($readable, $ast);
157
158
                if ($result instanceof \Generator) {
159
                    yield from $result;
160
                }
161
            }
162
        }
163
    }
164
}
165