Browse Source

Add action float, int and bool type coercion for passed parameters

Corey Taylor 4 years ago
parent
commit
d8656d0d02

+ 6 - 0
src/Controller/ControllerFactory.php

@@ -249,6 +249,12 @@ class ControllerFactory implements ControllerFactoryInterface, RequestHandlerInt
         switch ($type->getName()) {
             case 'string':
                 return $argument;
+            case 'float':
+                return is_numeric($argument) ? (float)$argument : null;
+            case 'int':
+                return filter_var($argument, FILTER_VALIDATE_INT, FILTER_NULL_ON_FAILURE);
+            case 'bool':
+                return $argument === '0' ? false : ($argument === '1' ? true : null);
         }
 
         return null;

+ 115 - 6
tests/TestCase/Controller/ControllerFactoryTest.php

@@ -674,23 +674,132 @@ class ControllerFactoryTest extends TestCase
     }
 
     /**
-     * Test that required int (non-string)
+     * Test that coercing string to float, int and bool params
      */
-    public function testInvokeRequiredIntParam(): void
+    public function testInvokePassedParametersCoercion(): void
     {
         $request = new ServerRequest([
-            'url' => 'test_plugin_three/dependencies/requiredInt',
+            'url' => 'test_plugin_three/dependencies/requiredTyped',
             'params' => [
                 'plugin' => null,
                 'controller' => 'Dependencies',
-                'action' => 'requiredInt',
-                'pass' => ['one'],
+                'action' => 'requiredTyped',
+                'pass' => ['1.0', '2', '0'],
+            ],
+        ]);
+        $controller = $this->factory->create($request);
+
+        $result = $this->factory->invoke($controller);
+        $data = json_decode((string)$result->getBody(), true);
+
+        $this->assertNotNull($data);
+        $this->assertSame(['one' => 1.0, 'two' => 2, 'three' => false], $data);
+    }
+
+    /**
+     * Test that default values work for typed parameters
+     */
+    public function testInvokeOptionalTypedParam(): void
+    {
+        $request = new ServerRequest([
+            'url' => 'test_plugin_three/dependencies/optionalTyped',
+            'params' => [
+                'plugin' => null,
+                'controller' => 'Dependencies',
+                'action' => 'optionalTyped',
+                'pass' => ['1.0'],
+            ],
+        ]);
+        $controller = $this->factory->create($request);
+
+        $result = $this->factory->invoke($controller);
+        $data = json_decode((string)$result->getBody(), true);
+
+        $this->assertNotNull($data);
+        $this->assertSame(['one' => 1.0, 'two' => 2, 'three' => true], $data);
+    }
+
+    /**
+     * Test using invalid value for supported type
+     */
+    public function testInvokePassedParametersUnsupportedFloatCoercion(): void
+    {
+        $request = new ServerRequest([
+            'url' => 'test_plugin_three/dependencies/requiredTyped',
+            'params' => [
+                'plugin' => null,
+                'controller' => 'Dependencies',
+                'action' => 'requiredTyped',
+                'pass' => ['true', '2', '1'],
+            ],
+        ]);
+        $controller = $this->factory->create($request);
+
+        $this->expectException(MissingActionException::class);
+        $this->expectExceptionMessage('Action Dependencies::requiredTyped() cannot coerce "true" to `float` for parameter `one`');
+        $this->factory->invoke($controller);
+    }
+
+    /**
+     * Test using invalid value for supported type
+     */
+    public function testInvokePassedParametersUnsupportedIntCoercion(): void
+    {
+        $request = new ServerRequest([
+            'url' => 'test_plugin_three/dependencies/requiredTyped',
+            'params' => [
+                'plugin' => null,
+                'controller' => 'Dependencies',
+                'action' => 'requiredTyped',
+                'pass' => ['1', '2.0', '1'],
+            ],
+        ]);
+        $controller = $this->factory->create($request);
+
+        $this->expectException(MissingActionException::class);
+        $this->expectExceptionMessage('Action Dependencies::requiredTyped() cannot coerce "2.0" to `int` for parameter `two`');
+        $this->factory->invoke($controller);
+    }
+
+    /**
+     * Test using invalid value for supported type
+     */
+    public function testInvokePassedParametersUnsupportedBoolCoercion(): void
+    {
+        $request = new ServerRequest([
+            'url' => 'test_plugin_three/dependencies/requiredTyped',
+            'params' => [
+                'plugin' => null,
+                'controller' => 'Dependencies',
+                'action' => 'requiredTyped',
+                'pass' => ['1', '1', 'true'],
+            ],
+        ]);
+        $controller = $this->factory->create($request);
+
+        $this->expectException(MissingActionException::class);
+        $this->expectExceptionMessage('Action Dependencies::requiredTyped() cannot coerce "true" to `bool` for parameter `three`');
+        $this->factory->invoke($controller);
+    }
+
+    /**
+     * Test using an unsupported type.
+     */
+    public function testInvokePassedParamUnsupportedType(): void
+    {
+        $request = new ServerRequest([
+            'url' => 'test_plugin_three/dependencies/unsupportedTyped',
+            'params' => [
+                'plugin' => null,
+                'controller' => 'Dependencies',
+                'action' => 'unsupportedTyped',
+                'pass' => ['test'],
             ],
         ]);
         $controller = $this->factory->create($request);
 
         $this->expectException(MissingActionException::class);
-        $this->expectExceptionMessage('Action Dependencies::requiredInt() cannot coerce "one" to `int` for parameter `one`');
+        $this->expectExceptionMessage('Action Dependencies::unsupportedTyped() cannot coerce "test" to `array` for parameter `one`');
         $this->factory->invoke($controller);
     }
 

+ 15 - 5
tests/test_app/TestApp/Controller/DependenciesController.php

@@ -43,6 +43,21 @@ class DependenciesController extends Controller
         return $this->response->withStringBody(json_encode(compact('str')));
     }
 
+    public function requiredTyped(float $one, int $two, bool $three)
+    {
+        return $this->response->withStringBody(json_encode(compact('one', 'two', 'three'), JSON_PRESERVE_ZERO_FRACTION));
+    }
+
+    public function optionalTyped(float $one = 1.0, int $two = 2, bool $three = true)
+    {
+        return $this->response->withStringBody(json_encode(compact('one', 'two', 'three'), JSON_PRESERVE_ZERO_FRACTION));
+    }
+
+    public function unsupportedTyped(array $one)
+    {
+        return $this->response->withStringBody(json_encode(compact('one')));
+    }
+
     /**
      * @param mixed $any
      * @return \Cake\Http\Response
@@ -85,9 +100,4 @@ class DependenciesController extends Controller
     {
         return $this->response->withStringBody(json_encode(compact('one')));
     }
-
-    public function requiredInt(int $one)
-    {
-        return $this->response->withStringBody(json_encode(compact('one')));
-    }
 }