Passed
Push — master ( b1522d...27f7d7 )
by 世昌
02:07
created

CompilableTemplate::isCompiled()   A

Complexity

Conditions 4
Paths 5

Size

Total Lines 10
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 7
nc 5
nop 0
dl 0
loc 10
rs 10
c 0
b 0
f 0
1
<?php
2
namespace suda\application\template;
3
4
use function array_key_exists;
5
use function constant;
6
use Exception;
7
use function in_array;
8
use function pathinfo;
9
use suda\application\Resource;
10
use suda\framework\runnable\Runnable;
11
use suda\framework\filesystem\FileSystem;
12
use suda\application\template\compiler\Compiler;
13
use suda\application\exception\MissingTemplateException;
14
15
/**
16
 * 可编译模板
17
 */
18
class CompilableTemplate extends RawTemplate
19
{
20
    /**
21
     * 模板源
22
     *
23
     * @var string
24
     */
25
    protected $source;
26
27
    /**
28
     * 模板配置
29
     *
30
     * @var array
31
     */
32
    protected $config;
33
34
    /**
35
     * 输出目录
36
     *
37
     * @var string
38
     */
39
    protected $output;
40
41
    /**
42
     * 父模版
43
     *
44
     * @var CompilableTemplate|null
45
     */
46
    protected $parent = null;
47
48
49
    /**
50
     * 模板钩子
51
     *
52
     * @var array
53
     */
54
    protected $hooks = [];
55
56
    /**
57
     * 继承的模板
58
     *
59
     * @var string|null
60
     */
61
    protected $extend = null;
62
63
    /**
64
     * 编译器
65
     *
66
     * @var Compiler
67
     */
68
    protected static $compiler;
69
70
    /**
71
     * 检测已经拷贝的目录
72
     *
73
     * @var array
74
     */
75
    protected static $copyedStaticPaths = [];
76
77
    /**
78
     * 静态目录
79
     *
80
     * @var string
81
     */
82
    protected $staticPath;
83
84
    /**
85
     * 模板名
86
     *
87
     * @var string
88
     */
89
    protected $name;
90
91
    /**
92
     * 构建模板
93
     *
94
     * @param string $source
95
     * @param array $config
96
     */
97
    public function __construct(string $source, array $config = [])
98
    {
99
        parent::__construct('', []);
100
        $this->source = $source;
101
        $this->name = pathinfo($source, PATHINFO_FILENAME);
102
        $this->config = $config;
103
    }
104
105
    protected function getStaticPath()
106
    {
107
        return Resource::getPathByRelativePath($this->config['static'] ?? 'static', dirname($this->getSourcePath()));
108
    }
109
110
    protected function getStaticOutpath()
111
    {
112
        $path = $this->config['assets-public'] ?? constant('SUDA_PUBLIC').'/assets/'. $this->getStaticName();
113
        FileSystem::make($path);
114
        return $path;
115
    }
116
117
    /**
118
     * 获取编译后的路径
119
     *
120
     * @return string
121
     */
122
    public function getPath()
123
    {
124
        $output = $this->config['output'] ?? constant('SUDA_DATA').'/template';
125
        FileSystem::make($output);
126
        return $output .'/'. $this->name.'-'.substr(md5_file($this->getSourcePath()), 10, 8).'.php';
127
    }
128
129
    /**
130
     * 获取源路径
131
     *
132
     * @return string|null
133
     */
134
    public function getSourcePath():?string
135
    {
136
        return $this->source ?? null;
137
    }
138
139
    /**
140
     * 输出
141
     * @ignore-dump
142
     * @return string
143
     * @throws Exception
144
     */
145
    public function getRenderedString()
146
    {
147
        $this->compile();
148
        return $this->render();
149
    }
150
151
    /**
152
     * 编译
153
     *
154
     * @return bool
155
     * @throws Exception
156
     */
157
    protected function compile()
158
    {
159
        if ($this->isCompiled() === false) {
160
            $destPath = $this->getPath();
161
            $sourcePath = $this->getSourcePath();
162
            $content = FileSystem::get($sourcePath);
0 ignored issues
show
Bug introduced by
It seems like $sourcePath can also be of type null; however, parameter $filename of suda\framework\filesystem\FileSystem::get() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

162
            $content = FileSystem::get(/** @scrutinizer ignore-type */ $sourcePath);
Loading history...
163
            if ($content !== null) {
164
                $compiled = $this->compiler()->compileText($content, $this->config);
165
                FileSystem::make(dirname($destPath));
166
                FileSystem::put($destPath, $compiled);
167
            }
168
            return true;
169
        }
170
        return false;
171
    }
172
173
    /**
174
     * 检查是否编译过
175
     * @return bool
176
     */
177
    protected function isCompiled()
178
    {
179
        $sourcePath = $this->getSourcePath();
180
        if ($sourcePath === null) {
181
            throw new MissingTemplateException($this->name);
182
        }
183
        $source = FileSystem::exist($sourcePath);
184
        $dest = FileSystem::exist($this->getPath());
185
        $notCompiled = $source === true && $dest === false;
186
        return ($notCompiled || SUDA_DEBUG) === false;
187
    }
188
    
189
    /**
190
     * 获取渲染后的字符串
191
     * @ignore-dump
192
     * @throws Exception
193
     */
194
    public function render()
195
    {
196
        ob_start();
197
        echo parent::getRenderedString();
198
        if ($this->extend) {
199
            $this->include($this->extend);
200
        }
201
        $content = trim(ob_get_clean());
202
        return $content;
203
    }
204
205
    /**
206
     * 创建模板
207
     * @param $template
208
     * @return CompilableTemplate
209
     */
210
    public function parent($template)
211
    {
212
        $this->parent = $template;
213
        return $this;
214
    }
215
216
    
217
218
    public function extend(string $name)
219
    {
220
        $this->extend = $name;
221
    }
222
223
    /**
224
     * @param string $path
225
     * @throws Exception
226
     */
227
    public function include(string $path)
228
    {
229
        $subfix = $this->config['subfix'] ?? '';
230
        $included = new self(Resource::getPathByRelativePath($path. $subfix, dirname($this->source)), $this->config);
231
        $included->parent = $this;
232
        echo $included->getRenderedString();
233
    }
234
235
    public function data(string $name, ...$args)
236
    {
237
        if (func_num_args() > 1) {
238
            return (new Runnable($name))->run($this, ...$args);
239
        }
240
        return (new Runnable($name))->apply([$this]);
241
    }
242
243
    protected function getStaticPrefix()
244
    {
245
        $this->prepareStaticSource();
246
        if (array_key_exists('assets-prefix', $this->config)) {
247
            $prefix = $this->config['assets-prefix'] ;
248
        } elseif (defined('SUDA_ASSETS')) {
249
            $prefix = constant('SUDA_ASSETS');
250
        } else {
251
            $prefix = '/assets';
252
        }
253
        return $prefix .'/'.$this->getStaticName();
254
    }
255
256
    protected function prepareStaticSource()
257
    {
258
        if (SUDA_DEBUG && is_dir($this->getStaticPath()) && !in_array($this->getStaticPath(), static::$copyedStaticPaths)) {
259
            FileSystem::copyDir($this->getStaticPath(), $this->getStaticOutpath());
260
            static::$copyedStaticPaths[] = $this->getStaticPath();
261
        }
262
    }
263
264
    protected function getStaticName()
265
    {
266
        return $this->config['static-name'] ?? substr(md5($this->getStaticPath()), 0, 8);
267
    }
268
269
    public function insert(string $name, $callback)
270
    {
271
        // 存在父模板
272
        if ($this->parent) {
273
            $this->parent->insert($name, $callback);
274
        } else {
275
            // 添加回调钩子
276
            $this->hooks[$name][] = new Runnable($callback);
277
        }
278
    }
279
280
    public function exec(string $name)
281
    {
282
        try {
283
            // 存在父模板
284
            if ($this->parent) {
285
                $this->parent->exec($name);
286
            } elseif (isset($this->hooks[$name])) {
287
                foreach ($this->hooks[$name] as $hook) {
288
                    $hook->run();
289
                }
290
            }
291
        } catch (Exception $e) {
292
            echo '<div style="color:red">'.$e->getMessage().'</div>';
293
            return;
294
        }
295
    }
296
297
    protected function compiler()
298
    {
299
        if (static::$compiler === null) {
300
            static::$compiler = $this->createCompiler();
301
        }
302
        return static::$compiler;
303
    }
304
305
    protected function createCompiler():Compiler
306
    {
307
        return new Compiler;
308
    }
309
}
310