Automattic\WooCommerce\Vendor\GraphQL\Language
Visitor::visit │ public static │ WC 1.0
Visit the AST (see class description for details).
Method of the class: Visitor{}
No Hooks.
Returns
Mixed.
Usage
$result = Visitor::visit( $root, $visitor, ?array $keyMap );
- $root(NodeList
|Node) (required) - .
- $visitor(VisitorArray) (required)
- .
- ?array $keyMap
- .
Default:null
Visitor::visit() Visitor::visit code WC 10.9.1
public static function visit(object $root, array $visitor, ?array $keyMap = null)
{
$visitorKeys = $keyMap ?? self::VISITOR_KEYS;
/**
* @var list<array{
* inList: bool,
* index: int,
* keys: Node|NodeList|mixed,
* edits: array<int, array{mixed, mixed}>,
* }> $stack */
$stack = [];
$inList = $root instanceof NodeList;
$keys = [$root];
$index = -1;
$edits = [];
$parent = null;
$path = [];
$ancestors = [];
do {
++$index;
$isLeaving = $index === count($keys);
$key = null;
$node = null;
$isEdited = $isLeaving && $edits !== [];
if ($isLeaving) {
$key = $ancestors === []
? null
: $path[count($path) - 1];
$node = $parent;
$parent = array_pop($ancestors);
if ($isEdited) {
if ($node instanceof Node || $node instanceof NodeList) {
$node = $node->cloneDeep();
}
$editOffset = 0;
foreach ($edits as [$editKey, $editValue]) {
if ($inList) {
$editKey -= $editOffset;
}
if ($inList && $editValue === null) {
assert($node instanceof NodeList, 'Follows from $inList');
$node->splice($editKey, 1);
++$editOffset;
} elseif ($node instanceof NodeList) {
if ($editValue instanceof NodeList) {
$node->splice($editKey, 1, $editValue);
$editOffset -= count($editValue) - 1;
} elseif ($editValue instanceof Node) {
$node[$editKey] = $editValue;
} else {
$notNodeOrNodeList = Utils::printSafe($editValue);
throw new \Exception("Can only add Node or NodeList to NodeList, got: {$notNodeOrNodeList}.");
}
} else {
$node->{$editKey} = $editValue;
}
}
}
// @phpstan-ignore-next-line the stack is guaranteed to be non-empty at this point
[
'index' => $index,
'keys' => $keys,
'edits' => $edits,
'inList' => $inList,
] = array_pop($stack);
} elseif ($parent === null) {
$node = $root;
} else {
$key = $inList
? $index
: $keys[$index];
$node = $parent instanceof NodeList
? $parent[$key]
: $parent->{$key};
if ($node === null) {
continue;
}
$path[] = $key;
}
$result = null;
if (! $node instanceof NodeList) {
if (! $node instanceof Node) {
$notNode = Utils::printSafe($node);
throw new \Exception("Invalid AST Node: {$notNode}.");
}
$visitFn = self::extractVisitFn($visitor, $node->kind, $isLeaving);
if ($visitFn !== null) {
$result = $visitFn($node, $key, $parent, $path, $ancestors);
if ($result !== null) {
if ($result instanceof VisitorStop) {
break;
}
if ($result instanceof VisitorSkipNode) {
if (! $isLeaving) {
array_pop($path);
}
continue;
}
$editValue = $result instanceof VisitorRemoveNode
? null
: $result;
$edits[] = [$key, $editValue];
if (! $isLeaving) {
if (! $editValue instanceof Node) {
array_pop($path);
continue;
}
$node = $editValue;
}
}
}
}
if ($result === null && $isEdited) {
$edits[] = [$key, $node];
}
if ($isLeaving) {
array_pop($path);
} else {
$stack[] = [
'inList' => $inList,
'index' => $index,
'keys' => $keys,
'edits' => $edits,
];
$inList = $node instanceof NodeList;
$keys = ($inList ? $node : $visitorKeys[$node->kind]) ?? [];
$index = -1;
$edits = [];
if ($parent !== null) {
$ancestors[] = $parent;
}
$parent = $node;
}
} while ($stack !== []);
return $edits === []
? $root
: $edits[0][1];
}
/**
* Returns marker for stopping.
*
* @api
*/
public static function stop(): VisitorStop
{
static $stop;
return $stop ??= new VisitorStop();
}
/**
* Returns marker for skipping the subtree at the current node.
*
* @api
*/
public static function skipNode(): VisitorSkipNode
{
static $skipNode;
return $skipNode ??= new VisitorSkipNode();
}
/**
* Returns marker for removing the current node.
*
* @api
*/
public static function removeNode(): VisitorRemoveNode
{
static $removeNode;
return $removeNode ??= new VisitorRemoveNode();
}
/**
* Combines the given visitors to run in parallel.
*
* @phpstan-param array<int, VisitorArray> $visitors
*
* @return VisitorArray
*/
public static function visitInParallel(array $visitors): array
{
$visitorsCount = count($visitors);
$skipping = new \SplFixedArray($visitorsCount);
return [
'enter' => static function (Node $node) use ($visitors, $skipping, $visitorsCount) {
for ($i = 0; $i < $visitorsCount; ++$i) {
if ($skipping[$i] !== null) {
continue;
}
$fn = self::extractVisitFn(
$visitors[$i],
$node->kind,
false
);
if ($fn === null) {
continue;
}
$result = $fn(...func_get_args());
if ($result === null) {
continue;
}
if ($result instanceof VisitorSkipNode) {
$skipping[$i] = $node;
} elseif ($result instanceof VisitorStop) {
$skipping[$i] = $result;
} else {
return $result;
}
}
return null;
},
'leave' => static function (Node $node) use ($visitors, $skipping, $visitorsCount) {
for ($i = 0; $i < $visitorsCount; ++$i) {
if ($skipping[$i] === null) {
$fn = self::extractVisitFn(
$visitors[$i],
$node->kind,
true
);
if ($fn !== null) {
$result = $fn(...func_get_args());
if ($result === null) {
continue;
}
if ($result instanceof VisitorStop) {
$skipping[$i] = $result;
} elseif ($result instanceof VisitorRemoveNode) {
return $result;
} else {
return $result;
}
}
} elseif ($skipping[$i] === $node) {
$skipping[$i] = null;
}
}
return null;
},
];
}
/**
* Creates a new visitor that updates TypeInfo and delegates to the given visitor.
*
* @phpstan-param VisitorArray $visitor
*
* @phpstan-return VisitorArray
*/
public static function visitWithTypeInfo(TypeInfo $typeInfo, array $visitor): array
{
return [
'enter' => static function (Node $node) use ($typeInfo, $visitor) {
$typeInfo->enter($node);
$fn = self::extractVisitFn($visitor, $node->kind, false);
if ($fn === null) {
return null;
}
$result = $fn(...func_get_args());
if ($result === null) {
return null;
}
$typeInfo->leave($node);
if ($result instanceof Node) {
$typeInfo->enter($result);
}
return $result;
},
'leave' => static function (Node $node) use ($typeInfo, $visitor) {
$fn = self::extractVisitFn($visitor, $node->kind, true);
$result = $fn !== null
? $fn(...func_get_args())
: null;
$typeInfo->leave($node);
return $result;
},
];
}
/**
* @phpstan-param VisitorArray $visitor
*
* @return (callable(Node $node, string|int|null $key, Node|NodeList<Node>|null $parent, array<int, int|string> $path, array<int, Node|NodeList<Node>> $ancestors): (VisitorOperation|Node|null))|(callable(Node): (VisitorOperation|Node|NodeList<Node>|void|false|null))|null
*/
protected static function extractVisitFn(array $visitor, string $kind, bool $isLeaving): ?callable
{
$kindVisitor = $visitor[$kind] ?? null;
if ($kindVisitor !== null) {
if (is_array($kindVisitor)) {
return $isLeaving
? $kindVisitor['leave'] ?? null
: $kindVisitor['enter'] ?? null;
}
if (! $isLeaving) {
return $kindVisitor;
}
}
$specificVisitor = $isLeaving
? $visitor['leave'] ?? null
: $visitor['enter'] ?? null;
if ($specificVisitor !== null && is_array($specificVisitor)) {
return $specificVisitor[$kind] ?? null;
}
return $specificVisitor;
}
}