Passed
Push — dev ( b8029f...5026e0 )
by 世昌
02:31
created

CompilableTemplate::isRaw()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace suda\application\template;
4
5
use Exception;
6
use ReflectionException;
7
use suda\application\Resource;
8
use suda\framework\runnable\Runnable;
9
use suda\framework\filesystem\FileSystem;
10
use suda\application\template\compiler\Compiler;
11
use suda\application\exception\NoTemplateFoundException;
12
13
/**
14
 * 可编译模板
15
 */
16
class CompilableTemplate extends RawTemplate
17
{
18
    /**
19
     * 模板源
20
     *
21
     * @var string
22
     */
23
    protected $source;
24
25
    /**
26
     * 模板配置
27
     *
28
     * @var array
29
     */
30
    protected $config;
31
32
    /**
33
     * 输出目录
34
     *
35
     * @var string
36
     */
37
    protected $output;
38
39
    /**
40
     * 父模版
41
     *
42
     * @var CompilableTemplate|null
43
     */
44
    protected $parent = null;
45
46
47
    /**
48
     * 模板钩子
49
     *
50
     * @var array
51
     */
52
    protected $hooks = [];
53
54
    /**
55
     * 继承的模板
56
     *
57
     * @var string|null
58
     */
59
    protected $extend = null;
60
61
    /**
62
     * 编译器
63
     *
64
     * @var Compiler
65
     */
66
    protected static $compiler;
67
68
    /**
69
     * 检测已经拷贝的目录
70
     *
71
     * @var array
72
     */
73
    protected static $copyedStaticPaths = [];
74
75
    /**
76
     * 静态目录
77
     *
78
     * @var string
79
     */
80
    protected $staticPath;
81
82
    /**
83
     * 模板名
84
     *
85
     * @var string
86
     */
87
    protected $name;
88
89
    /**
90
     * PHP模板
91
     * @var bool
92
     */
93
    protected $raw;
94
95
    /**
96
     * 构建模板
97
     *
98
     * @param string $source
99
     * @param array $config
100
     * @param bool $raw
101
     */
102
    public function __construct(string $source, array $config = [], bool $raw = false)
103
    {
104
        parent::__construct('', []);
105
        $this->source = $source;
106
        $this->name = pathinfo($source, PATHINFO_FILENAME);
107
        $this->config = $config;
108
        $this->raw = $raw;
109
    }
110
111
    protected function getStaticPath(?string $name = null)
112
    {
113
        $name = is_null($name) ? $this->config['static'] ?? 'static' : $name;
114
        return Resource::getPathByRelativePath($name, dirname($this->getSourcePath()));
115
    }
116
117
    protected function getStaticOutputPath(?string $name = null)
118
    {
119
        $public = defined('SUDA_PUBLIC') ? constant('SUDA_PUBLIC') : '.';
120
        $path = $this->config['assets-public'] ?? $public . '/assets/' . $this->getStaticName($name);
121
        FileSystem::make($path);
122
        return $path;
123
    }
124
125
    /**
126
     * 获取编译后的路径
127
     *
128
     * @return string
129
     */
130
    public function getPath()
131
    {
132
        if ($this->isRaw()) {
133
            return $this->source;
134
        }
135
        $output = $this->config['output'] ?? constant('SUDA_DATA') . '/template';
136
        FileSystem::make($output);
137
        return $output . '/' . $this->name . '-' . substr(md5_file($this->getSourcePath()), 10, 8) . '.php';
138
    }
139
140
    /**
141
     * @return bool
142
     */
143
    public function isRaw(): bool
144
    {
145
        return $this->raw;
146
    }
147
148
    /**
149
     * 获取源路径
150
     *
151
     * @return string|null
152
     */
153
    public function getSourcePath(): ?string
154
    {
155
        return $this->source ?? null;
156
    }
157
158
    /**
159
     * 输出
160
     * @ignore-dump
161
     * @return string
162
     * @throws Exception
163
     */
164
    public function getRenderedString()
165
    {
166
        $this->compile();
167
        return $this->render();
168
    }
169
170
    /**
171
     * 编译
172
     *
173
     * @return bool
174
     * @throws Exception
175
     */
176
    protected function compile()
177
    {
178
        if ($this->isCompiled() === false && ($sourcePath = $this->getSourcePath()) !== null) {
179
            $destPath = $this->getPath();
180
            $content = FileSystem::get($sourcePath);
181
            if ($content !== null) {
182
                $compiled = $this->compiler()->compileText($content, $this->config);
183
                FileSystem::make(dirname($destPath));
184
                FileSystem::put($destPath, $compiled);
185
            }
186
            return true;
187
        }
188
        return false;
189
    }
190
191
    /**
192
     * 检查是否编译过
193
     * @return bool
194
     */
195
    protected function isCompiled()
196
    {
197
        $sourcePath = $this->getSourcePath();
198
        if ($sourcePath === null) {
199
            throw new NoTemplateFoundException(
200
                'missing source ' . $this->name,
201
                E_USER_ERROR,
202
                $this->name,
203
                NoTemplateFoundException::T_SOURCE
204
            );
205
        }
206
        $source = FileSystem::exist($sourcePath);
207
        $dest = FileSystem::exist($this->getPath());
208
        $isDebug = defined('SUDA_DEBUG') ? constant('SUDA_DEBUG') : false;
209
        $notCompiled = $source === true && $dest === false;
210
        return ($notCompiled || $isDebug) === false;
211
    }
212
213
    /**
214
     * 获取渲染后的字符串
215
     * @ignore-dump
216
     * @throws Exception
217
     */
218
    public function render()
219
    {
220
        ob_start();
221
        echo parent::getRenderedString();
222
        if ($this->extend) {
223
            $this->include($this->extend);
224
        }
225
        $content = trim(ob_get_clean());
226
        return $content;
227
    }
228
229
    /**
230
     * 创建模板
231
     * @param $template
232
     * @return CompilableTemplate
233
     */
234
    public function parent($template)
235
    {
236
        $this->parent = $template;
237
        return $this;
238
    }
239
240
241
    public function extend(string $name)
242
    {
243
        $this->extend = $name;
244
    }
245
246
    /**
247
     * @param string $path
248
     * @throws Exception
249
     */
250
    public function include(string $path)
251
    {
252
        $subfix = $this->config['subfix'] ?? '';
253
        $included = new self(Resource::getPathByRelativePath($path . $subfix, dirname($this->source)), $this->config);
254
        $included->parent = $this;
255
        echo $included->getRenderedString();
256
    }
257
258
    /**
259
     * @param string $name
260
     * @param mixed ...$args
261
     * @return mixed
262
     * @throws ReflectionException
263
     */
264
    public function data(string $name, ...$args)
265
    {
266
        if (func_num_args() > 1) {
267
            return (new Runnable($name))->run($this, ...$args);
268
        }
269
        return (new Runnable($name))->apply([$this]);
270
    }
271
272
    protected function getStaticPrefix(?string $name = null)
273
    {
274
        $this->prepareStaticSource($name);
275
        if (array_key_exists('assets-prefix', $this->config)) {
276
            $prefix = $this->config['assets-prefix'];
277
        } elseif (defined('SUDA_ASSETS')) {
278
            $prefix = constant('SUDA_ASSETS');
279
        } else {
280
            $prefix = '/assets';
281
        }
282
        return '/' . ltrim($prefix, '/') . '/' . $this->getStaticName($name);
283
    }
284
285
    protected function prepareStaticSource(?string $name = null)
286
    {
287
        $isDebug = defined('SUDA_DEBUG') ? constant('SUDA_DEBUG') : false;
288
        $staticPath = $this->getStaticPath($name);
289
        if ($isDebug &&
290
            is_dir($staticPath) &&
291
            !in_array($staticPath, static::$copyedStaticPaths)) {
292
            FileSystem::copyDir($staticPath, $this->getStaticOutputPath($name));
293
            static::$copyedStaticPaths[] = $staticPath;
294
        }
295
    }
296
297
    protected function getStaticName(?string $name = null)
298
    {
299
        return $this->config['static-name'] ?? substr(md5($this->getStaticPath($name)), 0, 8);
300
    }
301
302
    public function insert(string $name, $callback)
303
    {
304
        // 存在父模板
305
        if ($this->parent) {
306
            $this->parent->insert($name, $callback);
307
        } else {
308
            // 添加回调钩子
309
            $this->hooks[$name][] = new Runnable($callback);
310
        }
311
    }
312
313
    public function exec(string $name)
314
    {
315
        try {
316
            // 存在父模板
317
            if ($this->parent) {
318
                $this->parent->exec($name);
319
            } elseif (isset($this->hooks[$name])) {
320
                foreach ($this->hooks[$name] as $hook) {
321
                    $hook->run();
322
                }
323
            }
324
        } catch (Exception $e) {
325
            echo '<div style="color:red">' . $e->getMessage() . '</div>';
326
            return;
327
        }
328
    }
329
330
    protected function compiler()
331
    {
332
        if (static::$compiler === null) {
333
            static::$compiler = $this->createCompiler();
334
        }
335
        return static::$compiler;
336
    }
337
338
    protected function createCompiler(): Compiler
339
    {
340
        $compiler = new Compiler;
341
        $compiler->init();
342
        return $compiler;
343
    }
344
}
345