AST
Elements
Elements represent HTML tags in the Forte AST. Forte tracks opening tags, closing tags, attributes, and children for each element. Forte also considers element scopes when pairing directives, and vice versa.
#Working with Elements
ElementNode is the primary node type for HTML elements. You can retrieve the tag name with tagNameText or check it against a pattern using is:
<?php
use Forte\Ast\Elements\ElementNode;
use Forte\Facades\Forte;
$doc = Forte::parse('<div class="container">Hello</div>');
$element = $doc->findElementByName('div');
$element->tagNameText(); // "div"
$element->is('div'); // true
$element->is('d*'); // true
For access to the full tag name node, use tagName:
<?php
$element->tagName(); // ElementNameNode
To get the rendered content between the opening and closing tags:
<?php
$element->innerContent(); // "Hello"
You can access the closing tag node directly, which returns null for self-closing or void elements:
<?php
$element->closingTag(); // ?ElementNameNode
#Element Types
Elements can take several structural forms. These type-check methods are useful when building rewriters or validators that need to handle each form differently.
Use isPaired to check for elements with both an opening and closing tag:
<?php
// <div>...</div>
$element->isPaired(); // true
HTML void elements (which cannot have children) are identified with isVoid:
<?php
// <br>, <img>, <input>, <hr>, <meta>, <link>
$element->isVoid(); // true
For self-closing syntax (<div />):
<?php
// <div />
$element->isSelfClosing(); // true
When the parser inserts a closing tag that was not present in the source, hasSyntheticClosing returns true:
<?php
$element->hasSyntheticClosing(); // bool
To check whether the element is a Blade component (i.e., its tag name matches a registered component prefix):
<?php
$element->isComponent(); // bool
#Special Text Elements
HTML gives several elements special parsing rules. Forte keeps tag-looking
content inside these elements as one TextNode rather than building nested
elements from it.
script, style, iframe, noembed, noframes, and xmp use raw-text
semantics. title and textarea use RCDATA semantics, which also decode HTML
character references when you request semantic text:
<?php
use Forte\Facades\Forte;
$doc = Forte::parse(
'<textarea>A&B <strong>literal</strong></textarea>'
);
$text = $doc->firstElement('textarea')->firstText();
$doc->queryElements('strong')->count(); // 0
$text->getContent(); // "A&B <strong>literal</strong>"
$text->getSemanticContent(); // "A&B <strong>literal</strong>"
Forte still renders the original source exactly. getContent preserves the
authored character references, while getSemanticContent follows the element's
HTML text semantics.
Only exact HTML tag names receive special treatment. A namespaced element such
as <native:script> remains an ordinary element and can contain parsed child
elements.
#Generic Type Arguments
Forte parses TSX-style generic type arguments on any element, including Blade components. Use genericTypeArguments to retrieve the type parameter string, or null when no generics are present:
<?php
use Forte\Facades\Forte;
$doc = Forte::parse('<List<Item> />');
$element = $doc->findElementByName('List');
$element->genericTypeArguments(); // "<Item>"
$element->isComponent(); // false
When no generic type is present, the method returns null:
<?php
use Forte\Facades\Forte;
$doc = Forte::parse('<div class="container">Hello</div>');
$element = $doc->findElementByName('div');
$element->genericTypeArguments(); // null
#Attributes
Attributes let you inspect the properties passed to an element. Use hasAttribute to check for an attribute by name, and getAttribute to retrieve its value:
<?php
$doc = Forte::parse('<div id="main" class="container" hidden>Hello</div>');
$element = $doc->findElementByName('div');
$element->hasAttribute('class'); // true
$element->getAttribute('class'); // "container"
$element->getAttribute('id'); // "main"
Shorthand accessors are available for the most common attributes:
<?php
$element->getClass(); // "container"
$element->getId(); // "main"
For full access to all attributes, use attributes (returns an Attributes collection) or getAttributes (returns an array):
<?php
$attrs = $element->attributes(); // Attributes collection
$array = $element->getAttributes(); // array<Attribute>
#The Attributes Collection
The Attributes collection extends Laravel's Collection with typed filtering methods for different kinds of Blade and HTML attributes.
#By Attribute Type
Filter attributes by their syntactic form. These methods help distinguish standard HTML attributes from Blade-specific ones:
<?php
$attrs = $element->attributes();
$attrs->static(); // standard HTML attributes (name="value")
$attrs->bound(); // Blade bound attributes (:name="expr")
$attrs->escaped(); // Blade escaped attributes (::name="expr")
$attrs->boolean(); // boolean attributes with no value (e.g. disabled)
$attrs->complex(); // attributes with interpolated names or values
$attrs->simple(); // non-Blade, non-complex attributes
#Blade Constructs
Blade constructs like directives and echoes can appear within an element's attribute list. These methods help you isolate them:
<?php
$attrs->bladeConstructs(); // all embedded Blade constructs
$attrs->directives(); // standalone directives (@csrf, @class, etc.)
$attrs->blockDirectives(); // block directives (@if...@endif, etc.)
$attrs->echoes(); // echo interpolations ({{ }}, {!! !!})
$attrs->phpTags(); // PHP tags (<?php ?>, <?= ?>)
You can also check for the presence of these without filtering:
<?php
$attrs->hasBladeConstruct(); // bool
$attrs->hasExpression(); // bool
#By Name
Filter or find attributes by their name, using exact match, regex, or allow/deny lists:
<?php
$attrs->whereNameIs('class'); // attributes named "class"
$attrs->whereNameMatches('/^data-/'); // attributes matching a regex
$attrs->onlyNames(['class', 'id']); // include only specific names
$attrs->exceptNames(['class', 'id']); // exclude specific names
To retrieve a single attribute by name (case-insensitive):
<?php
$classAttr = $attrs->find('class'); // ?Attribute
#Individual Attributes
Each Attribute instance provides methods for inspecting its name, value, and type.
You can inspect the name with nameText (without prefix) or rawName (including any : or :: prefix):
<?php
use Forte\Ast\Elements\Attribute;
foreach ($element->getAttributes() as $attr) {
$attr->nameText(); // string (e.g. "class")
$attr->rawName(); // string (e.g. ":class" for bound attributes)
}
For the attribute value, valueText returns null for boolean attributes, while valueOrDefault lets you provide a fallback:
<?php
$attr->valueText(); // ?string
$attr->valueOrDefault(''); // string (uses default if null)
#Static and Dynamic Values
Attribute semantic helpers distinguish values that can be proven from source from values that need Blade evaluation. Static helpers decode HTML character references before returning a value:
<?php
use Forte\Facades\Forte;
$doc = Forte::parse(
'<button class="Primary primary" aria-label="Save {{ $name }}" :disabled="$busy">Save</button>'
);
$button = $doc->firstElement('button');
$button->staticAttributeValueLower('class'); // "primary primary"
$button->attributeTokensLower('class'); // ["primary", "primary"]
$button->staticAttributeTokens('class'); // ["Primary", "primary"]
$button->attributeIsDynamic('aria-label'); // true
$button->hasUnconditionallyPresentAttribute('aria-label'); // true
$button->staticAttributeValue('disabled'); // null
An attribute can have a dynamic value while its presence remains guaranteed.
In the example, Blade changes the aria-label content but cannot remove the
attribute. Bound, shorthand, expression, and dynamic-name attributes do not
have guaranteed presence.
Use hasAnyAttribute and hasAllAttributes to test several names without
repeating individual lookups:
<?php
$button->hasAnyAttribute(['aria-label', 'title']); // true
$button->hasAllAttributes(['class', 'aria-label']); // true
#Type Checks
Each attribute can be identified by its syntactic form. These type checks help distinguish standard HTML from Blade-specific syntax:
<?php
$attr->isStatic(); // true for standard name="value" attributes
$attr->isBound(); // true for :name="expr" attributes
$attr->isEscaped(); // true for ::name="expr" attributes
$attr->isBoolean(); // true for value-less attributes (e.g. disabled)
$attr->isVariableShorthand(); // true for :$variable shorthand
$attr->isExpression(); // true for JSX-style {expression} attributes
$attr->isBladeConstruct(); // true for embedded Blade constructs
To check whether the name or value contains interpolation:
<?php
$attr->hasComplexName(); // bool
$attr->hasComplexValue(); // bool
You can also inspect the quoting style used for the value:
<?php
$attr->quote(); // ?string
When an attribute is a Blade construct, you can retrieve the underlying AST node for deeper inspection:
<?php
if ($attr->isBladeConstruct()) {
$node = $attr->getBladeConstruct(); // ?Node (DirectiveNode, EchoNode, etc.)
}
#Rendering Parts of an Element
You can render an element's opening tag, closing tag, or the full element with custom child content. These methods preserve the original source faithfully, including whitespace and attribute formatting.
Use renderOpeningTag to get just the opening tag with its attributes:
<?php
use Forte\Facades\Forte;
$doc = Forte::parse('<div class="container" id="main">Hello</div>');
$element = $doc->findElementByName('div');
$element->renderOpeningTag(); // '<div class="container" id="main">'
Use renderClosingTag to get just the closing tag. Self-closing and void elements return an empty string:
<?php
use Forte\Facades\Forte;
$doc = Forte::parse('<div class="container">Hello</div>');
$div = $doc->findElementByName('div');
$div->renderClosingTag(); // "</div>"
<?php
use Forte\Facades\Forte;
$doc = Forte::parse('<input type="text">');
$input = $doc->findElementByName('input');
$input->renderClosingTag(); // ""
Use renderWithChildren to render the full element but substitute the children with custom content. Self-closing and void elements return render() unchanged:
<?php
use Forte\Facades\Forte;
$doc = Forte::parse('<div class="card"><p>Old content</p></div>');
$element = $doc->findElementByName('div');
// '<div class="card"><span>New content</span></div>'
$element->renderWithChildren('<span>New content</span>');
#Finding Elements
You can locate elements by tag name using dedicated finder methods. The singular variant returns the first match, while the plural variant returns a LazyCollection:
<?php
$doc->findElementByName('div'); // ?ElementNode
$doc->findElementsByName('div'); // LazyCollection<ElementNode>
To traverse all elements in the document tree:
<?php
$doc->elements; // LazyCollection of all ElementNode instances
You may filter, count, and map the collection:
<?php
// Find all divs with a specific class
$containers = $doc->elements->filter(
fn($el) => $el->is('div') && $el->hasAttribute('class')
);
// Get all tag names
$tagNames = $doc->elements->map(
fn($el) => $el->tagNameText()
)->unique()->all();
#Special Element Types
The following node types represent non-standard or structural HTML constructs.
#Conditional Comments
Internet Explorer conditional comments are captured as ConditionalCommentNode. You can inspect the condition expression and content:
<?php
use Forte\Ast\Elements\ConditionalCommentNode;
$node->condition(); // string (e.g. "lt IE 9")
$node->content(); // string (content between markers)
$node->isDownlevelHidden(); // bool
$node->isDownlevelRevealed(); // bool
$node->hasClose(); // bool
#Bogus Comments
Malformed HTML comments are parsed as BogusCommentNode:
<?php
use Forte\Ast\Elements\BogusCommentNode;
$node->content(); // string
$node->hasClose(); // bool
$node->isEmpty(); // bool
#CDATA Sections
CDATA sections (<![CDATA[...]]>) are captured as CdataNode:
<?php
use Forte\Ast\Elements\CdataNode;
$node->content(); // string
$node->hasClose(); // bool
$node->isEmpty(); // bool
#Stray Closing Tags
Unmatched closing tags (no corresponding opening tag) are captured as StrayClosingTagNode:
<?php
use Forte\Ast\Elements\StrayClosingTagNode;
$node->tagNameText(); // string (e.g. "div")
#See Also
Continue with these related Forte guides:
- Basic Nodes: Common node API shared by all node types
- Components and Slots: Blade component elements and slot access
- Traversal: Navigate and query the document tree
- Rewriters: Transform elements using the visitor pattern