packages/ztd-query-postgres/src/Sql/Transaction/KeywordForm.php
1<?php
2
3declare(strict_types=1);
4
5namespace ZtdQuery\Platform\Postgres\Sql\Transaction;
6
7use ZtdQuery\Sql\SqlToken;
8use ZtdQuery\Sql\SqlTokenKind;
9
10/**
11 * Keyword form operations for PostgreSQL transaction.
12 *
13 * @visibility root
14 */
15final class KeywordForm
16{
17 /**
18 * @param list<SqlToken> $tokens
19 * @param list<list<string>> $forms
20 */
21 public function matchesAny(array $tokens, array $forms): bool
22 {
23 foreach ($forms as $form) {
24 if ($this->matches($tokens, $form)) {
25 return true;
26 }
27 }
28
29 return false;
30 }
31
32 /**
33 * @param list<SqlToken> $tokens
34 * @param list<list<string>> $prefixes
35 */
36 public function nameAfter(array $tokens, array $prefixes): ?string
37 {
38 foreach ($prefixes as $prefix) {
39 if (count($tokens) !== count($prefix) + 1 || !$this->matches(array_slice($tokens, 0, -1), $prefix)) {
40 continue;
41 }
42 $name = $tokens[count($prefix)];
43 if (!in_array($name->kind, [SqlTokenKind::Word, SqlTokenKind::QuotedIdentifier], true)) {
44 return null;
45 }
46
47 return $this->unquote($name->text);
48 }
49
50 return null;
51 }
52
53 /**
54 * @param list<SqlToken> $tokens
55 * @param list<string> $keywords
56 */
57 public function matches(array $tokens, array $keywords): bool
58 {
59 if (count($tokens) !== count($keywords)) {
60 return false;
61 }
62 foreach ($keywords as $index => $keyword) {
63 if (!$tokens[$index]->isKeyword($keyword)) {
64 return false;
65 }
66 }
67
68 return true;
69 }
70
71 /**
72 * Unquote.
73 */
74 public function unquote(string $identifier): ?string
75 {
76 $first = $identifier[0] ?? '';
77 if ($first === '`') {
78 return null;
79 }
80 if ($first !== '"') {
81 return $identifier;
82 }
83 if (($identifier[strlen($identifier) - 1] ?? '') !== '"') {
84 return null;
85 }
86
87 return str_replace('""', '"', substr($identifier, 1, -1));
88 }
89}
90