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

PHPMixin::trimBody()   B

Complexity

Conditions 7
Paths 15

Size

Total Lines 32
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 7
eloc 17
c 1
b 0
f 0
nc 15
nop 0
dl 0
loc 32
rs 8.8333
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
/**
15
 * PHPMixin provides the ability to safely inject nodes into PHP source code using given macro function.
16
 */
17
final class PHPMixin
18
{
19
    /** @var array */
20
    private $tokens = [];
21
22
    /** @var array */
23
    private $blocks = [];
24
25
    /**
26
     * @param array  $tokens
27
     * @param string $func
28
     */
29
    public function __construct(array $tokens, string $func)
30
    {
31
        $this->tokens = $tokens;
32
        $this->parse($func);
33
    }
34
35
    /**
36
     * @return string
37
     */
38
    public function compile(): string
39
    {
40
        $replace = [];
41
42
        foreach ($this->blocks as $block) {
43
            for ($i = $block['start']; $i <= $block['end']; $i++) {
44
                $replace[$i] = '';
45
            }
46
47
            $replace[$block['start']] = $block['value'];
48
        }
49
50
        $result = '';
51
        foreach ($this->tokens as $position => $token) {
52
            if (array_key_exists($position, $replace)) {
53
                $result .= $replace[$position];
54
                continue;
55
            }
56
57
            if (is_string($token)) {
58
                $result .= $token;
59
                continue;
60
            }
61
62
            $result .= $token[1];
63
        }
64
65
        return $result;
66
    }
67
68
    /**
69
     * Compiles the PHP blocks (with replacements) but excludes the php open, close tag and echo function.
70
     *
71
     * @return string
72
     */
73
    public function trimBody(): string
74
    {
75
        $replace = [];
76
77
        foreach ($this->blocks as $block) {
78
            for ($i = $block['start']; $i <= $block['end']; $i++) {
79
                $replace[$i] = '';
80
            }
81
82
            $replace[$block['start']] = $block['value'];
83
        }
84
85
        $result = '';
86
        foreach ($this->tokens as $position => $token) {
87
            if (array_key_exists($position, $replace)) {
88
                $result .= $replace[$position];
89
                continue;
90
            }
91
92
            if (is_string($token)) {
93
                $result .= $token;
94
                continue;
95
            }
96
97
            if (in_array($token[0], [T_OPEN_TAG, T_OPEN_TAG_WITH_ECHO, T_CLOSE_TAG, T_ECHO])) {
98
                continue;
99
            }
100
101
            $result .= $token[1];
102
        }
103
104
        return rtrim(trim($result), ';');
105
    }
106
107
    /**
108
     * Get macros detected in PHP code and their default values (if any).
109
     *
110
     * @return array
111
     */
112
    public function getBlocks(): array
113
    {
114
        $result = [];
115
        foreach ($this->blocks as $name => $macro) {
116
            $result[$name] = $macro['value'];
117
        }
118
119
        return $result;
120
    }
121
122
    /**
123
     * @param string $block
124
     * @return bool
125
     */
126
    public function has(string $block): bool
127
    {
128
        return isset($this->blocks[$block]);
129
    }
130
131
    /**
132
     * @param string $block
133
     * @param string $value
134
     */
135
    public function set(string $block, string $value): void
136
    {
137
        if (!isset($this->blocks[$block])) {
138
            return;
139
        }
140
141
        $this->blocks[$block]['value'] = $value;
142
    }
143
144
    /**
145
     * @param string $func
146
     */
147
    private function parse(string $func): void
148
    {
149
        $level = 0;
150
        $start = $name = $value = null;
151
        foreach ($this->tokens as $position => $token) {
152
            if (!is_array($token)) {
153
                $token = [$token, $token, 0];
154
            }
155
156
            switch ($token[0]) {
157
                case '(':
158
                    if ($start !== null) {
159
                        $level++;
160
                        $value .= $token[1];
161
                    }
162
                    break;
163
                case ')':
164
                    if ($start === null) {
165
                        break;
166
                    }
167
168
                    $level--;
169
                    $value .= $token[1];
170
                    if ($level === 0) {
171
                        $this->blocks[$name] = [
172
                            'start' => $start,
173
                            'value' => trim($value),
174
                            'end'   => $position
175
                        ];
176
177
                        // reset
178
                        $start = $name = $value = null;
179
                    }
180
                    break;
181
                case T_STRING:
182
                    if ($token[1] === $func) {
183
                        $start = $position;
184
                        $value = $token[1];
185
                        break;
186
                    }
187
188
                    if ($start !== null) {
189
                        $value .= $token[1];
190
                    }
191
                    break;
192
                case T_CONSTANT_ENCAPSED_STRING:
193
                    if ($start === null) {
194
                        break;
195
                    }
196
197
                    if ($name === null) {
198
                        $name = stripcslashes(substr($token[1], 1, -1));
199
                    }
200
                    $value .= $token[1];
201
                    break;
202
                case ',':
203
                    $value .= $token[1];
204
                    break;
205
                default:
206
                    if ($start !== null) {
207
                        $value .= $token[1];
208
                    }
209
            }
210
        }
211
    }
212
}
213