1 <?php declare(strict_types=1);
5 class Error extends \RuntimeException
11 * Creates an Exception signifying a parse error.
13 * @param string $message Error message
14 * @param array|int $attributes Attributes of node/token where error occurred
15 * (or start line of error -- deprecated)
17 public function __construct(string $message, $attributes = []) {
18 $this->rawMessage = (string) $message;
19 if (is_array($attributes)) {
20 $this->attributes = $attributes;
22 $this->attributes = ['startLine' => $attributes];
24 $this->updateMessage();
28 * Gets the error message
30 * @return string Error message
32 public function getRawMessage() : string {
33 return $this->rawMessage;
37 * Gets the line the error starts in.
39 * @return int Error start line
41 public function getStartLine() : int {
42 return $this->attributes['startLine'] ?? -1;
46 * Gets the line the error ends in.
48 * @return int Error end line
50 public function getEndLine() : int {
51 return $this->attributes['endLine'] ?? -1;
55 * Gets the attributes of the node/token the error occurred at.
59 public function getAttributes() : array {
60 return $this->attributes;
64 * Sets the attributes of the node/token the error occurred at.
66 * @param array $attributes
68 public function setAttributes(array $attributes) {
69 $this->attributes = $attributes;
70 $this->updateMessage();
74 * Sets the line of the PHP file the error occurred in.
76 * @param string $message Error message
78 public function setRawMessage(string $message) {
79 $this->rawMessage = (string) $message;
80 $this->updateMessage();
84 * Sets the line the error starts in.
86 * @param int $line Error start line
88 public function setStartLine(int $line) {
89 $this->attributes['startLine'] = (int) $line;
90 $this->updateMessage();
94 * Returns whether the error has start and end column information.
96 * For column information enable the startFilePos and endFilePos in the lexer options.
100 public function hasColumnInfo() : bool {
101 return isset($this->attributes['startFilePos'], $this->attributes['endFilePos']);
105 * Gets the start column (1-based) into the line where the error started.
107 * @param string $code Source code of the file
110 public function getStartColumn(string $code) : int {
111 if (!$this->hasColumnInfo()) {
112 throw new \RuntimeException('Error does not have column information');
115 return $this->toColumn($code, $this->attributes['startFilePos']);
119 * Gets the end column (1-based) into the line where the error ended.
121 * @param string $code Source code of the file
124 public function getEndColumn(string $code) : int {
125 if (!$this->hasColumnInfo()) {
126 throw new \RuntimeException('Error does not have column information');
129 return $this->toColumn($code, $this->attributes['endFilePos']);
133 * Formats message including line and column information.
135 * @param string $code Source code associated with the error, for calculation of the columns
137 * @return string Formatted message
139 public function getMessageWithColumnInfo(string $code) : string {
141 '%s from %d:%d to %d:%d', $this->getRawMessage(),
142 $this->getStartLine(), $this->getStartColumn($code),
143 $this->getEndLine(), $this->getEndColumn($code)
148 * Converts a file offset into a column.
150 * @param string $code Source code that $pos indexes into
151 * @param int $pos 0-based position in $code
153 * @return int 1-based column (relative to start of line)
155 private function toColumn(string $code, int $pos) : int {
156 if ($pos > strlen($code)) {
157 throw new \RuntimeException('Invalid position information');
160 $lineStartPos = strrpos($code, "\n", $pos - strlen($code));
161 if (false === $lineStartPos) {
165 return $pos - $lineStartPos;
169 * Updates the exception message after a change to rawMessage or rawLine.
171 protected function updateMessage() {
172 $this->message = $this->rawMessage;
174 if (-1 === $this->getStartLine()) {
175 $this->message .= ' on unknown line';
177 $this->message .= ' on line ' . $this->getStartLine();