1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
ar_pinp::allow( 'ar_html_zen' ); |
4
|
|
|
|
5
|
|
|
class ar_html_zen extends ar_htmlNodes { |
6
|
|
|
|
7
|
|
|
const T_EOF = 0; |
8
|
|
|
const T_IDENT = 1; |
9
|
|
|
const T_NUMBER = 2; |
10
|
|
|
const T_PLACEHOLDER = 3; |
11
|
|
|
const T_EXPRESSION_OPEN = 4; |
12
|
|
|
const T_EXPRESSION_CLOSE = 5; |
13
|
|
|
|
14
|
|
|
const T_OP_ATTRIBUTES_OPEN = 6; |
15
|
|
|
const T_OP_ATTRIBUTES_CLOSE = 7; |
16
|
|
|
const T_OP_FILTER = 8; |
17
|
|
|
const T_OP_MULTIPLIER = 9; |
18
|
|
|
const T_OP_ASSIGN = 11; |
19
|
|
|
const T_OP_ID = 12; |
20
|
|
|
const T_OP_CLASS = 13; |
21
|
|
|
const T_OP_CHILDREN = 14; |
22
|
|
|
const T_OP_SIBLINGS = 15; |
23
|
|
|
const T_OP_SETTING = 16; |
24
|
|
|
|
25
|
|
|
public function __construct( $string ) { |
26
|
|
|
$parser = new ar_html_zenParser($string); |
27
|
|
|
$nodes = $parser->run(); |
28
|
|
|
parent::__construct( (array)$this->compileNodes($nodes) ); |
29
|
|
|
} |
30
|
|
|
|
31
|
|
|
private function compileNodes($nodes, $childNodes = null) { |
32
|
|
|
if( !isset($childNodes) ) { |
33
|
|
|
$childNodes = ar_html::nodes(); |
34
|
|
|
} |
35
|
|
|
if( isset($nodes["children"]) ) { |
36
|
|
|
$childNodes = $this->compileNodes($nodes["children"], $childNodes); |
37
|
|
|
} |
38
|
|
|
unset($nodes["children"]); |
39
|
|
|
$result = array(); |
40
|
|
|
$mult = 1; |
41
|
|
|
if( isset($nodes["multiplier"]) ) { |
42
|
|
|
$mult = (int)$nodes["multiplier"]; |
43
|
|
|
unset($nodes["multiplier"]); |
44
|
|
|
} |
45
|
|
|
for($i=0;$i<$mult;$i++) { |
46
|
|
|
foreach( $nodes as $key => $value ) { |
47
|
|
|
if( $value["tagName"] ) { |
48
|
|
|
$tmult = 1; |
49
|
|
|
if( isset($value["multiplier"]) ) { |
50
|
|
|
$tmult = (int)$value["multiplier"]; |
51
|
|
|
} |
52
|
|
|
for($j=0;$j<$tmult;$j++) { |
53
|
|
|
$result[] = ar_html::tag( $value["tagName"], $value["attributes"], $childNodes->cloneNode(true)); |
54
|
|
|
} |
55
|
|
|
} else { |
56
|
|
|
$result = array_merge($result, (array)$this->compileNodes($value, $childNodes)); |
57
|
|
|
} |
58
|
|
|
} |
59
|
|
|
} |
60
|
|
|
return ar_html::nodes($result); |
61
|
|
|
} |
62
|
|
|
} |
63
|
|
|
|
64
|
|
|
class ar_html_zenScanner { |
65
|
|
|
protected $YYLINE; |
66
|
|
|
protected $YYBUFFER; |
67
|
|
|
protected $YYCURSOR; |
68
|
|
|
protected $YYSTATE; |
69
|
|
|
|
70
|
|
|
protected $class_ident = array(); |
71
|
|
|
protected $class_ident_next = array(); |
72
|
|
|
protected $class_number = array(); |
73
|
|
|
protected $class_whitespace = array(); |
74
|
|
|
|
75
|
|
|
protected $tokens = array(); |
76
|
|
|
|
77
|
|
|
public $token; |
78
|
|
|
public $token_value; |
79
|
|
|
public $token_ahead; |
80
|
|
|
public $token_ahead_value; |
81
|
|
|
|
82
|
|
|
|
83
|
|
|
function __construct($buffer) { |
84
|
|
|
$this->YYBUFFER = $buffer."\000"; |
85
|
|
|
$this->YYLINE = 0; |
86
|
|
|
$this->YYCURSOR = 0; |
87
|
|
|
$this->YYSTATE = STATE_TEXT; |
88
|
|
|
|
89
|
|
|
|
90
|
|
|
// Identifiers [a-zA-Z] |
91
|
|
|
$class_ident_start = array(); |
92
|
|
|
$start = ord('a'); |
93
|
|
|
$end = ord('z'); |
94
|
|
|
for ($i = $start; $i <= $end; $i++) { |
95
|
|
|
$class_ident_start[chr($i)] = chr($i); |
96
|
|
|
$class_ident_start[strtoupper(chr($i))] = strtoupper(chr($i)); |
97
|
|
|
} |
98
|
|
|
$this->class_ident = array_merge(array('-' => '-', '_' => '_'), $class_ident_start); |
99
|
|
|
$this->class_ident_start = $class_ident_start; |
|
|
|
|
100
|
|
|
// Numbers [0-9] |
101
|
|
|
$class_number = array(); |
102
|
|
|
$start = ord('0'); |
103
|
|
|
$end = ord('9'); |
104
|
|
View Code Duplication |
for ($i = $start; $i <= $end; $i++) { |
105
|
|
|
$class_ident_next[chr($i)] = chr($i); |
|
|
|
|
106
|
|
|
$class_number[chr($i)] = chr($i); |
107
|
|
|
} |
108
|
|
|
$this->class_number = $class_number; |
109
|
|
|
$this->class_ident = array_merge($this->class_ident, $class_ident_next); |
|
|
|
|
110
|
|
|
// Whitespace |
111
|
|
|
$class_whitespace = array(" " => " ", "\t" => "\t", "\r" => "\r", "\n" => "\n"); |
112
|
|
|
$this->class_whitespace = $class_whitespace; |
113
|
|
|
} |
114
|
|
|
|
115
|
|
View Code Duplication |
function next() { |
116
|
|
|
if (count($this->tokens) == 0) { |
117
|
|
|
$new_token = $this->scan($new_value); |
118
|
|
|
} else { |
119
|
|
|
$entry = array_shift($this->tokens); |
120
|
|
|
list($new_token, $new_value) = each($entry); |
121
|
|
|
} |
122
|
|
|
if (isset($this->token_ahead)) { |
123
|
|
|
$this->token = $this->token_ahead; |
124
|
|
|
$this->token_value = $this->token_ahead_value; |
125
|
|
|
} |
126
|
|
|
$this->token_ahead = $new_token; |
127
|
|
|
$this->token_ahead_value = $new_value; |
128
|
|
|
return $this->token; |
129
|
|
|
} |
130
|
|
|
|
131
|
|
|
|
132
|
|
|
function scan(&$value) { |
133
|
|
|
$YYCURSOR = &$this->YYCURSOR; |
134
|
|
|
$YYBUFFER = &$this->YYBUFFER; |
135
|
|
|
$yych = $YYBUFFER[$YYCURSOR]; |
136
|
|
|
$token = ""; |
137
|
|
|
|
138
|
|
|
do { |
139
|
|
|
switch (true) { |
140
|
|
|
case '"' === $yych: |
141
|
|
View Code Duplication |
case "'" === $yych: |
142
|
|
|
$quote = $yych; |
143
|
|
|
$yych = $YYBUFFER[++$YYCURSOR]; |
144
|
|
|
while ($yych !== "\000" && $yych !== $quote) { |
145
|
|
|
if ($yych == "\\") { |
146
|
|
|
$yych = $YYBUFFER[++$YYCURSOR]; |
147
|
|
|
if ($yych !== $quote && $yych != "\\") { |
148
|
|
|
$value .= "\\"; |
149
|
|
|
} |
150
|
|
|
} |
151
|
|
|
$value .= $yych; |
152
|
|
|
$yych = $YYBUFFER[++$YYCURSOR]; |
153
|
|
|
} |
154
|
|
|
++$YYCURSOR; |
155
|
|
|
return ar_html_zen::T_IDENT; |
156
|
|
|
break; |
157
|
|
|
case '|' === $yych: ($token || $token = ar_html_zen::T_OP_FILTER); |
158
|
|
|
case '(' === $yych: ($token || $token = ar_html_zen::T_EXPRESSION_OPEN); |
159
|
|
|
case ')' === $yych: ($token || $token = ar_html_zen::T_EXPRESSION_CLOSE); |
160
|
|
|
case '*' === $yych: ($token || $token = ar_html_zen::T_OP_MULTIPLIER); |
161
|
|
|
case '+' === $yych: ($token || $token = ar_html_zen::T_OP_SIBLINGS); |
162
|
|
|
case '[' === $yych: ($token || $token = ar_html_zen::T_OP_ATTRIBUTES_OPEN); |
163
|
|
|
case ']' === $yych: ($token || $token = ar_html_zen::T_OP_ATTRIBUTES_CLOSE); |
164
|
|
|
case '=' === $yych: ($token || $token = ar_html_zen::T_OP_ASSIGN); |
165
|
|
|
case '$' === $yych: ($token || $token = ar_html_zen::T_PLACEHOLDER); |
166
|
|
|
case '>' === $yych: ($token || $token = ar_html_zen::T_OP_CHILDREN); |
167
|
|
|
case '.' === $yych: ($token || $token = ar_html_zen::T_OP_CLASS); |
168
|
|
|
case '#' === $yych: ($token || $token = ar_html_zen::T_OP_ID); |
169
|
|
|
case ':' === $yych: ($token || $token = ar_html_zen::T_OP_SETTING); |
170
|
|
|
$value = $yych; |
171
|
|
|
++$YYCURSOR; |
172
|
|
|
return $token; |
173
|
|
|
break; |
174
|
|
|
case $this->class_whitespace[$yych] === $yych: |
175
|
|
|
$yych = $YYBUFFER[++$YYCURSOR]; continue; |
176
|
|
|
break; |
177
|
|
View Code Duplication |
case $this->class_number[$yych] === $yych: |
178
|
|
|
$value = ""; |
179
|
|
|
while ($this->class_number[$yych] == $yych && ($yych != "\000")) { |
180
|
|
|
$value .= $yych; |
181
|
|
|
$yych = $YYBUFFER[++$YYCURSOR]; |
182
|
|
|
} |
183
|
|
|
return ar_html_zen::T_NUMBER; |
184
|
|
|
break; |
185
|
|
|
case $this->class_ident_start[$yych] === $yych: |
|
|
|
|
186
|
|
|
$value = $yych; |
187
|
|
|
$yych = $YYBUFFER[++$YYCURSOR]; |
188
|
|
|
while ($this->class_ident[$yych] == $yych && ($yych != "\000")) { |
189
|
|
|
$value .= $yych; |
190
|
|
|
$yych = $YYBUFFER[++$YYCURSOR]; |
191
|
|
|
} |
192
|
|
|
return ar_html_zen::T_IDENT; |
193
|
|
|
break; |
194
|
|
|
case "\000" === $yych: |
195
|
|
|
$value = $yych; |
196
|
|
|
return ar_html_zen::T_EOF; |
197
|
|
|
break; |
198
|
|
|
default: |
199
|
|
|
$value = $yych; |
200
|
|
|
++$YYCURSOR; |
201
|
|
|
return $value; |
202
|
|
|
break; |
203
|
|
|
} |
204
|
|
|
} while(1); |
205
|
|
|
} |
206
|
|
|
} |
207
|
|
|
|
208
|
|
|
class ar_html_zenParser { |
209
|
|
|
protected $scanner; |
210
|
|
|
|
211
|
|
|
public function __construct($string) { |
212
|
|
|
$this->scanner = new ar_html_zenScanner($string); |
213
|
|
|
$this->scanner->next(); |
214
|
|
|
} |
215
|
|
|
|
216
|
|
|
public function run() { |
217
|
|
|
$nodelist = $this->parse(); |
218
|
|
|
return $nodelist; |
219
|
|
|
} |
220
|
|
|
|
221
|
|
|
private function parse() { |
222
|
|
|
$result = array(); |
223
|
|
|
$result[] = $this->parseExpression(); |
224
|
|
|
$bye = false; |
225
|
|
|
do { |
226
|
|
|
$token = $this->scanner->token_ahead; |
227
|
|
|
switch($token) { |
228
|
|
|
case ar_html_zen::T_OP_CHILDREN: |
229
|
|
|
$this->scanner->next(); |
230
|
|
|
$result["children"] = $this->parse(); |
231
|
|
|
break; |
232
|
|
|
case ar_html_zen::T_OP_SIBLINGS: |
233
|
|
|
$this->scanner->next(); |
234
|
|
|
$result[] = $this->parseExpression(); |
235
|
|
|
break; |
236
|
|
|
default: |
237
|
|
|
$bye = true; |
238
|
|
|
break; |
239
|
|
|
} |
240
|
|
|
} while( !$bye ); |
241
|
|
|
return $result; |
242
|
|
|
} |
243
|
|
|
|
244
|
|
|
private function parseExpression() { |
245
|
|
|
$token = $this->scanner->token_ahead; |
246
|
|
|
switch( $token ) { |
247
|
|
View Code Duplication |
case ar_html_zen::T_EXPRESSION_OPEN: |
248
|
|
|
$this->scanner->next(); |
249
|
|
|
$result = $this->parse(); |
250
|
|
|
if( $this->scanner->token_ahead != ar_html_zen::T_EXPRESSION_CLOSE ) { |
251
|
|
|
die("No closing ')' found for expression."); |
|
|
|
|
252
|
|
|
} |
253
|
|
|
$this->scanner->next(); |
254
|
|
|
break; |
255
|
|
|
case ar_html_zen::T_IDENT: |
256
|
|
|
$tagName = $this->scanner->token_ahead_value; |
257
|
|
|
$result["tagName"] = $tagName; |
|
|
|
|
258
|
|
|
$this->scanner->next(); |
259
|
|
|
$result["attributes"] = $this->parseAttributes(); |
260
|
|
|
break; |
261
|
|
|
} |
262
|
|
|
$mult = $this->parseMultiplier(); |
263
|
|
|
if( $mult ) { |
264
|
|
|
$result["multiplier"] = $mult; |
|
|
|
|
265
|
|
|
} |
266
|
|
|
return $result; |
267
|
|
|
} |
268
|
|
|
|
269
|
|
|
private function parseAttributes() { |
270
|
|
|
$bye = false; |
271
|
|
|
$result = array(); |
272
|
|
|
do { |
273
|
|
|
$token = $this->scanner->token_ahead; |
274
|
|
|
$key = false; |
275
|
|
|
switch( $token ) { |
276
|
|
|
case ar_html_zen::T_OP_ID: |
277
|
|
|
$key = "id"; |
278
|
|
|
case ar_html_zen::T_OP_CLASS: |
279
|
|
|
if( !$key ) { $key = "class"; } |
|
|
|
|
280
|
|
|
$this->scanner->next(); |
281
|
|
|
if( $this->scanner->token_ahead == ar_html_zen::T_IDENT ) { |
282
|
|
|
$result[$key][] = $this->scanner->token_ahead_value; |
283
|
|
|
$this->scanner->next(); |
284
|
|
|
} else { |
285
|
|
|
die('no ident found for attribute: '.$key); |
|
|
|
|
286
|
|
|
} |
287
|
|
|
break; |
288
|
|
View Code Duplication |
case ar_html_zen::T_OP_ATTRIBUTES_OPEN: |
289
|
|
|
$this->scanner->next(); |
290
|
|
|
$result = array_merge($result, $this->parseAttributeList() ); // FIXME: deep merge |
291
|
|
|
if( $this->scanner->token_ahead != ar_html_zen::T_OP_ATTRIBUTES_CLOSE ) { |
292
|
|
|
die("No attribute closing tag ']' found."); |
|
|
|
|
293
|
|
|
} |
294
|
|
|
$this->scanner->next(); |
295
|
|
|
break; |
296
|
|
|
default: |
297
|
|
|
$bye = true; |
298
|
|
|
break; |
299
|
|
|
} |
300
|
|
|
} while( !$bye ); |
301
|
|
|
return $result; |
302
|
|
|
} |
303
|
|
|
|
304
|
|
|
private function parseAttributeList() { |
305
|
|
|
$bye = false; |
306
|
|
|
$result = array(); |
307
|
|
|
do { |
308
|
|
|
$token = $this->scanner->token_ahead; |
309
|
|
|
switch( $token ) { |
310
|
|
|
case ar_html_zen::T_IDENT: |
311
|
|
|
$attr = $this->scanner->token_ahead_value; |
312
|
|
|
$value = ""; |
313
|
|
|
$this->scanner->next(); |
314
|
|
|
if( $this->scanner->token_ahead == ar_html_zen::T_OP_ASSIGN ) { |
315
|
|
|
$this->scanner->next(); |
316
|
|
|
if( $this->scanner->token_ahead != ar_html_zen::T_IDENT ) { |
317
|
|
|
die('Trying to assign empty attribute '.$this->scanner->token_ahead_value ); |
|
|
|
|
318
|
|
|
} |
319
|
|
|
$value = $this->scanner->token_ahead_value; |
320
|
|
|
$this->scanner->next(); |
321
|
|
|
} |
322
|
|
|
$result[$attr] = $value; |
323
|
|
|
break; |
324
|
|
|
default: |
325
|
|
|
$bye = true; |
326
|
|
|
break; |
327
|
|
|
} |
328
|
|
|
} while( !$bye ); |
329
|
|
|
return $result; |
330
|
|
|
} |
331
|
|
|
|
332
|
|
|
private function parseMultiplier() { |
333
|
|
|
$token = $this->scanner->token_ahead; |
334
|
|
|
if( $token == ar_html_zen::T_OP_MULTIPLIER ) { |
335
|
|
|
$this->scanner->next(); |
336
|
|
|
if( $this->scanner->token_ahead == ar_html_zen::T_NUMBER ) { |
337
|
|
|
$value = $this->scanner->token_ahead_value; |
338
|
|
|
$this->scanner->next(); |
339
|
|
|
return $value; |
340
|
|
|
} else { |
341
|
|
|
die('Invalid multiplier found: '.$this->scanner->token_ahead_value); |
|
|
|
|
342
|
|
|
} |
343
|
|
|
} |
344
|
|
|
return false; |
345
|
|
|
} |
346
|
|
|
} |
347
|
|
|
|
An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.
If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.