Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 11 additions & 6 deletions phpstan-baseline.neon
Original file line number Diff line number Diff line change
Expand Up @@ -1620,11 +1620,6 @@ parameters:
count: 1
path: src/Token.php

-
message: "#^Only booleans are allowed in a negated boolean, int\\<0, 16\\> given\\.$#"
count: 1
path: src/Token.php

-
message: "#^Only booleans are allowed in a negated boolean, int\\<0, 2\\> given\\.$#"
count: 1
Expand Down Expand Up @@ -2100,9 +2095,19 @@ parameters:
count: 4
path: tests/Builder/CreateStatementTest.php

-
message: "#^Dynamic call to static method PHPUnit\\\\Framework\\\\Assert\\:\\:assertEmpty\\(\\)\\.$#"
count: 2
path: tests/Builder/CreateStatementTest.php

-
message: "#^Dynamic call to static method PHPUnit\\\\Framework\\\\Assert\\:\\:assertEquals\\(\\)\\.$#"
count: 34
count: 35
path: tests/Builder/CreateStatementTest.php

-
message: "#^Dynamic call to static method PHPUnit\\\\Framework\\\\Assert\\:\\:assertStringContainsString\\(\\)\\.$#"
count: 1
path: tests/Builder/CreateStatementTest.php

-
Expand Down
10 changes: 9 additions & 1 deletion psalm-baseline.xml
Original file line number Diff line number Diff line change
Expand Up @@ -698,7 +698,7 @@
<code>$this-&gt;last</code>
<code>$this-&gt;last</code>
</LoopInvalidation>
<MixedArrayAccess occurrences="43">
<MixedArrayAccess occurrences="52">
<code>$this-&gt;str[$this-&gt;last + 1]</code>
<code>$this-&gt;str[$this-&gt;last++]</code>
<code>$this-&gt;str[$this-&gt;last]</code>
Expand Down Expand Up @@ -742,6 +742,14 @@
<code>$this-&gt;str[$this-&gt;last]</code>
<code>$this-&gt;str[$this-&gt;last]</code>
<code>$this-&gt;str[$this-&gt;last]</code>
<code>$this-&gt;str[$this-&gt;last]</code>
<code>$this-&gt;str[$this-&gt;last]</code>
<code>$this-&gt;str[$this-&gt;last]</code>
<code>$this-&gt;str[$this-&gt;last]</code>
<code>$this-&gt;str[$this-&gt;last]</code>
<code>$this-&gt;str[$this-&gt;last]</code>
<code>$this-&gt;str[$this-&gt;last]</code>
<code>$this-&gt;str[$this-&gt;last]</code>
</MixedArrayAccess>
<MixedAssignment occurrences="2">
<code>$lastToken</code>
Expand Down
36 changes: 35 additions & 1 deletion src/Lexer.php
Original file line number Diff line number Diff line change
Expand Up @@ -833,11 +833,17 @@ public function parseNumber()
//
// 10 -------------------[ 0 to 9 ]-------------------> 4
//
// 11 ----------------------[ ' ]---------------------> 12
//
// 12 --------[ 0 to 9, A to F, a to f ]--------------> 12
// 12 ----------------------[ ' ]---------------------> 13
//
// State 1 may be reached by negative numbers.
// State 2 is reached only by hex numbers.
// State 4 is reached only by float numbers.
// State 5 is reached only by numbers in approximate form.
// State 7 is reached only by numbers in bit representation.
// State 11 is reached only by hex string literals (x'...').
// State 10 is a forced proxy to state 4 ensuring a starting dot (= "0.something") precedes a digit, and not "e"
// or "E" causing wrongly interpreted scientific notation (".e[0 to 9]" is invalid). Such invalid notation could
// break the lexer when table names under a given database context starts with ".e[0-9]".
Expand Down Expand Up @@ -866,6 +872,8 @@ public function parseNumber()
$state = 10;
} elseif ($this->str[$this->last] === 'b') {
$state = 7;
} elseif ($this->str[$this->last] === 'x' || $this->str[$this->last] === 'X') {
$state = 11;
} elseif ($this->str[$this->last] !== '+') {
// `+` is a valid character in a number.
break;
Expand Down Expand Up @@ -949,6 +957,29 @@ public function parseNumber()
}
} elseif ($state === 9) {
break;
} elseif ($state === 11) {
// x'...' hex string literal - expect opening quote
$flags |= Token::FLAG_NUMBER_HEX_STRING;
if ($this->str[$this->last] !== '\'') {
break;
}

$state = 12;
} elseif ($state === 12) {
// x'...' hex string literal - hex digits or closing quote
if ($this->str[$this->last] === '\'') {
$state = 13;
} elseif (
! (
($this->str[$this->last] >= '0' && $this->str[$this->last] <= '9')
|| ($this->str[$this->last] >= 'A' && $this->str[$this->last] <= 'F')
|| ($this->str[$this->last] >= 'a' && $this->str[$this->last] <= 'f')
)
) {
break;
}
} elseif ($state === 13) {
break;
} elseif ($state === 10) {
$flags |= Token::FLAG_NUMBER_FLOAT;
if ($this->str[$this->last] < '0' || $this->str[$this->last] > '9') {
Expand All @@ -961,7 +992,10 @@ public function parseNumber()
$token .= $this->str[$this->last];
}

if ($state === 2 || $state === 3 || ($token !== '.' && $state === 4) || $state === 6 || $state === 9) {
if (
$state === 2 || $state === 3 || ($token !== '.' && $state === 4)
|| $state === 6 || $state === 9 || $state === 13
) {
--$this->last;

return new Token($token, Token::TYPE_NUMBER, $flags);
Expand Down
6 changes: 5 additions & 1 deletion src/Token.php
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ class Token
public const FLAG_NUMBER_APPROXIMATE = 4;
public const FLAG_NUMBER_NEGATIVE = 8;
public const FLAG_NUMBER_BINARY = 16;
public const FLAG_NUMBER_HEX_STRING = 32;

// Strings related flags.
public const FLAG_STRING_SINGLE_QUOTES = 1;
Expand Down Expand Up @@ -263,7 +264,10 @@ public function extract()
}
} elseif (($this->flags & self::FLAG_NUMBER_APPROXIMATE) || ($this->flags & self::FLAG_NUMBER_FLOAT)) {
$ret = (float) $ret;
} elseif (! ($this->flags & self::FLAG_NUMBER_BINARY)) {
} elseif (
($this->flags & self::FLAG_NUMBER_BINARY) === 0
&& ($this->flags & self::FLAG_NUMBER_HEX_STRING) === 0
) {
$ret = (int) $ret;
}

Expand Down
22 changes: 22 additions & 0 deletions tests/Builder/CreateStatementTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -871,4 +871,26 @@ public function testBuildCreateTableComplexIndexes(): void
$stmt->build()
);
}

public function testBuildCreateTableWithHexDefault(): void
{
$sql = "CREATE TABLE `test` (\n"
. " `IP` binary(16) NOT NULL DEFAULT x'00000000000000000000000000000000'\n"
. ') ENGINE=InnoDB';

$parser = new Parser($sql);
$this->assertEmpty($parser->errors);
$this->assertEquals($sql, $parser->statements[0]->build());
}

public function testBuildCreateTableWithUppercaseHexDefault(): void
{
$sql = "CREATE TABLE `test` (\n"
. " `IP` binary(16) NOT NULL DEFAULT X'FF'\n"
. ')';

$parser = new Parser($sql);
$this->assertEmpty($parser->errors);
$this->assertStringContainsString("DEFAULT X'FF'", $parser->statements[0]->build());
}
}