Skip to content

Commit 1e1693e

Browse files
committed
Merge branch '5.11.x'
Signed-off-by: Maurício Meneghini Fauth <[email protected]>
2 parents 91d2bbf + a5a74e7 commit 1e1693e

20 files changed

+1488
-65
lines changed

phpstan-baseline.neon

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -651,7 +651,7 @@ parameters:
651651
path: src/Statements/SelectStatement.php
652652

653653
-
654-
message: "#^Property PhpMyAdmin\\\\SqlParser\\\\Statements\\\\SelectStatement\\:\\:\\$expr \\(array\\<PhpMyAdmin\\\\SqlParser\\\\Components\\\\Expression\\>\\) does not accept array\\<PhpMyAdmin\\\\SqlParser\\\\Component\\>\\|PhpMyAdmin\\\\SqlParser\\\\Components\\\\ArrayObj\\|PhpMyAdmin\\\\SqlParser\\\\Components\\\\Expression\\|PhpMyAdmin\\\\SqlParser\\\\Components\\\\FunctionCall\\|PhpMyAdmin\\\\SqlParser\\\\Components\\\\IntoKeyword\\|PhpMyAdmin\\\\SqlParser\\\\Components\\\\Limit\\|PhpMyAdmin\\\\SqlParser\\\\Components\\\\OptionsArray\\|null\\.$#"
654+
message: "#^Property PhpMyAdmin\\\\SqlParser\\\\Statements\\\\SelectStatement\\:\\:\\$expr \\(array\\<PhpMyAdmin\\\\SqlParser\\\\Components\\\\CaseExpression\\|PhpMyAdmin\\\\SqlParser\\\\Components\\\\Expression\\>\\) does not accept array\\<PhpMyAdmin\\\\SqlParser\\\\Component\\>\\|PhpMyAdmin\\\\SqlParser\\\\Components\\\\ArrayObj\\|PhpMyAdmin\\\\SqlParser\\\\Components\\\\Expression\\|PhpMyAdmin\\\\SqlParser\\\\Components\\\\FunctionCall\\|PhpMyAdmin\\\\SqlParser\\\\Components\\\\IntoKeyword\\|PhpMyAdmin\\\\SqlParser\\\\Components\\\\Limit\\|PhpMyAdmin\\\\SqlParser\\\\Components\\\\OptionsArray\\|null\\.$#"
655655
count: 1
656656
path: src/Statements/SelectStatement.php
657657

psalm-baseline.xml

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<?xml version="1.0" encoding="UTF-8"?>
2-
<files psalm-version="5.25.0@01a8eb06b9e9cc6cfb6a320bf9fb14331919d505">
2+
<files psalm-version="5.26.1@d747f6500b38ac4f7dfc5edbcae6e4b637d7add0">
33
<file src="src/Components/AlterOperation.php">
44
<PossiblyNullReference>
55
<code><![CDATA[has]]></code>
@@ -859,6 +859,15 @@
859859
</PossiblyNullOperand>
860860
</file>
861861
<file src="src/Statements/SelectStatement.php">
862+
<MixedArrayOffset>
863+
<code><![CDATA[$retval[$thisDb]['tables'][$thisTable]['columns'][$expr->column]]]></code>
864+
<code><![CDATA[$table['columns'][$expr->column]]]></code>
865+
<code><![CDATA[$tables[$thisDb][$expr->table]]]></code>
866+
</MixedArrayOffset>
867+
<MixedReturnTypeCoercion>
868+
<code><![CDATA[$retval]]></code>
869+
<code><![CDATA[array<string, array<string, array<string, array<string, array<string, string>|string|null>>|null>>]]></code>
870+
</MixedReturnTypeCoercion>
862871
<PossiblyNullArrayOffset>
863872
<code><![CDATA[$tables[$thisDb]]]></code>
864873
</PossiblyNullArrayOffset>
@@ -1092,6 +1101,9 @@
10921101
<InvalidNullableReturnType>
10931102
<code><![CDATA[int]]></code>
10941103
</InvalidNullableReturnType>
1104+
<MixedArrayOffset>
1105+
<code><![CDATA[$tableAliases[$expr->table]]]></code>
1106+
</MixedArrayOffset>
10951107
<MixedAssignment>
10961108
<code><![CDATA[$expr]]></code>
10971109
<code><![CDATA[$expressions[]]]></code>

src/Lexer.php

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -699,7 +699,7 @@ public function parseNumber(): Token|null
699699
// 1 --------------------[ + or - ]-------------------> 1
700700
// 1 -------------------[ 0x or 0X ]------------------> 2
701701
// 1 --------------------[ 0 to 9 ]-------------------> 3
702-
// 1 -----------------------[ . ]---------------------> 4
702+
// 1 -----------------------[ . ]---------------------> 10
703703
// 1 -----------------------[ b ]---------------------> 7
704704
//
705705
// 2 --------------------[ 0 to F ]-------------------> 2
@@ -718,11 +718,16 @@ public function parseNumber(): Token|null
718718
// 8 --------------------[ 0 or 1 ]-------------------> 8
719719
// 8 -----------------------[ ' ]---------------------> 9
720720
//
721+
// 10 -------------------[ 0 to 9 ]-------------------> 4
722+
//
721723
// State 1 may be reached by negative numbers.
722724
// State 2 is reached only by hex numbers.
723725
// State 4 is reached only by float numbers.
724726
// State 5 is reached only by numbers in approximate form.
725727
// State 7 is reached only by numbers in bit representation.
728+
// State 10 is a forced proxy to state 4 ensuring a starting dot (= "0.something") precedes a digit, and not "e"
729+
// or "E" causing wrongly interpreted scientific notation (".e[0 to 9]" is invalid). Such invalid notation could
730+
// break the lexer when table names under a given database context starts with ".e[0-9]".
726731
//
727732
// Valid final states are: 2, 3, 4 and 6. Any parsing that finished in a
728733
// state other than these is invalid.
@@ -745,7 +750,7 @@ public function parseNumber(): Token|null
745750
} elseif ($this->str[$this->last] >= '0' && $this->str[$this->last] <= '9') {
746751
$state = 3;
747752
} elseif ($this->str[$this->last] === '.') {
748-
$state = 4;
753+
$state = 10;
749754
} elseif ($this->str[$this->last] === 'b') {
750755
$state = 7;
751756
} elseif ($this->str[$this->last] !== '+') {
@@ -772,7 +777,7 @@ public function parseNumber(): Token|null
772777
($this->str[$this->last] >= 'a' && $this->str[$this->last] <= 'z')
773778
|| ($this->str[$this->last] >= 'A' && $this->str[$this->last] <= 'Z')
774779
) {
775-
// A number can't be directly followed by a letter
780+
// A number can't be directly followed by a letter
776781
$state = -$state;
777782
} elseif ($this->str[$this->last] < '0' || $this->str[$this->last] > '9') {
778783
// Just digits and `.`, `e` and `E` are valid characters.
@@ -786,7 +791,7 @@ public function parseNumber(): Token|null
786791
($this->str[$this->last] >= 'a' && $this->str[$this->last] <= 'z')
787792
|| ($this->str[$this->last] >= 'A' && $this->str[$this->last] <= 'Z')
788793
) {
789-
// A number can't be directly followed by a letter
794+
// A number can't be directly followed by a letter
790795
$state = -$state;
791796
} elseif ($this->str[$this->last] < '0' || $this->str[$this->last] > '9') {
792797
// Just digits, `e` and `E` are valid characters.
@@ -803,7 +808,7 @@ public function parseNumber(): Token|null
803808
($this->str[$this->last] >= 'a' && $this->str[$this->last] <= 'z')
804809
|| ($this->str[$this->last] >= 'A' && $this->str[$this->last] <= 'Z')
805810
) {
806-
// A number can't be directly followed by a letter
811+
// A number can't be directly followed by a letter
807812
$state = -$state;
808813
} else {
809814
break;
@@ -828,6 +833,13 @@ public function parseNumber(): Token|null
828833
}
829834
} elseif ($state === 9) {
830835
break;
836+
} elseif ($state === 10) {
837+
$flags |= Token::FLAG_NUMBER_FLOAT;
838+
if ($this->str[$this->last] < '0' || $this->str[$this->last] > '9') {
839+
break;
840+
}
841+
842+
$state = 4;
831843
}
832844

833845
$token .= $this->str[$this->last];

src/Parsers/AlterOperations.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ final class AlterOperations implements Parseable
124124
'BY' => 2,
125125
'FOREIGN' => 2,
126126
'FULLTEXT' => 2,
127-
'KEY' => 2,
127+
'KEY' => [2, 'var'],
128128
'KEYS' => 2,
129129
'PARTITION' => 2,
130130
'PARTITION BY' => 2,

src/Statements/SelectStatement.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
namespace PhpMyAdmin\SqlParser\Statements;
66

77
use PhpMyAdmin\SqlParser\Components\ArrayObj;
8+
use PhpMyAdmin\SqlParser\Components\CaseExpression;
89
use PhpMyAdmin\SqlParser\Components\Condition;
910
use PhpMyAdmin\SqlParser\Components\Expression;
1011
use PhpMyAdmin\SqlParser\Components\FunctionCall;
@@ -233,7 +234,7 @@ class SelectStatement extends Statement
233234
/**
234235
* Expressions that are being selected by this statement.
235236
*
236-
* @var Expression[]
237+
* @var (CaseExpression|Expression)[]
237238
*/
238239
public array $expr = [];
239240

src/TokensList.php

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
use ArrayAccess;
88

9+
use function array_splice;
910
use function count;
1011
use function in_array;
1112
use function is_array;
@@ -179,53 +180,57 @@ public function getNextOfTypeAndFlag(TokenType $type, int $flag): Token|null
179180
}
180181

181182
/**
182-
* Sets an value inside the container.
183+
* Sets a Token inside the list of tokens.
184+
* When defined, offset must be positive otherwise the offset is ignored.
185+
* If the offset is not defined (like in array_push) or if it is greater than the number of Tokens already stored,
186+
* the Token is appended to the list of tokens.
183187
*
184-
* @param int|null $offset the offset to be set
188+
* @param int|null $offset the offset to be set. Must be positive otherwise, nothing will be stored.
185189
* @param Token $value the token to be saved
186190
*/
187191
public function offsetSet(mixed $offset, mixed $value): void
188192
{
189-
if ($offset === null) {
193+
if ($offset === null || $offset >= $this->count) {
190194
$this->tokens[$this->count++] = $value;
191-
} else {
195+
} elseif ($offset >= 0) {
192196
$this->tokens[$offset] = $value;
193197
}
194198
}
195199

196200
/**
197-
* Gets a value from the container.
201+
* Gets a Token from the list of tokens.
202+
* If the offset is negative or above the number of tokens set in the list, will return null.
198203
*
199204
* @param int $offset the offset to be returned
200205
*/
201206
public function offsetGet(mixed $offset): Token|null
202207
{
203-
return $offset < $this->count ? $this->tokens[$offset] : null;
208+
return $this->offsetExists($offset) ? $this->tokens[$offset] : null;
204209
}
205210

206211
/**
207212
* Checks if an offset was previously set.
213+
* If the offset is negative or above the number of tokens set in the list, will return false.
208214
*
209215
* @param int $offset the offset to be checked
210216
*/
211217
public function offsetExists(mixed $offset): bool
212218
{
213-
return $offset < $this->count;
219+
return $offset >= 0 && $offset < $this->count;
214220
}
215221

216222
/**
217-
* Unsets the value of an offset.
223+
* Unsets the value of an offset, if the offset exists.
218224
*
219225
* @param int $offset the offset to be unset
220226
*/
221227
public function offsetUnset(mixed $offset): void
222228
{
223-
unset($this->tokens[$offset]);
224-
--$this->count;
225-
for ($i = $offset; $i < $this->count; ++$i) {
226-
$this->tokens[$i] = $this->tokens[$i + 1];
229+
if (! $this->offsetExists($offset)) {
230+
return;
227231
}
228232

229-
unset($this->tokens[$this->count]);
233+
array_splice($this->tokens, $offset, 1);
234+
--$this->count;
230235
}
231236
}

tests/Lexer/TokensListTest.php

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -140,17 +140,41 @@ public function testArrayAccess(): void
140140
// offsetSet($offset, $value)
141141
$list[2] = $this->tokens[2];
142142

143+
// offsetSet($offset, $value) with overflowed offset
144+
$list[$list->count] = $this->tokens[1];
145+
$this->assertSame($list[$list->count - 1], $this->tokens[1]);
146+
$this->assertSame($list->count, count($this->tokens) + 1);
147+
148+
// offsetGet($offset) with negative offset
149+
$this->assertNull($list[-1]);
150+
151+
// offsetGet($offset) with overflow offset
152+
$this->assertNull($list[$list->count]);
153+
143154
// offsetGet($offset)
144155
for ($i = 0, $count = count($this->tokens); $i < $count; ++$i) {
145-
$this->assertEquals($this->tokens[$i], $list[$i]);
156+
$this->assertSame($this->tokens[$i], $list[$i]);
146157
}
147158

148159
// offsetExists($offset)
149160
$this->assertArrayHasKey(2, $list);
150-
$this->assertArrayNotHasKey(13, $list);
161+
$this->assertArrayNotHasKey(14, $list);
162+
$this->assertArrayNotHasKey(-8, $list);
151163

152164
// offsetUnset($offset)
165+
$currentCountTokens = $list->count;
153166
unset($list[2]);
154-
$this->assertEquals($this->tokens[3], $list[2]);
167+
$newCountTokens = $list->count;
168+
$this->assertSame($this->tokens[3], $list[2]);
169+
$this->assertSame($currentCountTokens - 1, $newCountTokens);
170+
171+
// offsetUnset($offset) with invalid offset (negative or overflowed)
172+
$currentListTokens = $list->tokens;
173+
unset($list[-1]);
174+
$this->assertSame($currentListTokens, $list->tokens);
175+
$this->assertSame($newCountTokens, $list->count); // No unset actually performed.
176+
unset($list[999]);
177+
$this->assertSame($currentListTokens, $list->tokens);
178+
$this->assertSame($newCountTokens, $list->count); // No unset actually performed.
155179
}
156180
}

tests/Parser/AlterStatementTest.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ public static function alterProvider(): array
3939
['parser/parseAlterErr4'],
4040
['parser/parseAlterTableRenameIndex1'],
4141
['parser/parseAlterTableRenameIndex2'],
42+
['parser/parseAlterTableRenameKey1'],
43+
['parser/parseAlterTableRenameKey2'],
4244
['parser/parseAlterTablePartitionByRange1'],
4345
['parser/parseAlterTablePartitionByRange2'],
4446
['parser/parseAlterTableCoalescePartition'],

tests/Parser/LoadStatementTest.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ public static function loadProvider(): array
3636
['parser/parseLoad5'],
3737
['parser/parseLoad6'],
3838
['parser/parseLoad7'],
39+
['parser/parseLoad8'],
3940
['parser/parseLoadErr1'],
4041
['parser/parseLoadErr2'],
4142
['parser/parseLoadErr3'],

tests/data/bugs/gh317.out

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -241,19 +241,15 @@
241241
"@type": "PhpMyAdmin\\SqlParser\\Components\\OptionsArray",
242242
"options": {
243243
"1": "ADD",
244-
"2": "KEY"
244+
"2": {
245+
"name": "KEY",
246+
"equals": false,
247+
"expr": "`IDX_REPAIR`",
248+
"value": "IDX_REPAIR"
249+
}
245250
}
246251
},
247-
"field": {
248-
"@type": "PhpMyAdmin\\SqlParser\\Components\\Expression",
249-
"database": null,
250-
"table": null,
251-
"column": "IDX_REPAIR",
252-
"expr": "`IDX_REPAIR`",
253-
"alias": null,
254-
"function": null,
255-
"subquery": null
256-
},
252+
"field": null,
257253
"partitions": null,
258254
"unknown": [
259255
{

0 commit comments

Comments
 (0)