diff --git a/CHANGELOG.md b/CHANGELOG.md index 63c341b1..0ab1cd13 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,8 @@ ## 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 diff --git a/src/Data/ArrayParser.php b/src/Data/ArrayParser.php index 1866ec0c..ac6e2bf9 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 990faa27..ee74e9ad 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 b9115af5..d240eab7 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)); } }