1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace PhpSchool\PSXTest; |
4
|
|
|
|
5
|
|
|
use PhpParser\Comment; |
6
|
|
|
use PhpParser\Error; |
7
|
|
|
use PhpParser\Parser\Tokens; |
8
|
|
|
use PhpSchool\PSX\Lexer; |
9
|
|
|
use PHPUnit_Framework_TestCase; |
10
|
|
|
|
11
|
|
|
/** |
12
|
|
|
* Class LexerTest |
13
|
|
|
* @package PhpSchool\PSXTest |
14
|
|
|
* @author Aydin Hassan <[email protected]> |
15
|
|
|
*/ |
16
|
|
|
class LexerTest extends PHPUnit_Framework_TestCase |
17
|
|
|
{ |
18
|
|
|
/* To allow overwriting in parent class */ |
19
|
|
|
protected function getLexer(array $options = array()) |
20
|
|
|
{ |
21
|
|
|
return new Lexer($options); |
22
|
|
|
} |
23
|
|
|
|
24
|
|
|
/** |
25
|
|
|
* @dataProvider provideTestError |
26
|
|
|
*/ |
27
|
|
|
public function testError($code, $message) |
28
|
|
|
{ |
29
|
|
|
if (defined('HHVM_VERSION')) { |
30
|
|
|
$this->markTestSkipped('HHVM does not throw warnings from token_get_all()'); |
31
|
|
|
} |
32
|
|
|
|
33
|
|
|
$lexer = $this->getLexer(); |
34
|
|
|
try { |
35
|
|
|
$lexer->startLexing($code); |
36
|
|
|
} catch (Error $e) { |
37
|
|
|
$this->assertSame($message, $e->getMessage()); |
38
|
|
|
|
39
|
|
|
return; |
40
|
|
|
} |
41
|
|
|
|
42
|
|
|
$this->fail('Expected PhpParser\Error'); |
43
|
|
|
} |
44
|
|
|
|
45
|
|
|
public function provideTestError() |
46
|
|
|
{ |
47
|
|
|
return array( |
48
|
|
|
array('<?php /*', 'Unterminated comment on line 1'), |
49
|
|
|
array('<?php ' . "\1", 'Unexpected character "' . "\1" . '" (ASCII 1) on unknown line'), |
50
|
|
|
array('<?php ' . "\0", 'Unexpected null byte on unknown line'), |
51
|
|
|
); |
52
|
|
|
} |
53
|
|
|
|
54
|
|
|
/** |
55
|
|
|
* @dataProvider provideTestLex |
56
|
|
|
*/ |
57
|
|
|
public function testLex($code, $options, $tokens) |
58
|
|
|
{ |
59
|
|
|
$lexer = $this->getLexer($options); |
60
|
|
|
$lexer->startLexing($code); |
61
|
|
|
while ($id = $lexer->getNextToken($value, $startAttributes, $endAttributes)) { |
62
|
|
|
$token = array_shift($tokens); |
63
|
|
|
|
64
|
|
|
$this->assertSame($token[0], $id); |
65
|
|
|
$this->assertSame($token[1], $value); |
66
|
|
|
$this->assertEquals($token[2], $startAttributes); |
67
|
|
|
$this->assertEquals($token[3], $endAttributes); |
68
|
|
|
} |
69
|
|
|
} |
70
|
|
|
|
71
|
|
|
public function provideTestLex() |
72
|
|
|
{ |
73
|
|
|
return array( |
74
|
|
|
// tests conversion of closing PHP tag and drop of whitespace and opening tags |
75
|
|
|
array( |
76
|
|
|
'<?php tokens ?>plaintext', |
77
|
|
|
array(), |
78
|
|
|
array( |
79
|
|
|
array( |
80
|
|
|
Tokens::T_STRING, 'tokens', |
81
|
|
|
array('startLine' => 1), array('endLine' => 1) |
82
|
|
|
), |
83
|
|
|
array( |
84
|
|
|
ord(';'), '?>', |
85
|
|
|
array('startLine' => 1), array('endLine' => 1) |
86
|
|
|
), |
87
|
|
|
array( |
88
|
|
|
Tokens::T_INLINE_HTML, 'plaintext', |
89
|
|
|
array('startLine' => 1), array('endLine' => 1) |
90
|
|
|
), |
91
|
|
|
) |
92
|
|
|
), |
93
|
|
|
// tests line numbers |
94
|
|
|
array( |
95
|
|
|
'<?php' . "\n" . '$ token /** doc' . "\n" . 'comment */ $', |
96
|
|
|
array(), |
97
|
|
|
array( |
98
|
|
|
array( |
99
|
|
|
ord('$'), '$', |
100
|
|
|
array('startLine' => 2), array('endLine' => 2) |
101
|
|
|
), |
102
|
|
|
array( |
103
|
|
|
Tokens::T_STRING, 'token', |
104
|
|
|
array('startLine' => 2), array('endLine' => 2) |
105
|
|
|
), |
106
|
|
|
array( |
107
|
|
|
ord('$'), '$', |
108
|
|
|
array( |
109
|
|
|
'startLine' => 3, |
110
|
|
|
'comments' => array(new Comment\Doc('/** doc' . "\n" . 'comment */', 2)) |
111
|
|
|
), |
112
|
|
|
array('endLine' => 3) |
113
|
|
|
), |
114
|
|
|
) |
115
|
|
|
), |
116
|
|
|
// tests comment extraction |
117
|
|
|
array( |
118
|
|
|
'<?php /* comment */ // comment' . "\n" . '/** docComment 1 *//** docComment 2 */ token', |
119
|
|
|
array(), |
120
|
|
|
array( |
121
|
|
|
array( |
122
|
|
|
Tokens::T_STRING, 'token', |
123
|
|
|
array( |
124
|
|
|
'startLine' => 2, |
125
|
|
|
'comments' => array( |
126
|
|
|
new Comment('/* comment */', 1), |
127
|
|
|
new Comment('// comment' . "\n", 1), |
128
|
|
|
new Comment\Doc('/** docComment 1 */', 2), |
129
|
|
|
new Comment\Doc('/** docComment 2 */', 2), |
130
|
|
|
), |
131
|
|
|
), |
132
|
|
|
array('endLine' => 2) |
133
|
|
|
), |
134
|
|
|
) |
135
|
|
|
), |
136
|
|
|
// tests differing start and end line |
137
|
|
|
array( |
138
|
|
|
'<?php "foo' . "\n" . 'bar"', |
139
|
|
|
array(), |
140
|
|
|
array( |
141
|
|
|
array( |
142
|
|
|
Tokens::T_CONSTANT_ENCAPSED_STRING, '"foo' . "\n" . 'bar"', |
143
|
|
|
array('startLine' => 1), array('endLine' => 2, 'originalValue' => "\"foo\nbar\"") |
144
|
|
|
), |
145
|
|
|
) |
146
|
|
|
), |
147
|
|
|
// tests exact file offsets |
148
|
|
|
array( |
149
|
|
|
'<?php "a";' . "\n" . '// foo' . "\n" . '"b";', |
150
|
|
|
array('usedAttributes' => array('startFilePos', 'endFilePos')), |
151
|
|
|
array( |
152
|
|
|
array( |
153
|
|
|
Tokens::T_CONSTANT_ENCAPSED_STRING, '"a"', |
154
|
|
|
array('startFilePos' => 6), array('endFilePos' => 8, 'originalValue' => '"a"') |
155
|
|
|
), |
156
|
|
|
array( |
157
|
|
|
ord(';'), ';', |
158
|
|
|
array('startFilePos' => 9), array('endFilePos' => 9) |
159
|
|
|
), |
160
|
|
|
array( |
161
|
|
|
Tokens::T_CONSTANT_ENCAPSED_STRING, '"b"', |
162
|
|
|
array('startFilePos' => 18), array('endFilePos' => 20, 'originalValue' => '"b"') |
163
|
|
|
), |
164
|
|
|
array( |
165
|
|
|
ord(';'), ';', |
166
|
|
|
array('startFilePos' => 21), array('endFilePos' => 21) |
167
|
|
|
), |
168
|
|
|
) |
169
|
|
|
), |
170
|
|
|
// tests token offsets |
171
|
|
|
array( |
172
|
|
|
'<?php "a";' . "\n" . '// foo' . "\n" . '"b";', |
173
|
|
|
array('usedAttributes' => array('startTokenPos', 'endTokenPos')), |
174
|
|
|
array( |
175
|
|
|
array( |
176
|
|
|
Tokens::T_CONSTANT_ENCAPSED_STRING, '"a"', |
177
|
|
|
array('startTokenPos' => 1), array('endTokenPos' => 1, 'originalValue' => '"a"') |
178
|
|
|
), |
179
|
|
|
array( |
180
|
|
|
ord(';'), ';', |
181
|
|
|
array('startTokenPos' => 2), array('endTokenPos' => 2) |
182
|
|
|
), |
183
|
|
|
array( |
184
|
|
|
Tokens::T_CONSTANT_ENCAPSED_STRING, '"b"', |
185
|
|
|
array('startTokenPos' => 5), array('endTokenPos' => 5, 'originalValue' => '"b"') |
186
|
|
|
), |
187
|
|
|
array( |
188
|
|
|
ord(';'), ';', |
189
|
|
|
array('startTokenPos' => 6), array('endTokenPos' => 6) |
190
|
|
|
), |
191
|
|
|
) |
192
|
|
|
), |
193
|
|
|
// tests all attributes being disabled |
194
|
|
|
array( |
195
|
|
|
'<?php /* foo */ $bar;', |
196
|
|
|
array('usedAttributes' => array()), |
197
|
|
|
array( |
198
|
|
|
array( |
199
|
|
|
Tokens::T_VARIABLE, '$bar', |
200
|
|
|
array(), array() |
201
|
|
|
), |
202
|
|
|
array( |
203
|
|
|
ord(';'), ';', |
204
|
|
|
array(), array() |
205
|
|
|
) |
206
|
|
|
) |
207
|
|
|
), |
208
|
|
|
// test traditional array syntax |
209
|
|
|
array( |
210
|
|
|
'<?php $bar = array();', |
211
|
|
|
array('usedAttributes' => array()), |
212
|
|
|
array( |
213
|
|
|
array( |
214
|
|
|
Tokens::T_VARIABLE, '$bar', |
215
|
|
|
array(), array() |
216
|
|
|
), |
217
|
|
|
array( |
218
|
|
|
ord('='), '=', |
219
|
|
|
array(), array() |
220
|
|
|
), |
221
|
|
|
array( |
222
|
|
|
Tokens::T_ARRAY, 'array', |
223
|
|
|
array('traditionalArray' => true), array() |
224
|
|
|
), |
225
|
|
|
array( |
226
|
|
|
ord('('), '(', |
227
|
|
|
array(), array() |
228
|
|
|
), |
229
|
|
|
array( |
230
|
|
|
ord(')'), ')', |
231
|
|
|
array(), array() |
232
|
|
|
), |
233
|
|
|
array( |
234
|
|
|
ord(';'), ';', |
235
|
|
|
array(), array() |
236
|
|
|
) |
237
|
|
|
) |
238
|
|
|
), |
239
|
|
|
array( |
240
|
|
|
'<?php die;', |
241
|
|
|
array('usedAttributes' => array()), |
242
|
|
|
array( |
243
|
|
|
array( |
244
|
|
|
Tokens::T_EXIT, 'die', |
245
|
|
|
array('isDie' => true), array() |
246
|
|
|
), |
247
|
|
|
array( |
248
|
|
|
ord(';'), ';', |
249
|
|
|
array(), array() |
250
|
|
|
) |
251
|
|
|
) |
252
|
|
|
) |
253
|
|
|
); |
254
|
|
|
} |
255
|
|
|
|
256
|
|
|
/** |
257
|
|
|
* @dataProvider provideTestHaltCompiler |
258
|
|
|
*/ |
259
|
|
|
public function testHandleHaltCompiler($code, $remaining) |
260
|
|
|
{ |
261
|
|
|
$lexer = $this->getLexer(); |
262
|
|
|
$lexer->startLexing($code); |
263
|
|
|
|
264
|
|
|
while (Tokens::T_HALT_COMPILER !== $lexer->getNextToken()) { |
|
|
|
|
265
|
|
|
} |
266
|
|
|
|
267
|
|
|
$this->assertSame($remaining, $lexer->handleHaltCompiler()); |
268
|
|
|
$this->assertSame(0, $lexer->getNextToken()); |
269
|
|
|
} |
270
|
|
|
|
271
|
|
|
public function provideTestHaltCompiler() |
272
|
|
|
{ |
273
|
|
|
return array( |
274
|
|
|
array('<?php ... __halt_compiler();Remaining Text', 'Remaining Text'), |
275
|
|
|
array('<?php ... __halt_compiler ( ) ;Remaining Text', 'Remaining Text'), |
276
|
|
|
array('<?php ... __halt_compiler() ?>Remaining Text', 'Remaining Text'), |
277
|
|
|
//array('<?php ... __halt_compiler();' . "\0", "\0"), |
|
|
|
|
278
|
|
|
//array('<?php ... __halt_compiler /* */ ( ) ;Remaining Text', 'Remaining Text'), |
|
|
|
|
279
|
|
|
); |
280
|
|
|
} |
281
|
|
|
|
282
|
|
|
/** |
283
|
|
|
* @expectedException \PhpParser\Error |
284
|
|
|
* @expectedExceptionMessage __HALT_COMPILER must be followed by "();" |
285
|
|
|
*/ |
286
|
|
|
public function testHandleHaltCompilerError() |
287
|
|
|
{ |
288
|
|
|
$lexer = $this->getLexer(); |
289
|
|
|
$lexer->startLexing('<?php ... __halt_compiler invalid ();'); |
290
|
|
|
|
291
|
|
|
while (Tokens::T_HALT_COMPILER !== $lexer->getNextToken()) { |
|
|
|
|
292
|
|
|
} |
293
|
|
|
$lexer->handleHaltCompiler(); |
294
|
|
|
} |
295
|
|
|
|
296
|
|
|
public function testGetTokens() |
297
|
|
|
{ |
298
|
|
|
$code = '<?php "a";' . "\n" . '// foo' . "\n" . '"b";'; |
299
|
|
|
$expectedTokens = array( |
300
|
|
|
array(T_OPEN_TAG, '<?php ', 1), |
301
|
|
|
array(T_CONSTANT_ENCAPSED_STRING, '"a"', 1), |
302
|
|
|
';', |
303
|
|
|
array(T_WHITESPACE, "\n", 1), |
304
|
|
|
array(T_COMMENT, '// foo' . "\n", 2), |
305
|
|
|
array(T_CONSTANT_ENCAPSED_STRING, '"b"', 3), |
306
|
|
|
';', |
307
|
|
|
); |
308
|
|
|
|
309
|
|
|
$lexer = $this->getLexer(); |
310
|
|
|
$lexer->startLexing($code); |
311
|
|
|
$this->assertSame($expectedTokens, $lexer->getTokens()); |
312
|
|
|
} |
313
|
|
|
} |
314
|
|
|
|
This check looks for
while
loops that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.Consider removing the loop.