AST Access
PHP Toml provides full access to the Abstract Syntax Tree (AST) for advanced use cases like analysis, transformation, or editor integrations.
Parsing to AST
use PhpCollective\Toml\Toml;
$document = Toml::parse(<<<'TOML'
[server]
host = "localhost"
port = 8080
TOML);Document Structure
The AST is a tree of nodes:
Document
└── items: array<KeyValue|Table>
├── Table (header: [server])
│ └── items: array<KeyValue>
│ ├── KeyValue (key: host, value: StringValue)
│ └── KeyValue (key: port, value: IntegerValue)Node Types
Document
The root node containing all top-level items:
$document->items; // array<KeyValue|Table>Table
A table header ([name] or [[name]]):
$table->key; // Key - table name parts
$table->key->parts; // array<string> - ["server", "database"]
$table->isArrayTable; // bool - true for [[name]]
$table->items; // array<KeyValue> - key-value pairs in this tableKeyValue
A key-value pair:
$kv->key; // Key - the key
$kv->key->parts; // array<string> - ["a", "b"] for "a.b = value"
$kv->value; // Value - the value nodeKey
A key (bare, quoted, or dotted):
$key->parts; // array<string> - key parts
$key->styles; // array<KeyStyle> - style for each partKeyStyle enum:
Bare- unquoted keyBasic-"quoted"Literal-'quoted'
Value Types
All value nodes extend AbstractValue:
StringValue
$value->getValue(); // string
$value->style; // StringStyle enumStringStyle enum:
Basic-"string"Literal-'string'MultiLineBasic-"""string"""MultiLineLiteral-'''string'''
IntegerValue
$value->getValue(); // int
$value->base; // IntegerBase enumIntegerBase enum:
Decimal-42Hexadecimal-0xDEADOctal-0o755Binary-0b1010
FloatValue
$value->getValue(); // floatBoolValue
$value->getValue(); // boolDateTime Values
// OffsetDateTime - 1979-05-27T07:32:00Z
$value->getValue(); // DateTimeImmutable
// LocalDateTime - 1979-05-27T07:32:00
$value->getValue(); // string (no timezone info)
// LocalDate - 1979-05-27
$value->getValue(); // string
// LocalTime - 07:32:00
$value->getValue(); // stringArrayValue
$value->items; // array<Value>InlineTable
$value->items; // array<KeyValue>Position Information
Every node has position information via getSpan():
$span = $node->getSpan();
$span->line; // 1-based line number
$span->column; // 1-based column number
$span->start; // 0-based byte offset
$span->end; // End byte offsetTrivia Preservation
You can ask the parser to retain leading and trailing trivia for document items and table entries:
$document = Toml::parse($tomlString, true);
$item = $document->items[0];
$leading = $item->getLeadingTrivia();
$trailing = $item->getTrailingTrivia();Trivia currently includes:
- whitespace
- comments
- line endings attached to parsed items
Each Trivia object has:
$trivia->kind; // TriviaKind::Whitespace, Comment, or Newline
$trivia->value; // The raw text (e.g., "# my comment\n")
$trivia->span; // Source locationThis is useful for editor tooling and source-aware analysis. When you re-encode with Toml::encodeDocument(), preserved trivia is included in the output.
Arrays and inline tables can also retain collection-local trivia used for round-trip encoding:
$document = Toml::parse($tomlString, true);
$array = $document->items[0]->value;
$array->openingTrivia; // trivia after '[' when available
$array->closingTrivia; // trivia before ']' when available
$array->hasTrailingComma; // whether the parsed array ended with a trailing commaThat collection-local trivia is primarily intended for editor-style round-trip encoding rather than for semantic analysis.
Walking the AST
Simple Iteration
foreach ($document->items as $item) {
if ($item instanceof \PhpCollective\Toml\Ast\Table) {
echo "Table: " . implode('.', $item->key->parts) . "\n";
foreach ($item->items as $kv) {
echo " Key: " . implode('.', $kv->key->parts) . "\n";
}
} elseif ($item instanceof \PhpCollective\Toml\Ast\KeyValue) {
echo "Root key: " . implode('.', $item->key->parts) . "\n";
}
}Finding Specific Keys
function findKey(Document $doc, string $path): ?Value
{
$parts = explode('.', $path);
// ... traverse the AST
}Re-encoding
After modifying the AST, encode back to TOML:
use PhpCollective\Toml\Ast\Value\StringStyle;
use PhpCollective\Toml\Ast\Value\StringValue;
use PhpCollective\Toml\Lexer\Span;
// Modify a value
$document->items[0]->value = new StringValue('new value', StringStyle::Basic, new Span(0, 0, 1, 1));
// Re-encode with source-aware formatting
$toml = Toml::encodeDocument(
$document,
new EncoderOptions(documentFormatting: DocumentFormattingMode::SourceAware),
);WARNING
encodeDocument() normalizes by default. Use DocumentFormattingMode::SourceAware when you want preserved formatting for unchanged parsed regions and local fallback rules for edited ones.
For the exact round-trip and editing guarantees, see Compatibility.
Use Cases
Configuration Validation
$document = Toml::parse($config);
foreach ($document->items as $item) {
if ($item instanceof Table && $item->key->parts === ['database']) {
// Validate database section
foreach ($item->items as $kv) {
if ($kv->key->parts === ['port']) {
$port = $kv->value->getValue();
if ($port < 1 || $port > 65535) {
throw new Exception("Invalid port at line {$kv->getSpan()->line}");
}
}
}
}
}Editor Integration
// Get position for a key
$span = $keyValue->getSpan();
echo "Definition at line {$span->line}, column {$span->column}";
// Highlight value range
$valueSpan = $keyValue->value->getSpan();
$start = $valueSpan->start;
$end = $valueSpan->end;Schema Generation
function generateSchema(Document $doc): array
{
$schema = [];
foreach ($doc->items as $item) {
if ($item instanceof KeyValue) {
$schema[$item->key->parts[0]] = getType($item->value);
}
}
return $schema;
}