ClassNameMatcherVisitor
To search for the first node matching a given class, use ClassNameMatcherVisitor — a thin MatcherVisitor specialization that matches by instanceof.
use TypeLang\Parser\Traverser;
use TypeLang\Parser\Traverser\ClassNameMatcherVisitor;
use TypeLang\Type\Name;
$type = new TypeLang\Parser\TypeParser()
->parse('array<array-key, mixed>');
$finder = Traverser::through(
visitor: new ClassNameMatcherVisitor(Name::class),
nodes: [$type],
);
var_dump($finder->isFound); // bool(true)
var_dump($finder->node);
object(TypeLang\Type\Name)#1 (3) {
["offset"]=> int(0)
["segments"]=> array(1) {
[0]=> object(TypeLang\Type\Identifier)#2 (2) {
["offset"]=> int(0)
["value"]=> string(5) "array"
}
}
["isFullyQualified"]=> bool(false)
}
Since it finds the first match in traversal order, searching for a more specific class than the root node's own returns the first nested occurrence. Here, looking for any NamedTypeNode inside a union returns the left operand:
use TypeLang\Type\NamedTypeNode;
$type = new TypeLang\Parser\TypeParser()
->parse('int|string');
$finder = Traverser::through(
visitor: new ClassNameMatcherVisitor(NamedTypeNode::class),
nodes: [$type],
);
// string(3) "int"
var_dump($finder->node->name->toString());
If nothing matches, $finder->node stays null and $finder->isFound is false:
use TypeLang\Type\Literal\BoolLiteralNode;
$finder = Traverser::through(
visitor: new ClassNameMatcherVisitor(BoolLiteralNode::class),
nodes: [$type],
);
var_dump($finder->isFound); // bool(false)
var_dump($finder->node); // NULL
05 July 2026