Passed
Push — master ( c776c7...570285 )
by Kirill
04:05
created

InjectPHP::isReference()   B

Complexity

Conditions 9
Paths 9

Size

Total Lines 29
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 9
eloc 16
c 1
b 0
f 0
nc 9
nop 1
dl 0
loc 29
rs 8.0555
1
<?php
2
3
/**
4
 * Spiral Framework.
5
 *
6
 * @license   MIT
7
 * @author    Anton Titov (Wolfy-J)
8
 */
9
10
declare(strict_types=1);
11
12
namespace Spiral\Stempler\Transform\Merge\Inject;
13
14
use Spiral\Stempler\Node\Block;
15
use Spiral\Stempler\Node\Dynamic\Output;
16
use Spiral\Stempler\Node\Mixin;
17
use Spiral\Stempler\Node\NodeInterface;
18
use Spiral\Stempler\Node\PHP;
19
use Spiral\Stempler\Node\Raw;
20
use Spiral\Stempler\Transform\BlockClaims;
21
use Spiral\Stempler\Transform\BlockFetcher;
22
use Spiral\Stempler\Transform\QuotedValue;
23
use Spiral\Stempler\VisitorContext;
24
use Spiral\Stempler\VisitorInterface;
25
26
/**
27
 * Injects block values into PHP source code using marco function.
28
 */
29
final class InjectPHP implements VisitorInterface
30
{
31
    // php marcos to inject values into
32
    private const PHP_MACRO_FUNCTION = 'inject';
33
34
    private const PHP_MARCO_EXISTS_FUNCTION = 'injected';
35
36
    /** @var BlockClaims */
37
    private $blocks;
38
39
    /** @var BlockFetcher */
40
    private $fetcher;
41
42
    /**
43
     * @param BlockClaims $blocks
44
     */
45
    public function __construct(BlockClaims $blocks)
46
    {
47
        $this->blocks = $blocks;
48
        $this->fetcher = new BlockFetcher();
49
    }
50
51
    /**
52
     * @inheritDoc
53
     */
54
    public function enterNode($node, VisitorContext $ctx)
55
    {
56
        if (
57
            !$node instanceof PHP
58
            || (
59
                strpos($node->content, self::PHP_MACRO_FUNCTION) === false
60
                && strpos($node->content, self::PHP_MARCO_EXISTS_FUNCTION) === false
61
            )
62
        ) {
63
            return null;
64
        }
65
66
        $php = new PHPMixin($node->tokens, self::PHP_MACRO_FUNCTION);
67
        foreach ($this->blocks->getNames() as $name) {
68
            $block = $this->blocks->get($name);
69
70
            if ($this->isReference($block)) {
71
                // resolved on later stage
72
                continue;
73
            }
74
75
            if ($php->has($name)) {
76
                $php->set($name, $this->trimPHP($this->blocks->claim($name)));
77
            }
78
        }
79
80
        $node->content = $php->compile();
81
        $node->tokens = token_get_all($node->content);
82
83
        $exists = new PHPMixin($node->tokens, self::PHP_MARCO_EXISTS_FUNCTION);
84
        foreach ($this->blocks->getNames() as $name) {
85
            $block = $this->blocks->get($name);
86
87
            if ($this->isReference($block)) {
88
                // resolved on later stage
89
                continue;
90
            }
91
92
            if ($exists->has($name)) {
93
                $exists->set($name, 'true');
94
            }
95
        }
96
97
        $node->content = $exists->compile();
98
        $node->tokens = token_get_all($node->content);
99
    }
100
101
    /**
102
     * @inheritDoc
103
     */
104
    public function leaveNode($node, VisitorContext $ctx): void
105
    {
106
    }
107
108
    /**
109
     * @param array|NodeInterface $node
110
     * @return bool
111
     */
112
    private function isReference($node): bool
113
    {
114
        switch (true) {
115
            case is_array($node):
116
                foreach ($node as $child) {
117
                    if ($this->isReference($child)) {
118
                        return true;
119
                    }
120
                }
121
122
                return false;
123
124
            case $node instanceof QuotedValue:
125
                return $this->isReference($node->getValue());
126
127
            case $node instanceof Mixin:
128
                foreach ($node->nodes as $child) {
129
                    if ($this->isReference($child)) {
130
                        return true;
131
                    }
132
                }
133
134
                return false;
135
136
            case $node instanceof Block:
137
                return true;
138
        }
139
140
        return false;
141
    }
142
143
    /**
144
     * @param array|NodeInterface $node
145
     * @return string
146
     */
147
    private function trimPHP($node): string
148
    {
149
        switch (true) {
150
            case is_array($node):
151
                $result = [];
152
                foreach ($node as $child) {
153
                    $result[] = $this->trimPHP($child);
154
                }
155
156
                return join('.', $result);
157
158
            case $node instanceof Mixin:
159
                $result = [];
160
                foreach ($node->nodes as $child) {
161
                    $result[] = $this->trimPHP($child);
162
                }
163
164
                return join('.', $result);
165
166
            case $node instanceof Raw:
167
                return $this->exportValue($node);
168
169
            case $node instanceof Output:
170
                return trim($node->body);
171
172
            case $node instanceof PHP:
173
                if ($node->getContext()->getValue(PHP::ORIGINAL_BODY) !== null) {
174
                    return $node->getContext()->getValue(PHP::ORIGINAL_BODY);
175
                }
176
177
                return (new PHPMixin($node->tokens, self::PHP_MACRO_FUNCTION))->trimBody();
178
179
            case $node instanceof QuotedValue:
180
                return $this->trimPHP($node->trimValue());
181
        }
182
183
        return 'null';
184
    }
185
186
    /**
187
     * @param Raw $node
188
     * @return string
189
     */
190
    private function exportValue(Raw $node): string
191
    {
192
        $value = $node->content;
193
        switch (true) {
194
            case strtolower($value) === 'true':
195
                return 'true';
196
            case strtolower($value) === 'false':
197
                return 'false';
198
            case is_float($value) || is_numeric($value):
199
                return (string) $value;
200
        }
201
202
        return var_export($node->content, true);
203
    }
204
}
205