1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* spiral |
4
|
|
|
* |
5
|
|
|
* @author Wolfy-J |
6
|
|
|
*/ |
7
|
|
|
|
8
|
|
|
namespace Spiral\Views\Engines; |
9
|
|
|
|
10
|
|
|
use Spiral\Core\ContainerInterface; |
11
|
|
|
use Spiral\Debug\Traits\BenchmarkTrait; |
12
|
|
|
use Spiral\Files\FilesInterface; |
13
|
|
|
use Spiral\Stempler\Supervisor; |
14
|
|
|
use Spiral\Stempler\Syntaxes\DarkSyntax; |
15
|
|
|
use Spiral\Views\EngineInterface; |
16
|
|
|
use Spiral\Views\Engines\Prototypes\AbstractEngine; |
17
|
|
|
use Spiral\Views\Engines\Stempler\LoaderBridge; |
18
|
|
|
use Spiral\Views\Engines\Stempler\StemplerCache; |
19
|
|
|
use Spiral\Views\Engines\Stempler\StemplerView; |
20
|
|
|
use Spiral\Views\EnvironmentInterface; |
21
|
|
|
use Spiral\Views\LoaderInterface; |
22
|
|
|
use Spiral\Views\Loaders\ModifiableLoader; |
23
|
|
|
use Spiral\Views\ProcessorInterface; |
24
|
|
|
use Spiral\Views\ViewInterface; |
25
|
|
|
use Spiral\Views\ViewSource; |
26
|
|
|
|
27
|
|
|
/** |
28
|
|
|
* Spiral Stempler template composer. |
29
|
|
|
*/ |
30
|
|
|
class StemplerEngine extends AbstractEngine |
31
|
|
|
{ |
32
|
|
|
use BenchmarkTrait; |
33
|
|
|
|
34
|
|
|
/** |
35
|
|
|
* Container is needed to provide proper scope isolation at moment of rendering. |
36
|
|
|
* |
37
|
|
|
* @var ContainerInterface |
38
|
|
|
*/ |
39
|
|
|
protected $container; |
40
|
|
|
|
41
|
|
|
/** |
42
|
|
|
* @var StemplerCache |
43
|
|
|
*/ |
44
|
|
|
protected $cache; |
45
|
|
|
|
46
|
|
|
/** |
47
|
|
|
* To be applied to view source before it's being loaded. |
48
|
|
|
* |
49
|
|
|
* @var array |
50
|
|
|
*/ |
51
|
|
|
protected $modifiers = []; |
52
|
|
|
|
53
|
|
|
/** |
54
|
|
|
* To be applied to already compiled view source. |
55
|
|
|
* |
56
|
|
|
* @var array |
57
|
|
|
*/ |
58
|
|
|
protected $processors = []; |
59
|
|
|
|
60
|
|
|
/** |
61
|
|
|
* @param EnvironmentInterface $environment |
62
|
|
|
* @param LoaderInterface $loader |
63
|
|
|
* @param FilesInterface $files |
64
|
|
|
* @param ContainerInterface $container |
65
|
|
|
* @param array $modifiers |
66
|
|
|
* @param array $processors |
67
|
|
|
*/ |
68
|
|
|
public function __construct( |
69
|
|
|
EnvironmentInterface $environment, |
70
|
|
|
LoaderInterface $loader, |
71
|
|
|
FilesInterface $files, |
72
|
|
|
ContainerInterface $container, |
73
|
|
|
array $modifiers = [], |
74
|
|
|
array $processors = [] |
75
|
|
|
) { |
76
|
|
|
parent::__construct($environment, $loader); |
77
|
|
|
|
78
|
|
|
$this->container = $container; |
79
|
|
|
|
80
|
|
|
$this->modifiers = $modifiers; |
81
|
|
|
$this->processors = $processors; |
82
|
|
|
|
83
|
|
|
$this->cache = new StemplerCache($this->environment, $files); |
84
|
|
|
} |
85
|
|
|
|
86
|
|
|
/** |
87
|
|
|
* {@inheritdoc} |
88
|
|
|
*/ |
89
|
|
|
public function get(string $path): ViewInterface |
90
|
|
|
{ |
91
|
|
|
return new StemplerView( |
92
|
|
|
$this->compile($path), |
93
|
|
|
$this->container |
94
|
|
|
); |
95
|
|
|
} |
96
|
|
|
|
97
|
|
|
/** |
98
|
|
|
* {@inheritdoc} |
99
|
|
|
* |
100
|
|
|
* @return ViewSource Points to compiled view source. |
101
|
|
|
*/ |
102
|
|
|
public function compile(string $path, bool $reset = false): ViewSource |
103
|
|
|
{ |
104
|
|
|
$context = $this->loader->getSource($path); |
105
|
|
|
$cached = new ViewSource( |
106
|
|
|
$this->cache->cacheFilename($path), |
107
|
|
|
$context->getName(), |
108
|
|
|
$context->getNamespace() |
109
|
|
|
); |
110
|
|
|
|
111
|
|
|
if ( |
112
|
|
|
$context->isFresh($this->cache->timeCached($cached->getFilename())) |
113
|
|
|
&& !$reset |
114
|
|
|
) { |
115
|
|
|
//Compiled and cached and not changed (simple cache check) |
116
|
|
|
return $cached; |
117
|
|
|
} |
118
|
|
|
|
119
|
|
|
$benchmark = $this->benchmark('compile', $path); |
120
|
|
|
try { |
121
|
|
|
//Compiling into cache |
122
|
|
|
$cached = $cached->withCode( |
123
|
|
|
$this->createSupervisor()->createNode($path)->compile() |
124
|
|
|
); |
125
|
|
|
} finally { |
126
|
|
|
$this->benchmark($benchmark); |
127
|
|
|
} |
128
|
|
|
|
129
|
|
|
$benchmark = $this->benchmark('cache', $path); |
130
|
|
|
try { |
131
|
|
|
//To simplify processors life let's write file to cache first (this might help in debugging) |
132
|
|
|
$this->cache->write( |
133
|
|
|
$cached->getFilename(), |
134
|
|
|
$cached->getCode() |
135
|
|
|
); |
136
|
|
|
|
137
|
|
|
//Ok, now we can apply processors |
138
|
|
|
$cached = $this->postProcess($cached); |
139
|
|
|
|
140
|
|
|
$this->cache->write( |
141
|
|
|
$cached->getFilename(), |
142
|
|
|
$cached->getCode() |
143
|
|
|
); |
144
|
|
|
} finally { |
145
|
|
|
$this->benchmark($benchmark); |
146
|
|
|
} |
147
|
|
|
|
148
|
|
|
return $cached; |
149
|
|
|
} |
150
|
|
|
|
151
|
|
|
/** |
152
|
|
|
* {@inheritdoc} |
153
|
|
|
* |
154
|
|
|
* @return $this |
155
|
|
|
*/ |
156
|
|
|
public function withEnvironment(EnvironmentInterface $environment): EngineInterface |
157
|
|
|
{ |
158
|
|
|
/** |
159
|
|
|
* @var self $engine |
160
|
|
|
*/ |
161
|
|
|
$engine = parent::withEnvironment($environment); |
162
|
|
|
$engine->cache = $engine->cache->withEnvironment($environment); |
163
|
|
|
|
164
|
|
|
return $engine; |
165
|
|
|
} |
166
|
|
|
|
167
|
|
|
/** |
168
|
|
|
* Create new instance of supervisor. |
169
|
|
|
* |
170
|
|
|
* @return Supervisor |
171
|
|
|
*/ |
172
|
|
|
protected function createSupervisor(): Supervisor |
173
|
|
|
{ |
174
|
|
|
//Prepare loader |
175
|
|
|
return new Supervisor($this->wrapLoader(), new DarkSyntax()); |
176
|
|
|
} |
177
|
|
|
|
178
|
|
|
/** |
179
|
|
|
* Run associated processors for post-processing. |
180
|
|
|
* |
181
|
|
|
* @param \Spiral\Views\ViewSource $source |
182
|
|
|
* |
183
|
|
|
* @return ViewSource |
184
|
|
|
*/ |
185
|
|
|
protected function postProcess(ViewSource $source): ViewSource |
186
|
|
|
{ |
187
|
|
|
foreach ($this->processors as $processor) { |
188
|
|
|
/** |
189
|
|
|
* @var ProcessorInterface |
190
|
|
|
*/ |
191
|
|
|
if (!is_object($processor)) { |
192
|
|
|
$processor = $this->container->make($processor); |
193
|
|
|
} |
194
|
|
|
|
195
|
|
|
$benchmark = $this->benchmark( |
196
|
|
|
'process', |
197
|
|
|
get_class($processor) . '-{' . $source->getName() |
198
|
|
|
); |
199
|
|
|
|
200
|
|
|
$source->getCode(); |
201
|
|
|
|
202
|
|
|
try { |
203
|
|
|
//Post processing |
204
|
|
|
$source = $source->withCode( |
205
|
|
|
$processor->modify($this->environment, $source, $source->getCode()) |
206
|
|
|
); |
207
|
|
|
} finally { |
208
|
|
|
$this->benchmark($benchmark); |
209
|
|
|
} |
210
|
|
|
} |
211
|
|
|
|
212
|
|
|
return $source; |
213
|
|
|
} |
214
|
|
|
|
215
|
|
|
/** |
216
|
|
|
* In most of cases stempler will get view processors to be executed before composing, to run |
217
|
|
|
* such processors we need special wrapper at top of environment. |
218
|
|
|
* |
219
|
|
|
* @return \Spiral\Stempler\LoaderInterface |
220
|
|
|
*/ |
221
|
|
View Code Duplication |
private function wrapLoader(): \Spiral\Stempler\LoaderInterface |
|
|
|
|
222
|
|
|
{ |
223
|
|
|
$processors = []; |
224
|
|
|
foreach ($this->modifiers as $modifier) { |
225
|
|
|
if (is_object($modifier)) { |
226
|
|
|
$processors[] = $modifier; |
227
|
|
|
} else { |
228
|
|
|
$processors[] = $this->container->make($modifier); |
229
|
|
|
} |
230
|
|
|
} |
231
|
|
|
|
232
|
|
|
return new LoaderBridge( |
233
|
|
|
new ModifiableLoader($this->environment, $this->loader, $processors) |
234
|
|
|
); |
235
|
|
|
} |
236
|
|
|
} |
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.