Skip to content

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

php
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:

php
$document->items; // array<KeyValue|Table>

Table

A table header ([name] or [[name]]):

php
$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 table

KeyValue

A key-value pair:

php
$kv->key;        // Key - the key
$kv->key->parts; // array<string> - ["a", "b"] for "a.b = value"
$kv->value;      // Value - the value node

Key

A key (bare, quoted, or dotted):

php
$key->parts;   // array<string> - key parts
$key->styles;  // array<KeyStyle> - style for each part

KeyStyle enum:

  • Bare - unquoted key
  • Basic - "quoted"
  • Literal - 'quoted'

Value Types

All value nodes extend AbstractValue:

StringValue

php
$value->getValue();  // string
$value->style;       // StringStyle enum

StringStyle enum:

  • Basic - "string"
  • Literal - 'string'
  • MultiLineBasic - """string"""
  • MultiLineLiteral - '''string'''

IntegerValue

php
$value->getValue();  // int
$value->base;        // IntegerBase enum

IntegerBase enum:

  • Decimal - 42
  • Hexadecimal - 0xDEAD
  • Octal - 0o755
  • Binary - 0b1010

FloatValue

php
$value->getValue();  // float

BoolValue

php
$value->getValue();  // bool

DateTime Values

php
// 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();  // string

ArrayValue

php
$value->items;  // array<Value>

InlineTable

php
$value->items;  // array<KeyValue>

Position Information

Every node has position information via getSpan():

php
$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 offset

Trivia Preservation

You can ask the parser to retain leading and trailing trivia for document items and table entries:

php
$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:

php
$trivia->kind;   // TriviaKind::Whitespace, Comment, or Newline
$trivia->value;  // The raw text (e.g., "# my comment\n")
$trivia->span;   // Source location

This 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:

php
$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 comma

That collection-local trivia is primarily intended for editor-style round-trip encoding rather than for semantic analysis.

Walking the AST

Simple Iteration

php
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

php
function findKey(Document $doc, string $path): ?Value
{
    $parts = explode('.', $path);
    // ... traverse the AST
}

Re-encoding

After modifying the AST, encode back to TOML:

php
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

php
$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

php
// 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

php
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;
}

Released under the MIT License.