From ebd56d9b262f6b210e98bdf6d96facc39c4784f7 Mon Sep 17 00:00:00 2001 From: Julian Vennen Date: Mon, 6 Jul 2026 11:44:45 +0200 Subject: [PATCH 1/2] Fix parsing arrays with a name that's a prefix of an existing array --- src/TomlKeystore.php | 2 +- tests/Unit/TomlDecodeTest.php | 8 ++++++++ tests/Unit/TomlErrorTest.php | 28 ++++++++++++++++++++++++++++ 3 files changed, 37 insertions(+), 1 deletion(-) diff --git a/src/TomlKeystore.php b/src/TomlKeystore.php index 1b86cbe..44c3233 100644 --- a/src/TomlKeystore.php +++ b/src/TomlKeystore.php @@ -242,7 +242,7 @@ protected function addArrayTableNode(ArrayTableNode $arrayTableNode): void } if ($index === 0 && ! $this->tables->filter( - static fn ($table) => str_starts_with((string) $table, $header))->isEmpty() + static fn ($table) => str_starts_with((string) $table, $header . "."))->isEmpty() ) { throw new TomlError('key duplication'); } diff --git a/tests/Unit/TomlDecodeTest.php b/tests/Unit/TomlDecodeTest.php index e0060be..2eed11b 100644 --- a/tests/Unit/TomlDecodeTest.php +++ b/tests/Unit/TomlDecodeTest.php @@ -63,6 +63,11 @@ function () { { "name": "plantain" } ] } + ], + "fruit": [ + { + "name": "strawberry" + } ] } JSON_STRING; @@ -111,6 +116,9 @@ function () { [[fruits.varieties]] name = "plantain" + +[[fruit]] # prefix of existing array +name = "strawberry" TOML_STRING; expect(toml_decode($toml))->toEqual(json_decode($json, false)) diff --git a/tests/Unit/TomlErrorTest.php b/tests/Unit/TomlErrorTest.php index f8628c5..5dbdd05 100644 --- a/tests/Unit/TomlErrorTest.php +++ b/tests/Unit/TomlErrorTest.php @@ -35,3 +35,31 @@ function () { expect(static fn () => toml_decode($toml, true))->toThrow(TomlError::class, $message); }); + +it('throws on invalid array order', + /** + * @throws TomlError + */ + function () { + + $message = <<<'MESSAGE' +Invalid TOML document: key duplication + +5: # This requires a to be an array table +6: [[a]] + ^ +7: y = 2 +MESSAGE; + + $toml = <<<'TOML_STRING' +# This creates a as a normal (non-array) table +[[a.b]] +x = 1 + +# This requires a to be an array table +[[a]] +y = 2 +TOML_STRING; + + expect(static fn () => toml_decode($toml, true))->toThrow(TomlError::class, $message); + }); From 7b0c08496bcb88b150a726861c2fa4200960a8a5 Mon Sep 17 00:00:00 2001 From: Roman Usachev Date: Fri, 7 Aug 2026 14:27:33 +0300 Subject: [PATCH 2/2] Fix array table prefix detection --- src/TomlKeystore.php | 2 +- tests/Unit/TomlDecodeTest.php | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/src/TomlKeystore.php b/src/TomlKeystore.php index 44c3233..0ddab20 100644 --- a/src/TomlKeystore.php +++ b/src/TomlKeystore.php @@ -234,7 +234,7 @@ protected function addArrayTableNode(ArrayTableNode $arrayTableNode): void continue; } - if (str_starts_with($header, $arrayTableHeader)) { + if (str_starts_with($header, $arrayTableHeader . '.')) { $key = $arrayTable.substr($header, strlen($arrayTableHeader)); break; diff --git a/tests/Unit/TomlDecodeTest.php b/tests/Unit/TomlDecodeTest.php index 2eed11b..a1e5dcb 100644 --- a/tests/Unit/TomlDecodeTest.php +++ b/tests/Unit/TomlDecodeTest.php @@ -125,6 +125,40 @@ function () { ->and(toml_decode($toml, true))->toEqual(json_decode($json, true)); }); +it('can decode array tables whose names share a prefix', + /** + * @throws TomlError + */ + function () { + $toml = <<<'TOML_STRING' +[[items]] +name = "first" + +[[itemsExtended]] +name = "second" + +[[itemsExtendedMore]] +name = "third" + +[[itemsExtendedMore.entries]] +name = "first entry" + +[itemsExtendedMore.entries.metadata] +type = "primary" + +[[itemsExtendedMore.entries]] +name = "second entry" + +[itemsExtendedMore.entries.metadata] +type = "secondary" +TOML_STRING; + + $decoded = toml_decode($toml, true); + + expect($decoded)->toHaveCount(3) + ->and($decoded['itemsExtendedMore'][0]['entries'][1]['metadata']['type'])->toBe('secondary'); + }); + it('can decode TOML datetime formats', /** * @throws TomlError