1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* Spiral Framework. |
4
|
|
|
* |
5
|
|
|
* @license MIT |
6
|
|
|
* @author Anton Titov (Wolfy-J) |
7
|
|
|
*/ |
8
|
|
|
|
9
|
|
|
namespace Spiral\Stempler; |
10
|
|
|
|
11
|
|
|
use Spiral\Stempler\Behaviours\BlockBehaviour; |
12
|
|
|
use Spiral\Stempler\Behaviours\ExtendsBehaviour; |
13
|
|
|
use Spiral\Stempler\Behaviours\IncludeBehaviour; |
14
|
|
|
use Spiral\Stempler\Exceptions\LoaderException; |
15
|
|
|
use Spiral\Stempler\Exceptions\StemplerException; |
16
|
|
|
use Spiral\Stempler\Importers\Stopper; |
17
|
|
|
|
18
|
|
|
/** |
19
|
|
|
* Supervisors used to control node behaviours and syntax. |
20
|
|
|
*/ |
21
|
|
|
class Supervisor implements SupervisorInterface |
22
|
|
|
{ |
23
|
|
|
/** |
24
|
|
|
* Used to create unique node names when required. |
25
|
|
|
* |
26
|
|
|
* @var int |
27
|
|
|
*/ |
28
|
|
|
private static $index = 0; |
29
|
|
|
|
30
|
|
|
/** |
31
|
|
|
* Active set of imports. |
32
|
|
|
* |
33
|
|
|
* @var ImporterInterface[] |
34
|
|
|
*/ |
35
|
|
|
private $importers = []; |
36
|
|
|
|
37
|
|
|
/** |
38
|
|
|
* @var SyntaxInterface |
39
|
|
|
*/ |
40
|
|
|
protected $syntax = null; |
41
|
|
|
|
42
|
|
|
/** |
43
|
|
|
* @var LoaderInterface |
44
|
|
|
*/ |
45
|
|
|
protected $loader = null; |
46
|
|
|
|
47
|
|
|
/** |
48
|
|
|
* @param LoaderInterface $loader |
49
|
|
|
* @param SyntaxInterface $syntax |
50
|
|
|
*/ |
51
|
|
|
public function __construct(LoaderInterface $loader, SyntaxInterface $syntax) |
52
|
|
|
{ |
53
|
|
|
$this->loader = $loader; |
54
|
|
|
$this->syntax = $syntax; |
55
|
|
|
} |
56
|
|
|
|
57
|
|
|
/** |
58
|
|
|
* {@inheritdoc} |
59
|
|
|
*/ |
60
|
|
|
public function syntax() |
61
|
|
|
{ |
62
|
|
|
return $this->syntax; |
63
|
|
|
} |
64
|
|
|
|
65
|
|
|
/** |
66
|
|
|
* Add new elements import locator. |
67
|
|
|
* |
68
|
|
|
* @param ImporterInterface $import |
69
|
|
|
*/ |
70
|
|
|
public function registerImporter(ImporterInterface $import) |
71
|
|
|
{ |
72
|
|
|
array_unshift($this->importers, $import); |
73
|
|
|
} |
74
|
|
|
|
75
|
|
|
/** |
76
|
|
|
* Active templater imports. |
77
|
|
|
* |
78
|
|
|
* @return ImporterInterface[] |
79
|
|
|
*/ |
80
|
|
|
public function getImporters() |
81
|
|
|
{ |
82
|
|
|
return $this->importers; |
83
|
|
|
} |
84
|
|
|
|
85
|
|
|
/** |
86
|
|
|
* Remove all element importers. |
87
|
|
|
*/ |
88
|
|
|
public function flushImporters() |
89
|
|
|
{ |
90
|
|
|
$this->importers = []; |
91
|
|
|
} |
92
|
|
|
|
93
|
|
|
/** |
94
|
|
|
* {@inheritdoc} |
95
|
|
|
*/ |
96
|
|
|
public function tokenBehaviour(array $token, array $content, Node $node) |
97
|
|
|
{ |
98
|
|
|
switch ($this->syntax->tokenType($token, $name)) { |
99
|
|
|
case SyntaxInterface::TYPE_BLOCK: |
100
|
|
|
//Tag declares block (section) |
101
|
|
|
return new BlockBehaviour($name); |
102
|
|
|
|
103
|
|
|
case SyntaxInterface::TYPE_EXTENDS: |
104
|
|
|
//Declares parent extending |
105
|
|
|
$extends = new ExtendsBehaviour( |
106
|
|
|
$this->createNode($this->syntax->resolvePath($token), $token), |
107
|
|
|
$token |
108
|
|
|
); |
109
|
|
|
|
110
|
|
|
//We have to combine parent imports with local one (this is why uses has to be defined |
111
|
|
|
//after extends tag!) |
|
|
|
|
112
|
|
|
$this->importers = $extends->parentImports(); |
113
|
|
|
|
114
|
|
|
//Sending command to extend parent |
115
|
|
|
return $extends; |
116
|
|
|
|
117
|
|
|
case SyntaxInterface::TYPE_IMPORTER: |
118
|
|
|
//Implementation specific |
119
|
|
|
$this->registerImporter($this->syntax->createImporter($token, $this)); |
120
|
|
|
|
121
|
|
|
//No need to include import tag into source |
122
|
|
|
return BehaviourInterface::SKIP_TOKEN; |
123
|
|
|
} |
124
|
|
|
|
125
|
|
|
//We now have to decide if element points to external view (source) to be imported |
126
|
|
|
foreach ($this->importers as $importer) { |
127
|
|
|
if ($importer->importable($name, $token)) { |
128
|
|
|
if ($importer instanceof Stopper) { |
129
|
|
|
//Native importer tells us to treat this element as simple html |
130
|
|
|
break; |
131
|
|
|
} |
132
|
|
|
|
133
|
|
|
//Let's include! |
134
|
|
|
return new IncludeBehaviour( |
135
|
|
|
$this, $importer->resolvePath($name, $token), $content, $token |
136
|
|
|
); |
137
|
|
|
} |
138
|
|
|
} |
139
|
|
|
|
140
|
|
|
return BehaviourInterface::SIMPLE_TAG; |
141
|
|
|
} |
142
|
|
|
|
143
|
|
|
/** |
144
|
|
|
* Create node based on given location with identical supervisor (cloned). |
145
|
|
|
* |
146
|
|
|
* @param string $path |
147
|
|
|
* @param array $token Context token. |
148
|
|
|
* @return Node |
149
|
|
|
* @throws StemplerException |
150
|
|
|
*/ |
151
|
|
|
public function createNode($path, array $token = []) |
152
|
|
|
{ |
153
|
|
|
//We support dots! |
154
|
|
|
if (!empty($token)) { |
155
|
|
|
$path = str_replace('.', '/', $path); |
156
|
|
|
} |
157
|
|
|
|
158
|
|
|
try { |
159
|
|
|
$source = $this->loader->getSource($path); |
160
|
|
|
} catch (LoaderException $exception) { |
161
|
|
|
throw new StemplerException($exception->getMessage(), $token, 0, $exception); |
162
|
|
|
} |
163
|
|
|
|
164
|
|
|
try { |
165
|
|
|
return new Node(clone $this, $this->uniquePlaceholder(), $source); |
166
|
|
|
} catch (StemplerException $exception) { |
167
|
|
|
//Wrapping to clarify location of error |
168
|
|
|
throw $this->clarifyException($path, $exception); |
169
|
|
|
} |
170
|
|
|
} |
171
|
|
|
|
172
|
|
|
/** |
173
|
|
|
* Get unique placeholder name, unique names are required in some cases to correctly process |
174
|
|
|
* includes and etc. |
175
|
|
|
* |
176
|
|
|
* @return string |
177
|
|
|
*/ |
178
|
|
|
public function uniquePlaceholder() |
179
|
|
|
{ |
180
|
|
|
return md5(self::$index++); |
181
|
|
|
} |
182
|
|
|
|
183
|
|
|
/** |
184
|
|
|
* Clarify exeption with it's actual location. |
185
|
|
|
* |
186
|
|
|
* @param string $path |
187
|
|
|
* @param StemplerException $exception |
188
|
|
|
* @return StemplerException |
189
|
|
|
*/ |
190
|
|
|
protected function clarifyException($path, StemplerException $exception) |
191
|
|
|
{ |
192
|
|
|
if (empty($exception->getToken())) { |
193
|
|
|
//Unable to locate |
194
|
|
|
return $exception; |
195
|
|
|
} |
196
|
|
|
|
197
|
|
|
//We will need only first tag line |
198
|
|
|
$target = explode("\n", $exception->getToken()[HtmlTokenizer::TOKEN_CONTENT])[0]; |
199
|
|
|
|
200
|
|
|
//Let's try to locate place where exception was used |
201
|
|
|
$lines = explode("\n", $this->loader->getSource($path)); |
202
|
|
|
|
203
|
|
|
foreach ($lines as $number => $line) { |
204
|
|
|
if (strpos($line, $target) !== false) { |
205
|
|
|
//We found where token were used (!!) |
206
|
|
|
$exception->setLocation($this->loader->localFilename($path), $number + 1); |
207
|
|
|
|
208
|
|
|
break; |
209
|
|
|
} |
210
|
|
|
} |
211
|
|
|
|
212
|
|
|
return $exception; |
213
|
|
|
} |
214
|
|
|
} |
Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.
The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.
This check looks for comments that seem to be mostly valid code and reports them.