D7net
Home
Console
Upload
information
Create File
Create Folder
About
Tools
:
/
usr
/
local
/
psa
/
admin
/
plib
/
modules
/
grafana
/
vendor
/
nikic
/
php-parser
/
lib
/
PhpParser
/
Filename :
NodeDumper.php
back
Copy
<?php declare (strict_types=1); namespace PleskGrafana\PhpParser; use PleskGrafana\PhpParser\Node\Expr\Array_; use PleskGrafana\PhpParser\Node\Expr\Include_; use PleskGrafana\PhpParser\Node\Expr\List_; use PleskGrafana\PhpParser\Node\Scalar\Int_; use PleskGrafana\PhpParser\Node\Scalar\InterpolatedString; use PleskGrafana\PhpParser\Node\Scalar\String_; use PleskGrafana\PhpParser\Node\Stmt\GroupUse; use PleskGrafana\PhpParser\Node\Stmt\Use_; use PleskGrafana\PhpParser\Node\UseItem; class NodeDumper { private bool $dumpComments; private bool $dumpPositions; private bool $dumpOtherAttributes; private ?string $code; private string $res; private string $nl; private const IGNORE_ATTRIBUTES = ['comments' => \true, 'startLine' => \true, 'endLine' => \true, 'startFilePos' => \true, 'endFilePos' => \true, 'startTokenPos' => \true, 'endTokenPos' => \true]; /** * Constructs a NodeDumper. * * Supported options: * * bool dumpComments: Whether comments should be dumped. * * bool dumpPositions: Whether line/offset information should be dumped. To dump offset * information, the code needs to be passed to dump(). * * bool dumpOtherAttributes: Whether non-comment, non-position attributes should be dumped. * * @param array $options Options (see description) */ public function __construct(array $options = []) { $this->dumpComments = !empty($options['dumpComments']); $this->dumpPositions = !empty($options['dumpPositions']); $this->dumpOtherAttributes = !empty($options['dumpOtherAttributes']); } /** * Dumps a node or array. * * @param array|Node $node Node or array to dump * @param string|null $code Code corresponding to dumped AST. This only needs to be passed if * the dumpPositions option is enabled and the dumping of node offsets * is desired. * * @return string Dumped value */ public function dump($node, ?string $code = null) : string { $this->code = $code; $this->res = ''; $this->nl = "\n"; $this->dumpRecursive($node, \false); return $this->res; } /** @param mixed $node */ protected function dumpRecursive($node, bool $indent = \true) : void { if ($indent) { $this->nl .= " "; } if ($node instanceof Node) { $this->res .= $node->getType(); if ($this->dumpPositions && null !== ($p = $this->dumpPosition($node))) { $this->res .= $p; } $this->res .= '('; foreach ($node->getSubNodeNames() as $key) { $this->res .= "{$this->nl} " . $key . ': '; $value = $node->{$key}; if (\is_int($value)) { if ('flags' === $key || 'newModifier' === $key) { $this->res .= $this->dumpFlags($value); continue; } if ('type' === $key && $node instanceof Include_) { $this->res .= $this->dumpIncludeType($value); continue; } if ('type' === $key && ($node instanceof Use_ || $node instanceof UseItem || $node instanceof GroupUse)) { $this->res .= $this->dumpUseType($value); continue; } } $this->dumpRecursive($value); } if ($this->dumpComments && ($comments = $node->getComments())) { $this->res .= "{$this->nl} comments: "; $this->dumpRecursive($comments); } if ($this->dumpOtherAttributes) { foreach ($node->getAttributes() as $key => $value) { if (isset(self::IGNORE_ATTRIBUTES[$key])) { continue; } $this->res .= "{$this->nl} {$key}: "; if (\is_int($value)) { if ('kind' === $key) { if ($node instanceof Int_) { $this->res .= $this->dumpIntKind($value); continue; } if ($node instanceof String_ || $node instanceof InterpolatedString) { $this->res .= $this->dumpStringKind($value); continue; } if ($node instanceof Array_) { $this->res .= $this->dumpArrayKind($value); continue; } if ($node instanceof List_) { $this->res .= $this->dumpListKind($value); continue; } } } $this->dumpRecursive($value); } } $this->res .= "{$this->nl})"; } elseif (\is_array($node)) { $this->res .= 'array('; foreach ($node as $key => $value) { $this->res .= "{$this->nl} " . $key . ': '; $this->dumpRecursive($value); } $this->res .= "{$this->nl})"; } elseif ($node instanceof Comment) { $this->res .= \str_replace("\n", $this->nl, $node->getReformattedText()); } elseif (\is_string($node)) { $this->res .= \str_replace("\n", $this->nl, (string) $node); } elseif (\is_int($node) || \is_float($node)) { $this->res .= $node; } elseif (null === $node) { $this->res .= 'null'; } elseif (\false === $node) { $this->res .= 'false'; } elseif (\true === $node) { $this->res .= 'true'; } else { throw new \InvalidArgumentException('Can only dump nodes and arrays.'); } if ($indent) { $this->nl = \substr($this->nl, 0, -4); } } protected function dumpFlags(int $flags) : string { $strs = []; if ($flags & Modifiers::PUBLIC) { $strs[] = 'PUBLIC'; } if ($flags & Modifiers::PROTECTED) { $strs[] = 'PROTECTED'; } if ($flags & Modifiers::PRIVATE) { $strs[] = 'PRIVATE'; } if ($flags & Modifiers::ABSTRACT) { $strs[] = 'ABSTRACT'; } if ($flags & Modifiers::STATIC) { $strs[] = 'STATIC'; } if ($flags & Modifiers::FINAL) { $strs[] = 'FINAL'; } if ($flags & Modifiers::READONLY) { $strs[] = 'READONLY'; } if ($flags & Modifiers::PUBLIC_SET) { $strs[] = 'PUBLIC_SET'; } if ($flags & Modifiers::PROTECTED_SET) { $strs[] = 'PROTECTED_SET'; } if ($flags & Modifiers::PRIVATE_SET) { $strs[] = 'PRIVATE_SET'; } if ($strs) { return \implode(' | ', $strs) . ' (' . $flags . ')'; } else { return (string) $flags; } } /** @param array<int, string> $map */ private function dumpEnum(int $value, array $map) : string { if (!isset($map[$value])) { return (string) $value; } return $map[$value] . ' (' . $value . ')'; } private function dumpIncludeType(int $type) : string { return $this->dumpEnum($type, [Include_::TYPE_INCLUDE => 'TYPE_INCLUDE', Include_::TYPE_INCLUDE_ONCE => 'TYPE_INCLUDE_ONCE', Include_::TYPE_REQUIRE => 'TYPE_REQUIRE', Include_::TYPE_REQUIRE_ONCE => 'TYPE_REQUIRE_ONCE']); } private function dumpUseType(int $type) : string { return $this->dumpEnum($type, [Use_::TYPE_UNKNOWN => 'TYPE_UNKNOWN', Use_::TYPE_NORMAL => 'TYPE_NORMAL', Use_::TYPE_FUNCTION => 'TYPE_FUNCTION', Use_::TYPE_CONSTANT => 'TYPE_CONSTANT']); } private function dumpIntKind(int $kind) : string { return $this->dumpEnum($kind, [Int_::KIND_BIN => 'KIND_BIN', Int_::KIND_OCT => 'KIND_OCT', Int_::KIND_DEC => 'KIND_DEC', Int_::KIND_HEX => 'KIND_HEX']); } private function dumpStringKind(int $kind) : string { return $this->dumpEnum($kind, [String_::KIND_SINGLE_QUOTED => 'KIND_SINGLE_QUOTED', String_::KIND_DOUBLE_QUOTED => 'KIND_DOUBLE_QUOTED', String_::KIND_HEREDOC => 'KIND_HEREDOC', String_::KIND_NOWDOC => 'KIND_NOWDOC']); } private function dumpArrayKind(int $kind) : string { return $this->dumpEnum($kind, [Array_::KIND_LONG => 'KIND_LONG', Array_::KIND_SHORT => 'KIND_SHORT']); } private function dumpListKind(int $kind) : string { return $this->dumpEnum($kind, [List_::KIND_LIST => 'KIND_LIST', List_::KIND_ARRAY => 'KIND_ARRAY']); } /** * Dump node position, if possible. * * @param Node $node Node for which to dump position * * @return string|null Dump of position, or null if position information not available */ protected function dumpPosition(Node $node) : ?string { if (!$node->hasAttribute('startLine') || !$node->hasAttribute('endLine')) { return null; } $start = $node->getStartLine(); $end = $node->getEndLine(); if ($node->hasAttribute('startFilePos') && $node->hasAttribute('endFilePos') && null !== $this->code) { $start .= ':' . $this->toColumn($this->code, $node->getStartFilePos()); $end .= ':' . $this->toColumn($this->code, $node->getEndFilePos()); } return "[{$start} - {$end}]"; } // Copied from Error class private function toColumn(string $code, int $pos) : int { if ($pos > \strlen($code)) { throw new \RuntimeException('Invalid position information'); } $lineStartPos = \strrpos($code, "\n", $pos - \strlen($code)); if (\false === $lineStartPos) { $lineStartPos = -1; } return $pos - $lineStartPos; } }