From 79588c291c3c0e891e31f31ae95a078b78b02022 Mon Sep 17 00:00:00 2001 From: Michael Moravec Date: Wed, 26 Aug 2015 17:08:35 +0200 Subject: [PATCH] Proxy: Added support for PHP 7 return & parameter type hints --- lib/Doctrine/Common/Proxy/ProxyGenerator.php | 29 ++++++++++++ .../Common/Proxy/ProxyClassGeneratorTest.php | 46 +++++++++++++++++++ .../Tests/Common/Proxy/ReturnTypesClass.php | 33 +++++++++++++ .../Common/Proxy/ScalarTypeHintsClass.php | 25 ++++++++++ 4 files changed, 133 insertions(+) create mode 100644 tests/Doctrine/Tests/Common/Proxy/ReturnTypesClass.php create mode 100644 tests/Doctrine/Tests/Common/Proxy/ScalarTypeHintsClass.php diff --git a/lib/Doctrine/Common/Proxy/ProxyGenerator.php b/lib/Doctrine/Common/Proxy/ProxyGenerator.php index 60e2a2aba..3559b4ad2 100644 --- a/lib/Doctrine/Common/Proxy/ProxyGenerator.php +++ b/lib/Doctrine/Common/Proxy/ProxyGenerator.php @@ -777,6 +777,7 @@ private function generateMethods(ClassMetadata $class) } $methods .= $name . '(' . $this->buildParametersString($class, $method, $method->getParameters()) . ')'; + $methods .= $this->getMethodReturnType($method); $methods .= "\n" . ' {' . "\n"; if ($this->isShortIdentifierGetter($method, $class)) { @@ -945,6 +946,10 @@ private function getParameterType(ClassMetadata $class, \ReflectionMethod $metho return 'callable'; } + if (PHP_VERSION_ID >= 70000 && $parameter->hasType() && $parameter->getType()->isBuiltin()) { + return (string) $parameter->getType(); + } + try { $parameterClass = $parameter->getClass(); @@ -1002,4 +1007,28 @@ function (\ReflectionParameter $parameter) { $parameters ); } + + /** + * @Param \ReflectionMethod $method + * + * @return string + */ + private function getMethodReturnType(\ReflectionMethod $method) + { + if (PHP_VERSION_ID < 70000 || !$method->hasReturnType()) { + return ''; + } + + $returnType = $method->getReturnType(); + $nameLower = strtolower((string) $returnType); + + if ($nameLower === 'self') { + return ': \\' . $method->getDeclaringClass()->getName(); + } + if ($nameLower === 'parent') { + return ': \\' . $method->getDeclaringClass()->getParentClass()->getName(); + } + + return ': ' . (!$returnType->isBuiltin() ? '\\' : '') . (string) $returnType; + } } diff --git a/tests/Doctrine/Tests/Common/Proxy/ProxyClassGeneratorTest.php b/tests/Doctrine/Tests/Common/Proxy/ProxyClassGeneratorTest.php index 5de83a180..d9c2677a4 100644 --- a/tests/Doctrine/Tests/Common/Proxy/ProxyClassGeneratorTest.php +++ b/tests/Doctrine/Tests/Common/Proxy/ProxyClassGeneratorTest.php @@ -184,6 +184,52 @@ public function testClassWithVariadicArgumentOnProxiedMethod() $this->assertEquals(1, substr_count($classCode, 'parent::addType(...$types)')); } + public function testClassWithScalarTypeHintsOnProxiedMethods() + { + if (PHP_VERSION_ID < 70000) { + $this->markTestSkipped('Scalar type hints are only supported in PHP >= 7.0.0.'); + } + + if (!class_exists('Doctrine\Tests\Common\ProxyProxy\__CG__\ScalarTypeHintsClass', false)) { + $className = 'Doctrine\Tests\Common\Proxy\ScalarTypeHintsClass'; + $metadata = $this->createClassMetadata($className, array('id')); + + $proxyGenerator = new ProxyGenerator(__DIR__ . '/generated', __NAMESPACE__ . 'Proxy', true); + $this->generateAndRequire($proxyGenerator, $metadata); + } + + $classCode = file_get_contents(__DIR__ . '/generated/__CG__DoctrineTestsCommonProxyScalarTypeHintsClass.php'); + + $this->assertEquals(1, substr_count($classCode, 'function singleTypeHint(string $param)')); + $this->assertEquals(1, substr_count($classCode, 'function multipleTypeHints(int $a, float $b, bool $c, string $d)')); + $this->assertEquals(1, substr_count($classCode, 'function combinationOfTypeHintsAndNormal(\stdClass $a, $b, int $c)')); + $this->assertEquals(1, substr_count($classCode, 'function typeHintsWithVariadic(int ...$foo)')); + } + + public function testClassWithReturnTypesOnProxiedMethods() + { + if (PHP_VERSION_ID < 70000) { + $this->markTestSkipped('Method return types are only supported in PHP >= 7.0.0.'); + } + + $className = 'Doctrine\Tests\Common\Proxy\ReturnTypesClass'; + if (!class_exists('Doctrine\Tests\Common\ProxyProxy\__CG__\ReturnTypesClass', false)) { + $metadata = $this->createClassMetadata($className, array('id')); + + $proxyGenerator = new ProxyGenerator(__DIR__ . '/generated', __NAMESPACE__ . 'Proxy', true); + $this->generateAndRequire($proxyGenerator, $metadata); + } + + $classCode = file_get_contents(__DIR__ . '/generated/__CG__DoctrineTestsCommonProxyReturnTypesClass.php'); + + $this->assertEquals(1, substr_count($classCode, 'function returnsClass(): \stdClass')); + $this->assertEquals(1, substr_count($classCode, 'function returnsScalar(): int')); + $this->assertEquals(1, substr_count($classCode, 'function returnsArray(): array')); + $this->assertEquals(1, substr_count($classCode, 'function returnsCallable(): callable')); + $this->assertEquals(1, substr_count($classCode, 'function returnsSelf(): \\' . $className)); + $this->assertEquals(1, substr_count($classCode, 'function returnsParent(): \stdClass')); + } + public function testClassWithInvalidTypeHintOnProxiedMethod() { $className = 'Doctrine\Tests\Common\Proxy\InvalidTypeHintClass'; diff --git a/tests/Doctrine/Tests/Common/Proxy/ReturnTypesClass.php b/tests/Doctrine/Tests/Common/Proxy/ReturnTypesClass.php new file mode 100644 index 000000000..0a10148f6 --- /dev/null +++ b/tests/Doctrine/Tests/Common/Proxy/ReturnTypesClass.php @@ -0,0 +1,33 @@ +