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