Analysing an Error
This page is about the data behind the diagnostics. For printing them, see Error Reporting.
ErrorPrinter turns an exception into a picture. Analyzer is the half that
works out what to draw: it takes any Throwable and gives back a
FailureResult - a plain representation of the error, telling what it says
about itself, where it happened, in which source and how much of it is at
fault. Nothing of the original exception is kept, so the result travels
wherever the exception itself cannot.
Use it when you want the facts rather than the output - a language server reporting diagnostics over LSP, a linter collecting errors into JSON, or your own renderer.
1use Phplrt\Exception\Analyzer; 2 3$result = new Analyzer()->analyze($e); 4 5$result->class; // the class of the exception, or an empty string 6$result->message; // the message of the exception, or an empty string 7$result->source; // Phplrt\Contracts\Source\ReadableInterface 8$result->position; // Phplrt\Contracts\Position\PositionInterface 9$result->level; // Phplrt\Exception\Analysis\FailureLevel 10$result->interval; // Phplrt\Exception\Analysis\FailureInterval|null 11$result->previous; // the same information about $e->getPrevious(), or null
Reading the source may fail, so analyze() declares
Phplrt\Contracts\Source\Exception\SourceExceptionInterface.
Where It Comes From
An exception implementing the lexer or parser runtime contract knows the source it was reading and the token it failed on, and that is where the analysis comes from:
RuntimeExceptionInterface |
any other Throwable |
|
|---|---|---|
class |
$e::class |
$e::class |
message |
$e->getMessage() |
$e->getMessage() |
source |
$e->source |
a FileSource over $e->getFile() |
position |
the position of the token offset | line $e->getLine(), column 1 |
level |
the severity of an ErrorException, or the default one |
the same |
interval |
the token offset and its size | null |
A parser error may be as large as the whole grammar rule the analysis failed
on rather than as large as one token, and it says so through
RuntimeExceptionInterface::$length. An exception implementing both contracts
is read as a parser one, because its own size is the more precise of the two.
An exception thrown outside any file - one restored from a serialized state, for example - belongs to no source at all and is analysed over an empty one.
The Fragment
FailureInterval is the fragment of the source the error covers, counted in
bytes from the beginning of it:
1$result->interval->offset; // the byte the fragment starts at 2$result->interval->length; // the size of the fragment, in bytes 3$result->interval->endsAt; // $offset + $length
It is null when the error tells nothing about its own size, which is the
case for every exception outside the contracts. Such an error points at a
position rather than at a fragment, and SnippetReader underlines a single
character for it.
The Chain
Every error that led to the one being analysed is described the same way, from the outermost exception to the innermost:
1use Phplrt\Contracts\Source\FileInterface; 2 3for ($current = $result; $current !== null; $current = $current->previous) { 4 \printf( 5 "%s at %s:%d:%d\n", 6 $current->class, 7 $current->source instanceof FileInterface ? $current->source->pathname : '-', 8 $current->position->line, 9 $current->position->column, 10 ); 11}
The chain is walked without recursion, so its length is not a limit.
Overrides
FailureResult is immutable, and with() gives a copy with one or
more values replaced:
1use Phplrt\Exception\Analysis\FailureInterval; 2use Phplrt\Source\VirtualSource; 3 4$result = $result->with( 5 source: VirtualSource::createFromString('config.txt', $code), 6 interval: new FailureInterval(offset: 26, length: 4), 7);
This is what PrintableError::withSource() and withInterval() do underneath
when an ordinary exception has to be pointed at a source of its own.
Positions
Positions are calculated by Phplrt\Position\PositionFactory, which reads the
source in chunks and counts the line delimiters. Pass your own when the
default chunk size does not suit the sources you deal with:
1use Phplrt\Exception\Analyzer; 2use Phplrt\Position\PositionFactory; 3 4$analyzer = new Analyzer(new PositionFactory(chunkSize: 65536));
A position is the line and the column of the beginning of the fragment, both counted from one. See Position for the details.