Skip to content

Commit

Permalink
Merge pull request #389 from greg0ire/generate_proper_object_return_type
Browse files Browse the repository at this point in the history
Generate proper object return type
  • Loading branch information
ciaranmcnulty authored Feb 19, 2018
2 parents 9f901e2 + bc94a80 commit dfd6be4
Show file tree
Hide file tree
Showing 5 changed files with 135 additions and 73 deletions.
25 changes: 24 additions & 1 deletion spec/Prophecy/Doubler/Generator/ClassCodeGeneratorSpec.php
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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');
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -128,6 +139,9 @@ public function &getRefValue( $refValue): string {
public function doSomething(): void {
return;
}
public function returnObject(): object {
return;
}
}
}
Expand All @@ -151,6 +165,9 @@ public function &getRefValue( $refValue): string {
public function doSomething(): void {
return;
}
public function returnObject(): \object {
return;
}
}
}
Expand All @@ -174,6 +191,9 @@ public function &getRefValue( $refValue): string {
public function doSomething() {
return;
}
public function returnObject(): \object {
return;
}
}
}
Expand All @@ -197,6 +217,9 @@ public function &getRefValue( $refValue) {
public function doSomething() {
return;
}
public function returnObject() {
return;
}
}
}
Expand Down
31 changes: 29 additions & 2 deletions spec/Prophecy/Doubler/Generator/Node/MethodNodeSpec.php
Original file line number Diff line number Diff line change
Expand Up @@ -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()
{
Expand Down Expand Up @@ -124,11 +124,38 @@ function it_does_not_have_return_type_by_default()

function it_setReturnType_sets_return_type()
{
$returnType = 'string';
$returnType = 'array';

$this->setReturnType($returnType);

$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');
}

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);
}
}
51 changes: 13 additions & 38 deletions src/Prophecy/Doubler/Generator/ClassCodeGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Expand Down Expand Up @@ -91,51 +101,16 @@ 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', '>=')) {
$php .= $argument->isNullable() ? '?' : '';
}

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;
}
$php .= $typeHintReference->isBuiltInParamTypeHint($hint) ? $hint : '\\'.$hint;
}

$php .= ' '.($argument->isPassedByReference() ? '&' : '');
Expand Down
55 changes: 23 additions & 32 deletions src/Prophecy/Doubler/Generator/Node/MethodNode.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

namespace Prophecy\Doubler\Generator\Node;

use Prophecy\Doubler\Generator\TypeHintReference;
use Prophecy\Exception\InvalidArgumentException;

/**
Expand All @@ -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()
Expand Down Expand Up @@ -112,38 +119,22 @@ public function hasReturnType()
*/
public function setReturnType($type = null)
{
switch ($type) {
case '':
$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;

case 'boolean':
$this->returnType = 'bool';
break;

case 'integer':
$this->returnType = 'int';
break;

default:
$this->returnType = '\\' . 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()
Expand Down
46 changes: 46 additions & 0 deletions src/Prophecy/Doubler/Generator/TypeHintReference.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<?php

namespace Prophecy\Doubler\Generator;

/**
* Tells whether a keyword refers to a class or to a built-in type for the
* current version of php
*/
final class TypeHintReference
{
public function isBuiltInParamTypeHint($type)
{
switch ($type) {
case 'self':
case 'array':
return true;

case 'callable':
return PHP_VERSION_ID >= 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);
}
}

0 comments on commit dfd6be4

Please sign in to comment.