Automattic\WooCommerce\Vendor\GraphQL\Language

Visitor::visitpublic staticWC 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() 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;
}
}