Querying

Diagnostics

When Forte encounters malformed syntax during parsing (unclosed echoes, unterminated comments, or invalid constructs), it records diagnostics rather than throwing exceptions. You can inspect these diagnostics to detect and report template errors.

#Accessing Diagnostics

The diagnostics method on a Document returns a DiagnosticBag containing all errors and warnings from both the lexer and parser stages. The hasErrors method provides a quick boolean check:

<?php
use Forte\Facades\Forte;
$doc = Forte::parse('{{ $unclosed');
$doc->hasErrors(); // true
$doc->diagnostics()->count(); // 1

A well-formed template produces an empty bag:

<?php
use Forte\Facades\Forte;
$doc = Forte::parse('<div>{{ $name }}</div>');
$doc->hasErrors(); // false
$doc->diagnostics()->isEmpty(); // true

#The Diagnostic Object

Each diagnostic is a readonly value object with a severity level, message, source offset range, and origin. You can inspect these properties directly:

<?php
use Forte\Diagnostics\DiagnosticSeverity;
use Forte\Facades\Forte;
$doc = Forte::parse('{{ $unclosed');
$diag = $doc->diagnostics()->first();
$diag->severity; // DiagnosticSeverity::Error
$diag->source; // "lexer"
$diag->code; // "UnexpectedEof"
$diag->start; // 12
$diag->end; // 13
$diag->length(); // 1

Convenience methods check the severity level without comparing enum values:

<?php
$diag->isError(); // true
$diag->isWarning(); // false
$diag->isInfo(); // false
$diag->isHint(); // false

Diagnostics implement Stringable, so you can cast them directly for logging or display:

<?php
use Forte\Facades\Forte;
$doc = Forte::parse('{{-- unclosed comment');
$diag = $doc->diagnostics()->first();
$output = (string) $diag; // "error(21-22): [UnclosedComment] Unclosed comment [lexer]"

#Filtering by Severity

DiagnosticBag provides dedicated methods to filter by severity level. Each returns a Laravel Collection:

<?php
use Forte\Facades\Forte;
$doc = Forte::parse('{{ $unclosed');
$bag = $doc->diagnostics();
$bag->errors()->count(); // 1
$bag->warnings()->count(); // 0
$bag->info()->count(); // 0
$bag->hints()->count(); // 0

Use atLeast to get all diagnostics at or above a given severity. Since errors have the highest priority (value 1), filtering at the warning level includes both warnings and errors:

<?php
use Forte\Diagnostics\DiagnosticSeverity;
use Forte\Facades\Forte;
$doc = Forte::parse('{{ $unclosed');
$bag = $doc->diagnostics();
$bag->atLeast(DiagnosticSeverity::Warning)->count(); // 1
$bag->atLeast(DiagnosticSeverity::Error)->count(); // 1

#Querying Diagnostics

You can narrow diagnostics by their origin using fromSource. Forte tags diagnostics as either "lexer" or "parser" depending on which stage produced them:

<?php
use Forte\Facades\Forte;
$doc = Forte::parse('{{ $unclosed');
$bag = $doc->diagnostics();
$bag->fromSource('lexer')->count(); // 1
$bag->fromSource('parser')->count(); // 0

To find diagnostics within a specific byte range of the source, use inRange:

<?php
$bag->inRange(0, 20); // Collection<Diagnostic>

The bag also supports first, last, and arbitrary filtering with filter:

<?php
$bag->first(); // ?Diagnostic
$bag->last(); // ?Diagnostic
$bag->filter(fn ($d) => $d->code === 'UnexpectedEof'); // Collection<Diagnostic>

#Sorting and Formatting

sortByPosition orders diagnostics by their start offset, and sortBySeverity orders them by severity (errors first):

<?php
$bag->sortByPosition();
$bag->sortBySeverity();

The format method produces a human-readable summary. When you pass the original source string, it includes line and column numbers:

<?php
use Forte\Facades\Forte;
$template = '{{ $unclosed';
$doc = Forte::parse($template);
$bag = $doc->diagnostics();
$bag->format($template); // "1:13 error(12-13): [UnexpectedEof] ..."

An empty bag formats to a simple message:

<?php
use Forte\Facades\Forte;
$doc = Forte::parse('<div>Hello</div>');
$doc->diagnostics()->format(); // "No diagnostics."

#DiagnosticSeverity

The DiagnosticSeverity enum defines four levels, ordered from most to least severe:

Level Value Label
Error 1 "error"
Warning 2 "warning"
Info 3 "info"
Hint 4 "hint"

The label method returns a lowercase string representation, and isAtLeast compares severity levels (lower value means higher severity):

<?php
use Forte\Diagnostics\DiagnosticSeverity;
DiagnosticSeverity::Error->label(); // "error"
DiagnosticSeverity::Warning->label(); // "warning"
DiagnosticSeverity::Error->isAtLeast(DiagnosticSeverity::Warning); // true
DiagnosticSeverity::Warning->isAtLeast(DiagnosticSeverity::Error); // false
DiagnosticSeverity::Warning->isAtLeast(DiagnosticSeverity::Warning); // true

#See also

  • Traversal: Navigate and query the document tree
  • XPath Queries: Query documents with XPath expressions
  • Documents: The Document class that provides diagnostics() and hasErrors()