From 694698cce0da90902338ae8460c72cb39e7215bb Mon Sep 17 00:00:00 2001 From: Tim Otten Date: Thu, 16 Feb 2023 16:43:46 -0800 Subject: [PATCH] castTypeSoftly() - Support magic floats and magic ints --- Civi/Api4/Utils/ReflectionUtils.php | 30 +++++++-- .../api/v4/Action/AbstractActionTest.php | 66 +++++++++++++++++++ 2 files changed, 91 insertions(+), 5 deletions(-) diff --git a/Civi/Api4/Utils/ReflectionUtils.php b/Civi/Api4/Utils/ReflectionUtils.php index 9703d2626b..409c87db04 100644 --- a/Civi/Api4/Utils/ReflectionUtils.php +++ b/Civi/Api4/Utils/ReflectionUtils.php @@ -217,19 +217,26 @@ class ReflectionUtils { } /** - * Cast the $value to the preferred $type. + * Cast the $value to the preferred $type (if we're fairly confident). * * This is like PHP's `settype()` but totally not. It only casts in narrow circumstances. - * This reflects an opinion that some casts are better than others. + * This reflects an opinion that some castings are better than others. * - * - Good: 1 => TRUE - * - Bad: 'hello' => TRUE + * These will be converted: + * + * cast('123', 'int') => 123 + * cast('123.4', 'float') => 123.4 + * cast('0', 'bool') => FALSE + * cast(1, 'bool') => TRUE + * + * However, a string like 'hello' will never cast to bool, int, or float -- because that's + * a senseless request. We'll leave that to someone else to figure. * * @param mixed $value * @param array $paramInfo * @return mixed * If the $value is agreeable to casting according to a type-rule from $paramInfo, then - * we return the converted value. Otherwise, leave return the original value. + * we return the converted value. Otherwise, return the original value. */ public static function castTypeSoftly($value, array $paramInfo) { if (count($paramInfo['type'] ?? []) !== 1) { @@ -243,6 +250,19 @@ class ReflectionUtils { return (bool) $value; } break; + + case 'int': + if (is_numeric($value)) { + return (int) $value; + } + break; + + case 'float': + if (is_numeric($value)) { + return (float) $value; + } + break; + } return $value; diff --git a/tests/phpunit/api/v4/Action/AbstractActionTest.php b/tests/phpunit/api/v4/Action/AbstractActionTest.php index ee028fd7c7..a34856f7b0 100644 --- a/tests/phpunit/api/v4/Action/AbstractActionTest.php +++ b/tests/phpunit/api/v4/Action/AbstractActionTest.php @@ -40,6 +40,26 @@ class AbstractActionTest extends Api4TestBase { */ protected $magicBool; + /** + * @var float + */ + protected $simpleFloat; + + /** + * @var float + */ + protected $magicFloat; + + /** + * @var int + */ + protected $simpleInt; + + /** + * @var int + */ + protected $magicInt; + /** * @param bool $simpleBool */ @@ -48,6 +68,22 @@ class AbstractActionTest extends Api4TestBase { return $this; } + /** + * @param float $simpleFloat + */ + public function setSimpleFloat(float $simpleFloat) { + $this->simpleFloat = $simpleFloat; + return $this; + } + + /** + * @param int $simpleInt + */ + public function setSimpleInt(int $simpleInt) { + $this->simpleInt = $simpleInt; + return $this; + } + /** * @param \Civi\Api4\Generic\Result $result */ @@ -71,6 +107,36 @@ class AbstractActionTest extends Api4TestBase { ], ]; + $exs['float'] = [ + ['simpleFloat', 'magicFloat'], + [ + // Each item is an example: [$inputValue, $expectValue] + [0, 0.0], + ['0', 0.0], + [100, 100.0], + ['200', 200.0], + [300.5, 300.5], + ['400.6', 400.6], + ], + ]; + + $exs['int'] = [ + ['simpleInt', 'magicInt'], + [ + // Each item is an example: [$inputValue, $expectValue] + [0, 0], + ['0', 0], + [100, 100], + ['200', 200], + ], + ]; + + // Magic fields are nullable. Not saying that's good or bad. It just is. + $exs['null'] = [ + ['magicBool', 'magicFloat', 'magicInt'], + [[NULL, NULL]], + ]; + return $exs; } -- 2.25.1