Skip to content

Commit 0ba4748

Browse files
authored
Improve performance of ArrayParser::parse() method (#477)
1 parent 193b83a commit 0ba4748

3 files changed

Lines changed: 56 additions & 37 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
## 2.0.1 under development
44

5-
- no changes in this release.
5+
- Enh #477: Improve performance of `ArrayParser::parse()` method (@Tigrov)
66

77
## 2.0.0 December 05, 2025
88

src/Data/ArrayParser.php

Lines changed: 12 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,11 @@
44

55
namespace Yiisoft\Db\Pgsql\Data;
66

7-
use function in_array;
7+
use function preg_match;
8+
use function strcspn;
9+
use function stripcslashes;
10+
use function strlen;
11+
use function substr;
812

913
/**
1014
* Array representation to PHP array parser for PostgreSQL Server.
@@ -64,31 +68,21 @@ private function parseArray(string $value, int &$i = 0): array
6468
*/
6569
private function parseQuotedString(string $value, int &$i): string
6670
{
67-
for ($result = '', ++$i;; ++$i) {
68-
if ($value[$i] === '\\') {
69-
++$i;
70-
} elseif ($value[$i] === '"') {
71-
++$i;
72-
return $result;
73-
}
71+
preg_match('/(?>[^"\\\\]+|\\\\.)*/', $value, $matches, 0, $i + 1);
72+
$i += strlen($matches[0]) + 2;
7473

75-
$result .= $value[$i];
76-
}
74+
return stripcslashes($matches[0]);
7775
}
7876

7977
/**
8078
* Parses unquoted string.
8179
*/
8280
private function parseUnquotedString(string $value, int &$i): ?string
8381
{
84-
for ($result = '';; ++$i) {
85-
if (in_array($value[$i], [',', '}'], true)) {
86-
return $result !== 'NULL'
87-
? $result
88-
: null;
89-
}
82+
$length = strcspn($value, ',}', $i);
83+
$result = substr($value, $i, $length);
84+
$i += $length;
9085

91-
$result .= $value[$i];
92-
}
86+
return $result !== 'NULL' ? $result : null;
9387
}
9488
}

tests/ArrayParserTest.php

Lines changed: 43 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,33 +4,58 @@
44

55
namespace Yiisoft\Db\Pgsql\Tests;
66

7+
use PHPUnit\Framework\Attributes\DataProvider;
78
use PHPUnit\Framework\TestCase;
89
use Yiisoft\Db\Pgsql\Data\ArrayParser;
910

1011
/**
1112
* @group pgsql
12-
*
13-
* @psalm-suppress PropertyNotSetInConstructor
1413
*/
1514
final class ArrayParserTest extends TestCase
1615
{
17-
public function testParser(): void
16+
public static function parserProvider(): iterable
1817
{
19-
$arrayParse = new ArrayParser();
20-
21-
$this->assertSame([0 => null, 1 => null], $arrayParse->parse('{NULL,NULL}'));
22-
$this->assertSame([], $arrayParse->parse('{}'));
23-
$this->assertSame([0 => null, 1 => null], $arrayParse->parse('{,}'));
24-
$this->assertSame([0 => '1', 1 => '2', 2 => '3'], $arrayParse->parse('{1,2,3}'));
25-
$this->assertSame([0 => '1', 1 => '-2', 2 => null, 3 => '42'], $arrayParse->parse('{1,-2,NULL,42}'));
26-
$this->assertSame([[0 => 'text'], [0 => null], [0 => '1']], $arrayParse->parse('{{text},{NULL},{1}}'));
27-
$this->assertSame([0 => ''], $arrayParse->parse('{""}'));
28-
$this->assertSame(
29-
[0 => '[",","null",true,"false","f"]'],
30-
$arrayParse->parse('{"[\",\",\"null\",true,\"false\",\"f\"]"}'),
31-
);
32-
18+
yield [[], '{}'];
19+
yield [[''], '{""}'];
20+
yield [[null, null], '{NULL,NULL}'];
21+
yield [[null, null], '{,}'];
22+
yield [
23+
["a\nb"],
24+
"{\"a\nb\"}",
25+
];
26+
yield [
27+
['1', '2', '3'],
28+
'{1,2,3}',
29+
];
30+
yield [
31+
['1', '-2', null, '42'],
32+
'{1,-2,NULL,42}',
33+
];
34+
yield [
35+
[['text'], [null], ['1']],
36+
'{{text},{NULL},{1}}',
37+
];
38+
yield [
39+
[',', '}', '"', '\\', '"\\,}', 'NULL', 't', 'f'],
40+
'{",","}","\\"","\\\\","\\"\\\\,}","NULL",t,f}',
41+
];
42+
yield [
43+
['[",","null",true,"false","f"]'],
44+
'{"[\",\",\"null\",true,\"false\",\"f\"]"}',
45+
];
46+
// Multibyte strings
47+
yield [
48+
['', '👍🏻', 'multibyte строка我👍🏻', 'נטשופ צרכנות'],
49+
'{我,👍🏻,"multibyte строка我👍🏻","נטשופ צרכנות"}',
50+
];
3351
// Similar cases can be in default values
34-
$this->assertSame(null, $arrayParse->parse("'{one,two}'::text[]"));
52+
yield [null, "'{one,two}'::text[]"];
53+
}
54+
55+
#[DataProvider('parserProvider')]
56+
public function testParser(?array $expected, string $value): void
57+
{
58+
$arrayParse = new ArrayParser();
59+
$this->assertSame($expected, $arrayParse->parse($value));
3560
}
3661
}

0 commit comments

Comments
 (0)