Skip to content

Commit bba116b

Browse files
authored
Merge pull request #17 from KentarouTakeda/nested-generics-and-closures-type-hint
Support nested generics and closures type hint
2 parents e6811e9 + 23fa082 commit bba116b

File tree

3 files changed

+105
-16
lines changed

3 files changed

+105
-16
lines changed

src/Barryvdh/Reflection/DocBlock/Tag/ParamTag.php

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -46,21 +46,38 @@ public function getContent()
4646
public function setContent($content)
4747
{
4848
Tag::setContent($content);
49-
$parts = preg_split(
50-
'/(\s+)/Su',
51-
$this->description,
52-
3,
53-
PREG_SPLIT_DELIM_CAPTURE
54-
);
55-
56-
// detect generic type
57-
if (isset($parts[0]) && isset($parts[2]) && strpos($parts[0], '<') !== false && strpos($parts[2], '>') !== false) {
58-
$parts[0] .= ' ' . $parts[2];
59-
unset($parts[1]);
60-
unset($parts[2]);
61-
$parts = array_values($parts);
49+
50+
$parts = [];
51+
$rest = $this->description;
52+
53+
// parsing generics and closures to detect types
54+
for($pos = 0, $stacks = []; $pos < strlen($rest); $pos++) {
55+
$char = $rest[$pos];
56+
57+
if($char === '<') {
58+
array_unshift($stacks, $char);
59+
}
60+
if($char === '(') {
61+
array_unshift($stacks, $char);
62+
}
63+
if($char === '>' && isset($stacks[0]) && $stacks[0] === '<') {
64+
array_shift($stacks);
65+
}
66+
if($char === ')' && isset($stacks[0]) && $stacks[0] === '(') {
67+
array_shift($stacks);
68+
}
69+
70+
if(!$stacks && preg_match('/\A(\s+)(.*)/su', substr($rest, $pos), $matches)) {
71+
$parts[0] = substr($rest, 0, $pos);
72+
$parts[1] = $matches[1];
73+
$rest = $matches[2];
74+
75+
break;
76+
}
6277
}
6378

79+
array_push($parts, ...preg_split('/(\s+)/u', $rest, 2, PREG_SPLIT_DELIM_CAPTURE));
80+
6481
// if the first item that is encountered is not a variable; it is a type
6582
if (isset($parts[0])
6683
&& (strlen($parts[0]) > 0)

src/Barryvdh/Reflection/DocBlock/Type/Collection.php

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -145,9 +145,9 @@ protected function explode($type)
145145
$type_parts[] = $curr_type;
146146
$curr_type = '';
147147
} else {
148-
if ($char === '<') {
148+
if ($char === '<' || $char === '(') {
149149
$nest_level++;
150-
} else if ($char === '>') {
150+
} else if ($char === '>' || $char === ')') {
151151
$nest_level--;
152152
}
153153

@@ -189,6 +189,10 @@ protected function expand($type)
189189
return $type;
190190
}
191191

192+
if($type[0] === '(') {
193+
return $type;
194+
}
195+
192196
if ($this->isTypeAnArray($type)) {
193197
return $this->expand(substr($type, 0, -2)) . self::OPERATOR_ARRAY;
194198
}

tests/Barryvdh/Reflection/DocBlock/Tag/ParamTagTest.php

Lines changed: 69 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,75 @@ public function provideDataForConstructor()
114114
array('int'),
115115
'$bob',
116116
"Type on a new line"
117-
)
117+
),
118+
119+
// generic array
120+
array(
121+
'param',
122+
'array<int, string> $names',
123+
'array<int, string>',
124+
array('array<int, string>'),
125+
'$names',
126+
''
127+
),
128+
129+
// nested generics
130+
array(
131+
'param',
132+
'array<int, array<string, mixed>> $arrays',
133+
'array<int, array<string, mixed>>',
134+
array('array<int, array<string, mixed>>'),
135+
'$arrays',
136+
''
137+
),
138+
139+
// closure
140+
array(
141+
'param',
142+
'(\Closure(int, string): bool) $callback',
143+
'(\Closure(int, string): bool)',
144+
array('(\Closure(int, string): bool)'),
145+
'$callback',
146+
''
147+
),
148+
149+
// generic array in closure
150+
array(
151+
'param',
152+
'(\Closure(array<int, string>): bool) $callback',
153+
'(\Closure(array<int, string>): bool)',
154+
array('(\Closure(array<int, string>): bool)'),
155+
'$callback',
156+
''
157+
),
158+
159+
// union types in closure
160+
array(
161+
'param',
162+
'(\Closure(int|string): bool)|bool $callback',
163+
'(\Closure(int|string): bool)|bool',
164+
array('(\Closure(int|string): bool)', 'bool'),
165+
'$callback',
166+
''
167+
),
168+
169+
// example from Laravel Framework - Eloquent Builder)
170+
array(
171+
'param',
172+
'array<array-key, array|(\Closure(\Illuminate\Database\Eloquent\Relations\Relation<*,*,*>): mixed)|string>|string $relations',
173+
'array<array-key, array|(\Closure(\Illuminate\Database\Eloquent\Relations\Relation<*,*,*>): mixed)|string>|string',
174+
array('array<array-key, array|(\Closure(\Illuminate\Database\Eloquent\Relations\Relation<*,*,*>): mixed)|string>', 'string'),
175+
'$relations',
176+
''
177+
),
178+
array(
179+
'param',
180+
'(\Closure(\Illuminate\Database\Eloquent\Relations\Relation<*,*,*>): mixed)|string|null $callback',
181+
'(\Closure(\Illuminate\Database\Eloquent\Relations\Relation<*,*,*>): mixed)|string|null',
182+
array('(\Closure(\Illuminate\Database\Eloquent\Relations\Relation<*,*,*>): mixed)', 'string', 'null'),
183+
'$callback',
184+
''
185+
),
118186
);
119187
}
120188
}

0 commit comments

Comments
 (0)