Skip to content

Commit cd6802e

Browse files
committed
Merge branch 'ragboyjr-parse-group-use-statements'
2 parents f989f45 + 3574aab commit cd6802e

File tree

2 files changed

+107
-26
lines changed

2 files changed

+107
-26
lines changed

src/Types/ContextFactory.php

Lines changed: 96 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -193,8 +193,7 @@ private function parseUseStatement(\ArrayIterator $tokens)
193193
while ($continue) {
194194
$this->skipToNextStringOrNamespaceSeparator($tokens);
195195

196-
list($alias, $fqnn) = $this->extractUseStatement($tokens);
197-
$uses[$alias] = $fqnn;
196+
$uses = array_merge($uses, $this->extractUseStatements($tokens));
198197
if ($tokens->current()[0] === self::T_LITERAL_END_OF_USE) {
199198
$continue = false;
200199
}
@@ -215,38 +214,113 @@ private function skipToNextStringOrNamespaceSeparator(\ArrayIterator $tokens)
215214

216215
/**
217216
* Deduce the namespace name and alias of an import when we are at the T_USE token or have not reached the end of
218-
* a USE statement yet.
217+
* a USE statement yet. This will return a key/value array of the alias => namespace.
219218
*
220219
* @return array
221220
*/
222-
private function extractUseStatement(\ArrayIterator $tokens)
221+
private function extractUseStatements(\ArrayIterator $tokens)
223222
{
224-
$result = [''];
225-
while ($tokens->valid()
226-
&& ($tokens->current()[0] !== self::T_LITERAL_USE_SEPARATOR)
227-
&& ($tokens->current()[0] !== self::T_LITERAL_END_OF_USE)
228-
) {
229-
if ($tokens->current()[0] === T_AS) {
230-
$result[] = '';
223+
$extractedUseStatements = [];
224+
$groupedNs = '';
225+
$currentNs = '';
226+
$currentAlias = null;
227+
$state = "start";
228+
229+
$i = 0;
230+
while ($tokens->valid()) {
231+
$i += 1;
232+
$currentToken = $tokens->current();
233+
$tokenId = is_string($currentToken) ? $currentToken : $currentToken[0];
234+
$tokenValue = is_string($currentToken) ? null : $currentToken[1];
235+
switch ($state) {
236+
case "start":
237+
switch ($tokenId) {
238+
case T_STRING:
239+
case T_NS_SEPARATOR:
240+
$currentNs .= $tokenValue;
241+
break;
242+
case T_CURLY_OPEN:
243+
case '{':
244+
$state = 'grouped';
245+
$groupedNs = $currentNs;
246+
break;
247+
case T_AS:
248+
$state = 'start-alias';
249+
break;
250+
case self::T_LITERAL_USE_SEPARATOR:
251+
case self::T_LITERAL_END_OF_USE:
252+
$state = 'end';
253+
break;
254+
default:
255+
break;
256+
}
257+
break;
258+
case "start-alias":
259+
switch ($tokenId) {
260+
case T_STRING:
261+
$currentAlias .= $tokenValue;
262+
break;
263+
case self::T_LITERAL_USE_SEPARATOR:
264+
case self::T_LITERAL_END_OF_USE:
265+
$state = 'end';
266+
break;
267+
default:
268+
break;
269+
}
270+
break;
271+
case "grouped":
272+
switch ($tokenId) {
273+
case T_STRING:
274+
case T_NS_SEPARATOR:
275+
$currentNs .= $tokenValue;
276+
break;
277+
case T_AS:
278+
$state = 'grouped-alias';
279+
break;
280+
case self::T_LITERAL_USE_SEPARATOR:
281+
$state = 'grouped';
282+
$extractedUseStatements[$currentAlias ?: $currentNs] = $currentNs;
283+
$currentNs = $groupedNs;
284+
$currentAlias = null;
285+
break;
286+
case self::T_LITERAL_END_OF_USE:
287+
$state = 'end';
288+
break;
289+
default:
290+
break;
291+
}
292+
break;
293+
case "grouped-alias":
294+
switch ($tokenId) {
295+
case T_STRING:
296+
$currentAlias .= $tokenValue;
297+
break;
298+
case self::T_LITERAL_USE_SEPARATOR:
299+
$state = 'grouped';
300+
$extractedUseStatements[$currentAlias ?: $currentNs] = $currentNs;
301+
$currentNs = $groupedNs;
302+
$currentAlias = null;
303+
break;
304+
case self::T_LITERAL_END_OF_USE:
305+
$state = 'end';
306+
break;
307+
default:
308+
break;
309+
}
231310
}
232311

233-
if ($tokens->current()[0] === T_STRING || $tokens->current()[0] === T_NS_SEPARATOR) {
234-
$result[count($result) - 1] .= $tokens->current()[1];
312+
if ($state == "end") {
313+
break;
235314
}
236315

237316
$tokens->next();
238317
}
239318

240-
if (count($result) === 1) {
241-
$backslashPos = strrpos($result[0], '\\');
242-
243-
if (false !== $backslashPos) {
244-
$result[] = substr($result[0], $backslashPos + 1);
245-
} else {
246-
$result[] = $result[0];
247-
}
319+
if ($groupedNs != $currentNs) {
320+
$extractedUseStatements[$currentAlias ?: $currentNs] = $currentNs;
248321
}
249322

250-
return array_reverse($result);
323+
324+
return $extractedUseStatements;
251325
}
252326
}

tests/unit/Types/ContextFactoryTest.php

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,15 @@
1313
namespace phpDocumentor\Reflection\Types {
1414

1515
// Added imports on purpose as mock for the unit tests, please do not remove.
16-
use \ReflectionClass;
17-
use Mockery as m;
18-
use phpDocumentor;
16+
use Mockery as m, phpDocumentor;
1917
use phpDocumentor\Reflection\DocBlock;
2018
use phpDocumentor\Reflection\DocBlock\Tag;
2119
use PHPUnit\Framework\TestCase; // yes, the slash is part of the test
20+
use PHPUnit\Framework\{
21+
Assert,
22+
Exception as e
23+
};
24+
use \ReflectionClass;
2225

2326
/**
2427
* @coversDefaultClass \phpDocumentor\Reflection\Types\ContextFactory
@@ -53,14 +56,18 @@ public function testReadsAliasesFromClassReflection()
5356
'Tag' => Tag::class,
5457
'phpDocumentor' => 'phpDocumentor',
5558
'TestCase' => TestCase::class,
59+
'Assert' => Assert::class,
60+
'e' => e::class,
5661
ReflectionClass::class => ReflectionClass::class,
5762
];
5863
$context = $fixture->createFromReflector(new ReflectionClass($this));
5964

6065
$actual = $context->getNamespaceAliases();
6166

6267
// sort so that order differences don't break it
63-
$this->assertSame(sort($expected), sort($actual));
68+
sort($expected);
69+
sort($actual);
70+
$this->assertSame($expected, $actual);
6471
}
6572

6673
/**

0 commit comments

Comments
 (0)