phplrt 4.0

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.