AST
Basic Nodes
Forte parses Blade templates into an AST of typed nodes. This article covers the basic node types and the common API shared by all nodes.
#Common Node API
Every node in the tree extends the base Node class. The following methods are available on all node types.
The kind method returns the node's integer kind constant from NodeKind:
1<?php
2
3use Forte\Facades\Forte;
4
5$doc = Forte::parse('<div>{{ $name }}</div>');
6
7$first = $doc->firstChild();
8$first->kind(); // int (NodeKind constant)
The render method returns the node's source representation as a string. For parsed nodes, this is the original source text. For synthetic nodes created by rewriters, this is the composed output:
1<?php
2
3$node->render(); // string
The getDocumentContent method returns the raw source text for a parsed node:
1<?php
2
3$node->getDocumentContent(); // string
#Position Information
Every parsed node tracks its position in the source document. All line and column values are 1-indexed:
1<?php
2
3$node->startOffset(); // int (byte offset, -1 for synthetic nodes)
4$node->endOffset(); // int
5$node->startLine(); // int (1-indexed)
6$node->endLine(); // int (1-indexed)
7$node->startColumn(); // int (1-indexed)
8$node->endColumn(); // int (1-indexed)
The lineSpan method returns the start and end lines as a keyed array. The lineCount method returns the number of lines the node spans:
1<?php
2
3use Forte\Facades\Forte;
4
5$doc = Forte::parse("<div>\n {{ \$name }}\n</div>");
6
7$div = $doc->findElementByName('div');
8
9$div->lineSpan(); // ["start" => 1, "end" => 3]
10$div->lineCount(); // 3
Use isMultiline and isSingleLine to check whether a node spans more than one line. The containsLine method checks whether a specific 1-indexed line falls within the node's range:
1<?php
2
3$div->isMultiline(); // true
4$div->isSingleLine(); // false
5$div->containsLine(2); // true
6$div->containsLine(5); // false
#Tree Identity
These methods describe the node's role within the tree structure:
1<?php
2
3$node->isLeaf(); // true if the node has no children
4$node->isRoot(); // true if the node is at the document root
5$node->isSynthetic(); // true if the node was created by a rewriter
#Type Checking
Every node provides type-check methods that test the node's kind without requiring instanceof checks:
1<?php
2
3use Forte\Facades\Forte;
4
5$doc = Forte::parse('<div>{{ $name }}</div>');
6
7$div = $doc->findElementByName('div');
8$echo = $doc->find(fn($n) => $n instanceof \Forte\Ast\EchoNode);
9
10$div->isElement(); // true
11$div->isText(); // false
12
13$echo->isEcho(); // true
14$echo->isElement(); // false
The full set of type-check methods:
1<?php
2
3$node->isText(); // TextNode
4$node->isElement(); // ElementNode
5$node->isDirective(); // DirectiveNode
6$node->isDirectiveBlock(); // DirectiveBlockNode
7$node->isEcho(); // EchoNode (escaped, raw, or triple)
8$node->isComment(); // CommentNode (HTML)
9$node->isBladeComment(); // BladeCommentNode
10$node->isBogusComment(); // BogusCommentNode
11$node->isConditionalComment(); // ConditionalCommentNode
12$node->isVerbatim(); // VerbatimNode
13$node->isPhpBlock(); // PhpBlockNode
14$node->isPhpTag(); // PhpTagNode
15$node->isXmlDeclaration(); // XmlDeclarationNode
16$node->isProcessingInstruction(); // ProcessingInstructionNode
#Safe Casting
Every node also provides safe cast methods that return the typed node or null if the node is not of that type. These are useful in callbacks where you need to narrow the type without a separate instanceof check:
1<?php
2
3use Forte\Facades\Forte;
4
5$doc = Forte::parse('<div>{{ $name }}</div>');
6
7$div = $doc->findElementByName('div');
8
9$div->asElement(); // ElementNode
10$div->asDirective(); // null
The full set of safe cast methods:
1<?php
2
3$node->asText(); // ?TextNode
4$node->asElement(); // ?ElementNode
5$node->asDirective(); // ?DirectiveNode
6$node->asDirectiveBlock(); // ?DirectiveBlockNode
7$node->asEcho(); // ?EchoNode
8$node->asComment(); // ?CommentNode
9$node->asBladeComment(); // ?BladeCommentNode
10$node->asBogusComment(); // ?BogusCommentNode
11$node->asConditionalComment(); // ?ConditionalCommentNode
12$node->asVerbatim(); // ?VerbatimNode
13$node->asPhpBlock(); // ?PhpBlockNode
14$node->asPhpTag(); // ?PhpTagNode
15$node->asCdata(); // ?CdataNode
16$node->asXmlDeclaration(); // ?XmlDeclarationNode
17$node->asComponent(); // ?ComponentNode
18$node->asSlot(); // ?SlotNode
19$node->asDoctype(); // ?DoctypeNode
20$node->asStrayClosingTag(); // ?StrayClosingTagNode
21$node->asGeneric(); // ?GenericNode
22$node->asEscape(); // ?EscapeNode
#Node Metadata
Nodes support arbitrary key-value metadata and lightweight string tags. Metadata is stored on the document and accessed through the node, making it available across rewriting passes and traversal callbacks.
Use setData and getData to store and retrieve values. The hasData method checks for a key's presence:
1<?php
2
3use Forte\Facades\Forte;
4
5$doc = Forte::parse('<div>Hello</div>');
6$div = $doc->findElementByName('div');
7
8$div->setData('processed', true);
9
10$div->hasData('processed'); // true
11$div->getData('processed'); // true
12$div->getData('missing', 'fallback'); // "fallback"
Use removeData to delete a key, and getAllData to retrieve all metadata for a node:
1<?php
2
3$div->removeData('processed');
4$div->getAllData(); // []
Tags are simple string labels for marking nodes without needing to store a value. Use tag and untag to add and remove them, hasTag to check for a tag, and getTags to retrieve all tags:
1<?php
2
3use Forte\Facades\Forte;
4
5$doc = Forte::parse('<div>Hello</div>');
6$div = $doc->findElementByName('div');
7
8$div->tag('reviewed');
9$div->tag('important');
10
11$div->hasTag('reviewed'); // true
12$div->getTags(); // ["reviewed", "important"]
13
14$div->untag('reviewed');
15$div->getTags(); // ["important"]
#Text Nodes
Use TextNode to work with plain text content between other constructs. It is useful for whitespace analysis or extracting literal text from templates.
The getContent method returns the full text content. The getTrimmedContent method returns the same value with leading and trailing whitespace removed:
1<?php
2
3use Forte\Ast\TextNode;
4
5$doc = Forte::parse('<div> Hello World </div>');
6
7$text = $doc->find(fn($n) => $n instanceof TextNode);
8
9$text->getContent(); // " Hello World "
10$text->getTrimmedContent(); // "Hello World"
The isWhitespace method checks whether the text contains only whitespace. The hasSignificantContent method returns the inverse:
1<?php
2
3$text->isWhitespace(); // false
4$text->hasSignificantContent(); // true
The countLeadingNewlines and countTrailingNewlines methods return the number of newline characters at the start and end of the text:
1<?php
2
3$text->countLeadingNewlines(); // int
4$text->countTrailingNewlines(); // int
#Echo/Interpolation Nodes
Blade interpolation (escaped {{ }}, raw {!! !!}, and triple {{{ }}}) is captured as EchoNode instances. These let you inspect or rewrite template expressions.
The expression method returns the inner expression with whitespace trimmed. The content method returns the inner content without the delimiters but without trimming:
1<?php
2
3use Forte\Ast\EchoNode;
4
5$doc = Forte::parse('{{ $name }}');
6
7$echo = $doc->find(fn($n) => $n instanceof EchoNode);
8
9$echo->expression(); // "$name"
10$echo->content(); // " $name "
The echoType method returns a string constant identifying the echo variant. The type-check methods provide a more direct way to identify the variant:
1<?php
2
3$echo->echoType(); // "escaped", "raw", "triple", or "unknown"
4$echo->isEscaped(); // true for {{ }}
5$echo->isRaw(); // true for {!! !!}
6$echo->isTriple(); // true for {{{ }}}
#Comments
Forte distinguishes HTML comments from Blade comments.
#HTML Comments
Standard HTML comments are parsed as CommentNode instances. The content method returns the text between <!-- and -->:
1<?php
2
3use Forte\Ast\Elements\CommentNode;
4
5$doc = Forte::parse('<!-- TODO: fix this -->');
6
7$comment = $doc->find(fn($n) => $n instanceof CommentNode);
8
9$comment->content(); // " TODO: fix this "
10$comment->isEmpty(); // false
The hasClose method checks whether the comment has a proper closing --> sequence:
1<?php
2
3$comment->hasClose(); // bool
#Blade Comments
Blade comments ({{-- --}}) are stripped from compiled output and parsed as BladeCommentNode. The content method returns the inner content, and the text method returns the same value trimmed:
1<?php
2
3use Forte\Ast\BladeCommentNode;
4
5$doc = Forte::parse('{{-- TODO: fix this --}}');
6
7$comment = $doc->find(fn($n) => $n instanceof BladeCommentNode);
8
9$comment->content(); // " TODO: fix this "
10$comment->text(); // "TODO: fix this"
11$comment->isEmpty(); // false
The hasClose method checks whether the Blade comment has a closing --}} sequence:
1<?php
2
3$comment->hasClose(); // bool
#PHP Nodes
Forte uses two distinct node types for PHP code, depending on how it is embedded in the template.
#PHP Blocks
The @php...@endphp form is captured as PhpBlockNode. The code method returns the trimmed PHP code, while content returns the raw content between the delimiters:
1<?php
2
3use Forte\Ast\PhpBlockNode;
4
5$doc = Forte::parse('@php $x = 1; @endphp');
6
7$block = $doc->find(fn($n) => $n instanceof PhpBlockNode);
8
9$block->code(); // "$x = 1;"
10$block->content(); // " $x = 1; "
11$block->hasClose(); // true
12$block->isEmpty(); // false
#PHP Tags
Raw PHP tags (<?php ?> and <?= ?>) are parsed as PhpTagNode. The code method returns the trimmed PHP code, while content returns the raw content between the tags:
1<?php
2
3use Forte\Ast\PhpTagNode;
4
5$doc = Forte::parse('<?php echo "hello"; ?>');
6
7$tag = $doc->find(fn($n) => $n instanceof PhpTagNode);
8
9$tag->code(); // 'echo "hello";'
10$tag->content(); // ' echo "hello"; '
11$tag->hasClose(); // true
The isShortEcho and isPhpTag methods identify the tag variant. The phpType method returns a string constant:
1<?php
2
3$tag->isShortEcho(); // true for <?= ...
4$tag->isPhpTag(); // true for <?php ...
5$tag->phpType(); // "echo" or "php"
#Verbatim Nodes
Content between @verbatim and @endverbatim is captured as a VerbatimNode:
1<?php
2
3use Forte\Ast\VerbatimNode;
4
5$doc = Forte::parse('@verbatim {{ $raw }} @endverbatim');
6
7$verbatim = $doc->find(fn($n) => $n instanceof VerbatimNode);
8
9$verbatim->content(); // " {{ $raw }} "
10$verbatim->hasClose(); // true
11$verbatim->isEmpty(); // false
#Doctype Nodes
The DoctypeNode captures <!DOCTYPE> declarations:
1<?php
2
3use Forte\Ast\DoctypeNode;
4
5$doc = Forte::parse('<!DOCTYPE html>');
6
7$doctype = $doc->find(
8 fn($n) => $n instanceof DoctypeNode
9);
10
11$doctype->type(); // "html"
12$doctype->isHtml5(); // true
13$doctype->isXhtml(); // false
#Other Node Types
The following node types appear less frequently but are available when working with non-standard markup.
#Escape Nodes
When Forte encounters @@ sequence in source, it produces an EscapeNode, a literal @ that bypasses directive parsing. The content method always returns '@':
1<?php
2
3use Forte\Ast\EscapeNode;
4
5$node->content(); // "@"
#Generic Nodes
Structural containers like fragments are wrapped in a GenericNode. The kindName method returns a human-readable name for the node kind:
1<?php
2
3use Forte\Ast\GenericNode;
4
5$node->kindName(); // string (e.g. "Fragment")
6$node->isFragment(); // bool
#Processing Instructions
XML processing instructions (<?target data?>) are parsed as ProcessingInstructionNode. The target method returns the instruction target, and data returns the remaining content:
1<?php
2
3use Forte\Ast\ProcessingInstructionNode;
4
5$node->target(); // string (e.g. "xml-stylesheet")
6$node->data(); // string (e.g. 'type="text/xsl" href="style.xsl"')
7$node->hasClose(); // bool
#XML Declarations
For <?xml ?> declarations, Forte provides XmlDeclarationNode with dedicated accessors for each standard attribute:
1<?php
2
3use Forte\Ast\XmlDeclarationNode;
4
5$node->version(); // ?string (e.g. "1.0")
6$node->encoding(); // ?string (e.g. "UTF-8")
7$node->standalone(); // ?string (e.g. "yes")
8$node->hasClose(); // bool
#CDATA Sections
CDATA sections (<![CDATA[...]]>) are captured as CdataNode:
1<?php
2
3use Forte\Ast\Elements\CdataNode;
4
5$node->content(); // string (content between delimiters)
6$node->hasClose(); // bool
7$node->isEmpty(); // bool
#See also
- Elements: HTML element nodes, attributes, and tag types
- Directives: Blade directive and block directive nodes
- Trivia: Whitespace analysis for text nodes
- Traversal: Navigate and query the document tree
- Documents: Parse templates and access the full document API