From eddc8c24330dcf93702e059cb158f4bab53409a2 Mon Sep 17 00:00:00 2001 From: Simon Podlipsky Date: Mon, 21 Oct 2024 12:31:48 +0200 Subject: [PATCH] fix: support for more complex Array Tuple params (#268) --- src/Param/ParamValueConverterRegistry.php | 54 ++++++++++++++++--- .../Param/ParamValueConverterRegistryTest.php | 6 +++ 2 files changed, 53 insertions(+), 7 deletions(-) diff --git a/src/Param/ParamValueConverterRegistry.php b/src/Param/ParamValueConverterRegistry.php index 0da90e8..fa102bf 100644 --- a/src/Param/ParamValueConverterRegistry.php +++ b/src/Param/ParamValueConverterRegistry.php @@ -15,10 +15,12 @@ use function explode; use function implode; use function in_array; +use function is_array; use function is_string; use function json_encode; use function sprintf; use function str_replace; +use function strlen; use function strtolower; use function trim; @@ -101,7 +103,7 @@ public function __construct() return $v; } - $types = array_map(static fn ($type) => explode(' ', trim($type))[1], explode(',', $type->params)); + $types = array_map(static fn ($type) => explode(' ', trim($type))[1], $this->splitTypes($type->params)); return sprintf('[%s]', implode(',', array_map( fn (array $row) => sprintf('(%s)', implode(',', array_map( @@ -167,17 +169,23 @@ public function __construct() return $this->get($innerType)($v, $innerType, true); }, $v), )), - 'Tuple' => function (array|string $v, Type $type) { - if (is_string($v)) { + 'Tuple' => function (mixed $v, Type $type) { + if (! is_array($v)) { return $v; } - $types = array_map(static fn ($p) => trim($p), explode(',', $type->params)); + $innerTypes = $this->splitTypes($type->params); - return '(' . implode( + $innerExpression = implode( ',', - array_map(fn (mixed $i) => $this->get($types[$i])($v[$i], null, true), array_keys($v)), - ) . ')'; + array_map(function (int $i) use ($innerTypes, $v) { + $innerType = Type::fromString($innerTypes[$i]); + + return $this->get($innerType)($v[$i], $innerType, true); + }, array_keys($v)), + ); + + return '(' . $innerExpression . ')'; }, ]; $this->registry = $registry; @@ -254,4 +262,36 @@ private static function dateIntervalConverter(): Closure { return static fn (int|float $v) => $v; } + + /** @return list */ + private function splitTypes(string $types): array + { + $result = []; + $depth = 0; + $current = ''; + + for ($i = 0; $i < strlen($types); $i++) { + $char = $types[$i]; + if ($char === '(') { + $depth++; + } elseif ($char === ')') { + $depth--; + } elseif ($char === ',' && $depth === 0) { + $result[] = $current; + $current = ''; + + continue; + } + + $current .= $char; + } + + $current = trim($current); + + if ($current !== '') { + $result[] = $current; + } + + return $result; + } } diff --git a/tests/Param/ParamValueConverterRegistryTest.php b/tests/Param/ParamValueConverterRegistryTest.php index 27b78ff..282dbe9 100644 --- a/tests/Param/ParamValueConverterRegistryTest.php +++ b/tests/Param/ParamValueConverterRegistryTest.php @@ -113,6 +113,12 @@ public static function providerConvert(): Generator yield 'Array LC' => ['Array(LowCardinality(String))', "['foo','bar']", "['foo','bar']"]; yield 'Array (array)' => ['Array(String)', ['foo', 'bar'], "['foo','bar']"]; yield 'Array Tuple' => ['Array(Tuple(String, String))', [['foo', 'bar']], "[('foo','bar')]"]; + yield 'Array Tuple Complex' => [ + 'Array(Tuple(Tuple(UInt32, UUID), String))', + [[[1, '084caa96-915b-449d-8fc6-0292c73d6399'], 'bar']], + "[((1,'084caa96-915b-449d-8fc6-0292c73d6399'),'bar')]", + ]; + yield 'Tuple' => ['Tuple(String, Int8)', "('k',1)", "('k',1)"]; yield 'Tuple (array)' => ['Tuple(String, Int8)', ['k', 1], "('k',1)"];