From 650269a18522db43ad900b2b6cd0e10fae5d9786 Mon Sep 17 00:00:00 2001 From: Henrique Moody Date: Tue, 10 Feb 2026 05:28:17 +0100 Subject: [PATCH] Delegate with null pipe for incompatible value types Separate handling of invalid formatters from non-scalar values. When a formatter is recognized but the value type is incompatible, pass null to the next modifier instead of the original pipe. This prevents incorrectly suggesting the next modifier should retry with the same formatter. Added tests using data providers to verify the distinction between pipe not being valid and modifier being unable to modify values. Assisted-by: OpenCode (GLM-4.7) --- src/Modifiers/FormatterModifier.php | 6 ++- tests/Helper/TestingModifier.php | 4 ++ .../Unit/Modifiers/FormatterModifierTest.php | 49 +++++++++++++++++++ 3 files changed, 58 insertions(+), 1 deletion(-) diff --git a/src/Modifiers/FormatterModifier.php b/src/Modifiers/FormatterModifier.php index 6e1d8f8..bedb6f4 100644 --- a/src/Modifiers/FormatterModifier.php +++ b/src/Modifiers/FormatterModifier.php @@ -40,10 +40,14 @@ public function modify(mixed $value, string|null $pipe): string $formatter = $this->tryToCreateFormatter($name, $arguments); - if ($formatter === null || !is_scalar($value)) { + if ($formatter === null) { return $this->nextModifier->modify($value, $pipe); } + if (!is_scalar($value)) { + return $this->nextModifier->modify($value, null); + } + return $formatter->format((string) $value); } diff --git a/tests/Helper/TestingModifier.php b/tests/Helper/TestingModifier.php index cdee9fc..57078b6 100644 --- a/tests/Helper/TestingModifier.php +++ b/tests/Helper/TestingModifier.php @@ -17,12 +17,16 @@ final class TestingModifier implements Modifier { + public string|null $lastPipe = null; + public function __construct(private string|null $customResult = null) { } public function modify(mixed $value, string|null $pipe): string { + $this->lastPipe = $pipe; + return $this->customResult ?? ($pipe ? sprintf('%s(%s)', $pipe, print_r($value, true)) : print_r($value, true)); } } diff --git a/tests/Unit/Modifiers/FormatterModifierTest.php b/tests/Unit/Modifiers/FormatterModifierTest.php index 42525c9..f323e79 100644 --- a/tests/Unit/Modifiers/FormatterModifierTest.php +++ b/tests/Unit/Modifiers/FormatterModifierTest.php @@ -27,6 +27,36 @@ #[CoversClass(FormatterModifier::class)] final class FormatterModifierTest extends TestCase { + #[Test] + #[DataProvider('providerForNonScalarValues')] + public function itShouldDelegateWithNullPipeForNonScalarValues( + mixed $value, + string $pipe, + ): void { + $nextModifier = new TestingModifier('fallback'); + $modifier = new FormatterModifier($nextModifier); + + $result = $modifier->modify($value, $pipe); + + self::assertSame('fallback', $result); + self::assertNull($nextModifier->lastPipe); + } + + #[Test] + #[DataProvider('providerForInvalidFormatters')] + public function itShouldDelegateWithOriginalPipeForInvalidFormatters( + mixed $value, + string $pipe, + ): void { + $nextModifier = new TestingModifier('fallback'); + $modifier = new FormatterModifier($nextModifier); + + $result = $modifier->modify($value, $pipe); + + self::assertSame('fallback', $result); + self::assertSame($pipe, $nextModifier->lastPipe); + } + #[Test] public function itShouldDelegateWhenPipeIsNull(): void { @@ -261,4 +291,23 @@ public function itShouldHandleEmptyArguments(): void self::assertSame($expected, $actual); } + + /** @return array */ + public static function providerForNonScalarValues(): array + { + return [ + 'array value delegates with null pipe' => [['array'], 'date:Y/m/d'], + 'object value delegates with null pipe' => [(object) ['key' => 'value'], 'number:2'], + 'null value delegates with null pipe' => [null, 'mask:1-3'], + ]; + } + + /** @return array */ + public static function providerForInvalidFormatters(): array + { + return [ + 'unknown formatter delegates with original pipe' => ['test value', 'unknown:formatter'], + 'invalid number formatter delegates with original pipe' => ['test', 'number:invalid-decimal'], + ]; + } }