$tokens * @return array */ private function extractUseStatements(array $tokens): array { $uses = []; $count = count($tokens); $braceDepth = 0; for ($i = 0; $i < $count; $i++) { $token = $tokens[$i]; // Track brace depth to distinguish top-level use from trait use if (!is_array($token)) { if ($token === '{') { $braceDepth++; } elseif ($token === '}') { $braceDepth--; } continue; } // Only process top-level use statements if ($token[0] !== T_USE || $braceDepth > 0) { continue; } // Skip function/const imports $nextToken = $this->findNextNonWhitespace($tokens, $i); if ($nextToken !== null && is_array($tokens[$nextToken])) { if ($tokens[$nextToken][0] === T_FUNCTION || $tokens[$nextToken][0] === T_CONST) { continue; } } // Extract the use statement(s) $useResult = $this->parseUseStatement($tokens, $i); foreach ($useResult['fqcns'] as $fqcn) { $uses[] = $fqcn; } } return array_unique($uses); } /** * Parse a single use statement, handling grouped imports. * * @param array $tokens * @return array{fqcns: array, endIndex: int} */ private function parseUseStatement(array $tokens, int $startIndex): array { $fqcns = []; $count = count($tokens); $baseNamespace = ''; $currentName = ''; $inGroup = false; for ($i = $startIndex + 1; $i < $count; $i++) { $token = $tokens[$i]; if ($token === ';') { if ($currentName !== '') { $fqcns[] = $inGroup ? $baseNamespace . $currentName : $currentName; } break; } if ($token === '{') { $baseNamespace = $currentName; $currentName = ''; $inGroup = true; continue; } if ($token === '}') { if ($currentName !== '') { $fqcns[] = $baseNamespace . $currentName; } continue; } if ($token === ',') { if ($currentName !== '') { $fqcns[] = $inGroup ? $baseNamespace . $currentName : $currentName; } $currentName = ''; continue; } if (!is_array($token)) { continue; } // Skip 'as' aliases - we only care about the original FQCN if ($token[0] === T_AS) { // Skip until comma or semicolon for ($j = $i + 1; $j < $count; $j++) { if ($tokens[$j] === ',' || $tokens[$j] === ';' || $tokens[$j] === '}') { $i = $j - 1; break; } } continue; } if ($token[0] === T_NAME_QUALIFIED || $token[0] === T_NAME_FULLY_QUALIFIED) { $currentName .= ltrim($token[1], '\\'); } elseif ($token[0] === T_STRING) { $currentName .= $token[1]; } elseif ($token[0] === T_NS_SEPARATOR) { $currentName .= '\\'; } } return ['fqcns' => $fqcns, 'endIndex' => $i]; } /** * Required from TokenNavigatorTrait. * * @param array $tokens */ abstract private function findNextNonWhitespace(array $tokens, int $index): ?int; }