Skip to content

Commit

Permalink
Indicate nullable types and type unions when formatting function sign…
Browse files Browse the repository at this point in the history
…atures.
  • Loading branch information
bobthecow committed Dec 6, 2023
1 parent 9fd520e commit 9137195
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 13 deletions.
39 changes: 26 additions & 13 deletions src/Formatter/SignatureFormatter.php
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ private static function formatFunctionReturnType(\ReflectionFunctionAbstract $re
return '';
}

return \sprintf(': %s', self::formatReflectionType($reflector->getReturnType()));
return \sprintf(': %s', self::formatReflectionType($reflector->getReturnType(), true));
}

/**
Expand Down Expand Up @@ -265,7 +265,9 @@ private static function formatFunctionParams(\ReflectionFunctionAbstract $reflec
$hint = '';
try {
if (\method_exists($param, 'getType')) {
$hint = self::formatReflectionType($param->getType());
// Only include the inquisitive nullable type iff param default value is not null.
$defaultIsNull = $param->isOptional() && $param->isDefaultValueAvailable() && $param->getDefaultValue() === null;
$hint = self::formatReflectionType($param->getType(), !$defaultIsNull);
} else {
if ($param->isArray()) {
$hint = '<keyword>array</keyword>';
Expand Down Expand Up @@ -321,25 +323,36 @@ private static function formatFunctionParams(\ReflectionFunctionAbstract $reflec
*
* @param \ReflectionType $type
*/
private static function formatReflectionType(\ReflectionType $type = null): string
private static function formatReflectionType(?\ReflectionType $type, bool $indicateNullable): string
{
if ($type === null) {
return '';
}

$types = $type instanceof \ReflectionUnionType ? $type->getTypes() : [$type];
$formattedTypes = [];
if ($type instanceof \ReflectionUnionType) {
$delimeter = '|';
} elseif ($type instanceof \ReflectionIntersectionType) {
$delimeter = '&';
} else {
return self::formatReflectionNamedType($type, $indicateNullable);
}

foreach ($types as $type) {
$typeStyle = $type->isBuiltin() ? 'keyword' : 'class';
$formattedTypes = [];
foreach ($type->getTypes() as $namedType) {
$formattedTypes[] = self::formatReflectionNamedType($namedType, $indicateNullable);
}

// PHP 7.0 didn't have `getName` on reflection types, so wheee!
$typeName = \method_exists($type, 'getName') ? $type->getName() : (string) $type;
return \implode($delimeter, $formattedTypes);
}

// @todo Do we want to include the ? for nullable types? Maybe only sometimes?
$formattedTypes[] = \sprintf('<%s>%s</%s>', $typeStyle, OutputFormatter::escape($typeName), $typeStyle);
}
/**
* Print a single named type.
*/
private static function formatReflectionNamedType(\ReflectionNamedType $type, bool $indicateNullable): string
{
$typeStyle = $type->isBuiltin() ? 'keyword' : 'class';
$nullable = $indicateNullable && $type->allowsNull() ? '?' : '';

return \implode('|', $formattedTypes);
return \sprintf('<%s>%s%s</%s>', $typeStyle, $nullable, OutputFormatter::escape($type->getName()), $typeStyle);
}
}
9 changes: 9 additions & 0 deletions test/Formatter/SignatureFormatterTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ private function anotherFakeMethod(array $one = [], $two = 2, $three = null)
{
}

private function nullableFakeMethod(?bool $one, ?string $two = null, $three = null): ?array
{
return null;
}

/**
* @dataProvider signatureReflectors
*/
Expand Down Expand Up @@ -85,6 +90,10 @@ public function signatureReflectors()
new \ReflectionMethod($this, 'anotherFakeMethod'),
'private function anotherFakeMethod(array $one = [], $two = 2, $three = null)',
],
[
new \ReflectionMethod($this, 'nullableFakeMethod'),
'private function nullableFakeMethod(?bool $one, string $two = null, $three = null): ?array',
],
];

if (\version_compare(\PHP_VERSION, '8.0', '>=')) {
Expand Down

0 comments on commit 9137195

Please sign in to comment.