Skip to content
Merged
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
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## 2.0.1 under development

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

## 2.0.0 December 05, 2025

Expand Down
30 changes: 12 additions & 18 deletions src/Data/ArrayParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@

namespace Yiisoft\Db\Pgsql\Data;

use function in_array;
use function preg_match;
use function strcspn;
use function stripcslashes;
use function strlen;
use function substr;

/**
* Array representation to PHP array parser for PostgreSQL Server.
Expand Down Expand Up @@ -64,31 +68,21 @@ private function parseArray(string $value, int &$i = 0): array
*/
private function parseQuotedString(string $value, int &$i): string
{
for ($result = '', ++$i;; ++$i) {
if ($value[$i] === '\\') {
++$i;
} elseif ($value[$i] === '"') {
++$i;
return $result;
}
preg_match('/(?>[^"\\\\]+|\\\\.)*/', $value, $matches, 0, $i + 1);
$i += strlen($matches[0]) + 2;

$result .= $value[$i];
}
return stripcslashes($matches[0]);
}

/**
* Parses unquoted string.
*/
private function parseUnquotedString(string $value, int &$i): ?string
{
for ($result = '';; ++$i) {
if (in_array($value[$i], [',', '}'], true)) {
return $result !== 'NULL'
? $result
: null;
}
$length = strcspn($value, ',}', $i);
$result = substr($value, $i, $length);
$i += $length;

$result .= $value[$i];
}
return $result !== 'NULL' ? $result : null;
}
}
61 changes: 43 additions & 18 deletions tests/ArrayParserTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,33 +4,58 @@

namespace Yiisoft\Db\Pgsql\Tests;

use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\TestCase;
use Yiisoft\Db\Pgsql\Data\ArrayParser;

/**
* @group pgsql
*
* @psalm-suppress PropertyNotSetInConstructor
*/
final class ArrayParserTest extends TestCase
{
public function testParser(): void
public static function parserProvider(): iterable
{
$arrayParse = new ArrayParser();

$this->assertSame([0 => null, 1 => null], $arrayParse->parse('{NULL,NULL}'));
$this->assertSame([], $arrayParse->parse('{}'));
$this->assertSame([0 => null, 1 => null], $arrayParse->parse('{,}'));
$this->assertSame([0 => '1', 1 => '2', 2 => '3'], $arrayParse->parse('{1,2,3}'));
$this->assertSame([0 => '1', 1 => '-2', 2 => null, 3 => '42'], $arrayParse->parse('{1,-2,NULL,42}'));
$this->assertSame([[0 => 'text'], [0 => null], [0 => '1']], $arrayParse->parse('{{text},{NULL},{1}}'));
$this->assertSame([0 => ''], $arrayParse->parse('{""}'));
$this->assertSame(
[0 => '[",","null",true,"false","f"]'],
$arrayParse->parse('{"[\",\",\"null\",true,\"false\",\"f\"]"}'),
);

yield [[], '{}'];
yield [[''], '{""}'];
yield [[null, null], '{NULL,NULL}'];
yield [[null, null], '{,}'];
yield [
["a\nb"],
"{\"a\nb\"}",
];
yield [
['1', '2', '3'],
'{1,2,3}',
];
yield [
['1', '-2', null, '42'],
'{1,-2,NULL,42}',
];
yield [
[['text'], [null], ['1']],
'{{text},{NULL},{1}}',
];
yield [
[',', '}', '"', '\\', '"\\,}', 'NULL', 't', 'f'],
'{",","}","\\"","\\\\","\\"\\\\,}","NULL",t,f}',
];
yield [
['[",","null",true,"false","f"]'],
'{"[\",\",\"null\",true,\"false\",\"f\"]"}',
];
// Multibyte strings
yield [
['我', '👍🏻', 'multibyte строка我👍🏻', 'נטשופ צרכנות'],
'{我,👍🏻,"multibyte строка我👍🏻","נטשופ צרכנות"}',
];
// Similar cases can be in default values
$this->assertSame(null, $arrayParse->parse("'{one,two}'::text[]"));
yield [null, "'{one,two}'::text[]"];
}

#[DataProvider('parserProvider')]
public function testParser(?array $expected, string $value): void
{
$arrayParse = new ArrayParser();
$this->assertSame($expected, $arrayParse->parse($value));
}
}
Loading