diff --git a/CHANGELOG.md b/CHANGELOG.md index e2fff134d..f5d39759e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Phony changelog +## 4.0.1 (2020-08-29) + +- **[FIXED]** Nullable variadics can now be mocked ([#248] - thanks [@keksa]). + +[#248]: https://github.com/eloquent/phony/issues/248 + ## 4.0.0 (2019-12-31) - **[BC BREAK]** PHP 7.1 is no longer supported. @@ -592,6 +598,8 @@ and version constraint will need to be updated: - **[NEW]** Initial implementation. + + [@jmalloc]: https://github.com/jmalloc [@keksa]: https://github.com/keksa [@pmall]: https://github.com/pmall diff --git a/src/Reflection/FunctionSignatureInspector.php b/src/Reflection/FunctionSignatureInspector.php index 04d05874c..8f51de962 100644 --- a/src/Reflection/FunctionSignatureInspector.php +++ b/src/Reflection/FunctionSignatureInspector.php @@ -91,10 +91,15 @@ public function signature(ReflectionFunctionAbstract $function): array } $byReference = $match[4]; + $isVariadic = $parameter->isVariadic(); - if ($parameter->isVariadic()) { + if ($isVariadic) { $variadic = '...'; $optional = false; + + if ($match[3]) { + $typehint = '?' . $typehint; + } } else { $variadic = ''; $optional = 'optional' === $match[1]; @@ -107,7 +112,7 @@ public function signature(ReflectionFunctionAbstract $function): array $defaultValue = ' = ' . var_export($parameter->getDefaultValue(), true); } - } elseif ($optional || $match[3]) { + } elseif (!$isVariadic && ($optional || $match[3])) { $defaultValue = ' = null'; } else { $defaultValue = ''; diff --git a/test/fixture/hook-generator/variadics-nullable-type/callback.php b/test/fixture/hook-generator/variadics-nullable-type/callback.php new file mode 100644 index 000000000..bb486c286 --- /dev/null +++ b/test/fixture/hook-generator/variadics-nullable-type/callback.php @@ -0,0 +1,5 @@ + 0) { + $arguments[] = $a0; + } + if ($argumentCount > 1) { + $arguments[] = $a1; + } + + for ($i = 2; $i < $argumentCount; ++$i) { + $arguments[] = $a2[$i - 2]; + } + + $name = 'foo\\bar\\functionname'; + + if ( + !isset( + \Eloquent\Phony\Hook\FunctionHookManager::$hooks[$name]['callback'] + ) + ) { + return \functionName(...$arguments); + } + + $callback = + \Eloquent\Phony\Hook\FunctionHookManager::$hooks[$name]['callback']; + + if ($callback instanceof \Eloquent\Phony\Invocation\Invocable) { + return $callback->invokeWith($arguments); + } + + return $callback(...$arguments); +} diff --git a/test/fixture/mock-generator/variadics/builder.php b/test/fixture/mock-generator/variadics/builder.php index ebd449cac..8a10544c4 100644 --- a/test/fixture/mock-generator/variadics/builder.php +++ b/test/fixture/mock-generator/variadics/builder.php @@ -5,6 +5,7 @@ 'methodA' => function ($a, $b, ...$c) {}, 'methodB' => function ($a, $b, stdClass ...$c) {}, 'methodC' => function ($a, $b, &...$c) {}, + 'methodD' => function ($a, $b, ?stdClass...$c) {}, ] ); diff --git a/test/fixture/mock-generator/variadics/expected.php b/test/fixture/mock-generator/variadics/expected.php index 08c64380b..e2876c49a 100644 --- a/test/fixture/mock-generator/variadics/expected.php +++ b/test/fixture/mock-generator/variadics/expected.php @@ -101,6 +101,38 @@ public function methodC( return $result; } + public function methodD( + $a0, + $a1, + ?\stdClass ...$a2 + ) { + $argumentCount = \func_num_args(); + $arguments = []; + + if ($argumentCount > 0) { + $arguments[] = $a0; + } + if ($argumentCount > 1) { + $arguments[] = $a1; + } + + for ($i = 2; $i < $argumentCount; ++$i) { + $arguments[] = $a2[$i - 2]; + } + + if (!$this->_handle) { + $result = null; + + return $result; + } + + $result = $this->_handle->spy(__FUNCTION__)->invokeWith( + new \Eloquent\Phony\Call\Arguments($arguments) + ); + + return $result; + } + private static $_uncallableMethods = []; private static $_traitMethods = []; private static $_customMethods = []; diff --git a/test/src/Test/TestInterfaceWithVariadicParameterWithNullableType.php b/test/src/Test/TestInterfaceWithVariadicParameterWithNullableType.php new file mode 100644 index 000000000..20e9bdcae --- /dev/null +++ b/test/src/Test/TestInterfaceWithVariadicParameterWithNullableType.php @@ -0,0 +1,12 @@ +assertSame([$a, $b], $handle->get()->method($a, $b)); } + public function testVariadicParameterMockingWithNullableType() + { + $handle = mock(TestInterfaceWithVariadicParameterWithNullableType::class); + $handle->method->does( + function () { + return func_get_args(); + } + ); + $a = (object) []; + $b = null; + $c = (object) []; + + $this->assertSame([$a, $b, $c], $handle->get()->method($a, $b, $c)); + } + public function testVariadicParameterMockingByReference() { $handle = mock(TestInterfaceWithVariadicParameterByReference::class);