Skip to content

Commit 7b931d6

Browse files
committed
Extractor: NOWDOC/HEREDOC are not converted to single line strings
it is necessary to provide an indentation that can be unindented
1 parent 3162d8d commit 7b931d6

File tree

8 files changed

+220
-78
lines changed

8 files changed

+220
-78
lines changed

src/PhpGenerator/Extractor.php

Lines changed: 61 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ public function extractFunctionBody(string $name): ?string
9595
private function getReformattedContents(array $nodes, int $level): string
9696
{
9797
$body = $this->getNodeContents(...$nodes);
98-
$body = $this->performReplacements($body, $this->prepareReplacements($nodes));
98+
$body = $this->performReplacements($body, $this->prepareReplacements($nodes, $level));
9999
return Helpers::unindent($body, $level);
100100
}
101101

@@ -104,11 +104,12 @@ private function getReformattedContents(array $nodes, int $level): string
104104
* @param Node[] $nodes
105105
* @return array<array{int, int, string}>
106106
*/
107-
private function prepareReplacements(array $nodes): array
107+
private function prepareReplacements(array $nodes, int $level): array
108108
{
109109
$start = $this->getNodeStartPos($nodes[0]);
110110
$replacements = [];
111-
(new NodeFinder)->find($nodes, function (Node $node) use (&$replacements, $start) {
111+
$indent = "\n" . str_repeat("\t", $level);
112+
(new NodeFinder)->find($nodes, function (Node $node) use (&$replacements, $start, $level, $indent) {
112113
if ($node instanceof Node\Name\FullyQualified) {
113114
if ($node->getAttribute('originalName') instanceof Node\Name) {
114115
$of = match (true) {
@@ -122,30 +123,64 @@ private function prepareReplacements(array $nodes): array
122123
Helpers::tagName($node->toCodeString(), $of),
123124
];
124125
}
125-
} elseif ($node instanceof Node\Scalar\String_ || $node instanceof Node\Scalar\EncapsedStringPart) {
126-
// multi-line strings => singleline
127-
$token = $this->getNodeContents($node);
128-
if (str_contains($token, "\n")) {
129-
$quote = $node instanceof Node\Scalar\String_ ? '"' : '';
130-
$replacements[] = [
131-
$node->getStartFilePos() - $start,
132-
$node->getEndFilePos() - $start,
133-
$quote . addcslashes($node->value, "\x00..\x1F") . $quote,
134-
];
126+
127+
} elseif (
128+
$node instanceof Node\Scalar\String_
129+
&& in_array($node->getAttribute('kind'), [Node\Scalar\String_::KIND_SINGLE_QUOTED, Node\Scalar\String_::KIND_DOUBLE_QUOTED], true)
130+
&& str_contains($node->getAttribute('rawValue'), "\n")
131+
) { // multi-line strings -> single line
132+
$replacements[] = [
133+
$node->getStartFilePos() - $start,
134+
$node->getEndFilePos() - $start,
135+
'"' . addcslashes($node->value, "\x00..\x1F") . '"',
136+
];
137+
138+
} elseif (
139+
$node instanceof Node\Scalar\String_
140+
&& in_array($node->getAttribute('kind'), [Node\Scalar\String_::KIND_NOWDOC, Node\Scalar\String_::KIND_HEREDOC], true)
141+
&& Helpers::unindent($node->getAttribute('docIndentation'), $level) === $node->getAttribute('docIndentation')
142+
) { // fix indentation of NOWDOW/HEREDOC
143+
$replacements[] = [
144+
$node->getStartFilePos() - $start,
145+
$node->getEndFilePos() - $start,
146+
str_replace("\n", $indent, $this->getNodeContents($node)),
147+
];
148+
149+
} elseif (
150+
$node instanceof Node\Scalar\Encapsed
151+
&& $node->getAttribute('kind') === Node\Scalar\String_::KIND_DOUBLE_QUOTED
152+
) { // multi-line strings -> single line
153+
foreach ($node->parts as $part) {
154+
if ($part instanceof Node\Scalar\EncapsedStringPart) {
155+
$replacements[] = [
156+
$part->getStartFilePos() - $start,
157+
$part->getEndFilePos() - $start,
158+
addcslashes($part->value, "\x00..\x1F\""),
159+
];
160+
}
135161
}
136-
} elseif ($node instanceof Node\Scalar\Encapsed) {
137-
// HEREDOC => "string"
138-
if ($node->getAttribute('kind') === Node\Scalar\String_::KIND_HEREDOC) {
139-
$replacements[] = [
140-
$node->getStartFilePos() - $start,
141-
$node->parts[0]->getStartFilePos() - $start - 1,
142-
'"',
143-
];
144-
$replacements[] = [
145-
end($node->parts)->getEndFilePos() - $start + 1,
146-
$node->getEndFilePos() - $start,
147-
'"',
148-
];
162+
} elseif (
163+
$node instanceof Node\Scalar\Encapsed && $node->getAttribute('kind') === Node\Scalar\String_::KIND_HEREDOC
164+
&& Helpers::unindent($node->getAttribute('docIndentation'), $level) === $node->getAttribute('docIndentation')
165+
) { // fix indentation of HEREDOC
166+
$replacements[] = [
167+
$tmp = $node->getStartFilePos() - $start + strlen($node->getAttribute('docLabel')) + 3, // <<<
168+
$tmp,
169+
$indent,
170+
];
171+
$replacements[] = [
172+
$tmp = $node->getEndFilePos() - $start - strlen($node->getAttribute('docLabel')),
173+
$tmp,
174+
$indent,
175+
];
176+
foreach ($node->parts as $part) {
177+
if ($part instanceof Node\Scalar\EncapsedStringPart) {
178+
$replacements[] = [
179+
$part->getStartFilePos() - $start,
180+
$part->getEndFilePos() - $start,
181+
str_replace("\n", $indent, $this->getNodeContents($part)),
182+
];
183+
}
149184
}
150185
}
151186
});
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
use Nette\PhpGenerator\Extractor;
6+
use Tester\Assert;
7+
8+
require __DIR__ . '/../bootstrap.php';
9+
10+
11+
$extractor = new Extractor(<<<'XX'
12+
<?php
13+
function quoted()
14+
{
15+
$s = '
16+
a
17+
b
18+
c
19+
';
20+
21+
$d = "
22+
a
23+
b
24+
c
25+
";
26+
27+
$id = "
28+
a
29+
{$b}
30+
$c
31+
";
32+
}
33+
34+
35+
function heredoc()
36+
{
37+
$s1 = <<<DOC
38+
a
39+
{$b}
40+
$c
41+
DOC;
42+
43+
$s2 = <<<DOC
44+
a
45+
{$b}
46+
$c x
47+
DOC;
48+
49+
$s3 = <<<DOC
50+
a
51+
{$b}
52+
$c x
53+
DOC;
54+
55+
$s4 = <<<DOC
56+
a
57+
{$b(<<<IN
58+
x {$d("
59+
$x
60+
")}
61+
IN)}
62+
$c xx
63+
yy
64+
DOC;
65+
}
66+
67+
68+
function nowdoc()
69+
{
70+
$s1 = <<<'DOC'
71+
a
72+
b
73+
c 'q1' "q2"
74+
DOC;
75+
76+
$s2 = <<<'DOC'
77+
a
78+
b
79+
c
80+
DOC;
81+
82+
$s3 = <<<'DOC'
83+
a
84+
b
85+
c
86+
DOC;
87+
}
88+
89+
XX);
90+
91+
92+
Assert::match(
93+
<<<'XX'
94+
$s = "\na\n\tb\n\t\tc\n";
95+
96+
$d = "\na\n\tb\n\t\tc\n";
97+
98+
$id = "\na\n\t{$b}\n\t\t$c\n";
99+
XX,
100+
$extractor->extractFunctionBody('quoted'),
101+
);
102+
103+
104+
Assert::match(
105+
<<<'XX'
106+
$s1 = <<<DOC
107+
a
108+
{$b}
109+
$c
110+
DOC;
111+
112+
$s2 = <<<DOC
113+
a
114+
{$b}
115+
$c x
116+
DOC;
117+
118+
$s3 = <<<DOC
119+
a
120+
{$b}
121+
$c x
122+
DOC;
123+
124+
$s4 = <<<DOC
125+
a
126+
{$b(<<<IN
127+
x {$d("\n\t$x\n")}
128+
IN)}
129+
$c xx
130+
yy
131+
DOC;
132+
XX,
133+
$extractor->extractFunctionBody('heredoc'),
134+
);
135+
136+
137+
Assert::match(
138+
<<<'XX'
139+
$s1 = <<<'DOC'
140+
a
141+
b
142+
c 'q1' "q2"
143+
DOC;
144+
145+
$s2 = <<<'DOC'
146+
a
147+
b
148+
c
149+
DOC;
150+
151+
$s3 = <<<'DOC'
152+
a
153+
b
154+
c
155+
DOC;
156+
XX,
157+
$extractor->extractFunctionBody('nowdoc'),
158+
);

tests/PhpGenerator/expected/ClassType.from.bodies.expect

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -80,13 +80,6 @@ abstract class Class7
8080
comment */
8181
// Alias Method will not be resolved in comment
8282
if ($member instanceof Method) {
83-
$s1 = "\na\n\tb\n\t\tc\n";
84-
$s2 = "\na\n\t{$b}\n\t\t$c\n";
85-
86-
$s3 = "a\n\t{$b}\n\t\t$c"
87-
;
88-
$s3 = "a\n\tb\n\t\tc"
89-
;
9083
// inline HTML is not supported
9184
?>
9285
a

tests/PhpGenerator/expected/Extractor.bodies.expect

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -90,13 +90,6 @@ abstract class Class7
9090
comment */
9191
// Alias Method will not be resolved in comment
9292
if ($member instanceof Method) {
93-
$s1 = "\na\n\tb\n\t\tc\n";
94-
$s2 = "\na\n\t{$b}\n\t\t$c\n";
95-
96-
$s3 = "a\n\t{$b}\n\t\t$c"
97-
;
98-
$s3 = "a\n\tb\n\t\tc"
99-
;
10093
// inline HTML is not supported
10194
?>
10295
a

tests/PhpGenerator/expected/Extractor.bodies.resolving.expect

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -85,13 +85,6 @@ abstract class Class7
8585
comment */
8686
// Alias Method will not be resolved in comment
8787
if ($member instanceof \Abc\Method) {
88-
$s1 = "\na\n\tb\n\t\tc\n";
89-
$s2 = "\na\n\t{$b}\n\t\t$c\n";
90-
91-
$s3 = "a\n\t{$b}\n\t\t$c"
92-
;
93-
$s3 = "a\n\tb\n\t\tc"
94-
;
9588
// inline HTML is not supported
9689
?>
9790
a

tests/PhpGenerator/expected/Extractor.bodies.unresolving.expect

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -85,13 +85,6 @@ abstract class Class7
8585
comment */
8686
// Alias Method will not be resolved in comment
8787
if ($member instanceof \Abc\Method) {
88-
$s1 = "\na\n\tb\n\t\tc\n";
89-
$s2 = "\na\n\t{$b}\n\t\t$c\n";
90-
91-
$s3 = "a\n\t{$b}\n\t\t$c"
92-
;
93-
$s3 = "a\n\tb\n\t\tc"
94-
;
9588
// inline HTML is not supported
9689
?>
9790
a

tests/PhpGenerator/expected/Extractor.expect

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ class Class1
2121
function comment2()
2222
{
2323
// comment
24-
"bar";
24+
'bar';
2525
}
2626

2727

tests/PhpGenerator/fixtures/bodies.php

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -77,29 +77,6 @@ function complex()
7777
comment */
7878
// Alias Method will not be resolved in comment
7979
if ($member instanceof Method) {
80-
$s1 = '
81-
a
82-
b
83-
c
84-
';
85-
$s2 = "
86-
a
87-
{$b}
88-
$c
89-
";
90-
91-
$s3 = <<<DOC
92-
a
93-
{$b}
94-
$c
95-
DOC
96-
;
97-
$s3 = <<<'DOC'
98-
a
99-
b
100-
c
101-
DOC
102-
;
10380
// inline HTML is not supported
10481
?>
10582
a

0 commit comments

Comments
 (0)