packages/ztd-query-postgres/src/Schema/Key/DefinitionTokens.php

1<?php
2
3declare(strict_types=1);
4
5namespace ZtdQuery\Platform\Postgres\Schema\Key;
6
7use ZtdQuery\Platform\Postgres\Sql\PgSqlLexerProfile;
8use ZtdQuery\Sql\SqlToken;
9use ZtdQuery\Sql\SqlTokenKind;
10use ZtdQuery\Sql\SqlTokenStream;
11
12/**
13 * Definition tokens operations for PostgreSQL foreignkey.
14 *
15 * @visibility root
16 */
17final class DefinitionTokens
18{
19    /**
20     * Table body.
21     */
22    public function tableBody(string $sql): ?string
23    {
24        $tokens = SqlTokenStream::tokenize($sql, PgSqlLexerProfile::create())->significantTokens();
25        $first = $tokens[0] ?? null;
26        if ($first === null || !$first->isKeyword('CREATE')) {
27            return null;
28        }
29
30        $tableFound = false;
31        $opening = null;
32        foreach ($tokens as $token) {
33            if (!$token->isTopLevel()) {
34                continue;
35            }
36            if (!$tableFound) {
37                if ($token->isKeyword('TABLE')) {
38                    $tableFound = true;
39                }
40                continue;
41            }
42            if ($opening === null) {
43                if (self::isSymbol($token, '(')) {
44                    $opening = $token;
45                }
46                continue;
47            }
48            if (self::isSymbol($token, ')')) {
49                return substr($sql, $opening->endOffset(), $token->offset - $opening->endOffset());
50            }
51        }
52
53        return null;
54    }
55
56    /**
57     * @param list<SqlToken> $tokens
58     */
59    public static function keywordIndex(array $tokens, string $keyword): ?int
60    {
61        foreach ($tokens as $index => $token) {
62            if ($token->isTopLevel() && $token->isKeyword($keyword)) {
63                return $index;
64            }
65        }
66
67        return null;
68    }
69
70    /**
71     * @param list<SqlToken> $tokens
72     */
73    public static function symbolIndex(array $tokens, string $symbol, int $start): ?int
74    {
75        foreach ($tokens as $index => $token) {
76            if ($index >= $start && $token->isTopLevel() && self::isSymbol($token, $symbol)) {
77                return $index;
78            }
79        }
80
81        return null;
82    }
83
84    /**
85     * Is symbol.
86     */
87    public static function isSymbol(?SqlToken $token, string $symbol): bool
88    {
89        if ($token === null) {
90            return false;
91        }
92
93        return $token->kind === SqlTokenKind::Symbol && $token->text === $symbol;
94    }
95}
96