mirror of
https://github.com/barryvdh/ReflectionDocBlock.git
synced 2026-08-18 01:57:13 +00:00
Support nested generics and closures type hint
This commit is contained in:
@@ -46,20 +46,37 @@ class ParamTag extends ReturnTag
|
|||||||
public function setContent($content)
|
public function setContent($content)
|
||||||
{
|
{
|
||||||
Tag::setContent($content);
|
Tag::setContent($content);
|
||||||
$parts = preg_split(
|
|
||||||
'/(\s+)/Su',
|
|
||||||
$this->description,
|
|
||||||
3,
|
|
||||||
PREG_SPLIT_DELIM_CAPTURE
|
|
||||||
);
|
|
||||||
|
|
||||||
// detect generic type
|
$parts = [];
|
||||||
if (isset($parts[0]) && isset($parts[2]) && strpos($parts[0], '<') !== false && strpos($parts[2], '>') !== false) {
|
$rest = $this->description;
|
||||||
$parts[0] .= ' ' . $parts[2];
|
|
||||||
unset($parts[1]);
|
// parsing generics and closures to detect types
|
||||||
unset($parts[2]);
|
for($pos = 0, $stacks = []; $pos < strlen($rest); $pos++) {
|
||||||
$parts = array_values($parts);
|
$char = $rest[$pos];
|
||||||
|
|
||||||
|
if($char === '<') {
|
||||||
|
array_unshift($stacks, $char);
|
||||||
}
|
}
|
||||||
|
if($char === '(') {
|
||||||
|
array_unshift($stacks, $char);
|
||||||
|
}
|
||||||
|
if($char === '>' && isset($stacks[0]) && $stacks[0] === '<') {
|
||||||
|
array_shift($stacks);
|
||||||
|
}
|
||||||
|
if($char === ')' && isset($stacks[0]) && $stacks[0] === '(') {
|
||||||
|
array_shift($stacks);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!$stacks && preg_match('/\A(\s+)(.*)/su', substr($rest, $pos), $matches)) {
|
||||||
|
$parts[0] = substr($rest, 0, $pos);
|
||||||
|
$parts[1] = $matches[1];
|
||||||
|
$rest = $matches[2];
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
array_push($parts, ...preg_split('/(\s+)/u', $rest, 2, PREG_SPLIT_DELIM_CAPTURE));
|
||||||
|
|
||||||
// if the first item that is encountered is not a variable; it is a type
|
// if the first item that is encountered is not a variable; it is a type
|
||||||
if (isset($parts[0])
|
if (isset($parts[0])
|
||||||
|
|||||||
@@ -145,9 +145,9 @@ class Collection extends \ArrayObject
|
|||||||
$type_parts[] = $curr_type;
|
$type_parts[] = $curr_type;
|
||||||
$curr_type = '';
|
$curr_type = '';
|
||||||
} else {
|
} else {
|
||||||
if ($char === '<') {
|
if ($char === '<' || $char === '(') {
|
||||||
$nest_level++;
|
$nest_level++;
|
||||||
} else if ($char === '>') {
|
} else if ($char === '>' || $char === ')') {
|
||||||
$nest_level--;
|
$nest_level--;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -189,6 +189,10 @@ class Collection extends \ArrayObject
|
|||||||
return $type;
|
return $type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if($type[0] === '(') {
|
||||||
|
return $type;
|
||||||
|
}
|
||||||
|
|
||||||
if ($this->isTypeAnArray($type)) {
|
if ($this->isTypeAnArray($type)) {
|
||||||
return $this->expand(substr($type, 0, -2)) . self::OPERATOR_ARRAY;
|
return $this->expand(substr($type, 0, -2)) . self::OPERATOR_ARRAY;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -114,7 +114,75 @@ class ParamTagTest extends TestCase
|
|||||||
array('int'),
|
array('int'),
|
||||||
'$bob',
|
'$bob',
|
||||||
"Type on a new line"
|
"Type on a new line"
|
||||||
)
|
),
|
||||||
|
|
||||||
|
// generic array
|
||||||
|
array(
|
||||||
|
'param',
|
||||||
|
'array<int, string> $names',
|
||||||
|
'array<int, string>',
|
||||||
|
array('array<int, string>'),
|
||||||
|
'$names',
|
||||||
|
''
|
||||||
|
),
|
||||||
|
|
||||||
|
// nested generics
|
||||||
|
array(
|
||||||
|
'param',
|
||||||
|
'array<int, array<string, mixed>> $arrays',
|
||||||
|
'array<int, array<string, mixed>>',
|
||||||
|
array('array<int, array<string, mixed>>'),
|
||||||
|
'$arrays',
|
||||||
|
''
|
||||||
|
),
|
||||||
|
|
||||||
|
// closure
|
||||||
|
array(
|
||||||
|
'param',
|
||||||
|
'(\Closure(int, string): bool) $callback',
|
||||||
|
'(\Closure(int, string): bool)',
|
||||||
|
array('(\Closure(int, string): bool)'),
|
||||||
|
'$callback',
|
||||||
|
''
|
||||||
|
),
|
||||||
|
|
||||||
|
// generic array in closure
|
||||||
|
array(
|
||||||
|
'param',
|
||||||
|
'(\Closure(array<int, string>): bool) $callback',
|
||||||
|
'(\Closure(array<int, string>): bool)',
|
||||||
|
array('(\Closure(array<int, string>): bool)'),
|
||||||
|
'$callback',
|
||||||
|
''
|
||||||
|
),
|
||||||
|
|
||||||
|
// union types in closure
|
||||||
|
array(
|
||||||
|
'param',
|
||||||
|
'(\Closure(int|string): bool)|bool $callback',
|
||||||
|
'(\Closure(int|string): bool)|bool',
|
||||||
|
array('(\Closure(int|string): bool)', 'bool'),
|
||||||
|
'$callback',
|
||||||
|
''
|
||||||
|
),
|
||||||
|
|
||||||
|
// example from Laravel Framework - Eloquent Builder)
|
||||||
|
array(
|
||||||
|
'param',
|
||||||
|
'array<array-key, array|(\Closure(\Illuminate\Database\Eloquent\Relations\Relation<*,*,*>): mixed)|string>|string $relations',
|
||||||
|
'array<array-key, array|(\Closure(\Illuminate\Database\Eloquent\Relations\Relation<*,*,*>): mixed)|string>|string',
|
||||||
|
array('array<array-key, array|(\Closure(\Illuminate\Database\Eloquent\Relations\Relation<*,*,*>): mixed)|string>', 'string'),
|
||||||
|
'$relations',
|
||||||
|
''
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'param',
|
||||||
|
'(\Closure(\Illuminate\Database\Eloquent\Relations\Relation<*,*,*>): mixed)|string|null $callback',
|
||||||
|
'(\Closure(\Illuminate\Database\Eloquent\Relations\Relation<*,*,*>): mixed)|string|null',
|
||||||
|
array('(\Closure(\Illuminate\Database\Eloquent\Relations\Relation<*,*,*>): mixed)', 'string', 'null'),
|
||||||
|
'$callback',
|
||||||
|
''
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user