### Development Setup Commands
Source: https://github.com/akankov/html-ast/blob/main/CONTRIBUTING.md
Run these commands to set up the development environment and execute various checks. Tooling is managed via Docker images for reproducibility.
```bash
make install
```
```bash
make test # PHPUnit on the default PHP_VERSION (8.4)
```
```bash
make test-all # PHPUnit on PHP 8.3, 8.4, 8.5
```
```bash
make phpstan # PHPStan level max
```
```bash
make phan # Phan with ext-ast (builds the docker image first)
```
```bash
make cs-check # PHP-CS-Fixer dry-run
```
```bash
make rector-check # Rector dry-run
```
```bash
make ci # Full CI pipeline locally
```
--------------------------------
### 30-Second Example: Parse, Traverse, and Print HTML
Source: https://github.com/akankov/html-ast/blob/main/README.md
This example demonstrates parsing an HTML string, traversing the resulting AST to remove 'data-testid' attributes using a visitor, and then printing the modified AST back to an HTML string. It utilizes the Parser, Visitor, NodeTraverser, and StandardPrinter classes.
```php
use Akankov\HtmlAst\Parser\Parser;
use Akankov\HtmlAst\Node\Element;
use Akankov\HtmlAst\Visitor\Visitor;
use Akankov\HtmlAst\Visitor\NodeTraverser;
use Akankov\HtmlAst\Visitor\VisitorAction;
use Akankov\HtmlAst\Printer\StandardPrinter;
$result = Parser::detect()->parse($html);
$stripTestIds = new class implements Visitor {
public function enterNode(\Akankov\HtmlAst\Node\Node $n): VisitorAction|\Akankov\HtmlAst\Node\Node|null
{
if ($n instanceof Element && $n->hasAttribute('data-testid')) {
return $n->withoutAttribute('data-testid');
}
return null;
}
public function leaveNode(\Akankov\HtmlAst\Node\Node $n): VisitorAction|\Akankov\HtmlAst\Node\Node|null
{
return null;
}
};
$tree = (new NodeTraverser())->traverse($result->tree, [$stripTestIds]);
$output = (new StandardPrinter())->print($tree);
```
--------------------------------
### Install html-ast Package
Source: https://github.com/akankov/html-ast/blob/main/README.md
Use Composer to install the html-ast package. For PHP 8.3, an additional package is required.
```bash
composer require akankov/html-ast
```
```bash
composer require masterminds/html5:^2.9
```
--------------------------------
### HTML AST Parser and Visitor Usage
Source: https://github.com/akankov/html-ast/blob/main/PLAN.md
A minimal usage example demonstrating how to parse HTML, traverse the resulting AST with a visitor, and print the tree.
```APIDOC
## Minimal usage example (sketch)
```php
use Akankov\HtmlAst\Parser\Parser;
use Akankov\HtmlAst\Node\Element;
use Akankov\HtmlAst\Visitor\Visitor;
use Akankov\HtmlAst\Visitor\NodeTraverser;
use Akankov\HtmlAst\Visitor\VisitorAction;
use Akankov\HtmlAst\Printer\StandardPrinter;
$result = Parser::detect()->parse($html); // ParseResult
$visitor = new class implements Visitor {
public function enterNode(\Akankov\HtmlAst\Node\Node $n): VisitorAction|\Akankov\HtmlAst\Node\Node|null { /* … */ }
public function leaveNode(\Akankov\HtmlAst\Node\Node $n): VisitorAction|\Akankov\HtmlAst\Node\Node|null { /* … */ }
};
$tree = (new NodeTraverser())->traverse($result->tree, [$visitor]);
$output = (new StandardPrinter())->print($tree);
```
```
--------------------------------
### Minimal HTML AST Parsing and Traversal Example
Source: https://github.com/akankov/html-ast/blob/main/PLAN.md
Demonstrates the basic usage of the HTML AST library for parsing HTML, traversing the resulting tree with a visitor, and printing the output. Requires specific imports for Parser, Node, Visitor, and Printer components.
```php
use Akankov\HtmlAst\Parser\Parser;
use Akankov\HtmlAst\Node\Element;
use Akankov\HtmlAst\Visitor\Visitor;
use Akankov\HtmlAst\Visitor\NodeTraverser;
use Akankov\HtmlAst\Visitor\VisitorAction;
use Akankov\HtmlAst\Printer\StandardPrinter;
$result = Parser::detect()->parse($html); // ParseResult
$visitor = new class implements Visitor {
public function enterNode(\Akankov\HtmlAst\Node\Node $n): VisitorAction|\Akankov\HtmlAst\Node\Node|null { /* … */ }
public function leaveNode(\Akankov\HtmlAst\Node\Node $n): VisitorAction|\Akankov\HtmlAst\Node\Node|null { /* … */ }
};
$tree = (new NodeTraverser())->traverse($result->tree, [$visitor]);
$output = (new StandardPrinter())->print($tree);
```
--------------------------------
### Configure Parse Options
Source: https://context7.com/akankov/html-ast/llms.txt
Utilize `ParseOptions` to control parsing behavior, including document vs. fragment mode and error handling (lenient vs. strict). This example shows creating options for full documents, strict parsing, fragment parsing, and fragment parsing with a custom context element.
```php
use Akankov\HtmlAst\Parser\ParseOptions;
use Akankov\HtmlAst\Parser\Parser;
$parser = Parser::detect();
// Full document, lenient (default)
$docOpts = ParseOptions::document();
// Full document, strict — throws on any parse error
$strictOpts = ParseOptions::document()->strict();
// Fragment mode: parse as children of a
context (default context)
$fragOpts = ParseOptions::fragment();
// Fragment mode with a custom context element (e.g. inside a )
$tableFragOpts = ParseOptions::fragment('table');
$html = 'World & beyond
';
$fragHtml = '| Cell |
';
$docResult = $parser->parse($html, $docOpts);
$fragResult = $parser->parse($fragHtml, $tableFragOpts);
// Check quirks mode on full document
use Akankov\HtmlAst\Node\QuirksMode;
$quirks = $docResult->tree->quirksMode; // QuirksMode::NoQuirks | Quirks | LimitedQuirks
// Fragment root carries its context element name
echo $fragResult->tree->context; // "table"
```
--------------------------------
### Get Byte-Offset Position Metadata with ByteRange
Source: https://context7.com/akankov/html-ast/llms.txt
Each Node includes a ByteRange object indicating its start and end byte offsets in the original source. Use SourceMap::lineColumn() to convert byte offsets to line and column numbers for diagnostics.
```php
use Akankov\HtmlAst\Parser\Parser;
use Akankov\HtmlAst\Position\{ByteRange, SourceMap};
use Akankov\HtmlAst\Node\Element;
$html = "\n\n Hello
\n";
$result = Parser::detect()->parse($html);
// Navigate to the element
$p = $result->tree->children[0] //
->children[1] //
(parser inserts it)
->children[0]; //
assert($p instanceof Element);
assert($p->tagName === 'p');
$range = $p->range;
echo $range->start; // e.g. 24 (byte offset of '
end; // e.g. 47 (byte offset just after '
')
echo $range->length(); // 23
echo $range->contains(30); // true — byte 30 falls inside ...
// Slice the original source using the range
$rawNode = substr($html, $range->start, $range->length());
echo $rawNode; // 'Hello
'
// Convert byte offset to line/column
$sourceMap = $result->sourceMap;
['line' => $line, 'column' => $col] = $sourceMap->lineColumn($range->start);
echo "p element starts at line {"{$line}"}, column {"{$col}"}\n";
// Direct ByteRange construction (for synthetic nodes)
$synthetic = new ByteRange(0, 0); // zero-length sentinel range for constructed nodes
```
--------------------------------
### Get ByteRange Positions from ParseResult
Source: https://context7.com/akankov/html-ast/llms.txt
Access ByteRange positions on AST nodes obtained from ParseResult to map AST locations back to source coordinates using SourceMap::lineColumn(). This is crucial for diagnostic tooling.
```php
use Html\SourceMap;
// Assuming $node is an AST node with ByteRange information
$byteRange = $node->byteRange();
$lineColumn = SourceMap::lineColumn($byteRange, $parseResult->sourceMap());
```
--------------------------------
### Dependency Injection with Contract Interfaces
Source: https://context7.com/akankov/html-ast/llms.txt
Demonstrates using stable contract interfaces for dependency injection in a minifier class. Requires importing contract interfaces and concrete implementations.
```php
use Akankov\HtmlAst\Contract\{ParserContract, VisitorContract, PrinterContract};
use Akankov\HtmlAst\Parser\{Parser, ParseOptions, ParseResult};
use Akankov\HtmlAst\Node\Node;
use Akankov\HtmlAst\Visitor\VisitorAction;
use Akankov\HtmlAst\Printer\StandardPrinter;
// ---- Dependency injection ----
class HtmlMinifier
{
public function __construct(
private readonly ParserContract $parser,
private readonly PrinterContract $printer,
) {}
public function minify(string $html): string
{
$result = $this->parser->parse($html, ParseOptions::document());
// ... apply visitor transformations ...
return $this->printer->print($result->tree);
}
}
$minifier = new HtmlMinifier(
parser: Parser::detect(),
printer: new StandardPrinter(),
);
echo $minifier->minify(' Hello
');
// ---- Test double implementing the stable contract ----
class StubParser implements ParserContract
{
public function __construct(private ParseResult $fixture) {}
public function parse(string $html, ?ParseOptions $options = null): ParseResult
{
return $this->fixture; // return pre-built fixture in tests
}
}
// ---- Custom visitor implementing VisitorContract ----
class AttributeSorter implements VisitorContract
{
public function enterNode(Node $n): Node|VisitorAction|null
{
if (!$n instanceof \Akankov\HtmlAst\Node\Element) {
return null;
}
$sorted = $n->attributes;
usort($sorted, fn($a, $b) => strcmp($a->name, $b->name));
return $n->withAttributes($sorted);
}
public function leaveNode(Node $n): Node|VisitorAction|null
{
return null;
}
}
```
--------------------------------
### Parsing HTML and Traversing AST
Source: https://context7.com/akankov/html-ast/llms.txt
Demonstrates parsing an HTML string into an AST and manually traversing the node hierarchy. Shows how to access node properties, attributes, and perform immutable mutations like removing attributes or replacing children.
```php
use Akankov\HtmlAst\Parser\Parser;
use Akankov\HtmlAst\Node{
Document, DocumentFragment, Element, Attribute,
Text, Comment, Doctype, CDataSection,
NodeKind, AttributeQuoteStyle, QuirksMode
};
$result = Parser::detect()->parse(
'Hello
'
);
// Walk the tree manually
$doc = $result->tree; // Document
assert($doc->quirksMode === QuirksMode::NoQuirks);
assert($doc->kind() === NodeKind::Document);
// Recurse into children (html > body > p)
foreach ($doc->children as $child) {
if ($child instanceof Element && $child->tagName === 'html') {
foreach ($child->children as $bodyOrHead) {
if ($bodyOrHead instanceof Element && $bodyOrHead->tagName === 'body') {
$p = $bodyOrHead->children[0]; //
assert($p instanceof Element);
assert($p->tagName === 'p');
// Attributes preserve quoting style
foreach ($p->attributes as $attr) {
assert($attr instanceof Attribute);
echo "{$attr->name}={$attr->value} ({$attr->quoteStyle->value})\n";
// class=main (double)
// id=hero (single)
}
// Check attribute existence
assert($p->hasAttribute('class') === true);
$classAttr = $p->getAttribute('class'); // Attribute|null
// Immutable mutation: remove an attribute, get a new Element
$pWithoutId = $p->withoutAttribute('id');
assert(!$pWithoutId->hasAttribute('id'));
assert($pWithoutId->hasAttribute('class')); // untouched
// Replace children immutably
$newText = new Text($p->range, 'New content');
$pWithNewChildren = $p->withChildren([$newText]);
}
}
}
}
// ByteRange on every node
$p = $doc->children[0]; // adjust to actual tree
echo $p->range->start; // byte offset in original source
echo $p->range->end; // exclusive end
echo $p->range->length(); // end - start
echo $p->range->contains(42); // bool — is byte 42 inside this node?
```
--------------------------------
### Compose Visitors for HTML AST Transformation
Source: https://context7.com/akankov/html-ast/llms.txt
Demonstrates composing multiple visitors to strip data-testid attributes, remove HTML comments, and halt traversal at script tags. Visitors are run in order on each node.
```php
use Akankov\HtmlAst\Parser\Parser;
use Akankov\HtmlAst\Visitor\{Visitor, NodeTraverser, VisitorAction};
use Akankov\HtmlAst\Node\{Node, Element};
use Akankov\HtmlAst\Printer\StandardPrinter;
// Visitor 1: strip all data-testid attributes
$stripTestIds = new class implements Visitor {
public function enterNode(Node $n): Node|VisitorAction|null
{
if ($n instanceof Element && $n->hasAttribute('data-testid')) {
return $n->withoutAttribute('data-testid'); // replace with cleaned element
}
return null; // continue
}
public function leaveNode(Node $n): Node|VisitorAction|null
{
return null;
}
};
// Visitor 2: remove all HTML comments
$removeComments = new class implements Visitor {
public function enterNode(Node $n): Node|VisitorAction|null
{
if ($n instanceof \Akankov\HtmlAst\Node\Comment) {
return VisitorAction::Remove;
}
return null;
}
public function leaveNode(Node $n): Node|VisitorAction|null
{
return null;
}
};
// Visitor 3: stop as soon as we find the first
world
');
$traverser = new NodeTraverser();
$transformed = $traverser->traverse($result->tree, [$skipScriptStyle, $uppercaseText]);
// text is uppercased; script content is untouched
use Akankov\HtmlAst\Printer\StandardPrinter;
echo (new StandardPrinter())->print($transformed);
// Expected:
HELLO
WORLD
```
--------------------------------
### Immutable Element Mutation with Wither Methods
Source: https://context7.com/akankov/html-ast/llms.txt
Illustrates how to use wither methods (`withoutAttribute`, `withAttributes`, `withChildren`) on `Element` nodes to create new instances with specific modifications. These methods are key for AST transformations.
```php
use Akankov\HtmlAst\Parser\Parser;
use Akankov\HtmlAst\Node{Element, Attribute, Text, AttributeQuoteStyle};
use Akankov\HtmlAst\Position\ByteRange;
$result = Parser::detect()->parse('Hi
');
$div = /* ... navigate to the div element ... */ $result->tree->children[0]->children[1]->children[0];
// withoutAttribute — remove a single attribute by name
$divNoDataV = $div->withoutAttribute('data-v');
// $divNoDataV has only class="box"
// withAttributes — replace the entire attribute list
$newAttr = new Attribute(
new ByteRange(0, 0), // synthetic range
'id',
'main',
AttributeQuoteStyle::Double,
);
$divWithNewAttrs = $div->withAttributes([$newAttr]);
// $divWithNewAttrs has only id="main"
// withChildren — replace child list
$newSpan = new Element(
new ByteRange(0, 0),
'span',
[],
[new Text(new ByteRange(0, 0), 'World')],
);
$divWithNewChildren = $div->withChildren([$newSpan]);
```
--------------------------------
### Document and Fragment Parsing Options
Source: https://github.com/akankov/html-ast/blob/main/docs/design/api-v0.1.md
Enables parsing of both full documents and HTML fragments. For fragments, specify the context element name for correct parsing.
```php
ParseOptions::document()
ParseOptions::fragment(string $context = 'body')
```
--------------------------------
### Visitor API Method Signatures
Source: https://github.com/akankov/html-ast/blob/main/docs/design/api-v0.1.md
Defines the method signatures for the visitor pattern, allowing for node traversal and manipulation. Return null to continue traversal.
```php
public function enterNode(Node $node): Node|VisitorAction|null;
public function leaveNode(Node $node): Node|VisitorAction|null;
```
--------------------------------
### HTML AST Library Structure Sketch
Source: https://github.com/akankov/html-ast/blob/main/PLAN.md
This sketch outlines the proposed directory structure and main components of the html-ast library, including Parser, Node, Token, Visitor, and Printer modules.
```plaintext
Akankov\HtmlAst\
├── Parser\
│ ├── Parser (interface)
│ ├── NativeParser (\Dom\HTMLDocument backend, 8.4+)
│ ├── MastermindsParser (masterminds/html5 backend, 8.3 fallback)
│ ├── ParseOptions (mode: document|fragment, strict|lenient, …)
│ └── ParseResult (tree, tokens, errors, sourceMap)
├── Node\
│ ├── Node (abstract base; readonly attributes; position ref)
│ ├── Document
│ ├── DocumentFragment
│ ├── Element (tagName, attributes, children)
│ ├── Attribute (name, value, quoteStyle, namespace)
│ ├── Text
│ ├── Comment
│ ├── Doctype
│ ├── ProcessingInstruction
│ └── CDataSection (only inside foreign content)
├── Token\
│ ├── Token (kind, range, raw text)
│ ├── TokenKind (enum: StartTag, EndTag, Text, Comment, Doctype, …)
│ └── TokenStream
├── Position\
│ ├── ByteRange (start, end)
│ └── SourceMap (offset → line/col on demand)
├── Visitor\
│ ├── Visitor (interface: enterNode, leaveNode)
│ ├── NodeTraverser (drives traversal; handles REMOVE/STOP/REPLACE)
│ └── VisitorAction (enum: Continue, SkipChildren, Stop, Remove, ReplaceWith)
├── Printer\
│ ├── Printer (interface)
│ ├── StandardPrinter (normalized output; no trivia)
│ └── LosslessPrinter (round-trips trivia, v0.2)
├── Contract
│ ├── ParserContract
│ ├── VisitorContract
│ └── PrinterContract
└── Internal
```
--------------------------------
### Using Contract Interfaces for Dependency Injection
Source: https://context7.com/akankov/html-ast/llms.txt
The Contract namespace provides a stable API boundary for dependency injection, allowing swapping of parser backends or mocking in tests without coupling to concrete implementations.
```php
use Html\Contract\ParserContract;
// Example of type hinting for dependency injection
function processHtml(ParserContract $parser, string $html): void {
$parseResult = $parser->parse($html);
// ... process AST ...
}
```
--------------------------------
### Parse HTML and Inspect Tokens
Source: https://context7.com/akankov/html-ast/llms.txt
Parses an HTML string into a token stream, allowing inspection of individual tokens and verification of round-trip fidelity. Requires importing various Token classes.
```php
use Akankov\HtmlAst\Parser\Parser;
use Akankov\HtmlAst\Token{
Token,
TokenStream,
TokenKind,
StartTagToken,
EndTagToken,
CharacterToken,
WhitespaceToken,
CommentToken,
DoctypeToken,
CdataToken,
EndOfFileToken,
TokenAttribute
};
use Akankov\HtmlAst\Position\ByteRange;
$html = "Hello & World
";
$result = Parser::detect()->parse($html);
$stream = $result->tokens;
echo $stream->count(); // number of tokens
// Verify round-trip fidelity guarantee
$reconstructed = implode('', array_map(fn(Token $t) => $t->raw, $stream->tokens));
assert($reconstructed === $html);
// Inspect individual tokens
foreach ($stream->tokens as $token) {
match ($token->kind) {
TokenKind::Doctype => printf("DOCTYPE raw=%s\n", var_export($token->raw, true)),
TokenKind::Comment => printf("COMMENT data=%s\n", $token->data), // CommentToken
TokenKind::StartTag => printf(
"START <%s> selfClosing=%s attrs=%d\n",
$token->tagName,
$token->selfClosing ? 'true' : 'false',
count($token->attributes),
), // StartTagToken
TokenKind::EndTag => printf("END %s>\n", $token->tagName), // EndTagToken
TokenKind::Character => printf("CHAR data=%s\n", $token->data), // CharacterToken
TokenKind::Whitespace => print("WHITESPACE\n"), // WhitespaceToken
TokenKind::EndOfFile => print("EOF\n"),
default => null,
};
}
// TokenAttribute: attributes on a StartTagToken preserve quoting style
foreach ($stream->tokens as $token) {
if ($token instanceof StartTagToken) {
foreach ($token->attributes as $attr) {
/** @var TokenAttribute $attr */
echo "{$attr->name}={$attr->value} quoted={$attr->quoteStyle->value}\n";
// class=hi quoted=single
}
}
}
// Slice tokens covering a specific byte range (e.g. a node's range)
$pNode = $result->tree->children[0]->children[1]->children[0];
$pTokens = $stream->slice($pNode->range); // list fully inside ...
```
--------------------------------
### Serialize AST to Normalized HTML with StandardPrinter
Source: https://context7.com/akankov/html-ast/llms.txt
Use StandardPrinter to convert an AST back into an HTML string. It enforces canonical attribute quoting, standard doctype, and HTML5 void element handling. For preserving original formatting, consider LosslessPrinter.
```php
use Akankov\HtmlAst\Parser\Parser;
use Akankov\HtmlAst\Printer\{Printer, StandardPrinter};
use Akankov\HtmlAst\Visitor\{Visitor, NodeTraverser, VisitorAction};
use Akankov\HtmlAst\Node\{Node, Element};
use Akankov\HtmlAst\Contract\PrinterContract;
// StandardPrinter implements both Printer and PrinterContract
$printer = new StandardPrinter();
// Parse → transform → print pipeline
$html = "";
$result = Parser::detect()->parse($html);
// Strip the id attribute via a visitor
$stripId = new class implements Visitor {
public function enterNode(Node $n): Node|VisitorAction|null
{
if ($n instanceof Element && $n->hasAttribute('id')) {
return $n->withoutAttribute('id');
}
return null;
}
public function leaveNode(Node $n): Node|VisitorAction|null { return null; }
};
$transformed = (new NodeTraverser())->traverse($result->tree, [$stripId]);
$output = $printer->print($transformed);
// StandardPrinter normalizes:
// • tag names lowercased: DIV → div
// • attributes double-quoted: CLASS='box' → class="box"
// • void elements:
→
,
→
// Expected output:
echo $output;
// Printer is type-hintable via the stable contract interface
function serializeSubtree(PrinterContract $p, Node $n): string
{
return $p->print($n);
}
echo serializeSubtree($printer, $result->tree);
```
--------------------------------
### Accessing ParseResult Data
Source: https://context7.com/akankov/html-ast/llms.txt
Demonstrates how to access the AST tree, parse errors, token stream, and source map from the ParseResult object. This is useful for inspecting the parsed HTML structure and any encountered issues.
```php
use Akankov\HtmlAst\Parser\Parser;
use Akankov\HtmlAst\Parser\ParseErrorKind;
use Akankov\HtmlAst\Node\Document;
use Akankov\HtmlAst\Node\DocumentFragment;
$parser = Parser::detect();
$result = $parser->parse('Bold
');
// Access the AST root
$root = $result->tree; // Document | DocumentFragment
// Inspect parse errors (lenient mode: tree is always available)
foreach ($result->errors as $error) {
/** @var \Akankov\HtmlAst\Parser\ParseError $error */
echo sprintf(
'[%s] %s at bytes %d–%d%s',
$error->kind->value, // e.g. "tokenizer_error"
$error->message,
$error->range->start,
$error->range->end,
PHP_EOL,
);
}
// Access the token stream (for formatters / fidelity printers)
echo $result->tokens->count(); // total token count
// Verify round-trip fidelity: concatenating every token's ->raw equals the input
$reconstructed = implode('', array_map(fn($t) => $t->raw, $result->tokens->tokens));
assert($reconstructed === 'Bold
');
// Access the source map for line/column lookup
$sourceMap = $result->sourceMap;
['line' => $line, 'column' => $col] = $sourceMap->lineColumn(5); // offset 5
```
--------------------------------
### Parse HTML to AST with html-ast
Source: https://context7.com/akankov/html-ast/llms.txt
Use Parser::detect()->parse($html) to obtain a ParseResult containing the AST. This method is designed to be robust and handle various HTML inputs.
```php
use Html
ode\ParseResult;
use Html\Parser;
$html = 'Hello
';
$parseResult = Parser::detect()->parse($html);
$ast = $parseResult->node();
```
--------------------------------
### Traverse and Transform HTML AST with Visitors
Source: https://context7.com/akankov/html-ast/llms.txt
Implement Visitor interfaces and use NodeTraverser::traverse() to modify the AST. Transformations can include replacing, removing, or skipping nodes, or stopping the traversal.
```php
use Html\NodeTraverser;
use Html\VisitorContract;
class MyVisitor implements VisitorContract {
// ... implementation ...
}
$traverser = new NodeTraverser();
$traverser->addVisitor(new MyVisitor());
$newAst = $traverser->traverse($ast);
```
--------------------------------
### Print HTML AST back to String
Source: https://context7.com/akankov/html-ast/llms.txt
Use StandardPrinter::print() to serialize the transformed AST back into an HTML string. This is the final step in the parse-traverse-print pipeline.
```php
use Html\Printer\StandardPrinter;
$printer = new StandardPrinter();
$html = $printer->print($newAst);
```
--------------------------------
### Lenient vs. Strict Parsing Options
Source: https://github.com/akankov/html-ast/blob/main/docs/design/api-v0.1.md
Configures the parser's error handling behavior. Use lenient mode for default HTML5 recovery, or strict mode to halt on the first error.
```php
ParseOptions::document()->lenient(); // default
ParseOptions::document()->strict(); // throws on first error
```
--------------------------------
### ParseOptions
Source: https://context7.com/akankov/html-ast/llms.txt
Immutable value object for configuring parse mode (document or fragment) and error handling (lenient or strict). Factory methods allow for safe chaining of options.
```APIDOC
## `ParseOptions` — Configure parse mode and error handling
Immutable value object controlling whether the parser runs in document or fragment mode and whether it recovers from errors (lenient, the default) or throws on the first error (strict). All factory methods return new instances, making chaining safe.
### Method
```php
use Akankov\HtmlAst\Parser\ParseOptions;
use Akankov\HtmlAst\Parser\Parser;
$parser = Parser::detect();
// Full document, lenient (default)
$docOpts = ParseOptions::document();
// Full document, strict — throws on any parse error
$strictOpts = ParseOptions::document()->strict();
// Fragment mode: parse as children of a context (default context)
$fragOpts = ParseOptions::fragment();
// Fragment mode with a custom context element (e.g. inside a )
$tableFragOpts = ParseOptions::fragment('table');
$html = 'World & beyond
';
$fragHtml = '| Cell |
';
$docResult = $parser->parse($html, $docOpts);
$fragResult = $parser->parse($fragHtml, $tableFragOpts);
// Check quirks mode on full document
use Akankov\HtmlAst\Node\QuirksMode;
$quirks = $docResult->tree->quirksMode; // QuirksMode::NoQuirks | Quirks | LimitedQuirks
// Fragment root carries its context element name
echo $fragResult->tree->context; // "table"
```
```
--------------------------------
### Handling Parse Errors with Match
Source: https://context7.com/akankov/html-ast/llms.txt
Shows how to iterate through parse errors and use a match expression to handle different `ParseErrorKind` values. This snippet also demonstrates extracting the problematic byte range from the source.
```php
use Akankov\HtmlAst\Parser\Parser;
use Akankov\HtmlAst\Parser\ParseError;
use Akankov\HtmlAst\Parser\ParseErrorKind;
$parser = Parser::detect();
$result = $parser->parse(''); // malformed doctype
foreach ($result->errors as $err) {
match ($err->kind) {
ParseErrorKind::TokenizerError => print "Tokenizer: {$err->message}\n",
ParseErrorKind::TreeConstructionError => print "Tree: {$err->message}\n",
ParseErrorKind::UnexpectedEndOfInput => print "Unexpected EOF\n",
ParseErrorKind::Other => print "Other: {$err->message}\n",
};
// Slice the original source to show the bad bytes
$badBytes = substr($result->sourceMap->source, $err->range->start, $err->range->length());
echo " Bad input: " . var_export($badBytes, true) . "\n";
}
```
--------------------------------
### Access Token Stream for Trivia Preservation
Source: https://context7.com/akankov/html-ast/llms.txt
The TokenStream on ParseResult::$tokens provides the trivia layer discarded by \Dom\HTMLDocument. Concatenating Token::$raw reconstructs the original input for exact transformations.
```php
foreach ($parseResult->tokens() as $token) {
// Process $token->raw, $token->type, etc.
}
```
=== COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.