1: | <?php declare(strict_types=1); |
2: | |
3: | namespace PhpParser; |
4: | |
5: | abstract 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: |