Skip to content

Commit 5eae391

Browse files
committed
implemented PHP-safe indentation
1 parent d3675a5 commit 5eae391

File tree

6 files changed

+274
-2
lines changed

6 files changed

+274
-2
lines changed

composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
],
1717
"require": {
1818
"php": ">=7.1",
19+
"ext-tokenizer": "*",
1920
"nette/utils": "^2.4.2 || ^3.0"
2021
},
2122
"require-dev": {

src/PhpGenerator/Dumper.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ public function dump($var, int $column = 0): string
3838
private function dumpVar(&$var, array $parents = [], int $level = 0, int $column = 0): string
3939
{
4040
if ($var instanceof Literal) {
41-
return ltrim(Nette\Utils\Strings::indent(trim((string) $var), $level), "\t");
41+
return ltrim(Helpers::indentPhp(trim((string) $var), $level), "\t");
4242

4343
} elseif ($var === null) {
4444
return 'null';

src/PhpGenerator/Helpers.php

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,35 @@ public static function unformatDocComment(string $comment): string
6161
}
6262

6363

64+
public static function indentPhp(string $s, int $level = 1, string $chars = "\t"): string
65+
{
66+
$tbl = [];
67+
$s = str_replace("\r\n", "\n", $s);
68+
69+
if ($level && strpos($s, "\n") !== false && preg_match('#\?>|<<<|"|\'#', $s)) {
70+
static $save = [T_CONSTANT_ENCAPSED_STRING => 1, T_ENCAPSED_AND_WHITESPACE => 1, T_INLINE_HTML => 1, T_START_HEREDOC => 1, T_CLOSE_TAG => 1];
71+
$tokens = token_get_all("<?php\n" . $s);
72+
unset($tokens[0]);
73+
$s = '';
74+
foreach ($tokens as $token) {
75+
if (isset($save[$token[0]]) && strpos($token[1], "\n") !== false) {
76+
$s .= $id = "\00" . count($tbl) . "\00";
77+
$tbl[$id] = $token[1];
78+
} else {
79+
$s .= is_array($token) ? $token[1] : $token;
80+
}
81+
}
82+
}
83+
84+
if ($level > 0) {
85+
$s = Nette\Utils\Strings::indent($s, $level, $chars);
86+
} elseif ($level < 0) {
87+
$s = preg_replace('#^(\t|\ \ \ \ ){1,' . (-$level) . '}#m', '', $s);
88+
}
89+
return strtr($s, $tbl);
90+
}
91+
92+
6493
public static function isIdentifier($value): bool
6594
{
6695
return is_string($value) && preg_match('#^' . self::PHP_IDENT . '$#D', $value);

src/PhpGenerator/Printer.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,7 @@ public function setTypeResolving(bool $state = true): self
211211
protected function indent(string $s): string
212212
{
213213
$s = str_replace("\t", $this->indentation, $s);
214-
return Strings::indent($s, 1, $this->indentation);
214+
return Helpers::indentPhp($s, 1, $this->indentation);
215215
}
216216

217217

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
<?php
2+
3+
/**
4+
* Test: Nette\PhpGenerator\Helpers::indentPhp()
5+
*/
6+
7+
declare(strict_types=1);
8+
9+
use Nette\PhpGenerator\Helpers;
10+
use Tester\Assert;
11+
12+
13+
require __DIR__ . '/../bootstrap.php';
14+
15+
16+
Assert::same('', Helpers::indentPhp(''));
17+
Assert::same("\n", Helpers::indentPhp("\n"));
18+
Assert::same("\tword", Helpers::indentPhp('word'));
19+
Assert::same("\n\tword", Helpers::indentPhp("\nword"));
20+
Assert::same("\n\tword\n", Helpers::indentPhp("\nword\n"));
21+
Assert::same("\n\t\tword\n", Helpers::indentPhp("\nword\n", 2));
22+
Assert::same("\n word\n", Helpers::indentPhp("\nword\n", 2, ' '));
23+
24+
same(<<<'XX'
25+
$s = "a
26+
b" + "{${'
27+
'}}";
28+
XX
29+
, Helpers::indentPhp(<<<'XX'
30+
$s = "a
31+
b" + "{${'
32+
'}}";
33+
XX
34+
));
35+
36+
37+
same(<<<'XX'
38+
$s = 'a
39+
b' + '';
40+
XX
41+
, Helpers::indentPhp(<<<'XX'
42+
$s = 'a
43+
b' + '';
44+
XX
45+
));
46+
47+
48+
same(<<<'XX'
49+
// single
50+
/*
51+
multi
52+
line */
53+
/**
54+
multi
55+
line */
56+
XX
57+
, Helpers::indentPhp(<<<'XX'
58+
// single
59+
/*
60+
multi
61+
line */
62+
/**
63+
multi
64+
line */
65+
XX
66+
));
67+
68+
69+
same(<<<'XX'
70+
?>
71+
a
72+
b
73+
<?php
74+
$c
75+
XX
76+
, Helpers::indentPhp(<<<'XX'
77+
?>
78+
a
79+
b
80+
<?php
81+
$c
82+
XX
83+
));
84+
85+
86+
same(<<<'XX'
87+
$s = <<<DOC
88+
a
89+
b
90+
DOC
91+
;
92+
XX
93+
, Helpers::indentPhp(<<<'XX'
94+
$s = <<<DOC
95+
a
96+
b
97+
DOC
98+
;
99+
XX
100+
));
101+
102+
103+
same(<<<'XX'
104+
$s = <<<'DOC'
105+
a
106+
b
107+
DOC
108+
;
109+
XX
110+
, Helpers::indentPhp(<<<'XX'
111+
$s = <<<'DOC'
112+
a
113+
b
114+
DOC
115+
;
116+
XX
117+
));
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
<?php
2+
3+
/**
4+
* Test: Nette\PhpGenerator\Helpers::(un)indentPhp()
5+
*/
6+
7+
declare(strict_types=1);
8+
9+
use Nette\PhpGenerator\Helpers;
10+
use Tester\Assert;
11+
12+
13+
require __DIR__ . '/../bootstrap.php';
14+
15+
16+
Assert::same('', Helpers::indentPhp('', -1));
17+
Assert::same("\n", Helpers::indentPhp("\n", -1));
18+
Assert::same('word', Helpers::indentPhp('word', -1));
19+
Assert::same("\nword", Helpers::indentPhp("\nword", -1));
20+
Assert::same("\nword\n", Helpers::indentPhp("\nword\n", -1));
21+
Assert::same('word', Helpers::indentPhp("\tword", -1));
22+
Assert::same("\tword", Helpers::indentPhp("\t\tword", -1));
23+
Assert::same('word', Helpers::indentPhp("\t\tword", -2));
24+
Assert::same("\nword", Helpers::indentPhp("\n\tword", -1));
25+
Assert::same("word\t", Helpers::indentPhp("word\t", -1));
26+
Assert::same("word\tword", Helpers::indentPhp("word\tword", -1));
27+
Assert::same("word\t\nword", Helpers::indentPhp("word\t\nword", -1));
28+
Assert::same('word', Helpers::indentPhp(' word', -1));
29+
Assert::same(' word', Helpers::indentPhp(' word', -1));
30+
31+
32+
same(<<<'XX'
33+
$s = "a
34+
b" + "{${'
35+
'}}";
36+
XX
37+
, Helpers::indentPhp(<<<'XX'
38+
$s = "a
39+
b" + "{${'
40+
'}}";
41+
XX
42+
, -1));
43+
44+
45+
same(<<<'XX'
46+
$s = 'a
47+
b' + '';
48+
XX
49+
, Helpers::indentPhp(<<<'XX'
50+
$s = 'a
51+
b' + '';
52+
XX
53+
, -1));
54+
55+
56+
same(<<<'XX'
57+
// single
58+
/*
59+
multi
60+
line */
61+
/**
62+
multi
63+
line */
64+
XX
65+
, Helpers::indentPhp(<<<'XX'
66+
// single
67+
/*
68+
multi
69+
line */
70+
/**
71+
multi
72+
line */
73+
XX
74+
, -1));
75+
76+
77+
same(<<<'XX'
78+
?>
79+
a
80+
b
81+
<?php
82+
$c
83+
XX
84+
, Helpers::indentPhp(<<<'XX'
85+
?>
86+
a
87+
b
88+
<?php
89+
$c
90+
XX
91+
, -1));
92+
93+
94+
same(<<<'XX'
95+
$s = <<<DOC
96+
a
97+
b
98+
DOC
99+
;
100+
XX
101+
, Helpers::indentPhp(<<<'XX'
102+
$s = <<<DOC
103+
a
104+
b
105+
DOC
106+
;
107+
XX
108+
, -1));
109+
110+
111+
same(<<<'XX'
112+
$s = <<<'DOC'
113+
a
114+
b
115+
DOC
116+
;
117+
XX
118+
, Helpers::indentPhp(<<<'XX'
119+
$s = <<<'DOC'
120+
a
121+
b
122+
DOC
123+
;
124+
XX
125+
, -1));

0 commit comments

Comments
 (0)