From 426f4acbcb7981465c8ceda1fd9fb71b79a3075b Mon Sep 17 00:00:00 2001 From: Tigrov Date: Wed, 10 Dec 2025 10:53:43 +0700 Subject: [PATCH 1/3] Improve performance of `StructuredParser::parse()` method --- src/Data/ArrayParser.php | 4 +-- src/Data/StructuredParser.php | 28 ++++++++---------- tests/StructuredParserTest.php | 52 +++++++++++++++++++++++++--------- 3 files changed, 52 insertions(+), 32 deletions(-) diff --git a/src/Data/ArrayParser.php b/src/Data/ArrayParser.php index 1866ec0c1..ac6e2bf90 100644 --- a/src/Data/ArrayParser.php +++ b/src/Data/ArrayParser.php @@ -6,7 +6,7 @@ use function preg_match; use function strcspn; -use function stripcslashes; +use function stripslashes; use function strlen; use function substr; @@ -71,7 +71,7 @@ private function parseQuotedString(string $value, int &$i): string preg_match('/(?>[^"\\\\]+|\\\\.)*/', $value, $matches, 0, $i + 1); $i += strlen($matches[0]) + 2; - return stripcslashes($matches[0]); + return stripslashes($matches[0]); } /** diff --git a/src/Data/StructuredParser.php b/src/Data/StructuredParser.php index 990faa27d..ee74e9adc 100644 --- a/src/Data/StructuredParser.php +++ b/src/Data/StructuredParser.php @@ -4,7 +4,11 @@ namespace Yiisoft\Db\Pgsql\Data; -use function in_array; +use function preg_match; +use function strcspn; +use function stripslashes; +use function strlen; +use function substr; /** * Structured type representation to PHP array parser for PostgreSQL Server. @@ -58,16 +62,10 @@ private function parseComposite(string $value): 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 stripslashes($matches[0]); } /** @@ -75,12 +73,10 @@ private function parseQuotedString(string $value, int &$i): string */ private function parseUnquotedString(string $value, int &$i): string { - for ($result = '';; ++$i) { - if (in_array($value[$i], [',', ')'], true)) { - return $result; - } + $length = strcspn($value, ',)', $i); + $result = substr($value, $i, $length); + $i += $length; - $result .= $value[$i]; - } + return $result; } } diff --git a/tests/StructuredParserTest.php b/tests/StructuredParserTest.php index b9115af5d..d240eab75 100644 --- a/tests/StructuredParserTest.php +++ b/tests/StructuredParserTest.php @@ -4,6 +4,7 @@ namespace Yiisoft\Db\Pgsql\Tests; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Yiisoft\Db\Pgsql\Data\StructuredParser; @@ -12,21 +13,44 @@ */ final class StructuredParserTest extends TestCase { - public function testParser(): void + public static function parserProvider(): iterable { - $parser = new StructuredParser(); - - $this->assertSame([null], $parser->parse('()')); - $this->assertSame([0 => null, 1 => null], $parser->parse('(,)')); - $this->assertSame([0 => '10.0', 1 => 'USD'], $parser->parse('(10.0,USD)')); - $this->assertSame([0 => '1', 1 => '-2', 2 => null, 3 => '42'], $parser->parse('(1,-2,,42)')); - $this->assertSame([0 => ''], $parser->parse('("")')); - $this->assertSame( - [0 => '[",","null",true,"false","f"]'], - $parser->parse('("[\",\",\"null\",true,\"false\",\"f\"]")'), - ); - + yield [[null], '()']; + yield [[''], '("")']; + yield [[null, null], '(,)']; + yield [ + ["a\nb"], + "(\"a\nb\")", + ]; + yield [ + ['10.0', 'USD'], + '(10.0,USD)', + ]; + yield [ + ['1', '-2', null, '42'], + '(1,-2,,42)', + ]; + yield [ + [',', ')', '"', '\\', '"\\,)', 'NULL', 't', 'f'], + '(",",")","\\"","\\\\","\\"\\\\,)",NULL,t,f)', + ]; + yield [ + ['[",","null",true,"false","f"]'], + '("[\",\",\"null\",true,\"false\",\"f\"]")', + ]; + // Multibyte strings + yield [ + ['ๆˆ‘', '๐Ÿ‘๐Ÿป', 'multibyte ัั‚ั€ะพะบะฐๆˆ‘๐Ÿ‘๐Ÿป', 'ื ื˜ืฉื•ืค ืฆืจื›ื ื•ืช'], + '(ๆˆ‘,๐Ÿ‘๐Ÿป,"multibyte ัั‚ั€ะพะบะฐๆˆ‘๐Ÿ‘๐Ÿป","ื ื˜ืฉื•ืค ืฆืจื›ื ื•ืช")', + ]; // Default values can have any expressions - $this->assertSame(null, $parser->parse("'(10.0,USD)::structured_type'")); + yield [null, "'(10.0,USD)::structured_type'"]; + } + + #[DataProvider('parserProvider')] + public function testParser(?array $expected, string $value): void + { + $parser = new StructuredParser(); + $this->assertSame($expected, $parser->parse($value)); } } From c7ae3a9e7c26f3c27fdb7f1b94f9ddbbfa22954c Mon Sep 17 00:00:00 2001 From: Tigrov Date: Wed, 10 Dec 2025 11:01:21 +0700 Subject: [PATCH 2/3] Add line to CHANGELOG.md [skip ci] --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 63c341b13..ea478febb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## 2.0.1 under development - Enh #477: Improve performance of `ArrayParser::parse()` method (@Tigrov) +- Enh #478: Improve performance of `StructuredParser::parse()` method (@Tigrov) ## 2.0.0 December 05, 2025 From ea4fbad7d0d3d3678284c75065126165a6a7017f Mon Sep 17 00:00:00 2001 From: Tigrov Date: Wed, 10 Dec 2025 11:02:50 +0700 Subject: [PATCH 3/3] Update CHANGELOG.md [skip ci] --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ea478febb..0ab1cd132 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ ## 2.0.1 under development -- Enh #477: Improve performance of `ArrayParser::parse()` method (@Tigrov) +- Enh #477, #478: Improve performance of `ArrayParser::parse()` method (@Tigrov) - Enh #478: Improve performance of `StructuredParser::parse()` method (@Tigrov) ## 2.0.0 December 05, 2025