Skip to content

Commit 6dfd1d2

Browse files
committed
Split up SetOperation
Signed-off-by: Kamil Tekiela <[email protected]>
1 parent 4546dcf commit 6dfd1d2

File tree

9 files changed

+156
-141
lines changed

9 files changed

+156
-141
lines changed

phpstan-baseline.neon

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,11 @@ parameters:
265265
count: 2
266266
path: src/Components/Lists/RenameOperations.php
267267

268+
-
269+
message: "#^Property PhpMyAdmin\\\\SqlParser\\\\Components\\\\SetOperation\\:\\:\\$value \\(string\\) does not accept string\\|null\\.$#"
270+
count: 1
271+
path: src/Components/Lists/SetOperations.php
272+
268273
-
269274
message: "#^Property PhpMyAdmin\\\\SqlParser\\\\Components\\\\LockExpression\\:\\:\\$table \\(PhpMyAdmin\\\\SqlParser\\\\Components\\\\Expression\\) does not accept PhpMyAdmin\\\\SqlParser\\\\Components\\\\Expression\\|null\\.$#"
270275
count: 1
@@ -390,11 +395,6 @@ parameters:
390395
count: 1
391396
path: src/Components/RenameOperation.php
392397

393-
-
394-
message: "#^Property PhpMyAdmin\\\\SqlParser\\\\Components\\\\SetOperation\\:\\:\\$value \\(string\\) does not accept string\\|null\\.$#"
395-
count: 1
396-
path: src/Components/SetOperation.php
397-
398398
-
399399
message: "#^Static property PhpMyAdmin\\\\SqlParser\\\\Context\\:\\:\\$keywords \\(non\\-empty\\-array\\<non\\-empty\\-string, int\\>\\) does not accept default value of type array\\{\\}\\.$#"
400400
count: 1
@@ -626,7 +626,7 @@ parameters:
626626
path: src/Statements/SetStatement.php
627627

628628
-
629-
message: "#^Parameter \\#1 \\$component of static method PhpMyAdmin\\\\SqlParser\\\\Components\\\\SetOperation\\:\\:buildAll\\(\\) expects array\\<PhpMyAdmin\\\\SqlParser\\\\Components\\\\SetOperation\\>, array\\<PhpMyAdmin\\\\SqlParser\\\\Components\\\\SetOperation\\>\\|null given\\.$#"
629+
message: "#^Parameter \\#1 \\$component of static method PhpMyAdmin\\\\SqlParser\\\\Components\\\\Lists\\\\SetOperations\\:\\:buildAll\\(\\) expects array\\<PhpMyAdmin\\\\SqlParser\\\\Components\\\\SetOperation\\>, array\\<PhpMyAdmin\\\\SqlParser\\\\Components\\\\SetOperation\\>\\|null given\\.$#"
630630
count: 1
631631
path: src/Statements/SetStatement.php
632632

psalm-baseline.xml

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,17 @@
320320
<code>$options</code>
321321
</UnusedParam>
322322
</file>
323+
<file src="src/Components/Lists/SetOperations.php">
324+
<PossiblyNullPropertyAssignmentValue>
325+
<code><![CDATA[$tmp->expr]]></code>
326+
</PossiblyNullPropertyAssignmentValue>
327+
<RedundantConditionGivenDocblockType>
328+
<code><![CDATA[$token->value === ',']]></code>
329+
</RedundantConditionGivenDocblockType>
330+
<UnusedParam>
331+
<code>$options</code>
332+
</UnusedParam>
333+
</file>
323334
<file src="src/Components/LockExpression.php">
324335
<MissingConstructor>
325336
<code>$table</code>
@@ -492,17 +503,6 @@
492503
<code>$old</code>
493504
</PossiblyNullPropertyAssignmentValue>
494505
</file>
495-
<file src="src/Components/SetOperation.php">
496-
<PossiblyNullPropertyAssignmentValue>
497-
<code><![CDATA[$tmp->expr]]></code>
498-
</PossiblyNullPropertyAssignmentValue>
499-
<RedundantConditionGivenDocblockType>
500-
<code><![CDATA[$token->value === ',']]></code>
501-
</RedundantConditionGivenDocblockType>
502-
<UnusedParam>
503-
<code>$options</code>
504-
</UnusedParam>
505-
</file>
506506
<file src="src/Components/UnionKeyword.php">
507507
<PossiblyUnusedMethod>
508508
<code>buildAll</code>
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpMyAdmin\SqlParser\Components\Lists;
6+
7+
use PhpMyAdmin\SqlParser\Components\Expression;
8+
use PhpMyAdmin\SqlParser\Components\SetOperation;
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
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+
public static function parse(Parser $parser, TokensList $list, array $options = []): array
31+
{
32+
$ret = [];
33+
34+
$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+
* @var int
47+
*/
48+
$state = 0;
49+
50+
/**
51+
* Token when the parser has seen the latest comma
52+
*
53+
* @var Token
54+
*/
55+
$commaLastSeenAt = null;
56+
57+
for (; $list->idx < $list->count; ++$list->idx) {
58+
/**
59+
* Token parsed at this moment.
60+
*/
61+
$token = $list->tokens[$list->idx];
62+
63+
// End of statement.
64+
if ($token->type === TokenType::Delimiter) {
65+
break;
66+
}
67+
68+
// Skipping whitespaces and comments.
69+
if (($token->type === TokenType::Whitespace) || ($token->type === TokenType::Comment)) {
70+
continue;
71+
}
72+
73+
// No keyword is expected.
74+
if (
75+
($token->type === TokenType::Keyword)
76+
&& ($token->flags & Token::FLAG_KEYWORD_RESERVED)
77+
&& ($state === 0)
78+
) {
79+
break;
80+
}
81+
82+
if ($state === 0) {
83+
if (in_array($token->token, ['=', ':='], true)) {
84+
$state = 1;
85+
} elseif ($token->value !== ',') {
86+
$expr->column .= $token->token;
87+
} elseif ($token->value === ',') {
88+
$commaLastSeenAt = $token;
89+
}
90+
} elseif ($state === 1) {
91+
$tmp = Expression::parse(
92+
$parser,
93+
$list,
94+
['breakOnAlias' => true],
95+
);
96+
if ($tmp === null) {
97+
$parser->error('Missing expression.', $token);
98+
break;
99+
}
100+
101+
$expr->column = trim($expr->column);
102+
$expr->value = $tmp->expr;
103+
$ret[] = $expr;
104+
$expr = new SetOperation();
105+
$state = 0;
106+
$commaLastSeenAt = null;
107+
}
108+
}
109+
110+
--$list->idx;
111+
112+
// We saw a comma, but didn't see a column-value pair after it
113+
if ($commaLastSeenAt !== null) {
114+
$parser->error('Unexpected token.', $commaLastSeenAt);
115+
}
116+
117+
return $ret;
118+
}
119+
120+
/** @param SetOperation[] $component the component to be built */
121+
public static function buildAll(array $component): string
122+
{
123+
return implode(', ', $component);
124+
}
125+
}

src/Components/SetOperation.php

Lines changed: 0 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,7 @@
55
namespace PhpMyAdmin\SqlParser\Components;
66

77
use PhpMyAdmin\SqlParser\Component;
8-
use PhpMyAdmin\SqlParser\Parser;
9-
use PhpMyAdmin\SqlParser\Token;
10-
use PhpMyAdmin\SqlParser\TokensList;
11-
use PhpMyAdmin\SqlParser\TokenType;
128

13-
use function implode;
14-
use function in_array;
15-
use function trim;
16-
17-
/**
18-
* `SET` keyword parser.
19-
*/
209
final class SetOperation implements Component
2110
{
2211
/**
@@ -43,114 +32,11 @@ public function __construct(string $column = '', string $value = '')
4332
$this->value = $value;
4433
}
4534

46-
/**
47-
* @param Parser $parser the parser that serves as context
48-
* @param TokensList $list the list of tokens that are being parsed
49-
* @param array<string, mixed> $options parameters for parsing
50-
*
51-
* @return SetOperation[]
52-
*/
53-
public static function parse(Parser $parser, TokensList $list, array $options = []): array
54-
{
55-
$ret = [];
56-
57-
$expr = new static();
58-
59-
/**
60-
* The state of the parser.
61-
*
62-
* Below are the states of the parser.
63-
*
64-
* 0 ---------------------[ col_name ]--------------------> 0
65-
* 0 ---------------------[ = or := ]---------------------> 1
66-
* 1 -----------------------[ value ]---------------------> 1
67-
* 1 ------------------------[ , ]------------------------> 0
68-
*
69-
* @var int
70-
*/
71-
$state = 0;
72-
73-
/**
74-
* Token when the parser has seen the latest comma
75-
*
76-
* @var Token
77-
*/
78-
$commaLastSeenAt = null;
79-
80-
for (; $list->idx < $list->count; ++$list->idx) {
81-
/**
82-
* Token parsed at this moment.
83-
*/
84-
$token = $list->tokens[$list->idx];
85-
86-
// End of statement.
87-
if ($token->type === TokenType::Delimiter) {
88-
break;
89-
}
90-
91-
// Skipping whitespaces and comments.
92-
if (($token->type === TokenType::Whitespace) || ($token->type === TokenType::Comment)) {
93-
continue;
94-
}
95-
96-
// No keyword is expected.
97-
if (
98-
($token->type === TokenType::Keyword)
99-
&& ($token->flags & Token::FLAG_KEYWORD_RESERVED)
100-
&& ($state === 0)
101-
) {
102-
break;
103-
}
104-
105-
if ($state === 0) {
106-
if (in_array($token->token, ['=', ':='], true)) {
107-
$state = 1;
108-
} elseif ($token->value !== ',') {
109-
$expr->column .= $token->token;
110-
} elseif ($token->value === ',') {
111-
$commaLastSeenAt = $token;
112-
}
113-
} elseif ($state === 1) {
114-
$tmp = Expression::parse(
115-
$parser,
116-
$list,
117-
['breakOnAlias' => true],
118-
);
119-
if ($tmp === null) {
120-
$parser->error('Missing expression.', $token);
121-
break;
122-
}
123-
124-
$expr->column = trim($expr->column);
125-
$expr->value = $tmp->expr;
126-
$ret[] = $expr;
127-
$expr = new static();
128-
$state = 0;
129-
$commaLastSeenAt = null;
130-
}
131-
}
132-
133-
--$list->idx;
134-
135-
// We saw a comma, but didn't see a column-value pair after it
136-
if ($commaLastSeenAt !== null) {
137-
$parser->error('Unexpected token.', $commaLastSeenAt);
138-
}
139-
140-
return $ret;
141-
}
142-
14335
public function build(): string
14436
{
14537
return $this->column . ' = ' . $this->value;
14638
}
14739

148-
/** @param SetOperation[] $component the component to be built */
149-
public static function buildAll(array $component): string
150-
{
151-
return implode(', ', $component);
152-
}
153-
15440
public function __toString(): string
15541
{
15642
return $this->build();

src/Parser.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -312,7 +312,7 @@ class Parser
312312
'options' => ['parseField' => 'table'],
313313
],
314314
'SET' => [
315-
'class' => Components\SetOperation::class,
315+
'class' => Components\Lists\SetOperations::class,
316316
'field' => 'set',
317317
],
318318
'SELECT' => [

src/Statements/InsertStatement.php

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use PhpMyAdmin\SqlParser\Components\ArrayObj;
88
use PhpMyAdmin\SqlParser\Components\IntoKeyword;
99
use PhpMyAdmin\SqlParser\Components\Lists\Array2d;
10+
use PhpMyAdmin\SqlParser\Components\Lists\SetOperations;
1011
use PhpMyAdmin\SqlParser\Components\OptionsArray;
1112
use PhpMyAdmin\SqlParser\Components\SetOperation;
1213
use PhpMyAdmin\SqlParser\Parser;
@@ -113,13 +114,13 @@ public function build(): string
113114
if ($this->values !== null && $this->values !== []) {
114115
$ret .= ' VALUES ' . ArrayObj::buildAll($this->values);
115116
} elseif ($this->set !== null && $this->set !== []) {
116-
$ret .= ' SET ' . SetOperation::buildAll($this->set);
117+
$ret .= ' SET ' . SetOperations::buildAll($this->set);
117118
} elseif ($this->select !== null && strlen((string) $this->select) > 0) {
118119
$ret .= ' ' . $this->select->build();
119120
}
120121

121122
if ($this->onDuplicateSet !== null && $this->onDuplicateSet !== []) {
122-
$ret .= ' ON DUPLICATE KEY UPDATE ' . SetOperation::buildAll($this->onDuplicateSet);
123+
$ret .= ' ON DUPLICATE KEY UPDATE ' . SetOperations::buildAll($this->onDuplicateSet);
123124
}
124125

125126
return $ret;
@@ -201,7 +202,7 @@ public function parse(Parser $parser, TokensList $list): void
201202
} elseif ($token->keyword === 'SET') {
202203
++$list->idx; // skip SET
203204

204-
$this->set = SetOperation::parse($parser, $list);
205+
$this->set = SetOperations::parse($parser, $list);
205206
} elseif ($token->keyword === 'SELECT') {
206207
$this->select = new SelectStatement($parser, $list);
207208
} elseif ($token->keyword === 'WITH') {
@@ -233,7 +234,7 @@ public function parse(Parser $parser, TokensList $list): void
233234

234235
if ($miniState === 5) {
235236
++$list->idx;
236-
$this->onDuplicateSet = SetOperation::parse($parser, $list);
237+
$this->onDuplicateSet = SetOperations::parse($parser, $list);
237238
$state = 3;
238239
}
239240
}

0 commit comments

Comments
 (0)