Passed
Pull Request — master (#483)
by
unknown
03:28
created

SetOperation::buildAll()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace PhpMyAdmin\SqlParser\Components;
6
7
use PhpMyAdmin\SqlParser\Component;
8
use PhpMyAdmin\SqlParser\Parser;
9
use PhpMyAdmin\SqlParser\Token;
10
use PhpMyAdmin\SqlParser\TokensList;
11
12
use function implode;
13
use function trim;
14
15
/**
16
 * `SET` keyword parser.
17
 */
18
final class SetOperation implements Component
19
{
20
    /**
21
     * The name of the column that is being updated.
22
     *
23
     * @var string
24
     */
25
    public $column;
26
27
    /**
28
     * The new value.
29
     *
30
     * @var string
31
     */
32
    public $value;
33
34
    /**
35
     * @param string $column Field's name..
36
     * @param string $value  new value
37
     */
38 110
    public function __construct($column = '', $value = '')
39
    {
40 110
        $this->column = $column;
41 110
        $this->value = $value;
42
    }
43
44
    /**
45
     * @param Parser               $parser  the parser that serves as context
46
     * @param TokensList           $list    the list of tokens that are being parsed
47
     * @param array<string, mixed> $options parameters for parsing
48
     *
49
     * @return SetOperation[]
50
     */
51 110
    public static function parse(Parser $parser, TokensList $list, array $options = []): array
52
    {
53 110
        $ret = [];
54
55 110
        $expr = new static();
56
57
        /**
58
         * The state of the parser.
59
         *
60
         * Below are the states of the parser.
61
         *
62
         *      0 ---------------------[ col_name ]--------------------> 0
63
         *      0 ---------------------[ = or := ]---------------------> 1
64
         *      1 -----------------------[ value ]---------------------> 1
65
         *      1 ------------------------[ , ]------------------------> 0
66
         *
67
         * @var int
68
         */
69 110
        $state = 0;
70
71
        /**
72
         * Token when the parser has seen the latest comma
73
         *
74
         * @var Token
75
         */
76 110
        $commaLastSeenAt = null;
77
78 110
        for (; $list->idx < $list->count; ++$list->idx) {
79
            /**
80
             * Token parsed at this moment.
81
             */
82 110
            $token = $list->tokens[$list->idx];
83
84
            // End of statement.
85 110
            if ($token->type === Token::TYPE_DELIMITER) {
86 86
                break;
87
            }
88
89
            // Skipping whitespaces and comments.
90 92
            if (($token->type === Token::TYPE_WHITESPACE) || ($token->type === Token::TYPE_COMMENT)) {
91 82
                continue;
92
            }
93
94
            // No keyword is expected.
95
            if (
96 92
                ($token->type === Token::TYPE_KEYWORD)
97 92
                && ($token->flags & Token::FLAG_KEYWORD_RESERVED)
98 92
                && ($state === 0)
99
            ) {
100 26
                break;
101
            }
102
103 92
            if ($state === 0) {
104 92
                if (in_array($token->token, ['=', ':='], true)) {
0 ignored issues
show
introduced by
Function in_array() should not be referenced via a fallback global name, but via a use statement.
Loading history...
105 92
                    $state = 1;
106 90
                } elseif ($token->value !== ',') {
107 90
                    $expr->column .= $token->token;
108 22
                } elseif ($token->value === ',') {
109 57
                    $commaLastSeenAt = $token;
110
                }
111 92
            } elseif ($state === 1) {
112 92
                $tmp = Expression::parse(
113 92
                    $parser,
114 92
                    $list,
115 92
                    ['breakOnAlias' => true]
116 92
                );
117 92
                if ($tmp === null) {
118 2
                    $parser->error('Missing expression.', $token);
119 2
                    break;
120
                }
121
122 90
                $expr->column = trim($expr->column);
123 90
                $expr->value = $tmp->expr;
124 90
                $ret[] = $expr;
125 90
                $expr = new static();
126 90
                $state = 0;
127 90
                $commaLastSeenAt = null;
128
            }
129
        }
130
131 110
        --$list->idx;
132
133
        // We saw a comma, but didn't see a column-value pair after it
134 110
        if ($commaLastSeenAt !== null) {
0 ignored issues
show
introduced by
The condition $commaLastSeenAt !== null is always false.
Loading history...
135 2
            $parser->error('Unexpected token.', $commaLastSeenAt);
136
        }
137
138 110
        return $ret;
139
    }
140
141
    /**
142
     * @param SetOperation $component the component to be built
143
     */
144 36
    public static function build($component): string
145
    {
146 36
        return $component->column . ' = ' . $component->value;
147
    }
148
149
    /**
150
     * @param SetOperation[] $component the component to be built
151
     */
152 38
    public static function buildAll(array $component): string
153
    {
154 38
        return implode(', ', $component);
155
    }
156
157 36
    public function __toString(): string
158
    {
159 36
        return static::build($this);
160
    }
161
}
162