function PrettyPrinterAbstract::pArray
Perform a format-preserving pretty print of an array.
Parameters
Node[] $nodes New nodes:
Node[] $origNodes Original nodes:
int $pos Current token position (updated by reference):
int $indentAdjustment Adjustment for indentation:
string $parentNodeClass Class of the containing node.:
string $subNodeName Name of array subnode.:
null|int $fixup Fixup information for array item nodes:
Return value
null|string Result of pretty print or null if cannot preserve formatting
2 calls to PrettyPrinterAbstract::pArray()
- PrettyPrinterAbstract::p in vendor/
nikic/ php-parser/ lib/ PhpParser/ PrettyPrinterAbstract.php - Pretty prints a node.
- PrettyPrinterAbstract::printFormatPreserving in vendor/
nikic/ php-parser/ lib/ PhpParser/ PrettyPrinterAbstract.php - Perform a format-preserving pretty print of an AST.
File
-
vendor/
nikic/ php-parser/ lib/ PhpParser/ PrettyPrinterAbstract.php, line 779
Class
Namespace
PhpParserCode
protected function pArray(array $nodes, array $origNodes, int &$pos, int $indentAdjustment, string $parentNodeClass, string $subNodeName, ?int $fixup) : ?string {
$diff = $this->nodeListDiffer
->diffWithReplacements($origNodes, $nodes);
$mapKey = $parentNodeClass . '->' . $subNodeName;
$insertStr = $this->listInsertionMap[$mapKey] ?? null;
$isStmtList = $subNodeName === 'stmts';
$beforeFirstKeepOrReplace = true;
$skipRemovedNode = false;
$delayedAdd = [];
$lastElemIndentLevel = $this->indentLevel;
$insertNewline = false;
if ($insertStr === "\n") {
$insertStr = '';
$insertNewline = true;
}
if ($isStmtList && \count($origNodes) === 1 && \count($nodes) !== 1) {
$startPos = $origNodes[0]->getStartTokenPos();
$endPos = $origNodes[0]->getEndTokenPos();
\assert($startPos >= 0 && $endPos >= 0);
if (!$this->origTokens
->haveBraces($startPos, $endPos)) {
// This was a single statement without braces, but either additional statements
// have been added, or the single statement has been removed. This requires the
// addition of braces. For now fall back.
// TODO: Try to preserve formatting
return null;
}
}
$result = '';
foreach ($diff as $i => $diffElem) {
$diffType = $diffElem->type;
/** @var Node|string|null $arrItem */
$arrItem = $diffElem->new;
/** @var Node|string|null $origArrItem */
$origArrItem = $diffElem->old;
if ($diffType === DiffElem::TYPE_KEEP || $diffType === DiffElem::TYPE_REPLACE) {
$beforeFirstKeepOrReplace = false;
if ($origArrItem === null || $arrItem === null) {
// We can only handle the case where both are null
if ($origArrItem === $arrItem) {
continue;
}
return null;
}
if (!$arrItem instanceof Node || !$origArrItem instanceof Node) {
// We can only deal with nodes. This can occur for Names, which use string arrays.
return null;
}
$itemStartPos = $origArrItem->getStartTokenPos();
$itemEndPos = $origArrItem->getEndTokenPos();
\assert($itemStartPos >= 0 && $itemEndPos >= 0 && $itemStartPos >= $pos);
$origIndentLevel = $this->indentLevel;
$lastElemIndentLevel = max($this->origTokens
->getIndentationBefore($itemStartPos) + $indentAdjustment, 0);
$this->setIndentLevel($lastElemIndentLevel);
$comments = $arrItem->getComments();
$origComments = $origArrItem->getComments();
$commentStartPos = $origComments ? $origComments[0]->getStartTokenPos() : $itemStartPos;
\assert($commentStartPos >= 0);
if ($commentStartPos < $pos) {
// Comments may be assigned to multiple nodes if they start at the same position.
// Make sure we don't try to print them multiple times.
$commentStartPos = $itemStartPos;
}
if ($skipRemovedNode) {
if ($isStmtList && $this->origTokens
->haveTagInRange($pos, $itemStartPos)) {
// We'd remove an opening/closing PHP tag.
// TODO: Preserve formatting.
$this->setIndentLevel($origIndentLevel);
return null;
}
}
else {
$result .= $this->origTokens
->getTokenCode($pos, $commentStartPos, $indentAdjustment);
}
if (!empty($delayedAdd)) {
/** @var Node $delayedAddNode */
foreach ($delayedAdd as $delayedAddNode) {
if ($insertNewline) {
$delayedAddComments = $delayedAddNode->getComments();
if ($delayedAddComments) {
$result .= $this->pComments($delayedAddComments) . $this->nl;
}
}
$this->safeAppend($result, $this->p($delayedAddNode, self::MAX_PRECEDENCE, self::MAX_PRECEDENCE, true));
if ($insertNewline) {
$result .= $insertStr . $this->nl;
}
else {
$result .= $insertStr;
}
}
$delayedAdd = [];
}
if ($comments !== $origComments) {
if ($comments) {
$result .= $this->pComments($comments) . $this->nl;
}
}
else {
$result .= $this->origTokens
->getTokenCode($commentStartPos, $itemStartPos, $indentAdjustment);
}
// If we had to remove anything, we have done so now.
$skipRemovedNode = false;
}
elseif ($diffType === DiffElem::TYPE_ADD) {
if (null === $insertStr) {
// We don't have insertion information for this list type
return null;
}
if (!$arrItem instanceof Node) {
// We only support list insertion of nodes.
return null;
}
// We go multiline if the original code was multiline,
// or if it's an array item with a comment above it.
// Match always uses multiline formatting.
if ($insertStr === ', ' && ($this->isMultiline($origNodes) || $arrItem->getComments() || $parentNodeClass === Expr\Match_::class)) {
$insertStr = ',';
$insertNewline = true;
}
if ($beforeFirstKeepOrReplace) {
// Will be inserted at the next "replace" or "keep" element
$delayedAdd[] = $arrItem;
continue;
}
$itemStartPos = $pos;
$itemEndPos = $pos - 1;
$origIndentLevel = $this->indentLevel;
$this->setIndentLevel($lastElemIndentLevel);
if ($insertNewline) {
$result .= $insertStr . $this->nl;
$comments = $arrItem->getComments();
if ($comments) {
$result .= $this->pComments($comments) . $this->nl;
}
}
else {
$result .= $insertStr;
}
}
elseif ($diffType === DiffElem::TYPE_REMOVE) {
if (!$origArrItem instanceof Node) {
// We only support removal for nodes
return null;
}
$itemStartPos = $origArrItem->getStartTokenPos();
$itemEndPos = $origArrItem->getEndTokenPos();
\assert($itemStartPos >= 0 && $itemEndPos >= 0);
// Consider comments part of the node.
$origComments = $origArrItem->getComments();
if ($origComments) {
$itemStartPos = $origComments[0]->getStartTokenPos();
}
if ($i === 0) {
// If we're removing from the start, keep the tokens before the node and drop those after it,
// instead of the other way around.
$result .= $this->origTokens
->getTokenCode($pos, $itemStartPos, $indentAdjustment);
$skipRemovedNode = true;
}
else {
if ($isStmtList && $this->origTokens
->haveTagInRange($pos, $itemStartPos)) {
// We'd remove an opening/closing PHP tag.
// TODO: Preserve formatting.
return null;
}
}
$pos = $itemEndPos + 1;
continue;
}
else {
throw new \Exception("Shouldn't happen");
}
if (null !== $fixup && $arrItem->getAttribute('origNode') !== $origArrItem) {
$res = $this->pFixup($fixup, $arrItem, null, $itemStartPos, $itemEndPos);
}
else {
$res = $this->p($arrItem, self::MAX_PRECEDENCE, self::MAX_PRECEDENCE, true);
}
$this->safeAppend($result, $res);
$this->setIndentLevel($origIndentLevel);
$pos = $itemEndPos + 1;
}
if ($skipRemovedNode) {
// TODO: Support removing single node.
return null;
}
if (!empty($delayedAdd)) {
if (!isset($this->emptyListInsertionMap[$mapKey])) {
return null;
}
list($findToken, $extraLeft, $extraRight) = $this->emptyListInsertionMap[$mapKey];
if (null !== $findToken) {
$insertPos = $this->origTokens
->findRight($pos, $findToken) + 1;
$result .= $this->origTokens
->getTokenCode($pos, $insertPos, $indentAdjustment);
$pos = $insertPos;
}
$first = true;
$result .= $extraLeft;
foreach ($delayedAdd as $delayedAddNode) {
if (!$first) {
$result .= $insertStr;
if ($insertNewline) {
$result .= $this->nl;
}
}
$result .= $this->p($delayedAddNode, self::MAX_PRECEDENCE, self::MAX_PRECEDENCE, true);
$first = false;
}
$result .= $extraRight === "\n" ? $this->nl : $extraRight;
}
return $result;
}