Skip to content

Commit e961047

Browse files
authored
Array parser refactoring (#289)
* Array parser refactoring * Without check of `strlen($value)` * Fix test issues * Old design * Update * Update * Split test for one- and multi-dimensional arrays * Add test * Add test
1 parent 8da22b0 commit e961047

4 files changed

Lines changed: 52 additions & 57 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
- Enh #282: Support `numeric` arrays, improve support of domain types and `int` and `varchar` array types (@Tigrov)
66
- Enh #284: Add tests for `binary` type and fix casting of default value (@Tigrov)
7+
- Bug #287: Fix `bit` type (@Tigrov)
8+
- Enh #289: Array parser refactoring (@Tigrov)
79

810
## 1.0.0 April 12, 2023
911

src/ArrayParser.php

Lines changed: 43 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -5,34 +5,22 @@
55
namespace Yiisoft\Db\Pgsql;
66

77
use function in_array;
8-
use function strlen;
98

109
/**
1110
* Array representation to PHP array parser for PostgreSQL Server.
1211
*/
1312
final class ArrayParser
1413
{
15-
/**
16-
* @var string Character used in an array.
17-
*/
18-
private string $delimiter = ',';
19-
2014
/**
2115
* Convert an array from PostgresSQL to PHP.
2216
*
2317
* @param string|null $value String to convert.
2418
*/
2519
public function parse(string|null $value): array|null
2620
{
27-
if ($value === null) {
28-
return null;
29-
}
30-
31-
if ($value === '{}') {
32-
return [];
33-
}
34-
35-
return $this->parseArray($value);
21+
return $value !== null
22+
? $this->parseArray($value)
23+
: null;
3624
}
3725

3826
/**
@@ -43,33 +31,23 @@ public function parse(string|null $value): array|null
4331
*/
4432
private function parseArray(string $value, int &$i = 0): array
4533
{
46-
$result = [];
47-
$len = strlen($value);
34+
if ($value[++$i] === '}') {
35+
++$i;
36+
return [];
37+
}
4838

49-
for (++$i; $i < $len; ++$i) {
50-
switch ($value[$i]) {
51-
case '{':
52-
$result[] = $this->parseArray($value, $i);
53-
break;
54-
case '}':
55-
break 2;
56-
case $this->delimiter:
57-
/** `{}` case */
58-
if (empty($result)) {
59-
$result[] = null;
60-
}
39+
for ($result = [];; ++$i) {
40+
$result[] = match ($value[$i]) {
41+
'{' => $this->parseArray($value, $i),
42+
',', '}' => null,
43+
default => $this->parseString($value, $i),
44+
};
6145

62-
/** `{,}` case */
63-
if (in_array($value[$i + 1], [$this->delimiter, '}'], true)) {
64-
$result[] = null;
65-
}
66-
break;
67-
default:
68-
$result[] = $this->parseString($value, $i);
46+
if ($value[$i] === '}') {
47+
++$i;
48+
return $result;
6949
}
7050
}
71-
72-
return $result;
7351
}
7452

7553
/**
@@ -80,27 +58,41 @@ private function parseArray(string $value, int &$i = 0): array
8058
*/
8159
private function parseString(string $value, int &$i): string|null
8260
{
83-
$isQuoted = $value[$i] === '"';
84-
$stringEndChars = $isQuoted ? ['"'] : [$this->delimiter, '}'];
85-
$result = '';
86-
$len = strlen($value);
61+
return $value[$i] === '"'
62+
? $this->parseQuotedString($value, $i)
63+
: $this->parseUnquotedString($value, $i);
64+
}
8765

88-
for ($i += $isQuoted ? 1 : 0; $i < $len; ++$i) {
89-
if (in_array($value[$i], ['\\', '"'], true) && in_array($value[$i + 1], [$value[$i], '"'], true)) {
66+
/**
67+
* Parses quoted string.
68+
*/
69+
private function parseQuotedString(string $value, int &$i): string
70+
{
71+
for ($result = '', ++$i;; ++$i) {
72+
if ($value[$i] === '\\') {
73+
++$i;
74+
} elseif ($value[$i] === '"') {
9075
++$i;
91-
} elseif (in_array($value[$i], $stringEndChars, true)) {
92-
break;
76+
return $result;
9377
}
9478

9579
$result .= $value[$i];
9680
}
81+
}
9782

98-
$i -= $isQuoted ? 0 : 1;
83+
/**
84+
* Parses unquoted string.
85+
*/
86+
private function parseUnquotedString(string $value, int &$i): string|null
87+
{
88+
for ($result = '';; ++$i) {
89+
if (in_array($value[$i], [',', '}'], true)) {
90+
return $result !== 'NULL'
91+
? $result
92+
: null;
93+
}
9994

100-
if (!$isQuoted && $result === 'NULL') {
101-
$result = null;
95+
$result .= $value[$i];
10296
}
103-
104-
return $result;
10597
}
10698
}

tests/ArrayParserTest.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,10 @@ public function testParser(): void
2525
$this->assertSame([0 => '1', 1 => '2', 2 => '3'], $arrayParse->parse('{1,2,3}'));
2626
$this->assertSame([0 => '1', 1 => '-2', 2 => null, 3 => '42'], $arrayParse->parse('{1,-2,NULL,42}'));
2727
$this->assertSame([[0 => 'text'], [0 => null], [0 => '1']], $arrayParse->parse('{{text},{NULL},{1}}'));
28+
$this->assertSame([0 => ''], $arrayParse->parse('{""}'));
2829
$this->assertSame(
29-
[[0 => '[",","null",true,"false","f"]'], 1 => ''],
30-
$arrayParse->parse('"{"[\",\",\"null\",true,\"false\",\"f\"]"}"')
30+
[0 => '[",","null",true,"false","f"]'],
31+
$arrayParse->parse('{"[\",\",\"null\",true,\"false\",\"f\"]"}')
3132
);
3233
}
3334
}

tests/ColumnSchemaTest.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,8 @@ public function testPhpTypeCast(): void
5252
'varbit_col' => 0b1_1100_1000, // 456
5353
'bigint_col' => 9_223_372_036_854_775_806,
5454
'intarray_col' => [1, -2, null, '42'],
55-
'numericarray_col' => [1.2, -2.2, null],
56-
'varchararray_col' => ['', 'some text', null],
55+
'numericarray_col' => [null, 1.2, -2.2, null, null],
56+
'varchararray_col' => ['', 'some text', '""', '\\\\', '[",","null",true,"false","f"]', null],
5757
'textarray2_col' => new ArrayExpression(null),
5858
'json_col' => [['a' => 1, 'b' => null, 'c' => [1, 3, 5]]],
5959
'jsonb_col' => new JsonExpression(new ArrayExpression([1, 2, 3])),
@@ -90,8 +90,8 @@ public function testPhpTypeCast(): void
9090
$this->assertSame(0b1_1100_1000, $varbitColPhpTypeCast);
9191
$this->assertSame(33.22, $numericColPhpTypeCast);
9292
$this->assertSame([1, -2, null, 42], $intArrayColPhpType);
93-
$this->assertSame([1.2, -2.2, null], $numericArrayColPhpTypeCast);
94-
$this->assertSame(['', 'some text', null], $varcharArrayColPhpTypeCast);
93+
$this->assertSame([null, 1.2, -2.2, null, null], $numericArrayColPhpTypeCast);
94+
$this->assertSame(['', 'some text', '""', '\\\\', '[",","null",true,"false","f"]', null], $varcharArrayColPhpTypeCast);
9595
$this->assertNull($textArray2ColPhpType);
9696
$this->assertSame([['a' => 1, 'b' => null, 'c' => [1, 3, 5]]], $jsonColPhpType);
9797
$this->assertSame(['1', '2', '3'], $jsonBColPhpType);

0 commit comments

Comments
 (0)