1<?php declare(strict_types=1); 2 3namespace PhpParser; 4 5abstract class NodeAbstract implements Node, \JsonSerializable { 6 /** @var array<string, mixed> Attributes */ 7 protected array $attributes; 8 9 /** 10 * Creates a Node. 11 * 12 * @param array<string, mixed> $attributes Array of attributes 13 */ 14 public function __construct(array $attributes = []) { 15 $this->attributes = $attributes; 16 } 17 18 /** 19 * Gets line the node started in (alias of getStartLine). 20 * 21 * @return int Start line (or -1 if not available) 22 * @phpstan-return -1|positive-int 23 */ 24 public function getLine(): int { 25 return $this->attributes['startLine'] ?? -1; 26 } 27 28 /** 29 * Gets line the node started in. 30 * 31 * Requires the 'startLine' attribute to be enabled in the lexer (enabled by default). 32 * 33 * @return int Start line (or -1 if not available) 34 * @phpstan-return -1|positive-int 35 */ 36 public function getStartLine(): int { 37 return $this->attributes['startLine'] ?? -1; 38 } 39 40 /** 41 * Gets the line the node ended in. 42 * 43 * Requires the 'endLine' attribute to be enabled in the lexer (enabled by default). 44 * 45 * @return int End line (or -1 if not available) 46 * @phpstan-return -1|positive-int 47 */ 48 public function getEndLine(): int { 49 return $this->attributes['endLine'] ?? -1; 50 } 51 52 /** 53 * Gets the token offset of the first token that is part of this node. 54 * 55 * The offset is an index into the array returned by Lexer::getTokens(). 56 * 57 * Requires the 'startTokenPos' attribute to be enabled in the lexer (DISABLED by default). 58 * 59 * @return int Token start position (or -1 if not available) 60 */ 61 public function getStartTokenPos(): int { 62 return $this->attributes['startTokenPos'] ?? -1; 63 } 64 65 /** 66 * Gets the token offset of the last token that is part of this node. 67 * 68 * The offset is an index into the array returned by Lexer::getTokens(). 69 * 70 * Requires the 'endTokenPos' attribute to be enabled in the lexer (DISABLED by default). 71 * 72 * @return int Token end position (or -1 if not available) 73 */ 74 public function getEndTokenPos(): int { 75 return $this->attributes['endTokenPos'] ?? -1; 76 } 77 78 /** 79 * Gets the file offset of the first character that is part of this node. 80 * 81 * Requires the 'startFilePos' attribute to be enabled in the lexer (DISABLED by default). 82 * 83 * @return int File start position (or -1 if not available) 84 */ 85 public function getStartFilePos(): int { 86 return $this->attributes['startFilePos'] ?? -1; 87 } 88 89 /** 90 * Gets the file offset of the last character that is part of this node. 91 * 92 * Requires the 'endFilePos' attribute to be enabled in the lexer (DISABLED by default). 93 * 94 * @return int File end position (or -1 if not available) 95 */ 96 public function getEndFilePos(): int { 97 return $this->attributes['endFilePos'] ?? -1; 98 } 99 100 /** 101 * Gets all comments directly preceding this node. 102 * 103 * The comments are also available through the "comments" attribute. 104 * 105 * @return Comment[] 106 */ 107 public function getComments(): array { 108 return $this->attributes['comments'] ?? []; 109 } 110 111 /** 112 * Gets the doc comment of the node. 113 * 114 * @return null|Comment\Doc Doc comment object or null 115 */ 116 public function getDocComment(): ?Comment\Doc { 117 $comments = $this->getComments(); 118 for ($i = count($comments) - 1; $i >= 0; $i--) { 119 $comment = $comments[$i]; 120 if ($comment instanceof Comment\Doc) { 121 return $comment; 122 } 123 } 124 125 return null; 126 } 127 128 /** 129 * Sets the doc comment of the node. 130 * 131 * This will either replace an existing doc comment or add it to the comments array. 132 * 133 * @param Comment\Doc $docComment Doc comment to set 134 */ 135 public function setDocComment(Comment\Doc $docComment): void { 136 $comments = $this->getComments(); 137 for ($i = count($comments) - 1; $i >= 0; $i--) { 138 if ($comments[$i] instanceof Comment\Doc) { 139 // Replace existing doc comment. 140 $comments[$i] = $docComment; 141 $this->setAttribute('comments', $comments); 142 return; 143 } 144 } 145 146 // Append new doc comment. 147 $comments[] = $docComment; 148 $this->setAttribute('comments', $comments); 149 } 150 151 public function setAttribute(string $key, $value): void { 152 $this->attributes[$key] = $value; 153 } 154 155 public function hasAttribute(string $key): bool { 156 return array_key_exists($key, $this->attributes); 157 } 158 159 public function getAttribute(string $key, $default = null) { 160 if (array_key_exists($key, $this->attributes)) { 161 return $this->attributes[$key]; 162 } 163 164 return $default; 165 } 166 167 public function getAttributes(): array { 168 return $this->attributes; 169 } 170 171 public function setAttributes(array $attributes): void { 172 $this->attributes = $attributes; 173 } 174 175 /** 176 * @return array<string, mixed> 177 */ 178 public function jsonSerialize(): array { 179 return ['nodeType' => $this->getType()] + get_object_vars($this); 180 } 181} 182