From f6fefd90d3eb301e19cfd33f09aa6b79ff64e056 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Paris?= Date: Sun, 11 Feb 2018 21:28:35 +0100 Subject: [PATCH 1/4] Extract type hint reference to a collaborator --- .../Doubler/Generator/Node/MethodNodeSpec.php | 2 +- .../Doubler/Generator/ClassCodeGenerator.php | 53 ++++++------------- .../Doubler/Generator/Node/MethodNode.php | 38 ++++++------- .../Doubler/Generator/TypeHintReference.php | 46 ++++++++++++++++ 4 files changed, 82 insertions(+), 57 deletions(-) create mode 100644 src/Prophecy/Doubler/Generator/TypeHintReference.php diff --git a/spec/Prophecy/Doubler/Generator/Node/MethodNodeSpec.php b/spec/Prophecy/Doubler/Generator/Node/MethodNodeSpec.php index 14cfe8dec..53d4f93e3 100644 --- a/spec/Prophecy/Doubler/Generator/Node/MethodNodeSpec.php +++ b/spec/Prophecy/Doubler/Generator/Node/MethodNodeSpec.php @@ -124,7 +124,7 @@ function it_does_not_have_return_type_by_default() function it_setReturnType_sets_return_type() { - $returnType = 'string'; + $returnType = 'array'; $this->setReturnType($returnType); diff --git a/src/Prophecy/Doubler/Generator/ClassCodeGenerator.php b/src/Prophecy/Doubler/Generator/ClassCodeGenerator.php index 7be7dfe3f..25665105d 100644 --- a/src/Prophecy/Doubler/Generator/ClassCodeGenerator.php +++ b/src/Prophecy/Doubler/Generator/ClassCodeGenerator.php @@ -19,6 +19,16 @@ */ class ClassCodeGenerator { + /** + * @var TypeHintReference + */ + private $typeHintReference; + + public function __construct(TypeHintReference $typeHintReference = null) + { + $this->typeHintReference = $typeHintReference ?: new TypeHintReference(); + } + /** * Generates PHP code for class node. * @@ -91,7 +101,8 @@ private function getReturnType(Node\MethodNode $method) private function generateArguments(array $arguments) { - return array_map(function (Node\ArgumentNode $argument) { + $typeHintReference = $this->typeHintReference; + return array_map(function (Node\ArgumentNode $argument) use ($typeHintReference) { $php = ''; if (version_compare(PHP_VERSION, '7.1', '>=')) { @@ -99,42 +110,10 @@ private function generateArguments(array $arguments) } if ($hint = $argument->getTypeHint()) { - switch ($hint) { - case 'array': - case 'callable': - $php .= $hint; - break; - - case 'iterable': - if (version_compare(PHP_VERSION, '7.1', '>=')) { - $php .= $hint; - break; - } - - $php .= '\\'.$hint; - break; - - case 'object': - if (version_compare(PHP_VERSION, '7.2', '>=')) { - $php .= $hint; - break; - } - - $php .= '\\'.$hint; - break; - - case 'string': - case 'int': - case 'float': - case 'bool': - if (version_compare(PHP_VERSION, '7.0', '>=')) { - $php .= $hint; - break; - } - // Fall-through to default case for PHP 5.x - - default: - $php .= '\\'.$hint; + if ($typeHintReference->isBuiltInParamTypeHint($hint)) { + $php .= $hint; + } else { + $php .= '\\'.$hint; } } diff --git a/src/Prophecy/Doubler/Generator/Node/MethodNode.php b/src/Prophecy/Doubler/Generator/Node/MethodNode.php index ee363ab3f..70e2ee92f 100644 --- a/src/Prophecy/Doubler/Generator/Node/MethodNode.php +++ b/src/Prophecy/Doubler/Generator/Node/MethodNode.php @@ -11,6 +11,7 @@ namespace Prophecy\Doubler\Generator\Node; +use Prophecy\Doubler\Generator\TypeHintReference; use Prophecy\Exception\InvalidArgumentException; /** @@ -33,14 +34,20 @@ class MethodNode */ private $arguments = array(); + /** + * @var TypeHintReference + */ + private $typeHintReference; + /** * @param string $name * @param string $code */ - public function __construct($name, $code = null) + public function __construct($name, $code = null, TypeHintReference $typeHintReference = null) { $this->name = $name; $this->code = $code; + $this->typeHintReference = $typeHintReference ?: new TypeHintReference(); } public function getVisibility() @@ -117,32 +124,25 @@ public function setReturnType($type = null) $this->returnType = null; break; - case 'string': - case 'float': - case 'int': - case 'bool': - case 'array': - case 'callable': - case 'iterable': - case 'void': - $this->returnType = $type; - break; - case 'double': case 'real': - $this->returnType = 'float'; - break; + $type = 'float'; + // intentional fall through case 'boolean': - $this->returnType = 'bool'; - break; + $type = 'bool'; + // intentional fall through case 'integer': - $this->returnType = 'int'; - break; + $type = 'int'; + // intentional fall through default: - $this->returnType = '\\' . ltrim($type, '\\'); + if ($this->typeHintReference->isBuiltInReturnTypeHint($type)) { + $this->returnType = $type; + } else { + $this->returnType = '\\' . ltrim($type, '\\'); + } } } diff --git a/src/Prophecy/Doubler/Generator/TypeHintReference.php b/src/Prophecy/Doubler/Generator/TypeHintReference.php new file mode 100644 index 000000000..ce952029b --- /dev/null +++ b/src/Prophecy/Doubler/Generator/TypeHintReference.php @@ -0,0 +1,46 @@ += 50400; + + case 'bool': + case 'float': + case 'int': + case 'string': + return PHP_VERSION_ID >= 70000; + + case 'iterable': + return PHP_VERSION_ID >= 70100; + + case 'object': + return PHP_VERSION_ID >= 70200; + + default: + return false; + } + } + + public function isBuiltInReturnTypeHint($type) + { + if ($type === 'void') { + return PHP_VERSION_ID >= 70100; + } + + return $this->isBuiltInParamTypeHint($type); + } +} From 21626bfcd3d3456b0eb844dd5045a0c32a483736 Mon Sep 17 00:00:00 2001 From: Olivier Laviale Date: Fri, 2 Feb 2018 12:29:33 +0100 Subject: [PATCH 2/4] Add specs --- .../Generator/ClassCodeGeneratorSpec.php | 25 ++++++++++++++++++- .../Doubler/Generator/Node/MethodNodeSpec.php | 8 +++++- .../Doubler/Generator/Node/MethodNode.php | 7 ++++++ 3 files changed, 38 insertions(+), 2 deletions(-) diff --git a/spec/Prophecy/Doubler/Generator/ClassCodeGeneratorSpec.php b/spec/Prophecy/Doubler/Generator/ClassCodeGeneratorSpec.php index ba1122022..4e5930759 100644 --- a/spec/Prophecy/Doubler/Generator/ClassCodeGeneratorSpec.php +++ b/spec/Prophecy/Doubler/Generator/ClassCodeGeneratorSpec.php @@ -17,6 +17,7 @@ function it_generates_proper_php_code_for_specific_ClassNode( MethodNode $method2, MethodNode $method3, MethodNode $method4, + MethodNode $method5, ArgumentNode $argument11, ArgumentNode $argument12, ArgumentNode $argument13, @@ -28,7 +29,7 @@ function it_generates_proper_php_code_for_specific_ClassNode( 'Prophecy\Doubler\Generator\MirroredInterface', 'ArrayAccess', 'ArrayIterator' )); $class->getProperties()->willReturn(array('name' => 'public', 'email' => 'private')); - $class->getMethods()->willReturn(array($method1, $method2, $method3, $method4)); + $class->getMethods()->willReturn(array($method1, $method2, $method3, $method4, $method5)); $method1->getName()->willReturn('getName'); $method1->getVisibility()->willReturn('public'); @@ -69,6 +70,16 @@ function it_generates_proper_php_code_for_specific_ClassNode( $method4->hasNullableReturnType()->willReturn(false); $method4->getCode()->willReturn('return;'); + $method5->getName()->willReturn('returnObject'); + $method5->getVisibility()->willReturn('public'); + $method5->returnsReference()->willReturn(false); + $method5->isStatic()->willReturn(false); + $method5->getArguments()->willReturn(array()); + $method5->hasReturnType()->willReturn(true); + $method5->getReturnType()->willReturn(version_compare(PHP_VERSION, '7.2', '>=') ? 'object' : '\object'); + $method5->hasNullableReturnType()->willReturn(false); + $method5->getCode()->willReturn('return;'); + $argument11->getName()->willReturn('fullname'); $argument11->getTypeHint()->willReturn('array'); $argument11->isOptional()->willReturn(true); @@ -128,6 +139,9 @@ public function &getRefValue( $refValue): string { public function doSomething(): void { return; } +public function returnObject(): object { +return; +} } } @@ -151,6 +165,9 @@ public function &getRefValue( $refValue): string { public function doSomething(): void { return; } +public function returnObject(): \object { +return; +} } } @@ -174,6 +191,9 @@ public function &getRefValue( $refValue): string { public function doSomething() { return; } +public function returnObject(): \object { +return; +} } } @@ -197,6 +217,9 @@ public function &getRefValue( $refValue) { public function doSomething() { return; } +public function returnObject() { +return; +} } } diff --git a/spec/Prophecy/Doubler/Generator/Node/MethodNodeSpec.php b/spec/Prophecy/Doubler/Generator/Node/MethodNodeSpec.php index 53d4f93e3..4862bc986 100644 --- a/spec/Prophecy/Doubler/Generator/Node/MethodNodeSpec.php +++ b/spec/Prophecy/Doubler/Generator/Node/MethodNodeSpec.php @@ -42,7 +42,7 @@ function it_should_be_settable_as_returning_a_reference_through_setter() { $this->setReturnsReference(); $this->returnsReference()->shouldReturn(true); - } + } function it_should_be_settable_as_static_through_setter() { @@ -131,4 +131,10 @@ function it_setReturnType_sets_return_type() $this->hasReturnType()->shouldReturn(true); $this->getReturnType()->shouldReturn($returnType); } + + function it_handles_object_return_type() + { + $this->setReturnType('object'); + $this->getReturnType()->shouldReturn(version_compare(PHP_VERSION, '7.2', '>=') ? 'object' : '\object'); + } } diff --git a/src/Prophecy/Doubler/Generator/Node/MethodNode.php b/src/Prophecy/Doubler/Generator/Node/MethodNode.php index 70e2ee92f..ea1879a63 100644 --- a/src/Prophecy/Doubler/Generator/Node/MethodNode.php +++ b/src/Prophecy/Doubler/Generator/Node/MethodNode.php @@ -137,6 +137,13 @@ public function setReturnType($type = null) $type = 'int'; // intentional fall through + case 'object': + if (version_compare(PHP_VERSION, '7.2', '>=')) { + $this->returnType = $type; + break; + } + // Fall-through to default case for PHP < 7.2 + default: if ($this->typeHintReference->isBuiltInReturnTypeHint($type)) { $this->returnType = $type; From 2bcb5b272aafb38a06b1838626c808554adb2736 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Paris?= Date: Tue, 13 Feb 2018 00:50:48 +0100 Subject: [PATCH 3/4] Avoid else --- src/Prophecy/Doubler/Generator/ClassCodeGenerator.php | 6 +----- src/Prophecy/Doubler/Generator/Node/MethodNode.php | 8 +++----- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/src/Prophecy/Doubler/Generator/ClassCodeGenerator.php b/src/Prophecy/Doubler/Generator/ClassCodeGenerator.php index 25665105d..891faa8fb 100644 --- a/src/Prophecy/Doubler/Generator/ClassCodeGenerator.php +++ b/src/Prophecy/Doubler/Generator/ClassCodeGenerator.php @@ -110,11 +110,7 @@ private function generateArguments(array $arguments) } if ($hint = $argument->getTypeHint()) { - if ($typeHintReference->isBuiltInParamTypeHint($hint)) { - $php .= $hint; - } else { - $php .= '\\'.$hint; - } + $php .= $typeHintReference->isBuiltInParamTypeHint($hint) ? $hint : '\\'.$hint; } $php .= ' '.($argument->isPassedByReference() ? '&' : ''); diff --git a/src/Prophecy/Doubler/Generator/Node/MethodNode.php b/src/Prophecy/Doubler/Generator/Node/MethodNode.php index ea1879a63..5e4e0a170 100644 --- a/src/Prophecy/Doubler/Generator/Node/MethodNode.php +++ b/src/Prophecy/Doubler/Generator/Node/MethodNode.php @@ -145,11 +145,9 @@ public function setReturnType($type = null) // Fall-through to default case for PHP < 7.2 default: - if ($this->typeHintReference->isBuiltInReturnTypeHint($type)) { - $this->returnType = $type; - } else { - $this->returnType = '\\' . ltrim($type, '\\'); - } + $this->returnType = $this->typeHintReference->isBuiltInReturnTypeHint($type) ? + $type : + '\\' . ltrim($type, '\\'); } } From bc94a8009f127a2805ff232b2acf53f3a56a0a1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Paris?= Date: Wed, 14 Feb 2018 09:40:16 +0100 Subject: [PATCH 4/4] Avoid fishy switch It's completely broken, and code is easier to understand that way. --- .../Doubler/Generator/Node/MethodNodeSpec.php | 21 +++++++++ .../Doubler/Generator/Node/MethodNode.php | 44 +++++++------------ 2 files changed, 36 insertions(+), 29 deletions(-) diff --git a/spec/Prophecy/Doubler/Generator/Node/MethodNodeSpec.php b/spec/Prophecy/Doubler/Generator/Node/MethodNodeSpec.php index 4862bc986..8ae07a59b 100644 --- a/spec/Prophecy/Doubler/Generator/Node/MethodNodeSpec.php +++ b/spec/Prophecy/Doubler/Generator/Node/MethodNodeSpec.php @@ -137,4 +137,25 @@ function it_handles_object_return_type() $this->setReturnType('object'); $this->getReturnType()->shouldReturn(version_compare(PHP_VERSION, '7.2', '>=') ? 'object' : '\object'); } + + function it_handles_type_aliases() + { + $this->setReturnType('double'); + $this->getReturnType()->shouldReturn(version_compare(PHP_VERSION, '7.0', '>=') ? 'float' : '\float'); + + $this->setReturnType('real'); + $this->getReturnType()->shouldReturn(version_compare(PHP_VERSION, '7.0', '>=') ? 'float' : '\float'); + + $this->setReturnType('boolean'); + $this->getReturnType()->shouldReturn(version_compare(PHP_VERSION, '7.0', '>=') ? 'bool' : '\bool'); + + $this->setReturnType('integer'); + $this->getReturnType()->shouldReturn(version_compare(PHP_VERSION, '7.0', '>=') ? 'int' : '\int'); + } + + function it_handles_null_return_type() + { + $this->setReturnType(null); + $this->getReturnType()->shouldReturn(null); + } } diff --git a/src/Prophecy/Doubler/Generator/Node/MethodNode.php b/src/Prophecy/Doubler/Generator/Node/MethodNode.php index 5e4e0a170..c74b48314 100644 --- a/src/Prophecy/Doubler/Generator/Node/MethodNode.php +++ b/src/Prophecy/Doubler/Generator/Node/MethodNode.php @@ -119,36 +119,22 @@ public function hasReturnType() */ public function setReturnType($type = null) { - switch ($type) { - case '': - $this->returnType = null; - break; - - case 'double': - case 'real': - $type = 'float'; - // intentional fall through - - case 'boolean': - $type = 'bool'; - // intentional fall through - - case 'integer': - $type = 'int'; - // intentional fall through - - case 'object': - if (version_compare(PHP_VERSION, '7.2', '>=')) { - $this->returnType = $type; - break; - } - // Fall-through to default case for PHP < 7.2 - - default: - $this->returnType = $this->typeHintReference->isBuiltInReturnTypeHint($type) ? - $type : - '\\' . ltrim($type, '\\'); + if ($type === '' || $type === null) { + $this->returnType = null; + return; } + $typeMap = array( + 'double' => 'float', + 'real' => 'float', + 'boolean' => 'bool', + 'integer' => 'int', + ); + if (isset($typeMap[$type])) { + $type = $typeMap[$type]; + } + $this->returnType = $this->typeHintReference->isBuiltInReturnTypeHint($type) ? + $type : + '\\' . ltrim($type, '\\'); } public function getReturnType()