Compiler::processCommands()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 6
nc 2
nop 1
dl 0
loc 9
rs 10
c 0
b 0
f 0
1
<?php
2
namespace suda\application\template\compiler;
3
4
use Exception;
5
use function preg_replace_callback;
6
use function str_replace;
7
8
/**
9
 * 可执行命令表达式
10
 *
11
 */
12
class Compiler
13
{
14
    /**
15
     * 定义的标签
16
     *
17
     * @var array
18
     */
19
    protected $tag = [
20
        'raw' => ['{{!', '}}', '<?php echo $code; ?>'],
21
        'comment' => ['{--', '--}', '<?php /* $code */ ?>'],
22
    ];
23
24
    /**
25
     * 编译的TAG
26
     *
27
     * @var Tag[]
28
     */
29
    protected $tags=[];
30
31
    /**
32
     * 命令对象
33
     *
34
     * @var CommandInterface[]
35
     */
36
    protected $commands=[];
37
38
    /**
39
     * 初始化
40
     */
41
    public function init()
42
    {
43
        $this->registerCommand(new Command);
44
        foreach ($this->tag as $name => $value) {
45
            $this->registerTag(new Tag($name, $value[0], $value[1], $value[2]));
46
        }
47
    }
48
49
    /**
50
     * @return Tag[]
51
     */
52
    public function getTags(): array
53
    {
54
        return $this->tags;
55
    }
56
57
    /**
58
     * @param Tag[] $tags
59
     */
60
    public function setTags(array $tags): void
61
    {
62
        $this->tags = $tags;
63
    }
64
65
    /**
66
     * 注册命令
67
     *
68
     * @param CommandInterface $command
69
     * @return void
70
     */
71
    public function registerCommand(CommandInterface $command)
72
    {
73
        $this->commands[] = $command;
74
    }
75
76
    /**
77
     * 注册标记
78
     *
79
     * @param Tag $tag
80
     * @return void
81
     */
82
    public function registerTag(Tag $tag)
83
    {
84
        $this->tags[$tag->getName()] =$tag;
85
    }
86
87
    /**
88
     * 编译
89
     *
90
     * @param string $text 代码文本
91
     * @param array $tagConfig 标签配置
92
     * @return string
93
     * @throws Exception
94
     */
95
    public function compileText(string $text, array $tagConfig = []):string
96
    {
97
        $this->applyTagConfig($tagConfig);
98
        $result  = '';
99
        foreach (token_get_all($text) as $token) {
100
            if (is_array($token)) {
101
                list($tag, $content) = $token;
102
                // 所有将要编译的文本
103
                // 跳过各种的PHP
104
                if ($tag == T_INLINE_HTML) {
105
                    $content=$this->processTags($content);
106
                    $content=$this->processCommands($content);
107
                }
108
                $result .= $content;
109
            } else {
110
                $result .= $token;
111
            }
112
        }
113
        return $result;
114
    }
115
116
    /**
117
     * 替换标签配置
118
     * @param array $config
119
     */
120
    protected function applyTagConfig(array $config)
121
    {
122
        foreach ($config as $name => $tagConfig) {
123
            if (array_key_exists($name, $this->tags)) {
124
                // 创建标签副本
125
                $tag = clone $this->tags[$name];
126
                $tag->setConfig($tagConfig);
127
                $this->tags[$name] = $tag;
128
            }
129
        }
130
    }
131
132
    /**
133
     * 处理标签
134
     *
135
     * @param string $text
136
     * @return string
137
     */
138
    protected function processTags(string $text):string
139
    {
140
        foreach ($this->tags as $tag) {
141
            $pregExp = sprintf('/(!)?%s\s*(.+?)\s*%s/', preg_quote($tag->getOpen()), preg_quote($tag->getClose()));
142
            $text = preg_replace_callback($pregExp, function ($match) use ($tag) {
143
                if ($match[1] === '!') {
144
                    return substr($match[0], 1);
145
                } else {
146
                    return $tag->compile($match[2]);
147
                }
148
            }, $text);
149
        }
150
        return $text;
151
    }
152
153
    /**
154
     * @param string $text
155
     * @return string
156
     * @throws Exception
157
     */
158
    protected function processCommands(string $text):string
159
    {
160
        $pregExp ='/\B\@(\!)?([\w\x{4e00}-\x{9aff}]+)(\s*)(\( ( (?>[^()]+) | (?4) )* \) )? /ux';
161
        $code = preg_replace_callback($pregExp, [$this,'doMatchCommand'], $text);
162
        $error = preg_last_error();
163
        if ($error !== PREG_NO_ERROR) {
164
            throw new Exception($error);
165
        }
166
        return $code;
167
    }
168
169
    protected function doMatchCommand($match)
170
    {
171
        if (count($match) >= 5) {
172
            list($input, $ignore, $name, $space, $params) = $match;
173
        } else {
174
            list($input, $ignore, $name, $space) = $match;
175
            $params = '';
176
        }
177
        if ($ignore ==='!') {
178
            return str_replace('@!', '@', $input);
179
        } else {
180
            foreach ($this->commands as $command) {
181
                if ($command->has($name)) {
182
                    $code = $command->parse($name, $params);
183
                    return empty($params) ? $code : $code.$space;
184
                }
185
            }
186
            return $input;
187
        }
188
    }
189
}
190