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:

1<?php
2
3use Forte\Facades\Forte;
4
5$doc = Forte::parse('{{ $unclosed');
6
7$doc->hasErrors(); // true
8$doc->diagnostics()->count(); // 1

A well-formed template produces an empty bag:

1<?php
2
3use Forte\Facades\Forte;
4
5$doc = Forte::parse('<div>{{ $name }}</div>');
6
7$doc->hasErrors(); // false
8$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:

1<?php
2
3use Forte\Diagnostics\DiagnosticSeverity;
4use Forte\Facades\Forte;
5
6$doc = Forte::parse('{{ $unclosed');
7$diag = $doc->diagnostics()->first();
8
9$diag->severity; // DiagnosticSeverity::Error
10$diag->source; // "lexer"
11$diag->code; // "UnexpectedEof"
12$diag->start; // 12
13$diag->end; // 13
14$diag->length(); // 1

Convenience methods check the severity level without comparing enum values:

1<?php
2
3$diag->isError(); // true
4$diag->isWarning(); // false
5$diag->isInfo(); // false
6$diag->isHint(); // false

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

1<?php
2
3use Forte\Facades\Forte;
4
5$doc = Forte::parse('{{-- unclosed comment');
6$diag = $doc->diagnostics()->first();
7
8$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:

1<?php
2
3use Forte\Facades\Forte;
4
5$doc = Forte::parse('{{ $unclosed');
6$bag = $doc->diagnostics();
7
8$bag->errors()->count(); // 1
9$bag->warnings()->count(); // 0
10$bag->info()->count(); // 0
11$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:

1<?php
2
3use Forte\Diagnostics\DiagnosticSeverity;
4use Forte\Facades\Forte;
5
6$doc = Forte::parse('{{ $unclosed');
7$bag = $doc->diagnostics();
8
9$bag->atLeast(DiagnosticSeverity::Warning)->count(); // 1
10$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:

1<?php
2
3use Forte\Facades\Forte;
4
5$doc = Forte::parse('{{ $unclosed');
6$bag = $doc->diagnostics();
7
8$bag->fromSource('lexer')->count(); // 1
9$bag->fromSource('parser')->count(); // 0

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

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

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

1<?php
2
3$bag->first(); // ?Diagnostic
4$bag->last(); // ?Diagnostic
5
6$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):

1<?php
2
3$bag->sortByPosition();
4$bag->sortBySeverity();

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

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

An empty bag formats to a simple message:

1<?php
2
3use Forte\Facades\Forte;
4
5$doc = Forte::parse('<div>Hello</div>');
6
7$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):

1<?php
2
3use Forte\Diagnostics\DiagnosticSeverity;
4
5DiagnosticSeverity::Error->label(); // "error"
6DiagnosticSeverity::Warning->label(); // "warning"
7
8DiagnosticSeverity::Error->isAtLeast(DiagnosticSeverity::Warning); // true
9DiagnosticSeverity::Warning->isAtLeast(DiagnosticSeverity::Error); // false
10DiagnosticSeverity::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()