Passed
Push — master ( b817d9...04ae87 )
by 世昌
02:23
created

Compiler::init()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 3
nc 2
nop 0
dl 0
loc 5
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
    public function init()
39
    {
40
        $this->registerCommand(new Command);
41
        foreach ($this->tag as $name => $value) {
42
            $this->registerTag(new Tag($name, $value[0], $value[1], $value[2]));
43
        }
44
    }
45
46
    /**
47
     * 注册命令
48
     *
49
     * @param CommandInterface $command
50
     * @return void
51
     */
52
    public function registerCommand(CommandInterface $command)
53
    {
54
        $this->commands[] = $command;
55
    }
56
57
    /**
58
     * 注册标记
59
     *
60
     * @param Tag $tag
61
     * @return void
62
     */
63
    public function registerTag(Tag $tag)
64
    {
65
        $this->tags[$tag->getName()] =$tag;
66
    }
67
68
    /**
69
     * 编译
70
     *
71
     * @param string $text 代码文本
72
     * @param array $tagConfig 标签配置
73
     * @return string
74
     * @throws Exception
75
     */
76
    public function compileText(string $text, array $tagConfig = []):string
77
    {
78
        $this->applyTagConfig($tagConfig);
79
        $result  = '';
80
        foreach (token_get_all($text) as $token) {
81
            if (is_array($token)) {
82
                list($tag, $content) = $token;
83
                // 所有将要编译的文本
84
                // 跳过各种的PHP
85
                if ($tag == T_INLINE_HTML) {
86
                    $content=$this->proccesTags($content);
87
                    $content=$this->processCommands($content);
88
                }
89
                $result .= $content;
90
            } else {
91
                $result .= $token;
92
            }
93
        }
94
        return $result;
95
    }
96
97
    protected function applyTagConfig(array $config)
98
    {
99
        foreach ($config as $name => $tagConfig) {
100
            if (array_key_exists($name, $this->tags)) {
101
                $this->tags[$name]->setConfig($tagConfig);
102
            }
103
        }
104
    }
105
106
107
    protected function proccesTags(string $text):string
108
    {
109
        foreach ($this->tags as $tag) {
110
            $pregExp = sprintf('/(!)?%s\s*(.+?)\s*%s/', preg_quote($tag->getOpen()), preg_quote($tag->getClose()));
111
            $text = preg_replace_callback($pregExp, function ($match) use ($tag) {
112
                if ($match[1] === '!') {
113
                    return substr($match[0], 1);
114
                } else {
115
                    return $tag->compile($match[2]);
116
                }
117
            }, $text);
118
        }
119
        return $text;
120
    }
121
122
    /**
123
     * @param string $text
124
     * @return string
125
     * @throws Exception
126
     */
127
    protected function processCommands(string $text):string
128
    {
129
        $pregExp ='/\B\@(\!)?([\w\x{4e00}-\x{9aff}]+)(\s*)(\( ( (?>[^()]+) | (?4) )* \) )? /ux';
130
        $code = preg_replace_callback($pregExp, [$this,'doMatchCommand'], $text);
131
        $error = preg_last_error();
132
        if ($error !== PREG_NO_ERROR) {
133
            throw new Exception($error);
134
        }
135
        return $code;
136
    }
137
138
    protected function doMatchCommand($match)
139
    {
140
        if (count($match) >= 5) {
141
            list($input, $ignore, $name, $space, $params) = $match;
142
        } else {
143
            list($input, $ignore, $name, $space) = $match;
144
            $params = '';
145
        }
146
        if ($ignore ==='!') {
147
            return str_replace('@!', '@', $input);
148
        } else {
149
            foreach ($this->commands as $command) {
150
                if ($command->has($name)) {
151
                    $code = $command->parse($name, $params);
152
                    return empty($params) ? $code : $code.$space;
153
                }
154
            }
155
            return $input;
156
        }
157
    }
158
}
159