From f1dc58df723e6a4a94a05d0d61694c9f1acdde01 Mon Sep 17 00:00:00 2001 From: Kamil Tekiela Date: Tue, 16 Jan 2024 18:23:54 +0100 Subject: [PATCH 1/2] Split up Parser::KEYWORD_PARSERS Signed-off-by: Kamil Tekiela --- phpstan-baseline.neon | 10 ---------- psalm-baseline.xml | 6 ------ src/Parser.php | 5 ----- src/Statement.php | 4 ++-- src/Utils/Formatter.php | 11 ++++++++++- 5 files changed, 12 insertions(+), 24 deletions(-) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index fa8ea0c1..c21b19e8 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -540,16 +540,6 @@ parameters: count: 1 path: src/Statement.php - - - message: "#^Offset 'class' does not exist on array\\{\\}\\|array\\{class\\: 'PhpMyAdmin\\\\\\\\SqlParser\\\\\\\\Components\\\\\\\\Array2d', field\\: 'values'\\}\\|array\\{class\\: 'PhpMyAdmin\\\\\\\\SqlParser\\\\\\\\Components\\\\\\\\ArrayObj', field\\: 'partition'\\}\\|array\\{class\\: 'PhpMyAdmin\\\\\\\\SqlParser\\\\\\\\Components\\\\\\\\Condition', field\\: 'having'\\|'where'\\}\\|array\\{class\\: 'PhpMyAdmin\\\\\\\\SqlParser\\\\\\\\Components\\\\\\\\Expression', field\\: 'table', options\\: array\\{parseField\\: 'table'\\}\\}\\|array\\{class\\: 'PhpMyAdmin\\\\\\\\SqlParser\\\\\\\\Components\\\\\\\\ExpressionArray', field\\: 'expr'\\}\\|array\\{class\\: 'PhpMyAdmin\\\\\\\\SqlParser\\\\\\\\Components\\\\\\\\ExpressionArray', field\\: 'fields'\\|'tables', options\\: array\\{parseField\\: 'table'\\}\\}\\|array\\{class\\: 'PhpMyAdmin\\\\\\\\SqlParser\\\\\\\\Components\\\\\\\\ExpressionArray', field\\: 'from', options\\: array\\{field\\: 'table'\\}\\}\\|array\\{class\\: 'PhpMyAdmin\\\\\\\\SqlParser\\\\\\\\Components\\\\\\\\FunctionCall', field\\: 'call'\\|'procedure'\\}\\|array\\{class\\: 'PhpMyAdmin\\\\\\\\SqlParser\\\\\\\\Components\\\\\\\\GroupKeyword', field\\: 'group'\\}\\|array\\{class\\: 'PhpMyAdmin\\\\\\\\SqlParser\\\\\\\\Components\\\\\\\\IndexHint', field\\: 'index_hints'\\}\\|array\\{class\\: 'PhpMyAdmin\\\\\\\\SqlParser\\\\\\\\Components\\\\\\\\IntoKeyword', field\\: 'into'\\}\\|array\\{class\\: 'PhpMyAdmin\\\\\\\\SqlParser\\\\\\\\Components\\\\\\\\JoinKeyword', field\\: 'join'\\}\\|array\\{class\\: 'PhpMyAdmin\\\\\\\\SqlParser\\\\\\\\Components\\\\\\\\Limit', field\\: 'limit'\\}\\|array\\{class\\: 'PhpMyAdmin\\\\\\\\SqlParser\\\\\\\\Components\\\\\\\\OptionsArray', field\\: 'endOptions'\\|'groupOptions'\\|'options'\\}\\|array\\{class\\: 'PhpMyAdmin\\\\\\\\SqlParser\\\\\\\\Components\\\\\\\\OrderKeyword', field\\: 'order'\\}\\|array\\{class\\: 'PhpMyAdmin\\\\\\\\SqlParser\\\\\\\\Components\\\\\\\\RenameOperation', field\\: 'renames'\\}\\|array\\{class\\: 'PhpMyAdmin\\\\\\\\SqlParser\\\\\\\\Components\\\\\\\\SetOperation', field\\: 'set'\\}\\|array\\{class\\: 'PhpMyAdmin\\\\\\\\SqlParser\\\\\\\\Components\\\\\\\\UnionKeyword', field\\: 'union'\\}\\.$#" - count: 1 - path: src/Statement.php - - - - message: "#^Offset 'field' does not exist on array\\{\\}\\|array\\{class\\: 'PhpMyAdmin\\\\\\\\SqlParser\\\\\\\\Components\\\\\\\\Array2d', field\\: 'values'\\}\\|array\\{class\\: 'PhpMyAdmin\\\\\\\\SqlParser\\\\\\\\Components\\\\\\\\ArrayObj', field\\: 'partition'\\}\\|array\\{class\\: 'PhpMyAdmin\\\\\\\\SqlParser\\\\\\\\Components\\\\\\\\Condition', field\\: 'having'\\|'where'\\}\\|array\\{class\\: 'PhpMyAdmin\\\\\\\\SqlParser\\\\\\\\Components\\\\\\\\Expression', field\\: 'table', options\\: array\\{parseField\\: 'table'\\}\\}\\|array\\{class\\: 'PhpMyAdmin\\\\\\\\SqlParser\\\\\\\\Components\\\\\\\\ExpressionArray', field\\: 'expr'\\}\\|array\\{class\\: 'PhpMyAdmin\\\\\\\\SqlParser\\\\\\\\Components\\\\\\\\ExpressionArray', field\\: 'fields'\\|'tables', options\\: array\\{parseField\\: 'table'\\}\\}\\|array\\{class\\: 'PhpMyAdmin\\\\\\\\SqlParser\\\\\\\\Components\\\\\\\\ExpressionArray', field\\: 'from', options\\: array\\{field\\: 'table'\\}\\}\\|array\\{class\\: 'PhpMyAdmin\\\\\\\\SqlParser\\\\\\\\Components\\\\\\\\FunctionCall', field\\: 'call'\\|'procedure'\\}\\|array\\{class\\: 'PhpMyAdmin\\\\\\\\SqlParser\\\\\\\\Components\\\\\\\\GroupKeyword', field\\: 'group'\\}\\|array\\{class\\: 'PhpMyAdmin\\\\\\\\SqlParser\\\\\\\\Components\\\\\\\\IndexHint', field\\: 'index_hints'\\}\\|array\\{class\\: 'PhpMyAdmin\\\\\\\\SqlParser\\\\\\\\Components\\\\\\\\IntoKeyword', field\\: 'into'\\}\\|array\\{class\\: 'PhpMyAdmin\\\\\\\\SqlParser\\\\\\\\Components\\\\\\\\JoinKeyword', field\\: 'join'\\}\\|array\\{class\\: 'PhpMyAdmin\\\\\\\\SqlParser\\\\\\\\Components\\\\\\\\Limit', field\\: 'limit'\\}\\|array\\{class\\: 'PhpMyAdmin\\\\\\\\SqlParser\\\\\\\\Components\\\\\\\\OptionsArray', field\\: 'endOptions'\\|'groupOptions'\\|'options'\\}\\|array\\{class\\: 'PhpMyAdmin\\\\\\\\SqlParser\\\\\\\\Components\\\\\\\\OrderKeyword', field\\: 'order'\\}\\|array\\{class\\: 'PhpMyAdmin\\\\\\\\SqlParser\\\\\\\\Components\\\\\\\\RenameOperation', field\\: 'renames'\\}\\|array\\{class\\: 'PhpMyAdmin\\\\\\\\SqlParser\\\\\\\\Components\\\\\\\\SetOperation', field\\: 'set'\\}\\|array\\{class\\: 'PhpMyAdmin\\\\\\\\SqlParser\\\\\\\\Components\\\\\\\\UnionKeyword', field\\: 'union'\\}\\.$#" - count: 1 - path: src/Statement.php - - message: "#^Property PhpMyAdmin\\\\SqlParser\\\\Statement\\:\\:\\$options \\(PhpMyAdmin\\\\SqlParser\\\\Components\\\\OptionsArray\\|null\\) does not accept mixed\\.$#" count: 1 diff --git a/psalm-baseline.xml b/psalm-baseline.xml index 34d6b381..1c158853 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -797,12 +797,6 @@ $field->build()]]> - - - - - - bool diff --git a/src/Parser.php b/src/Parser.php index 7212a581..a95816e9 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -115,11 +115,6 @@ class Parser * Array of classes that are used in parsing SQL components. */ public const KEYWORD_PARSERS = [ - // This is not a proper keyword and was added here to help the - // formatter. - 'PARTITION BY' => [], - 'SUBPARTITION BY' => [], - // This is not a proper keyword and was added here to help the // builder. '_OPTIONS' => [ diff --git a/src/Statement.php b/src/Statement.php index 4aa069b2..88266005 100644 --- a/src/Statement.php +++ b/src/Statement.php @@ -286,7 +286,7 @@ public function parse(Parser $parser, TokensList $list): void $options = []; // Looking for duplicated clauses. - if (! empty(Parser::KEYWORD_PARSERS[$token->value]) || ! empty(Parser::STATEMENT_PARSERS[$token->value])) { + if (isset(Parser::KEYWORD_PARSERS[$token->value]) || ! empty(Parser::STATEMENT_PARSERS[$token->value])) { if (! empty($parsedClauses[$token->value])) { $parser->error('This type of clause was previously parsed.', $token); break; @@ -300,7 +300,7 @@ public function parse(Parser $parser, TokensList $list): void // but it might be the beginning of a statement of truncate, // so let the value use the keyword field for truncate type. $tokenValue = in_array($token->keyword, ['TRUNCATE']) ? $token->keyword : $token->value; - if (! empty(Parser::KEYWORD_PARSERS[$tokenValue]) && $list->idx < $list->count) { + if (isset(Parser::KEYWORD_PARSERS[$tokenValue]) && $list->idx < $list->count) { $class = Parser::KEYWORD_PARSERS[$tokenValue]['class']; $field = Parser::KEYWORD_PARSERS[$tokenValue]['field']; if (! empty(Parser::KEYWORD_PARSERS[$tokenValue]['options'])) { diff --git a/src/Utils/Formatter.php b/src/Utils/Formatter.php index fc8837b2..67214f2b 100644 --- a/src/Utils/Formatter.php +++ b/src/Utils/Formatter.php @@ -78,6 +78,11 @@ class Formatter 'VALUES' => true, ]; + private const FORMATTERS = [ + 'PARTITION BY', + 'SUBPARTITION BY', + ]; + /** @param array>> $options the formatting options */ public function __construct(array $options = []) { @@ -747,7 +752,11 @@ public static function isClause(Token $token): int|false return 2; } - if ($token->type === TokenType::Keyword && isset(Parser::KEYWORD_PARSERS[$token->keyword])) { + if ( + $token->type === TokenType::Keyword && ( + in_array($token->keyword, self::FORMATTERS, true) || isset(Parser::KEYWORD_PARSERS[$token->keyword]) + ) + ) { return 1; } From fbaf8318b7066c6f16e86a83a379743820343182 Mon Sep 17 00:00:00 2001 From: Kamil Tekiela Date: Tue, 16 Jan 2024 18:00:16 +0100 Subject: [PATCH 2/2] Improve documentation of getClauses() Signed-off-by: Kamil Tekiela --- src/Statement.php | 47 ++++-------------- src/Statements/DeleteStatement.php | 19 ++++---- src/Statements/DropStatement.php | 11 ++--- src/Statements/SelectStatement.php | 78 +++++++++++++++--------------- src/Statements/SetStatement.php | 7 ++- src/Statements/UpdateStatement.php | 23 +++++---- src/Statements/WithStatement.php | 9 ++-- 7 files changed, 80 insertions(+), 114 deletions(-) diff --git a/src/Statement.php b/src/Statement.php index 88266005..407bab9b 100644 --- a/src/Statement.php +++ b/src/Statement.php @@ -48,18 +48,13 @@ abstract class Statement implements Stringable */ public static array $statementOptions = []; + protected const ADD_CLAUSE = 1; + protected const ADD_KEYWORD = 2; + /** * The clauses of this statement, in order. * - * The value attributed to each clause is used by the builder and it may - * have one of the following values: - * - * - 1 = 01 - add the clause only - * - 2 = 10 - add the keyword - * - 3 = 11 - add both the keyword and the clause - * - * @var array> - * @psalm-var array + * @var array}> */ public static array $clauses = []; @@ -115,29 +110,7 @@ public function build(): string */ $built = []; - /** - * Statement's clauses. - */ - $clauses = $this->getClauses(); - - foreach ($clauses as $clause) { - /** - * The name of the clause. - */ - $name = $clause[0]; - - /** - * The type of the clause. - * - * @see Statement::$clauses - */ - $type = $clause[1]; - - /** - * The builder (parser) of this clause. - */ - $class = Parser::KEYWORD_PARSERS[$name]['class']; - + foreach ($this->getClauses() as [$name, $type]) { /** * The name of the field that is used as source for the builder. * Same field is used to store the result of parsing. @@ -150,7 +123,7 @@ public function build(): string } // Checking if this field was already built. - if ($type & 1) { + if ($type & self::ADD_CLAUSE) { if (! empty($built[$field])) { continue; } @@ -159,16 +132,17 @@ public function build(): string } // Checking if the name of the clause should be added. - if ($type & 2) { + if ($type & self::ADD_KEYWORD) { $query = trim($query) . ' ' . $name; } // Checking if the result of the builder should be added. - if (! ($type & 1)) { + if (! ($type & self::ADD_CLAUSE)) { continue; } if (is_array($this->$field)) { + $class = Parser::KEYWORD_PARSERS[$name]['class']; $query = trim($query) . ' ' . $class::buildAll($this->$field); } else { $query = trim($query) . ' ' . $this->$field->build(); @@ -419,8 +393,7 @@ public function after(Parser $parser, TokensList $list, Token $token): void /** * Gets the clauses of this statement. * - * @return array> - * @psalm-return array + * @return array}> */ public function getClauses(): array { diff --git a/src/Statements/DeleteStatement.php b/src/Statements/DeleteStatement.php index 9cc99555..dee3bd79 100644 --- a/src/Statements/DeleteStatement.php +++ b/src/Statements/DeleteStatement.php @@ -62,42 +62,41 @@ class DeleteStatement extends Statement * * @see Statement::$clauses * - * @var array> - * @psalm-var array + * @var array}> */ public static array $clauses = [ 'DELETE' => [ 'DELETE', - 2, + Statement::ADD_KEYWORD, ], // Used for options. '_OPTIONS' => [ '_OPTIONS', - 1, + Statement::ADD_CLAUSE, ], 'FROM' => [ 'FROM', - 3, + Statement::ADD_CLAUSE | Statement::ADD_KEYWORD, ], 'PARTITION' => [ 'PARTITION', - 3, + Statement::ADD_CLAUSE | Statement::ADD_KEYWORD, ], 'USING' => [ 'USING', - 3, + Statement::ADD_CLAUSE | Statement::ADD_KEYWORD, ], 'WHERE' => [ 'WHERE', - 3, + Statement::ADD_CLAUSE | Statement::ADD_KEYWORD, ], 'ORDER BY' => [ 'ORDER BY', - 3, + Statement::ADD_CLAUSE | Statement::ADD_KEYWORD, ], 'LIMIT' => [ 'LIMIT', - 3, + Statement::ADD_CLAUSE | Statement::ADD_KEYWORD, ], ]; diff --git a/src/Statements/DropStatement.php b/src/Statements/DropStatement.php index bd42b9cc..26b6123f 100644 --- a/src/Statements/DropStatement.php +++ b/src/Statements/DropStatement.php @@ -42,27 +42,26 @@ class DropStatement extends Statement * * @see Statement::$clauses * - * @var array> - * @psalm-var array + * @var array}> */ public static array $clauses = [ 'DROP' => [ 'DROP', - 2, + Statement::ADD_KEYWORD, ], // Used for options. '_OPTIONS' => [ '_OPTIONS', - 1, + Statement::ADD_CLAUSE, ], // Used for select expressions. 'DROP_' => [ 'DROP', - 1, + Statement::ADD_CLAUSE, ], 'ON' => [ 'ON', - 3, + Statement::ADD_CLAUSE | Statement::ADD_KEYWORD, ], ]; diff --git a/src/Statements/SelectStatement.php b/src/Statements/SelectStatement.php index e32db3e0..c2b7cefb 100644 --- a/src/Statements/SelectStatement.php +++ b/src/Statements/SelectStatement.php @@ -83,144 +83,143 @@ class SelectStatement extends Statement * * @see Statement::$clauses * - * @var array> - * @psalm-var array + * @var array}> */ public static array $clauses = [ 'SELECT' => [ 'SELECT', - 2, + Statement::ADD_KEYWORD, ], // Used for options. '_OPTIONS' => [ '_OPTIONS', - 1, + Statement::ADD_CLAUSE, ], // Used for selected expressions. '_SELECT' => [ 'SELECT', - 1, + Statement::ADD_CLAUSE, ], 'INTO' => [ 'INTO', - 3, + Statement::ADD_CLAUSE | Statement::ADD_KEYWORD, ], 'FROM' => [ 'FROM', - 3, + Statement::ADD_CLAUSE | Statement::ADD_KEYWORD, ], 'FORCE' => [ 'FORCE', - 1, + Statement::ADD_CLAUSE, ], 'USE' => [ 'USE', - 1, + Statement::ADD_CLAUSE, ], 'IGNORE' => [ 'IGNORE', - 3, + Statement::ADD_CLAUSE | Statement::ADD_KEYWORD, ], 'PARTITION' => [ 'PARTITION', - 3, + Statement::ADD_CLAUSE | Statement::ADD_KEYWORD, ], 'JOIN' => [ 'JOIN', - 1, + Statement::ADD_CLAUSE, ], 'FULL JOIN' => [ 'FULL JOIN', - 1, + Statement::ADD_CLAUSE, ], 'INNER JOIN' => [ 'INNER JOIN', - 1, + Statement::ADD_CLAUSE, ], 'LEFT JOIN' => [ 'LEFT JOIN', - 1, + Statement::ADD_CLAUSE, ], 'LEFT OUTER JOIN' => [ 'LEFT OUTER JOIN', - 1, + Statement::ADD_CLAUSE, ], 'RIGHT JOIN' => [ 'RIGHT JOIN', - 1, + Statement::ADD_CLAUSE, ], 'RIGHT OUTER JOIN' => [ 'RIGHT OUTER JOIN', - 1, + Statement::ADD_CLAUSE, ], 'NATURAL JOIN' => [ 'NATURAL JOIN', - 1, + Statement::ADD_CLAUSE, ], 'NATURAL LEFT JOIN' => [ 'NATURAL LEFT JOIN', - 1, + Statement::ADD_CLAUSE, ], 'NATURAL RIGHT JOIN' => [ 'NATURAL RIGHT JOIN', - 1, + Statement::ADD_CLAUSE, ], 'NATURAL LEFT OUTER JOIN' => [ 'NATURAL LEFT OUTER JOIN', - 1, + Statement::ADD_CLAUSE, ], 'NATURAL RIGHT OUTER JOIN' => [ 'NATURAL RIGHT JOIN', - 1, + Statement::ADD_CLAUSE, ], 'WHERE' => [ 'WHERE', - 3, + Statement::ADD_CLAUSE | Statement::ADD_KEYWORD, ], 'GROUP BY' => [ 'GROUP BY', - 3, + Statement::ADD_CLAUSE | Statement::ADD_KEYWORD, ], '_GROUP_OPTIONS' => [ '_GROUP_OPTIONS', - 1, + Statement::ADD_CLAUSE, ], 'HAVING' => [ 'HAVING', - 3, + Statement::ADD_CLAUSE | Statement::ADD_KEYWORD, ], 'ORDER BY' => [ 'ORDER BY', - 3, + Statement::ADD_CLAUSE | Statement::ADD_KEYWORD, ], 'LIMIT' => [ 'LIMIT', - 3, + Statement::ADD_CLAUSE | Statement::ADD_KEYWORD, ], 'PROCEDURE' => [ 'PROCEDURE', - 3, + Statement::ADD_CLAUSE | Statement::ADD_KEYWORD, ], 'UNION' => [ 'UNION', - 1, + Statement::ADD_CLAUSE, ], 'EXCEPT' => [ 'EXCEPT', - 1, + Statement::ADD_CLAUSE, ], 'INTERSECT' => [ 'INTERSECT', - 1, + Statement::ADD_CLAUSE, ], '_END_OPTIONS' => [ '_END_OPTIONS', - 1, + Statement::ADD_CLAUSE, ], // These are available only when `UNION` is present. - // 'ORDER BY' => ['ORDER BY', 3], - // 'LIMIT' => ['LIMIT', 3], + // 'ORDER BY' => ['ORDER BY', Statement::ADD_CLAUSE|Statement::ADD_KEYWORD], + // 'LIMIT' => ['LIMIT', Statement::ADD_CLAUSE|Statement::ADD_KEYWORD], ]; /** @@ -321,8 +320,7 @@ class SelectStatement extends Statement /** * Gets the clauses of this statement. * - * @return array> - * @psalm-return array + * @return array}> */ public function getClauses(): array { @@ -334,11 +332,11 @@ public function getClauses(): array unset($clauses['ORDER BY'], $clauses['LIMIT']); $clauses['ORDER BY'] = [ 'ORDER BY', - 3, + Statement::ADD_CLAUSE | Statement::ADD_KEYWORD, ]; $clauses['LIMIT'] = [ 'LIMIT', - 3, + Statement::ADD_CLAUSE | Statement::ADD_KEYWORD, ]; return $clauses; diff --git a/src/Statements/SetStatement.php b/src/Statements/SetStatement.php index 94d7c948..4a7c12a3 100644 --- a/src/Statements/SetStatement.php +++ b/src/Statements/SetStatement.php @@ -20,17 +20,16 @@ class SetStatement extends Statement * * @see Statement::$clauses * - * @var array> - * @psalm-var array + * @var array}> */ public static array $clauses = [ 'SET' => [ 'SET', - 3, + Statement::ADD_CLAUSE | Statement::ADD_KEYWORD, ], '_END_OPTIONS' => [ '_END_OPTIONS', - 1, + Statement::ADD_CLAUSE, ], ]; diff --git a/src/Statements/UpdateStatement.php b/src/Statements/UpdateStatement.php index 91d9a4f7..ee6ae17a 100644 --- a/src/Statements/UpdateStatement.php +++ b/src/Statements/UpdateStatement.php @@ -46,51 +46,50 @@ class UpdateStatement extends Statement * * @see Statement::$clauses * - * @var array> - * @psalm-var array + * @var array}> */ public static array $clauses = [ 'UPDATE' => [ 'UPDATE', - 2, + Statement::ADD_KEYWORD, ], // Used for options. '_OPTIONS' => [ '_OPTIONS', - 1, + Statement::ADD_CLAUSE, ], // Used for updated tables. '_UPDATE' => [ 'UPDATE', - 1, + Statement::ADD_CLAUSE, ], 'JOIN' => [ 'JOIN', - 1, + Statement::ADD_CLAUSE, ], 'LEFT JOIN' => [ 'LEFT JOIN', - 1, + Statement::ADD_CLAUSE, ], 'INNER JOIN' => [ 'INNER JOIN', - 1, + Statement::ADD_CLAUSE, ], 'SET' => [ 'SET', - 3, + Statement::ADD_CLAUSE | Statement::ADD_KEYWORD, ], 'WHERE' => [ 'WHERE', - 3, + Statement::ADD_CLAUSE | Statement::ADD_KEYWORD, ], 'ORDER BY' => [ 'ORDER BY', - 3, + Statement::ADD_CLAUSE | Statement::ADD_KEYWORD, ], 'LIMIT' => [ 'LIMIT', - 3, + Statement::ADD_CLAUSE | Statement::ADD_KEYWORD, ], ]; diff --git a/src/Statements/WithStatement.php b/src/Statements/WithStatement.php index 6aee3de3..307e371d 100644 --- a/src/Statements/WithStatement.php +++ b/src/Statements/WithStatement.php @@ -37,22 +37,21 @@ final class WithStatement extends Statement * * @see Statement::$clauses * - * @var array> - * @psalm-var array + * @var array}> */ public static array $clauses = [ 'WITH' => [ 'WITH', - 2, + Statement::ADD_KEYWORD, ], // Used for options. '_OPTIONS' => [ '_OPTIONS', - 1, + Statement::ADD_CLAUSE, ], 'AS' => [ 'AS', - 2, + Statement::ADD_KEYWORD, ], ];