Issues (119)

src/Parsers/SetOperations.php (1 issue)

Severity
1
<?php
2
3
declare(strict_types=1);
4
5
namespace PhpMyAdmin\SqlParser\Parsers;
6
7
use PhpMyAdmin\SqlParser\Components\SetOperation;
8
use PhpMyAdmin\SqlParser\Parseable;
9
use PhpMyAdmin\SqlParser\Parser;
10
use PhpMyAdmin\SqlParser\Token;
11
use PhpMyAdmin\SqlParser\TokensList;
12
use PhpMyAdmin\SqlParser\TokenType;
13
14
use function implode;
15
use function in_array;
16
use function trim;
17
18
/**
19
 * `SET` keyword parser.
20
 */
21
final class SetOperations implements Parseable
22
{
23
    /**
24
     * @param Parser               $parser  the parser that serves as context
25
     * @param TokensList           $list    the list of tokens that are being parsed
26
     * @param array<string, mixed> $options parameters for parsing
27
     *
28
     * @return SetOperation[]
29
     */
30 120
    public static function parse(Parser $parser, TokensList $list, array $options = []): array
31
    {
32 120
        $ret = [];
33
34 120
        $expr = new SetOperation();
35
36
        /**
37
         * The state of the parser.
38
         *
39
         * Below are the states of the parser.
40
         *
41
         *      0 ---------------------[ col_name ]--------------------> 0
42
         *      0 ---------------------[ = or := ]---------------------> 1
43
         *      1 -----------------------[ value ]---------------------> 1
44
         *      1 ------------------------[ , ]------------------------> 0
45
         */
46 120
        $state = 0;
47
48
        /**
49
         * Token when the parser has seen the latest comma
50
         */
51 120
        $commaLastSeenAt = null;
52
53 120
        for (; $list->idx < $list->count; ++$list->idx) {
54
            /**
55
             * Token parsed at this moment.
56
             */
57 120
            $token = $list->tokens[$list->idx];
58
59
            // End of statement.
60 120
            if ($token->type === TokenType::Delimiter) {
61 90
                break;
62
            }
63
64
            // Skipping whitespaces and comments.
65 100
            if (($token->type === TokenType::Whitespace) || ($token->type === TokenType::Comment)) {
66 90
                continue;
67
            }
68
69
            // No keyword is expected.
70
            if (
71 100
                ($token->type === TokenType::Keyword)
72 100
                && ($token->flags & Token::FLAG_KEYWORD_RESERVED)
73 100
                && ($state === 0)
74
            ) {
75 32
                break;
76
            }
77
78 100
            if ($state === 0) {
79 100
                if (in_array($token->token, ['=', ':='], true)) {
80 100
                    $state = 1;
81 98
                } elseif ($token->value !== ',') {
82 98
                    $expr->column .= $token->token;
83 24
                } elseif ($token->value === ',') {
84 24
                    $commaLastSeenAt = $token;
85
                }
86
            } else {
87 100
                $tmp = Expressions::parse(
88 100
                    $parser,
89 100
                    $list,
90 100
                    ['breakOnAlias' => true],
91 100
                );
92 100
                if ($tmp === null) {
93 2
                    $parser->error('Missing expression.', $token);
94 2
                    break;
95
                }
96
97 98
                $expr->column = trim($expr->column);
98 98
                $expr->value = $tmp->expr;
99 98
                $ret[] = $expr;
100 98
                $expr = new SetOperation();
101 98
                $state = 0;
102 98
                $commaLastSeenAt = null;
103
            }
104
        }
105
106 120
        --$list->idx;
107
108
        // We saw a comma, but didn't see a column-value pair after it
109 120
        if ($commaLastSeenAt !== null) {
0 ignored issues
show
The condition $commaLastSeenAt !== null is always false.
Loading history...
110 2
            $parser->error('Unexpected token.', $commaLastSeenAt);
111
        }
112
113 120
        return $ret;
114
    }
115
116
    /** @param SetOperation[] $component the component to be built */
117 38
    public static function buildAll(array $component): string
118
    {
119 38
        return implode(', ', $component);
120
    }
121
}
122