1
|
|
|
<?php namespace Webwizo\Shortcodes\Compilers; |
2
|
|
|
|
3
|
|
|
use Illuminate\Support\Str; |
4
|
|
|
|
5
|
|
|
class ShortcodeCompiler |
6
|
|
|
{ |
7
|
|
|
|
8
|
|
|
/** |
9
|
|
|
* Enabled state |
10
|
|
|
* |
11
|
|
|
* @var boolean |
12
|
|
|
*/ |
13
|
|
|
protected $enabled = false; |
14
|
|
|
|
15
|
|
|
/** |
16
|
|
|
* Enable strip state |
17
|
|
|
* |
18
|
|
|
* @var boolean |
19
|
|
|
*/ |
20
|
|
|
protected $strip = false; |
21
|
|
|
|
22
|
|
|
/** |
23
|
|
|
* @var |
24
|
|
|
*/ |
25
|
|
|
protected $matches; |
26
|
|
|
|
27
|
|
|
/** |
28
|
|
|
* Registered laravel-shortcodes |
29
|
|
|
* |
30
|
|
|
* @var array |
31
|
|
|
*/ |
32
|
|
|
protected $registered = []; |
33
|
|
|
|
34
|
|
|
/** |
35
|
|
|
* Attached View Data |
36
|
|
|
* |
37
|
|
|
* @var array |
38
|
|
|
*/ |
39
|
|
|
protected $data = []; |
40
|
|
|
|
41
|
|
|
/** |
42
|
|
|
* Enable |
43
|
|
|
* |
44
|
|
|
* @return void |
45
|
|
|
*/ |
46
|
|
|
public function enable() |
47
|
|
|
{ |
48
|
|
|
$this->enabled = true; |
49
|
|
|
} |
50
|
|
|
|
51
|
|
|
/** |
52
|
|
|
* Disable |
53
|
|
|
* |
54
|
|
|
* @return void |
55
|
|
|
*/ |
56
|
|
|
public function disable() |
57
|
|
|
{ |
58
|
|
|
$this->enabled = false; |
59
|
|
|
} |
60
|
|
|
|
61
|
|
|
/** |
62
|
|
|
* Add a new shortcode |
63
|
|
|
* |
64
|
|
|
* @param string $name |
65
|
|
|
* @param callable|string $callback |
66
|
|
|
*/ |
67
|
|
|
public function add($name, $callback) |
68
|
|
|
{ |
69
|
|
|
$this->registered[$name] = $callback; |
70
|
|
|
} |
71
|
|
|
|
72
|
|
|
public function attachData($data) |
73
|
|
|
{ |
74
|
|
|
$this->data = $data; |
75
|
|
|
} |
76
|
|
|
|
77
|
|
|
/** |
78
|
|
|
* Compile the contents |
79
|
|
|
* |
80
|
|
|
* @param string $value |
81
|
|
|
* |
82
|
|
|
* @return string |
83
|
|
|
*/ |
84
|
|
|
public function compile($value) |
85
|
|
|
{ |
86
|
|
|
// Only continue is laravel-shortcodes have been registered |
87
|
|
|
if (!$this->enabled || !$this->hasShortcodes()) { |
88
|
|
|
return $value; |
89
|
|
|
} |
90
|
|
|
// Set empty result |
91
|
|
|
$result = ''; |
92
|
|
|
// Here we will loop through all of the tokens returned by the Zend lexer and |
93
|
|
|
// parse each one into the corresponding valid PHP. We will then have this |
94
|
|
|
// template as the correctly rendered PHP that can be rendered natively. |
95
|
|
|
foreach (token_get_all($value) as $token) { |
96
|
|
|
$result .= is_array($token) ? $this->parseToken($token) : $token; |
97
|
|
|
} |
98
|
|
|
|
99
|
|
|
return $result; |
100
|
|
|
} |
101
|
|
|
|
102
|
|
|
/** |
103
|
|
|
* Check if laravel-shortcodes have been registered |
104
|
|
|
* |
105
|
|
|
* @return boolean |
106
|
|
|
*/ |
107
|
|
|
public function hasShortcodes() |
108
|
|
|
{ |
109
|
|
|
return !empty($this->registered); |
110
|
|
|
} |
111
|
|
|
|
112
|
|
|
/** |
113
|
|
|
* Parse the tokens from the template. |
114
|
|
|
* |
115
|
|
|
* @param array $token |
116
|
|
|
* |
117
|
|
|
* @return string |
118
|
|
|
*/ |
119
|
|
|
protected function parseToken($token) |
120
|
|
|
{ |
121
|
|
|
list($id, $content) = $token; |
122
|
|
|
if ($id == T_INLINE_HTML) { |
123
|
|
|
$content = $this->renderShortcodes($content); |
124
|
|
|
} |
125
|
|
|
|
126
|
|
|
return $content; |
127
|
|
|
} |
128
|
|
|
|
129
|
|
|
/** |
130
|
|
|
* Render laravel-shortcodes |
131
|
|
|
* |
132
|
|
|
* @param string $value |
133
|
|
|
* |
134
|
|
|
* @return string |
135
|
|
|
*/ |
136
|
|
|
protected function renderShortcodes($value) |
137
|
|
|
{ |
138
|
|
|
$pattern = $this->getRegex(); |
139
|
|
|
|
140
|
|
|
return preg_replace_callback("/{$pattern}/s", [$this, 'render'], $value); |
141
|
|
|
} |
142
|
|
|
|
143
|
|
|
/** |
144
|
|
|
* Render the current calld shortcode. |
145
|
|
|
* |
146
|
|
|
* @param array $matches |
147
|
|
|
* |
148
|
|
|
* @return string |
149
|
|
|
*/ |
150
|
|
|
public function render($matches) |
151
|
|
|
{ |
152
|
|
|
// Compile the shortcode |
153
|
|
|
$compiled = $this->compileShortcode($matches); |
154
|
|
|
$name = $compiled->getName(); |
155
|
|
|
|
156
|
|
|
// Render the shortcode through the callback |
157
|
|
|
return call_user_func_array($this->getCallback($name), [ |
158
|
|
|
$compiled, |
159
|
|
|
$compiled->getContent(), |
160
|
|
|
$this, |
161
|
|
|
$name, |
162
|
|
|
$this->getData() |
163
|
|
|
]); |
164
|
|
|
} |
165
|
|
|
|
166
|
|
|
/** |
167
|
|
|
* Get Compiled Attributes. |
168
|
|
|
* |
169
|
|
|
* @param $matches |
170
|
|
|
* |
171
|
|
|
* @return \Webwizo\Shortcodes\Shortcode |
172
|
|
|
*/ |
173
|
|
|
protected function compileShortcode($matches) |
174
|
|
|
{ |
175
|
|
|
// Set matches |
176
|
|
|
$this->setMatches($matches); |
177
|
|
|
// pars the attributes |
178
|
|
|
$attributes = $this->parseAttributes($this->matches[3]); |
179
|
|
|
|
180
|
|
|
// return shortcode instance |
181
|
|
|
return new Shortcode( |
182
|
|
|
$this->getName(), |
183
|
|
|
$attributes, |
184
|
|
|
$this->getContent() |
185
|
|
|
); |
186
|
|
|
} |
187
|
|
|
|
188
|
|
|
/** |
189
|
|
|
* Set the matches |
190
|
|
|
* |
191
|
|
|
* @param array $matches |
192
|
|
|
*/ |
193
|
|
|
protected function setMatches($matches = []) |
194
|
|
|
{ |
195
|
|
|
$this->matches = $matches; |
196
|
|
|
} |
197
|
|
|
|
198
|
|
|
/** |
199
|
|
|
* Return the shortcode name |
200
|
|
|
* |
201
|
|
|
* @return string |
202
|
|
|
*/ |
203
|
|
|
public function getName() |
204
|
|
|
{ |
205
|
|
|
return $this->matches[2]; |
206
|
|
|
} |
207
|
|
|
|
208
|
|
|
/** |
209
|
|
|
* Return the shortcode content |
210
|
|
|
* |
211
|
|
|
* @return string |
212
|
|
|
*/ |
213
|
|
|
public function getContent() |
214
|
|
|
{ |
215
|
|
|
// Compile the content, to support nested laravel-shortcodes |
216
|
|
|
return $this->compile($this->matches[5]); |
217
|
|
|
} |
218
|
|
|
|
219
|
|
|
/** |
220
|
|
|
* Return the view data |
221
|
|
|
* |
222
|
|
|
* @return array |
223
|
|
|
*/ |
224
|
|
|
public function getData() |
225
|
|
|
{ |
226
|
|
|
return $this->data; |
227
|
|
|
} |
228
|
|
|
|
229
|
|
|
|
230
|
|
|
/** |
231
|
|
|
* Get the callback for the current shortcode (class or callback) |
232
|
|
|
* |
233
|
|
|
* @param string $name |
234
|
|
|
* |
235
|
|
|
* @return callable|array |
236
|
|
|
*/ |
237
|
|
|
public function getCallback($name) |
238
|
|
|
{ |
239
|
|
|
// Get the callback from the laravel-shortcodes array |
240
|
|
|
$callback = $this->registered[$name]; |
241
|
|
|
// if is a string |
242
|
|
|
if (is_string($callback)) { |
243
|
|
|
// Parse the callback |
244
|
|
|
list($class, $method) = Str::parseCallback($callback, 'register'); |
245
|
|
|
// If the class exist |
246
|
|
|
if (class_exists($class)) { |
247
|
|
|
// return class and method |
|
|
|
|
248
|
|
|
return [ |
249
|
|
|
app($class), |
250
|
|
|
$method |
251
|
|
|
]; |
252
|
|
|
} |
253
|
|
|
} |
254
|
|
|
|
255
|
|
|
return $callback; |
256
|
|
|
} |
257
|
|
|
|
258
|
|
|
/** |
259
|
|
|
* Parse the shortcode attributes |
260
|
|
|
* |
261
|
|
|
* @author Wordpress |
262
|
|
|
* @return array |
263
|
|
|
*/ |
264
|
|
|
protected function parseAttributes($text) |
265
|
|
|
{ |
266
|
|
|
$attributes = []; |
267
|
|
|
// attributes pattern |
268
|
|
|
$pattern = '/(\w+)\s*=\s*"([^"]*)"(?:\s|$)|(\w+)\s*=\s*\'([^\']*)\'(?:\s|$)|(\w+)\s*=\s*([^\s\'"]+)(?:\s|$)|"([^"]*)"(?:\s|$)|(\S+)(?:\s|$)/'; |
269
|
|
|
// Match |
270
|
|
|
if (preg_match_all($pattern, preg_replace('/[\x{00a0}\x{200b}]+/u', " ", $text), $match, PREG_SET_ORDER)) { |
271
|
|
|
foreach ($match as $m) { |
272
|
|
|
if (!empty($m[1])) { |
273
|
|
|
$attributes[strtolower($m[1])] = stripcslashes($m[2]); |
274
|
|
|
} elseif (!empty($m[3])) { |
275
|
|
|
$attributes[strtolower($m[3])] = stripcslashes($m[4]); |
276
|
|
|
} elseif (!empty($m[5])) { |
277
|
|
|
$attributes[strtolower($m[5])] = stripcslashes($m[6]); |
278
|
|
|
} elseif (isset($m[7]) && strlen($m[7])) { |
279
|
|
|
$attributes[] = stripcslashes($m[7]); |
280
|
|
|
} elseif (isset($m[8])) { |
281
|
|
|
$attributes[] = stripcslashes($m[8]); |
282
|
|
|
} |
283
|
|
|
} |
284
|
|
|
} else { |
285
|
|
|
$attributes = ltrim($text); |
286
|
|
|
} |
287
|
|
|
|
288
|
|
|
// return attributes |
289
|
|
|
return is_array($attributes) ? $attributes : [$attributes]; |
290
|
|
|
} |
291
|
|
|
|
292
|
|
|
/** |
293
|
|
|
* Get shortcode names |
294
|
|
|
* |
295
|
|
|
* @return string |
296
|
|
|
*/ |
297
|
|
|
protected function getShortcodeNames() |
298
|
|
|
{ |
299
|
|
|
return join('|', array_map('preg_quote', array_keys($this->registered))); |
300
|
|
|
} |
301
|
|
|
|
302
|
|
|
/** |
303
|
|
|
* Get shortcode regex. |
304
|
|
|
* |
305
|
|
|
* @author Wordpress |
306
|
|
|
* @return string |
307
|
|
|
*/ |
308
|
|
|
protected function getRegex() |
309
|
|
|
{ |
310
|
|
|
$shortcodeNames = $this->getShortcodeNames(); |
311
|
|
|
|
312
|
|
|
return "\\[(\\[?)($shortcodeNames)(?![\\w-])([^\\]\\/]*(?:\\/(?!\\])[^\\]\\/]*)*?)(?:(\\/)\\]|\\](?:([^\\[]*+(?:\\[(?!\\/\\2\\])[^\\[]*+)*+)\\[\\/\\2\\])?)(\\]?)"; |
313
|
|
|
} |
314
|
|
|
|
315
|
|
|
/** |
316
|
|
|
* Remove all shortcode tags from the given content. |
317
|
|
|
* |
318
|
|
|
* @param string $content Content to remove shortcode tags. |
319
|
|
|
* |
320
|
|
|
* @return string Content without shortcode tags. |
321
|
|
|
*/ |
322
|
|
|
public function strip($content) |
323
|
|
|
{ |
324
|
|
|
if (empty($this->registered)) { |
325
|
|
|
return $content; |
326
|
|
|
} |
327
|
|
|
$pattern = $this->getRegex(); |
328
|
|
|
|
329
|
|
|
return preg_replace_callback("/{$pattern}/s", [$this, 'stripTag'], $content); |
330
|
|
|
} |
331
|
|
|
|
332
|
|
|
/** |
333
|
|
|
* @return boolean |
334
|
|
|
*/ |
335
|
|
|
public function getStrip() |
336
|
|
|
{ |
337
|
|
|
return $this->strip; |
338
|
|
|
} |
339
|
|
|
|
340
|
|
|
/** |
341
|
|
|
* @param boolean $strip |
342
|
|
|
*/ |
343
|
|
|
public function setStrip($strip) |
344
|
|
|
{ |
345
|
|
|
$this->strip = $strip; |
346
|
|
|
} |
347
|
|
|
|
348
|
|
|
/** |
349
|
|
|
* Remove shortcode tag |
350
|
|
|
* |
351
|
|
|
* @param type $m |
352
|
|
|
* |
353
|
|
|
* @return string Content without shortcode tag. |
354
|
|
|
*/ |
355
|
|
|
protected function stripTag($m) |
356
|
|
|
{ |
357
|
|
|
if ($m[1] == '[' && $m[6] == ']') { |
358
|
|
|
return substr($m[0], 1, -1); |
359
|
|
|
} |
360
|
|
|
|
361
|
|
|
return $m[1] . $m[6]; |
362
|
|
|
} |
363
|
|
|
|
364
|
|
|
/** |
365
|
|
|
* Get registered shortcodes |
366
|
|
|
* |
367
|
|
|
* @return array shortcode tags. |
368
|
|
|
*/ |
369
|
|
|
public function getRegistered() |
370
|
|
|
{ |
371
|
|
|
return $this->registered; |
372
|
|
|
} |
373
|
|
|
} |
374
|
|
|
|
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.