Skip to content

Commit

Permalink
Indent heredoc/nowdoc when targeting PHP >= 7.3
Browse files Browse the repository at this point in the history
  • Loading branch information
nikic committed Aug 27, 2023
1 parent 8d58380 commit 5da5231
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 18 deletions.
1 change: 1 addition & 0 deletions UPGRADE-5.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,7 @@ The pretty printer now accepts a `phpVersion` option, which accepts a `PhpVersio
* For PHP >= 7.0 (default), parentheses around `yield` expressions will only be printed when necessary. Previously, parentheses were always printed, even if `yield` was used as a statement.
* For PHP >= 7.1 (default), the short array syntax `[]` will be used for destructuring by default (instead of `list()`). This does not affect nodes that specify and explicit syntax using the `kind` attribute.
* For PHP >= 7.3 (default), a newline is no longer forced after heredoc/nowdoc strings, as the requirement for this has been removed with the introduction of flexible heredoc/nowdoc strings.
* For PHP >= 7.3 (default), heredoc/nowdoc strings are now indented just like regular code. This was allowed with the introduction of flexible heredoc/nowdoc strings.

### Changes to precedence handling in the pretty printer

Expand Down
29 changes: 19 additions & 10 deletions lib/PhpParser/PrettyPrinter/Standard.php
Original file line number Diff line number Diff line change
Expand Up @@ -127,20 +127,26 @@ protected function pScalar_MagicConst_Trait(MagicConst\Trait_ $node): string {

// Scalars

private function indentString(string $str): string {
return str_replace("\n", $this->nl, $str);
}

protected function pScalar_String(Scalar\String_ $node): string {
$kind = $node->getAttribute('kind', Scalar\String_::KIND_SINGLE_QUOTED);
switch ($kind) {
case Scalar\String_::KIND_NOWDOC:
$label = $node->getAttribute('docLabel');
if ($label && !$this->containsEndLabel($node->value, $label)) {
$shouldIdent = $this->phpVersion->supportsFlexibleHeredoc();
$nl = $shouldIdent ? $this->nl : $this->newline;
if ($node->value === '') {
return "<<<'$label'{$this->newline}$label{$this->docStringEndToken}";
return "<<<'$label'$nl$label{$this->docStringEndToken}";
}

// Make sure trailing \r is not combined with following \n into CRLF.
if ($node->value[strlen($node->value) - 1] !== "\r") {
return "<<<'$label'{$this->newline}{$node->value}{$this->newline}$label"
. $this->docStringEndToken;
$value = $shouldIdent ? $this->indentString($node->value) : $node->value;
return "<<<'$label'$nl$value$nl$label{$this->docStringEndToken}";
}
}
/* break missing intentionally */
Expand All @@ -151,12 +157,12 @@ protected function pScalar_String(Scalar\String_ $node): string {
$label = $node->getAttribute('docLabel');
$escaped = $this->escapeString($node->value, null);
if ($label && !$this->containsEndLabel($escaped, $label)) {
$nl = $this->phpVersion->supportsFlexibleHeredoc() ? $this->nl : $this->newline;
if ($escaped === '') {
return "<<<$label{$this->newline}$label{$this->docStringEndToken}";
return "<<<$label$nl$label{$this->docStringEndToken}";
}

return "<<<$label{$this->newline}$escaped{$this->newline}$label"
. $this->docStringEndToken;
return "<<<$label$nl$escaped$nl$label{$this->docStringEndToken}";
}
/* break missing intentionally */
// no break
Expand All @@ -170,15 +176,16 @@ protected function pScalar_InterpolatedString(Scalar\InterpolatedString $node):
if ($node->getAttribute('kind') === Scalar\String_::KIND_HEREDOC) {
$label = $node->getAttribute('docLabel');
if ($label && !$this->encapsedContainsEndLabel($node->parts, $label)) {
$nl = $this->phpVersion->supportsFlexibleHeredoc() ? $this->nl : $this->newline;
if (count($node->parts) === 1
&& $node->parts[0] instanceof Node\InterpolatedStringPart
&& $node->parts[0]->value === ''
) {
return "<<<$label{$this->newline}$label{$this->docStringEndToken}";
return "<<<$label$nl$label{$this->docStringEndToken}";
}

return "<<<$label{$this->newline}" . $this->pEncapsList($node->parts, null)
. "{$this->newline}$label{$this->docStringEndToken}";
return "<<<$label$nl" . $this->pEncapsList($node->parts, null)
. "$nl$label{$this->docStringEndToken}";
}
}
return '"' . $this->pEncapsList($node->parts, '"') . '"';
Expand Down Expand Up @@ -1055,6 +1062,9 @@ protected function escapeString(string $string, ?string $quote): string {
// But do escape isolated \r. Combined with the terminating newline, it might get
// interpreted as \r\n and dropped from the string contents.
$escaped = preg_replace('/\r(?!\n)/', '\\r', $escaped);
if ($this->phpVersion->supportsFlexibleHeredoc()) {
$escaped = $this->indentString($escaped);
}
} else {
$escaped = addcslashes($string, "\n\r\t\f\v$" . $quote . "\\");
}
Expand All @@ -1079,7 +1089,6 @@ protected function escapeString(string $string, ?string $quote): string {
return preg_replace_callback($regex, function ($matches): string {
assert(strlen($matches[0]) === 1);
$hex = dechex(ord($matches[0]));
;
return '\\x' . str_pad($hex, 2, '0', \STR_PAD_LEFT);
}, $escaped);
}
Expand Down
50 changes: 42 additions & 8 deletions test/code/prettyPrinter/expr/docStrings.test
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,27 @@ STR
);

function test() {
<<<'STR'
STR;
<<<'STR'
Foo
Bar
Baz
STR;
<<<STR
STR;
<<<STR
Foo
Bar
STR;
<<<STR
Bar
STR;
Baz
STR;
<<<STR
$bar
$baz
STR;
}
-----
<<<'STR'
Expand Down Expand Up @@ -75,28 +90,47 @@ B
STR);
function test()
{
<<<'STR'
STR;
<<<'STR'
Foo
Bar
Baz
STR;
<<<STR
Foo
STR;
STR;
<<<STR
Foo
Bar
STR;
STR;
<<<STR
Bar
Baz
STR;
<<<STR
{$bar}
{$baz}
STR;
}
-----
<?php
foo(<<<STR
if (1) {
foo(<<<STR
abc
STR
, <<<STR
, <<<STR
abc
STR
);
);
}
-----
!!version=7.2
foo(<<<STR
if (1) {
foo(<<<STR
abc
STR
, <<<STR
abc
STR
);
}

0 comments on commit 5da5231

Please sign in to comment.