diff --git a/src/Optimizely/Utils/Validator.php b/src/Optimizely/Utils/Validator.php index 5b8dd7e0..7f10c2f1 100644 --- a/src/Optimizely/Utils/Validator.php +++ b/src/Optimizely/Utils/Validator.php @@ -70,17 +70,52 @@ public static function areAttributesValid($attributes) return count(array_filter(array_keys($attributes), 'is_string')) > 0; } + /** + * @param $value The value to validate. + * + * @return boolean Representing whether attribute's value is + * a number and not NAN, INF, -INF or greater than absolute limit of 2^53. + */ + public static function isFiniteNumber($value) + { + if (!(is_int($value) || is_float($value))) { + return false; + } + + if (is_nan($value) || is_infinite($value)) { + return false; + } + + if (abs($value) > pow(2, 53)) { + return false; + } + + return true; + } + /** * @param $attributeKey The key to validate. * @param $attributeValue The value to validate. * * @return boolean Representing whether attribute's key and value are - * valid for event payload or not. + * valid for event payload or not. Valid attribute key must be a string. + * Valid attribute value can be a string, bool, or a finite number. */ public static function isAttributeValid($attributeKey, $attributeValue) { - $validTypes = array('boolean', 'double', 'integer', 'string'); - return is_string($attributeKey) && in_array(gettype($attributeValue), $validTypes); + if (!is_string($attributeKey)) { + return false; + } + + if (is_string($attributeValue) || is_bool($attributeValue)) { + return true; + } + + if (is_int($attributeValue) || is_float($attributeValue)) { + return Validator::isFiniteNumber($attributeValue); + } + + return false; } /** diff --git a/tests/UtilsTests/ValidatorTest.php b/tests/UtilsTests/ValidatorTest.php index 715b54a9..8ecf8e87 100644 --- a/tests/UtilsTests/ValidatorTest.php +++ b/tests/UtilsTests/ValidatorTest.php @@ -123,6 +123,34 @@ public function testisAttributeValidAttributeWithInValidKeyValue() $this->assertFalse(Validator::isAttributeValid(5.5, 'value')); } + public function testIsFiniteNumberWithInvalidValues() + { + $this->assertFalse(Validator::IsFiniteNumber('HelloWorld')); + $this->assertFalse(Validator::IsFiniteNumber(true)); + $this->assertFalse(Validator::IsFiniteNumber(false)); + $this->assertFalse(Validator::IsFiniteNumber(null)); + $this->assertFalse(Validator::IsFiniteNumber((object)[])); + $this->assertFalse(Validator::IsFiniteNumber([])); + $this->assertFalse(Validator::IsFiniteNumber(INF)); + $this->assertFalse(Validator::IsFiniteNumber(-INF)); + $this->assertFalse(Validator::IsFiniteNumber(NAN)); + $this->assertFalse(Validator::IsFiniteNumber(pow(2,53) + 1)); + $this->assertFalse(Validator::IsFiniteNumber(-pow(2,53) - 1)); + $this->assertFalse(Validator::IsFiniteNumber(pow(2,53) + 2.0)); + $this->assertFalse(Validator::IsFiniteNumber(-pow(2,53) - 2.0)); + } + + public function testIsFiniteNumberWithValidValues() + { + $this->assertTrue(Validator::IsFiniteNumber(0)); + $this->assertTrue(Validator::IsFiniteNumber(5)); + $this->assertTrue(Validator::IsFiniteNumber(5.5)); + // float pow(2,53) + 1.0 evaluates to float pow(2,53) + $this->assertTrue(Validator::IsFiniteNumber(pow(2,53) + 1.0)); + $this->assertTrue(Validator::IsFiniteNumber(-pow(2,53) - 1.0)); + $this->assertTrue(Validator::IsFiniteNumber(pow(2,53))); + } + public function testAreEventTagsValidValidEventTags() { // Empty attributes