Skip to content

Commit 44efe11

Browse files
committed
* Multiline array formatting for printer
* Print array using original format * Fix echo/die differentiation
1 parent 886379e commit 44efe11

9 files changed

+413
-9
lines changed

src/Factory.php

+8-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace PhpSchool\PSX;
44

5+
use PhpParser\Lexer;
56
use PhpParser\ParserFactory;
67
use Colors\Color;
78

@@ -17,11 +18,17 @@ class Factory
1718
*/
1819
public function __invoke()
1920
{
21+
$lexer = new Lexer([
22+
'usedAttributes' => [
23+
'comments', 'startLine', 'endLine', 'startFilePos', 'endFilePos', 'startTokenPos', 'endTokenPos'
24+
]
25+
]);
26+
2027
$parserFactory = new ParserFactory;
2128
$color = new Color;
2229
$color->setForceStyle(true);
2330
return new SyntaxHighlighter(
24-
$parserFactory->create(ParserFactory::PREFER_PHP7, new Lexer()),
31+
$parserFactory->create(ParserFactory::PREFER_PHP7, $lexer),
2532
new SyntaxHighlightPrinter(
2633
new SyntaxHighlighterConfig,
2734
new ColorsAdapter($color)

src/Lexer.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ public function getNextToken(&$value = null, &$startAttributes = null, &$endAttr
2121
}
2222

2323
if ($tokenId == Tokens::T_EXIT) {
24-
$endAttributes['isDie'] = strtolower($value) === 'die';
24+
$startAttributes['isDie'] = strtolower($value) === 'die';
2525
}
2626

2727
return $tokenId;

src/SyntaxHighlightPrinter.php

+28-3
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,8 @@ public function __construct(
5353
*
5454
* @return string Pretty printed statements
5555
*/
56-
public function prettyPrintFile(array $stmts) {
56+
public function prettyPrintFile(array $stmts)
57+
{
5758
$p = rtrim($this->prettyPrint($stmts));
5859
$p = preg_replace('/^\?>\n?/', '', $p, -1, $count);
5960
$p = preg_replace('/<\?php$/', '', $p);
@@ -499,10 +500,34 @@ public function pExpr_New(Expr\New_ $node)
499500
public function pExpr_Array(Expr\Array_ $node)
500501
{
501502
if ($this->options['shortArraySyntax'] || !$node->hasAttribute('traditionalArray')) {
502-
return '[' . $this->pCommaSeparated($node->items) . ']';
503+
return '[' . $this->pArrayList($node, $node->items) . ']';
503504
} else {
504-
return 'array(' . $this->pCommaSeparated($node->items) . ')';
505+
return 'array(' . $this->pArrayList($node, $node->items) . ')';
506+
}
507+
}
508+
509+
/**
510+
* @param Expr\Array_ $parent
511+
* @param array $nodes
512+
* @return string
513+
*/
514+
public function pArrayList(Expr\Array_ $parent, array $nodes)
515+
{
516+
$lineNumbers = [$parent->getAttribute('startLine', null)];
517+
foreach ($nodes as $node) {
518+
$lineNumbers[] = $node->getAttribute('startLine', null);
519+
}
520+
521+
//all the same line
522+
if (count(array_unique($lineNumbers)) == 1) {
523+
return $this->pCommaSeparated($nodes);
524+
}
525+
526+
$output = "\n";
527+
foreach ($nodes as $key => $node) {
528+
$output .= sprintf(" %s,\n", $this->p($node));
505529
}
530+
return $output;
506531
}
507532

508533
/**

test/LexerTest.php

+313
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,313 @@
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)
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)
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)
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)
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)
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+
}

0 commit comments

Comments
 (0)