Error.php 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. <?php declare(strict_types=1);
  2. namespace PhpParser;
  3. class Error extends \RuntimeException {
  4. protected string $rawMessage;
  5. /** @var array<string, mixed> */
  6. protected array $attributes;
  7. /**
  8. * Creates an Exception signifying a parse error.
  9. *
  10. * @param string $message Error message
  11. * @param array<string, mixed> $attributes Attributes of node/token where error occurred
  12. */
  13. public function __construct(string $message, array $attributes = []) {
  14. $this->rawMessage = $message;
  15. $this->attributes = $attributes;
  16. $this->updateMessage();
  17. }
  18. /**
  19. * Gets the error message
  20. *
  21. * @return string Error message
  22. */
  23. public function getRawMessage(): string {
  24. return $this->rawMessage;
  25. }
  26. /**
  27. * Gets the line the error starts in.
  28. *
  29. * @return int Error start line
  30. */
  31. public function getStartLine(): int {
  32. return $this->attributes['startLine'] ?? -1;
  33. }
  34. /**
  35. * Gets the line the error ends in.
  36. *
  37. * @return int Error end line
  38. */
  39. public function getEndLine(): int {
  40. return $this->attributes['endLine'] ?? -1;
  41. }
  42. /**
  43. * Gets the attributes of the node/token the error occurred at.
  44. *
  45. * @return array<string, mixed>
  46. */
  47. public function getAttributes(): array {
  48. return $this->attributes;
  49. }
  50. /**
  51. * Sets the attributes of the node/token the error occurred at.
  52. *
  53. * @param array<string, mixed> $attributes
  54. */
  55. public function setAttributes(array $attributes): void {
  56. $this->attributes = $attributes;
  57. $this->updateMessage();
  58. }
  59. /**
  60. * Sets the line of the PHP file the error occurred in.
  61. *
  62. * @param string $message Error message
  63. */
  64. public function setRawMessage(string $message): void {
  65. $this->rawMessage = $message;
  66. $this->updateMessage();
  67. }
  68. /**
  69. * Sets the line the error starts in.
  70. *
  71. * @param int $line Error start line
  72. */
  73. public function setStartLine(int $line): void {
  74. $this->attributes['startLine'] = $line;
  75. $this->updateMessage();
  76. }
  77. /**
  78. * Returns whether the error has start and end column information.
  79. *
  80. * For column information enable the startFilePos and endFilePos in the lexer options.
  81. */
  82. public function hasColumnInfo(): bool {
  83. return isset($this->attributes['startFilePos'], $this->attributes['endFilePos']);
  84. }
  85. /**
  86. * Gets the start column (1-based) into the line where the error started.
  87. *
  88. * @param string $code Source code of the file
  89. */
  90. public function getStartColumn(string $code): int {
  91. if (!$this->hasColumnInfo()) {
  92. throw new \RuntimeException('Error does not have column information');
  93. }
  94. return $this->toColumn($code, $this->attributes['startFilePos']);
  95. }
  96. /**
  97. * Gets the end column (1-based) into the line where the error ended.
  98. *
  99. * @param string $code Source code of the file
  100. */
  101. public function getEndColumn(string $code): int {
  102. if (!$this->hasColumnInfo()) {
  103. throw new \RuntimeException('Error does not have column information');
  104. }
  105. return $this->toColumn($code, $this->attributes['endFilePos']);
  106. }
  107. /**
  108. * Formats message including line and column information.
  109. *
  110. * @param string $code Source code associated with the error, for calculation of the columns
  111. *
  112. * @return string Formatted message
  113. */
  114. public function getMessageWithColumnInfo(string $code): string {
  115. return sprintf(
  116. '%s from %d:%d to %d:%d', $this->getRawMessage(),
  117. $this->getStartLine(), $this->getStartColumn($code),
  118. $this->getEndLine(), $this->getEndColumn($code)
  119. );
  120. }
  121. /**
  122. * Converts a file offset into a column.
  123. *
  124. * @param string $code Source code that $pos indexes into
  125. * @param int $pos 0-based position in $code
  126. *
  127. * @return int 1-based column (relative to start of line)
  128. */
  129. private function toColumn(string $code, int $pos): int {
  130. if ($pos > strlen($code)) {
  131. throw new \RuntimeException('Invalid position information');
  132. }
  133. $lineStartPos = strrpos($code, "\n", $pos - strlen($code));
  134. if (false === $lineStartPos) {
  135. $lineStartPos = -1;
  136. }
  137. return $pos - $lineStartPos;
  138. }
  139. /**
  140. * Updates the exception message after a change to rawMessage or rawLine.
  141. */
  142. protected function updateMessage(): void {
  143. $this->message = $this->rawMessage;
  144. if (-1 === $this->getStartLine()) {
  145. $this->message .= ' on unknown line';
  146. } else {
  147. $this->message .= ' on line ' . $this->getStartLine();
  148. }
  149. }
  150. }