Skip to content
This repository has been archived by the owner on Jan 29, 2020. It is now read-only.

Commit

Permalink
Merge pull request #87 from Ocramius/feature/php-7-1-support
Browse files Browse the repository at this point in the history
PHP 7.1 support
  • Loading branch information
Ocramius authored Oct 24, 2016
2 parents 4606aad + c682384 commit 3449c7c
Show file tree
Hide file tree
Showing 15 changed files with 493 additions and 36 deletions.
11 changes: 10 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,15 @@ matrix:
- php: 7
env:
- DEPS=latest
- php: 7.1
env:
- DEPS=lowest
- php: 7.1
env:
- DEPS=locked
- php: 7.1
env:
- DEPS=latest
- php: hhvm
env:
- DEPS=lowest
Expand All @@ -65,7 +74,7 @@ notifications:

before_install:
- travis_retry composer self-update
- if [[ $TRAVIS_PHP_VERSION != "hhvm" && $TEST_COVERAGE != 'true' ]]; then phpenv config-rm xdebug.ini ; fi
- if [[ $TRAVIS_PHP_VERSION != "hhvm" && $TEST_COVERAGE != 'true' ]]; then phpenv config-rm xdebug.ini || true ; fi

install:
- if [[ $DEPS == 'latest' ]]; then travis_retry composer update $COMPOSER_ARGS ; fi
Expand Down
25 changes: 22 additions & 3 deletions src/Generator/MethodGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

namespace Zend\Code\Generator;

use ReflectionMethod;
use Zend\Code\Reflection\MethodReflection;

class MethodGenerator extends AbstractMemberGenerator
Expand Down Expand Up @@ -389,12 +390,30 @@ private static function extractReturnTypeFromMethodReflection(MethodReflection $
return null;
}

$returnTypeString = (string) $returnType;
if (! method_exists($returnType, 'getName')) {
return self::expandLiteralType((string) $returnType, $methodReflection);
}

return ($returnType->allowsNull() ? '?' : '')
. self::expandLiteralType($returnType->getName(), $methodReflection);
}

if ('self' === strtolower($returnType)) {
/**
* @param string $literalReturnType
* @param ReflectionMethod $methodReflection
*
* @return string
*/
private static function expandLiteralType($literalReturnType, ReflectionMethod $methodReflection)
{
if ('self' === strtolower($literalReturnType)) {
return $methodReflection->getDeclaringClass()->getName();
}

return $returnTypeString;
if ('parent' === strtolower($literalReturnType)) {
return $methodReflection->getDeclaringClass()->getParentClass()->getName();
}

return $literalReturnType;
}
}
30 changes: 24 additions & 6 deletions src/Generator/ParameterGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

namespace Zend\Code\Generator;

use ReflectionParameter;
use Zend\Code\Reflection\ParameterReflection;

class ParameterGenerator extends AbstractGenerator
Expand Down Expand Up @@ -334,14 +335,12 @@ private static function extractFQCNTypeFromReflectionType(ParameterReflection $r
return null;
}

$typeString = (string) $type;

if ('self' === strtolower($typeString)) {
// exceptional case: `self` must expand to the reflection type declaring class
return $reflectionParameter->getDeclaringClass()->getName();
if (! method_exists($type, 'getName')) {
return self::expandLiteralParameterType((string) $type, $reflectionParameter);
}

return $typeString;
return ($type->allowsNull() ? '?' : '')
. self::expandLiteralParameterType($type->getName(), $reflectionParameter);
}

/**
Expand All @@ -368,6 +367,25 @@ private static function prePhp7ExtractFQCNTypeFromReflectionType(ParameterReflec
return null;
}

/**
* @param string $literalParameterType
* @param ReflectionParameter $reflectionParameter
*
* @return string
*/
private static function expandLiteralParameterType($literalParameterType, ReflectionParameter $reflectionParameter)
{
if ('self' === strtolower($literalParameterType)) {
return $reflectionParameter->getDeclaringClass()->getName();
}

if ('parent' === strtolower($literalParameterType)) {
return $reflectionParameter->getDeclaringClass()->getParentClass()->getName();
}

return $literalParameterType;
}

/**
* @param string|null $type
*
Expand Down
47 changes: 37 additions & 10 deletions src/Generator/TypeGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,19 +23,23 @@ final class TypeGenerator implements GeneratorInterface
*/
private $type;

/**
* @var bool
*/
private $nullable;

/**
* @var string[]
*
* @link http://php.net/manual/en/functions.arguments.php#functions.arguments.type-declaration
*/
private static $internalPhpTypes = ['int', 'float', 'string', 'bool', 'array', 'callable'];
private static $internalPhpTypes = ['void', 'int', 'float', 'string', 'bool', 'array', 'callable', 'iterable'];

// @codingStandardsIgnoreStart
/**
* @var string a regex pattern to match valid class names or types
*/
private static $validIdentifierMatcher = '/^[a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*(\\\\[a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*)*$/';
// @codingStandardsIgnoreEnd
private static $validIdentifierMatcher = '/^[a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*'
. '(\\\\[a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*)*$/';

/**
* @param string $type
Expand All @@ -46,7 +50,8 @@ final class TypeGenerator implements GeneratorInterface
*/
public static function fromTypeString($type)
{
list($wasTrimmed, $trimmedType) = self::trimType($type);
list($nullable, $trimmedNullable) = self::trimNullable($type);
list($wasTrimmed, $trimmedType) = self::trimType($trimmedNullable);

if (! preg_match(self::$validIdentifierMatcher, $trimmedType)) {
throw new InvalidArgumentException(sprintf(
Expand All @@ -65,9 +70,14 @@ public static function fromTypeString($type)
));
}

if ($nullable && $isInternalPhpType && 'void' === strtolower($trimmedType)) {
throw new InvalidArgumentException(sprintf('Provided type "%s" cannot be nullable', $type));
}

$instance = new self();

$instance->type = $trimmedType;
$instance->nullable = $nullable;
$instance->isInternalPhpType = self::isInternalPhpType($trimmedType);

return $instance;
Expand All @@ -82,26 +92,43 @@ private function __construct()
*/
public function generate()
{
$nullable = $this->nullable ? '?' : '';

if ($this->isInternalPhpType) {
return strtolower($this->type);
return $nullable . strtolower($this->type);
}

return '\\' . $this->type;
return $nullable . '\\' . $this->type;
}

/**
* @return string the cleaned type string
*/
public function __toString()
{
return ltrim($this->generate(), '\\');
return ltrim($this->generate(), '?\\');
}

/**
* @param string $type
*
* @return bool[]|string[] ordered tuple, first key represents whether the type is nullable, second is the
* trimmed string
*/
private static function trimNullable($type)
{
if (0 === strpos($type, '?')) {
return [true, substr($type, 1)];
}

return [false, $type];
}

/**
* @param string $type
*
* @return bool[]|int[] ordered tuple, first key represents whether the values was trimmed, second is the
* trimmed string
* @return bool[]|string[] ordered tuple, first key represents whether the values was trimmed, second is the
* trimmed string
*/
private static function trimType($type)
{
Expand Down
30 changes: 29 additions & 1 deletion test/Generator/MethodGeneratorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@
use Zend\Code\Generator\ValueGenerator;
use Zend\Code\Reflection\MethodReflection;
use ZendTest\Code\TestAsset\ClassWithByRefReturnMethod;
use ZendTest\Code\TestAsset\EmptyClass;
use ZendTest\Code\TestAsset\InternalHintsClass;
use ZendTest\Code\TestAsset\IterableHintsClass;
use ZendTest\Code\TestAsset\NullableReturnTypeHintedClass;
use ZendTest\Code\TestAsset\ReturnTypeHintedClass;

/**
Expand Down Expand Up @@ -338,17 +341,42 @@ public function testFrom(string $className, string $methodName, string $expected

public function returnTypeHintClassesProvider()
{
return [
$parameters = [
[ReturnTypeHintedClass::class, 'voidReturn', 'void'],
[ReturnTypeHintedClass::class, 'arrayReturn', 'array'],
[ReturnTypeHintedClass::class, 'callableReturn', 'callable'],
[ReturnTypeHintedClass::class, 'intReturn', 'int'],
[ReturnTypeHintedClass::class, 'floatReturn', 'float'],
[ReturnTypeHintedClass::class, 'stringReturn', 'string'],
[ReturnTypeHintedClass::class, 'boolReturn', 'bool'],
[ReturnTypeHintedClass::class, 'selfReturn', '\\' . ReturnTypeHintedClass::class],
[ReturnTypeHintedClass::class, 'parentReturn', '\\' . EmptyClass::class],
[ReturnTypeHintedClass::class, 'classReturn', '\\' . ReturnTypeHintedClass::class],
[ReturnTypeHintedClass::class, 'otherClassReturn', '\\' . InternalHintsClass::class],
[NullableReturnTypeHintedClass::class, 'arrayReturn', '?array'],
[NullableReturnTypeHintedClass::class, 'callableReturn', '?callable'],
[NullableReturnTypeHintedClass::class, 'intReturn', '?int'],
[NullableReturnTypeHintedClass::class, 'floatReturn', '?float'],
[NullableReturnTypeHintedClass::class, 'stringReturn', '?string'],
[NullableReturnTypeHintedClass::class, 'boolReturn', '?bool'],
[NullableReturnTypeHintedClass::class, 'selfReturn', '?\\' . NullableReturnTypeHintedClass::class],
[NullableReturnTypeHintedClass::class, 'parentReturn', '?\\' . EmptyClass::class],
[NullableReturnTypeHintedClass::class, 'classReturn', '?\\' . NullableReturnTypeHintedClass::class],
[NullableReturnTypeHintedClass::class, 'otherClassReturn', '?\\' . InternalHintsClass::class],
[IterableHintsClass::class, 'iterableReturnValue', 'iterable'],
[IterableHintsClass::class, 'nullableIterableReturnValue', '?iterable'],
];

return array_filter(
$parameters,
function (array $parameter) {
return PHP_VERSION_ID >= 70100
|| (
false === strpos($parameter[2], '?')
&& ! in_array(strtolower($parameter[2]), ['void', 'iterable'])
);
}
);
}

/**
Expand Down
Loading

0 comments on commit 3449c7c

Please sign in to comment.