castTypeSoftly() - Support magic floats and magic ints
authorTim Otten <totten@civicrm.org>
Fri, 17 Feb 2023 00:43:46 +0000 (16:43 -0800)
committerTim Otten <totten@civicrm.org>
Fri, 17 Feb 2023 03:53:08 +0000 (19:53 -0800)
Civi/Api4/Utils/ReflectionUtils.php
tests/phpunit/api/v4/Action/AbstractActionTest.php

index 9703d2626b99cffc4fa83e088ae20ed5d407c185..409c87db04d55bcb3af96ed4ae9732aebbfa9b50 100644 (file)
@@ -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;
index ee028fd7c770cc8aedf9af9d861ad3b572ff1cce..a34856f7b0d51d49b5ed8b475554ea0a7be932d0 100644 (file)
@@ -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;
   }