DumperVisitor
DumperVisitor is the abstract base shared by StreamDumperVisitor and StringDumperVisitor. It implements the whole traversal side of rendering — indentation tracking and node formatting — and leaves only the sink itself abstract:
abstract class DumperVisitor extends Visitor
{
public const string DEFAULT_SIMPLIFIED_NODE_NAMESPACE
= 'TypeLang\\Type\\';
public function __construct(
string $simplifyNodeNamespace
= self::DEFAULT_SIMPLIFIED_NODE_NAMESPACE,
) {}
abstract protected function write(string $data): void;
}
Each visited node is rendered as <indent><ShortClassName>[(value)], one line per node: the class name has $simplifyNodeNamespace stripped off its front, and if the node implements \Stringable, its string representation is appended in parentheses.
Writing a Custom Sink
Extend DumperVisitor and implement write() to render anywhere you like — into an array of lines, a PSR logger, a buffer shared with other output, etc:
use TypeLang\Parser\Traverser;
use TypeLang\Parser\Traverser\DumperVisitor;
final class LineCollectorDumperVisitor extends DumperVisitor
{
/** @var list<string> */
public array $lines = [];
protected function write(string $data): void
{
$this->lines[] = \rtrim($data, "\n");
}
}
$type = new TypeLang\Parser\TypeParser()
->parse('int|null');
$visitor = new LineCollectorDumperVisitor();
Traverser::new([$visitor])->traverse([$type]);
var_dump($visitor->lines);
array(5) {
[0]=> string(13) "UnionTypeNode"
[1]=> string(15) " NamedTypeNode"
[2]=> string(13) " Name(int)"
[3]=> string(21) " Identifier(int)"
[4]=> string(31) " Literal\NullLiteralNode(null)"
}
Disabling Namespace Simplification
Pass an empty string (or any prefix that won't match) as $simplifyNodeNamespace to render fully-qualified class names instead:
$visitor = new LineCollectorDumperVisitor(
simplifyNodeNamespace: '',
);
TypeLang\Type\UnionTypeNode
TypeLang\Type\NamedTypeNode
TypeLang\Type\Name(int)
TypeLang\Type\Identifier(int)
TypeLang\Type\Literal\NullLiteralNode(null)
05 July 2026