From d748fb2f52116eec359f1bfd686af721d958fcc6 Mon Sep 17 00:00:00 2001 From: Graham Campbell Date: Wed, 10 Jun 2026 23:08:29 +0100 Subject: [PATCH] Fixed 0.0 to be truthy in filters and logical operators, like every other number --- CHANGELOG.md | 1 + src/Utils.php | 2 +- tests/CompilerRuntimeTest.php | 41 +++++++++++++++++++++++++++++++++++ tests/UtilsTest.php | 28 ++++++++++++++++++++++++ 4 files changed, 71 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6476f17..0ac69b1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ * Changed sort(), sort_by(), max(), min(), max_by() and min_by() to order strings by code point. * Fixed max_by() and min_by() to error on mixed-type keys instead of returning arbitrary elements. * Fixed max() returning null or erroring when the first array element is falsy, e.g. max([0, 1]). +* Fixed 0.0 to be truthy in filters and logical operators, like every other number. * Fixed the compiled runtime to apply JMESPath truthiness to || and &&. * Fixed @(foo), foo[-] and oversized index literals to throw syntax errors. * Fixed PHP warnings emitted while parsing certain invalid expressions. diff --git a/src/Utils.php b/src/Utils.php index bfa1732..b4b17ca 100644 --- a/src/Utils.php +++ b/src/Utils.php @@ -22,7 +22,7 @@ class Utils public static function isTruthy($value) { if (!$value) { - return $value === 0 || $value === '0'; + return $value === 0 || $value === 0.0 || $value === '0'; } elseif ($value instanceof \stdClass) { return (bool) get_object_vars($value); } else { diff --git a/tests/CompilerRuntimeTest.php b/tests/CompilerRuntimeTest.php index 62d9f15..b14f57f 100644 --- a/tests/CompilerRuntimeTest.php +++ b/tests/CompilerRuntimeTest.php @@ -56,6 +56,47 @@ public function testRuntimesUseJmesPathTruthinessForEmptyObjects(): void } } + public function testRuntimesTreatFloatZeroAsTruthy(): void + { + $dir = $this->createTempDir(); + $data = ['orders' => [['price' => 0.0], ['price' => 1.0], ['price' => 2.0]]]; + + try { + foreach ([new AstRuntime(), new CompilerRuntime($dir)] as $runtime) { + $this->assertSame([0.0, 1.0, 2.0], $runtime('orders[?price].price', $data)); + $this->assertSame(0.0, $runtime('a || b', ['a' => 0.0, 'b' => 'fallback'])); + $this->assertSame('x', $runtime('a && b', ['a' => 0.0, 'b' => 'x'])); + $this->assertFalse($runtime('!a', ['a' => 0.0])); + } + } finally { + $this->removeTempDir($dir); + } + } + + public function testRuntimesApplyJmesPathTruthinessInFilters(): void + { + $dir = $this->createTempDir(); + $data = ['orders' => [ + ['price' => 0], + ['price' => 0.0], + ['price' => '0'], + ['price' => '0.0'], + ['price' => ''], + ['price' => null], + ['price' => false], + ['price' => []], + ['price' => new \stdClass()], + ]]; + + try { + foreach ([new AstRuntime(), new CompilerRuntime($dir)] as $runtime) { + $this->assertSame([0, 0.0, '0', '0.0'], $runtime('orders[?price].price', $data)); + } + } finally { + $this->removeTempDir($dir); + } + } + public function testRuntimesUseJsonSemanticEqualityForNumbers(): void { $dir = $this->createTempDir(); diff --git a/tests/UtilsTest.php b/tests/UtilsTest.php index 71770e5..427b896 100644 --- a/tests/UtilsTest.php +++ b/tests/UtilsTest.php @@ -116,6 +116,34 @@ public function testChecksIfObject($given, $result): void $this->assertSame($result, Utils::isObject($given)); } + public static function isTruthyProvider(): array + { + return [ + [0, true], + [0.0, true], + [-0.0, true], + ['0', true], + ['0.0', true], + [1, true], + ['a', true], + [[0], true], + [(object) ['a' => 1], true], + [null, false], + [false, false], + ['', false], + [[], false], + [new \stdClass(), false], + ]; + } + + /** + * @dataProvider isTruthyProvider + */ + public function testChecksTruthiness($given, bool $result): void + { + $this->assertSame($result, Utils::isTruthy($given)); + } + public function testHasStableSort(): void { $data = [new _TestStr(), new _TestStr(), 0, 10, 2];