Protokoll #9985

ID9985
Zeitstempel2025-12-23 14:14:46.776023
Clientroot
IP
Warning: Undefined array key "request_ip" in /var/www/dev.campus.systemische-tools.de/src/View/protokoll/show.php on line 13

Deprecated: htmlspecialchars(): Passing null to parameter #1 ($string) of type string is deprecated in /var/www/dev.campus.systemische-tools.de/src/View/protokoll/show.php on line 13
Modellclaude-sonnet-4-20250514
Statuscompleted
Tokens12,676 (Input: 0, Output: 0)
Dauer104 ms
Request-Zeit
Warning: Undefined array key "request_timestamp" in /var/www/dev.campus.systemische-tools.de/src/View/protokoll/show.php on line 18
Response-Zeit-

Warning: Undefined array key "error_message" in /var/www/dev.campus.systemische-tools.de/src/View/protokoll/show.php on line 22

Request

{
    "event": "PreToolUse",
    "tool_name": "Write",
    "tool_input": {
        "file_path": "\/var\/www\/dev.campus.systemische-tools.de\/src\/Infrastructure\/CodeAnalysis\/PhpFileParser.php",
        "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Infrastructure\\CodeAnalysis;\n\n\/\/ @responsibility: Tokenizer-basierte PHP-Dateianalyse mit Dependency-Extraktion\n\nfinal class PhpFileParser\n{\n    \/** @var array<string, string> Maps short name to FQCN from use statements *\/\n    private array $importMap = [];\n\n    \/** @var string|null Current namespace *\/\n    private ?string $currentNamespace = null;\n\n    \/**\n     * Parst eine PHP-Datei und extrahiert Metadaten inkl. Dependencies.\n     *\n     * @return array{\n     *     namespace: string|null,\n     *     classes: array<array{name: string, type: string, line: int}>,\n     *     functions: array<array{name: string, visibility: string|null, line: int}>,\n     *     uses: array<string>,\n     *     extends_class: string|null,\n     *     implements_interfaces: array<string>,\n     *     traits_used: array<string>,\n     *     constructor_deps: array<string>,\n     *     error: string|null\n     * }\n     *\/\n    public function parse(string $filePath): array\n    {\n        $this->importMap = [];\n        $this->currentNamespace = null;\n\n        $result = [\n            'namespace' => null,\n            'classes' => [],\n            'functions' => [],\n            'uses' => [],\n            'extends_class' => null,\n            'implements_interfaces' => [],\n            'traits_used' => [],\n            'constructor_deps' => [],\n            'error' => null,\n        ];\n\n        if (!file_exists($filePath) || !is_readable($filePath)) {\n            $result['error'] = 'Datei nicht lesbar';\n\n            return $result;\n        }\n\n        $content = file_get_contents($filePath);\n        if ($content === false) {\n            $result['error'] = 'Datei konnte nicht gelesen werden';\n\n            return $result;\n        }\n\n        try {\n            $tokens = @token_get_all($content);\n        } catch (\\Throwable $e) {\n            $result['error'] = 'Token-Fehler: ' . $e->getMessage();\n\n            return $result;\n        }\n\n        \/\/ First pass: Extract namespace and use statements for resolution\n        $result['namespace'] = $this->extractNamespace($tokens);\n        $this->currentNamespace = $result['namespace'];\n        $result['uses'] = $this->extractUseStatements($tokens);\n\n        \/\/ Build import map for FQCN resolution\n        foreach ($result['uses'] as $fqcn) {\n            $parts = explode('\\\\', $fqcn);\n            $shortName = end($parts);\n            $this->importMap[$shortName] = $fqcn;\n        }\n\n        \/\/ Second pass: Extract structural elements\n        $result['classes'] = $this->extractClasses($tokens);\n        $result['functions'] = $this->extractFunctions($tokens);\n\n        \/\/ Third pass: Extract dependencies requiring resolution\n        $result['extends_class'] = $this->extractExtends($tokens);\n        $result['implements_interfaces'] = $this->extractImplements($tokens);\n        $result['traits_used'] = $this->extractTraits($tokens);\n        $result['constructor_deps'] = $this->extractConstructorDeps($tokens);\n\n        return $result;\n    }\n\n    \/**\n     * @param array<mixed> $tokens\n     *\/\n    private function extractNamespace(array $tokens): ?string\n    {\n        $namespace = '';\n        $capturing = false;\n\n        foreach ($tokens as $token) {\n            if (is_array($token)) {\n                if ($token[0] === T_NAMESPACE) {\n                    $capturing = true;\n\n                    continue;\n                }\n\n                if ($capturing) {\n                    if ($token[0] === T_NAME_QUALIFIED || $token[0] === T_STRING) {\n                        $namespace .= $token[1];\n                    } elseif ($token[0] === T_NS_SEPARATOR) {\n                        $namespace .= '\\\\';\n                    }\n                }\n            } elseif ($capturing && ($token === ';' || $token === '{')) {\n                break;\n            }\n        }\n\n        return $namespace !== '' ? $namespace : null;\n    }\n\n    \/**\n     * Extracts use statements (top-level imports only).\n     *\n     * @param array<mixed> $tokens\n     * @return array<string>\n     *\/\n    private function extractUseStatements(array $tokens): array\n    {\n        $uses = [];\n        $count = count($tokens);\n        $braceDepth = 0;\n\n        for ($i = 0; $i < $count; $i++) {\n            $token = $tokens[$i];\n\n            \/\/ Track brace depth to distinguish top-level use from trait use\n            if (!is_array($token)) {\n                if ($token === '{') {\n                    $braceDepth++;\n                } elseif ($token === '}') {\n                    $braceDepth--;\n                }\n\n                continue;\n            }\n\n            \/\/ Only process top-level use statements\n            if ($token[0] !== T_USE || $braceDepth > 0) {\n                continue;\n            }\n\n            \/\/ Skip function\/const imports\n            $nextToken = $this->findNextNonWhitespace($tokens, $i);\n            if ($nextToken !== null && is_array($tokens[$nextToken])) {\n                if ($tokens[$nextToken][0] === T_FUNCTION || $tokens[$nextToken][0] === T_CONST) {\n                    continue;\n                }\n            }\n\n            \/\/ Extract the use statement(s)\n            $useResult = $this->parseUseStatement($tokens, $i);\n            foreach ($useResult['fqcns'] as $fqcn) {\n                $uses[] = $fqcn;\n            }\n        }\n\n        return array_unique($uses);\n    }\n\n    \/**\n     * Parse a single use statement, handling grouped imports.\n     *\n     * @param array<mixed> $tokens\n     * @return array{fqcns: array<string>, endIndex: int}\n     *\/\n    private function parseUseStatement(array $tokens, int $startIndex): array\n    {\n        $fqcns = [];\n        $count = count($tokens);\n        $baseNamespace = '';\n        $currentName = '';\n        $inGroup = false;\n\n        for ($i = $startIndex + 1; $i < $count; $i++) {\n            $token = $tokens[$i];\n\n            if ($token === ';') {\n                if ($currentName !== '') {\n                    $fqcns[] = $inGroup ? $baseNamespace . $currentName : $currentName;\n                }\n\n                break;\n            }\n\n            if ($token === '{') {\n                $baseNamespace = $currentName;\n                $currentName = '';\n                $inGroup = true;\n\n                continue;\n            }\n\n            if ($token === '}') {\n                if ($currentName !== '') {\n                    $fqcns[] = $baseNamespace . $currentName;\n                }\n\n                continue;\n            }\n\n            if ($token === ',') {\n                if ($currentName !== '') {\n                    $fqcns[] = $inGroup ? $baseNamespace . $currentName : $currentName;\n                }\n                $currentName = '';\n\n                continue;\n            }\n\n            if (!is_array($token)) {\n                continue;\n            }\n\n            \/\/ Skip 'as' aliases - we only care about the original FQCN\n            if ($token[0] === T_AS) {\n                \/\/ Skip until comma or semicolon\n                for ($j = $i + 1; $j < $count; $j++) {\n                    if ($tokens[$j] === ',' || $tokens[$j] === ';' || $tokens[$j] === '}') {\n                        $i = $j - 1;\n\n                        break;\n                    }\n                }\n\n                continue;\n            }\n\n            if ($token[0] === T_NAME_QUALIFIED || $token[0] === T_NAME_FULLY_QUALIFIED) {\n                $currentName .= ltrim($token[1], '\\\\');\n            } elseif ($token[0] === T_STRING) {\n                $currentName .= $token[1];\n            } elseif ($token[0] === T_NS_SEPARATOR) {\n                $currentName .= '\\\\';\n            }\n        }\n\n        return ['fqcns' => $fqcns, 'endIndex' => $i ?? $startIndex];\n    }\n\n    \/**\n     * @param array<mixed> $tokens\n     * @return array<array{name: string, type: string, line: int}>\n     *\/\n    private function extractClasses(array $tokens): array\n    {\n        $classes = [];\n        $count = count($tokens);\n\n        for ($i = 0; $i < $count; $i++) {\n            $token = $tokens[$i];\n\n            if (!is_array($token)) {\n                continue;\n            }\n\n            $type = match ($token[0]) {\n                T_CLASS => 'class',\n                T_INTERFACE => 'interface',\n                T_TRAIT => 'trait',\n                T_ENUM => 'enum',\n                default => null,\n            };\n\n            if ($type === null) {\n                continue;\n            }\n\n            \/\/ Skip anonymous class statements\n            $prevIndex = $this->findPrevNonWhitespace($tokens, $i);\n            if ($prevIndex !== null && is_array($tokens[$prevIndex]) && $tokens[$prevIndex][0] === T_NEW) {\n                continue;\n            }\n\n            \/\/ Find class name\n            for ($j = $i + 1; $j < $count; $j++) {\n                if (is_array($tokens[$j]) && $tokens[$j][0] === T_STRING) {\n                    $classes[] = [\n                        'name' => $tokens[$j][1],\n                        'type' => $type,\n                        'line' => $token[2],\n                    ];\n\n                    break;\n                }\n            }\n        }\n\n        return $classes;\n    }\n\n    \/**\n     * @param array<mixed> $tokens\n     * @return array<array{name: string, visibility: string|null, line: int}>\n     *\/\n    private function extractFunctions(array $tokens): array\n    {\n        $functions = [];\n        $count = count($tokens);\n        $braceDepth = 0;\n        $inClass = false;\n\n        for ($i = 0; $i < $count; $i++) {\n            $token = $tokens[$i];\n\n            if (!is_array($token)) {\n                if ($token === '{') {\n                    $braceDepth++;\n                } elseif ($token === '}') {\n                    $braceDepth--;\n                    if ($braceDepth === 0) {\n                        $inClass = false;\n                    }\n                }\n\n                continue;\n            }\n\n            if (in_array($token[0], [T_CLASS, T_INTERFACE, T_TRAIT, T_ENUM], true)) {\n                $inClass = true;\n            }\n\n            ... [TRUNCATED-5b003c1dd9e0459a]"
    }
}

Response

{
    "tool_response": {
        "type": "update",
        "filePath": "\/var\/www\/dev.campus.systemische-tools.de\/src\/Infrastructure\/CodeAnalysis\/PhpFileParser.php",
        "content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Infrastructure\\CodeAnalysis;\n\n\/\/ @responsibility: Tokenizer-basierte PHP-Dateianalyse mit Dependency-Extraktion\n\nfinal class PhpFileParser\n{\n    \/** @var array<string, string> Maps short name to FQCN from use statements *\/\n    private array $importMap = [];\n\n    \/** @var string|null Current namespace *\/\n    private ?string $currentNamespace = null;\n\n    \/**\n     * Parst eine PHP-Datei und extrahiert Metadaten inkl. Dependencies.\n     *\n     * @return array{\n     *     namespace: string|null,\n     *     classes: array<array{name: string, type: string, line: int}>,\n     *     functions: array<array{name: string, visibility: string|null, line: int}>,\n     *     uses: array<string>,\n     *     extends_class: string|null,\n     *     implements_interfaces: array<string>,\n     *     traits_used: array<string>,\n     *     constructor_deps: array<string>,\n     *     error: string|null\n     * }\n     *\/\n    public function parse(string $filePath): array\n    {\n        $this->importMap = [];\n        $this->currentNamespace = null;\n\n        $result = [\n            'namespace' => null,\n            'classes' => [],\n            'functions' => [],\n            'uses' => [],\n            'extends_class' => null,\n            'implements_interfaces' => [],\n            'traits_used' => [],\n            'constructor_deps' => [],\n            'error' => null,\n        ];\n\n        if (!file_exists($filePath) || !is_readable($filePath)) {\n            $result['error'] = 'Datei nicht lesbar';\n\n            return $result;\n        }\n\n        $content = file_get_contents($filePath);\n        if ($content === false) {\n            $result['error'] = 'Datei konnte nicht gelesen werden';\n\n            return $result;\n        }\n\n        try {\n            $tokens = @token_get_all($content);\n        } catch (\\Throwable $e) {\n            $result['error'] = 'Token-Fehler: ' . $e->getMessage();\n\n            return $result;\n        }\n\n        \/\/ First pass: Extract namespace and use statements for resolution\n        $result['namespace'] = $this->extractNamespace($tokens);\n        $this->currentNamespace = $result['namespace'];\n        $result['uses'] = $this->extractUseStatements($tokens);\n\n        \/\/ Build import map for FQCN resolution\n        foreach ($result['uses'] as $fqcn) {\n            $parts = explode('\\\\', $fqcn);\n            $shortName = end($parts);\n            $this->importMap[$shortName] = $fqcn;\n        }\n\n        \/\/ Second pass: Extract structural elements\n        $result['classes'] = $this->extractClasses($tokens);\n        $result['functions'] = $this->extractFunctions($tokens);\n\n        \/\/ Third pass: Extract dependencies requiring resolution\n        $result['extends_class'] = $this->extractExtends($tokens);\n        $result['implements_interfaces'] = $this->extractImplements($tokens);\n        $result['traits_used'] = $this->extractTraits($tokens);\n        $result['constructor_deps'] = $this->extractConstructorDeps($tokens);\n\n        return $result;\n    }\n\n    \/**\n     * @param array<mixed> $tokens\n     *\/\n    private function extractNamespace(array $tokens): ?string\n    {\n        $namespace = '';\n        $capturing = false;\n\n        foreach ($tokens as $token) {\n            if (is_array($token)) {\n                if ($token[0] === T_NAMESPACE) {\n                    $capturing = true;\n\n                    continue;\n                }\n\n                if ($capturing) {\n                    if ($token[0] === T_NAME_QUALIFIED || $token[0] === T_STRING) {\n                        $namespace .= $token[1];\n                    } elseif ($token[0] === T_NS_SEPARATOR) {\n                        $namespace .= '\\\\';\n                    }\n                }\n            } elseif ($capturing && ($token === ';' || $token === '{')) {\n                break;\n            }\n        }\n\n        return $namespace !== '' ? $namespace : null;\n    }\n\n    \/**\n     * Extracts use statements (top-level imports only).\n     *\n     * @param array<mixed> $tokens\n     * @return array<string>\n     *\/\n    private function extractUseStatements(array $tokens): array\n    {\n        $uses = [];\n        $count = count($tokens);\n        $braceDepth = 0;\n\n        for ($i = 0; $i < $count; $i++) {\n            $token = $tokens[$i];\n\n            \/\/ Track brace depth to distinguish top-level use from trait use\n            if (!is_array($token)) {\n                if ($token === '{') {\n                    $braceDepth++;\n                } elseif ($token === '}') {\n                    $braceDepth--;\n                }\n\n                continue;\n            }\n\n            \/\/ Only process top-level use statements\n            if ($token[0] !== T_USE || $braceDepth > 0) {\n                continue;\n            }\n\n            \/\/ Skip function\/const imports\n            $nextToken = $this->findNextNonWhitespace($tokens, $i);\n            if ($nextToken !== null && is_array($tokens[$nextToken])) {\n                if ($tokens[$nextToken][0] === T_FUNCTION || $tokens[$nextToken][0] === T_CONST) {\n                    continue;\n                }\n            }\n\n            \/\/ Extract the use statement(s)\n            $useResult = $this->parseUseStatement($tokens, $i);\n            foreach ($useResult['fqcns'] as $fqcn) {\n                $uses[] = $fqcn;\n            }\n        }\n\n        return array_unique($uses);\n    }\n\n    \/**\n     * Parse a single use statement, handling grouped imports.\n     *\n     * @param array<mixed> $tokens\n     * @return array{fqcns: array<string>, endIndex: int}\n     *\/\n    private function parseUseStatement(array $tokens, int $startIndex): array\n    {\n        $fqcns = [];\n        $count = count($tokens);\n        $baseNamespace = '';\n        $currentName = '';\n        $inGroup = false;\n\n        for ($i = $startIndex + 1; $i < $count; $i++) {\n            $token = $tokens[$i];\n\n            if ($token === ';') {\n                if ($currentName !== '') {\n                    $fqcns[] = $inGroup ? $baseNamespace . $currentName : $currentName;\n                }\n\n                break;\n            }\n\n            if ($token === '{') {\n                $baseNamespace = $currentName;\n                $currentName = '';\n                $inGroup = true;\n\n                continue;\n            }\n\n            if ($token === '}') {\n                if ($currentName !== '') {\n                    $fqcns[] = $baseNamespace . $currentName;\n                }\n\n                continue;\n            }\n\n            if ($token === ',') {\n                if ($currentName !== '') {\n                    $fqcns[] = $inGroup ? $baseNamespace . $currentName : $currentName;\n                }\n                $currentName = '';\n\n                continue;\n            }\n\n            if (!is_array($token)) {\n                continue;\n            }\n\n            \/\/ Skip 'as' aliases - we only care about the original FQCN\n            if ($token[0] === T_AS) {\n                \/\/ Skip until comma or semicolon\n                for ($j = $i + 1; $j < $count; $j++) {\n                    if ($tokens[$j] === ',' || $tokens[$j] === ';' || $tokens[$j] === '}') {\n                        $i = $j - 1;\n\n                        break;\n                    }\n                }\n\n                continue;\n            }\n\n            if ($token[0] === T_NAME_QUALIFIED || $token[0] === T_NAME_FULLY_QUALIFIED) {\n                $currentName .= ltrim($token[1], '\\\\');\n            } elseif ($token[0] === T_STRING) {\n                $currentName .= $token[1];\n            } elseif ($token[0] === T_NS_SEPARATOR) {\n                $currentName .= '\\\\';\n            }\n        }\n\n        return ['fqcns' => $fqcns, 'endIndex' => $i ?? $startIndex];\n    }\n\n    \/**\n     * @param array<mixed> $tokens\n     * @return array<array{name: string, type: string, line: int}>\n     *\/\n    private function extractClasses(array $tokens): array\n    {\n        $classes = [];\n        $count = count($tokens);\n\n        for ($i = 0; $i < $count; $i++) {\n            $token = $tokens[$i];\n\n            if (!is_array($token)) {\n                continue;\n            }\n\n            $type = match ($token[0]) {\n                T_CLASS => 'class',\n                T_INTERFACE => 'interface',\n                T_TRAIT => 'trait',\n                T_ENUM => 'enum',\n                default => null,\n            };\n\n            if ($type === null) {\n                continue;\n            }\n\n            \/\/ Skip anonymous class statements\n            $prevIndex = $this->findPrevNonWhitespace($tokens, $i);\n            if ($prevIndex !== null && is_array($tokens[$prevIndex]) && $tokens[$prevIndex][0] === T_NEW) {\n                continue;\n            }\n\n            \/\/ Find class name\n            for ($j = $i + 1; $j < $count; $j++) {\n                if (is_array($tokens[$j]) && $tokens[$j][0] === T_STRING) {\n                    $classes[] = [\n                        'name' => $tokens[$j][1],\n                        'type' => $type,\n                        'line' => $token[2],\n                    ];\n\n                    break;\n                }\n            }\n        }\n\n        return $classes;\n    }\n\n    \/**\n     * @param array<mixed> $tokens\n     * @return array<array{name: string, visibility: string|null, line: int}>\n     *\/\n    private function extractFunctions(array $tokens): array\n    {\n        $functions = [];\n        $count = count($tokens);\n        $braceDepth = 0;\n        $inClass = false;\n\n        for ($i = 0; $i < $count; $i++) {\n            $token = $tokens[$i];\n\n            if (!is_array($token)) {\n                if ($token === '{') {\n                    $braceDepth++;\n                } elseif ($token === '}') {\n                    $braceDepth--;\n                    if ($braceDepth === 0) {\n                        $inClass = false;\n                    }\n                }\n\n                continue;\n            }\n\n            if (in_array($token[0], [T_CLASS, T_INTERFACE, T_TRAIT, T_ENUM], true)) {\n                $inClass = true;\n            }\n\n            ... [TRUNCATED-5b003c1dd9e0459a]",
        "structuredPatch": [
            {
                "oldStart": 4,
                "oldLines": 26,
                "newStart": 4,
                "newLines": 45,
                "lines": [
                    " ",
                    " namespace Infrastructure\\CodeAnalysis;",
                    " ",
                    "-\/\/ @responsibility: Tokenizer-basierte PHP-Dateianalyse",
                    "+\/\/ @responsibility: Tokenizer-basierte PHP-Dateianalyse mit Dependency-Extraktion",
                    " ",
                    " final class PhpFileParser",
                    " {",
                    "+    \/** @var array<string, string> Maps short name to FQCN from use statements *\/",
                    "+    private array $importMap = [];",
                    "+",
                    "+    \/** @var string|null Current namespace *\/",
                    "+    private ?string $currentNamespace = null;",
                    "+",
                    "     \/**",
                    "-     * Parst eine PHP-Datei und extrahiert Metadaten.",
                    "+     * Parst eine PHP-Datei und extrahiert Metadaten inkl. Dependencies.",
                    "      *",
                    "      * @return array{",
                    "      *     namespace: string|null,",
                    "      *     classes: array<array{name: string, type: string, line: int}>,",
                    "      *     functions: array<array{name: string, visibility: string|null, line: int}>,",
                    "+     *     uses: array<string>,",
                    "+     *     extends_class: string|null,",
                    "+     *     implements_interfaces: array<string>,",
                    "+     *     traits_used: array<string>,",
                    "+     *     constructor_deps: array<string>,",
                    "      *     error: string|null",
                    "      * }",
                    "      *\/",
                    "     public function parse(string $filePath): array",
                    "     {",
                    "+        $this->importMap = [];",
                    "+        $this->currentNamespace = null;",
                    "+",
                    "         $result = [",
                    "             'namespace' => null,",
                    "             'classes' => [],",
                    "             'functions' => [],",
                    "+            'uses' => [],",
                    "+            'extends_class' => null,",
                    "+            'implements_interfaces' => [],",
                    "+            'traits_used' => [],",
                    "+            'constructor_deps' => [],",
                    "             'error' => null,",
                    "         ];",
                    " "
                ]
            },
            {
                "oldStart": 48,
                "oldLines": 10,
                "newStart": 67,
                "newLines": 28,
                "lines": [
                    "             return $result;",
                    "         }",
                    " ",
                    "+        \/\/ First pass: Extract namespace and use statements for resolution",
                    "         $result['namespace'] = $this->extractNamespace($tokens);",
                    "+        $this->currentNamespace = $result['namespace'];",
                    "+        $result['uses'] = $this->extractUseStatements($tokens);",
                    "+",
                    "+        \/\/ Build import map for FQCN resolution",
                    "+        foreach ($result['uses'] as $fqcn) {",
                    "+            $parts = explode('\\\\', $fqcn);",
                    "+            $shortName = end($parts);",
                    "+            $this->importMap[$shortName] = $fqcn;",
                    "+        }",
                    "+",
                    "+        \/\/ Second pass: Extract structural elements",
                    "         $result['classes'] = $this->extractClasses($tokens);",
                    "         $result['functions'] = $this->extractFunctions($tokens);",
                    " ",
                    "+        \/\/ Third pass: Extract dependencies requiring resolution",
                    "+        $result['extends_class'] = $this->extractExtends($tokens);",
                    "+        $result['implements_interfaces'] = $this->extractImplements($tokens);",
                    "+        $result['traits_used'] = $this->extractTraits($tokens);",
                    "+        $result['constructor_deps'] = $this->extractConstructorDeps($tokens);",
                    "+",
                    "         return $result;",
                    "     }",
                    " "
                ]
            },
            {
                "oldStart": 67,
                "oldLines": 6,
                "newStart": 104,
                "newLines": 7,
                "lines": [
                    "             if (is_array($token)) {",
                    "                 if ($token[0] === T_NAMESPACE) {",
                    "                     $capturing = true;",
                    "+",
                    "                     continue;",
                    "                 }",
                    " "
                ]
            },
            {
                "oldStart": 86,
                "oldLines": 7,
                "newStart": 124,
                "newLines": 136,
                "lines": [
                    "     }",
                    " ",
                    "     \/**",
                    "+     * Extracts use statements (top-level imports only).",
                    "+     *",
                    "      * @param array<mixed> $tokens",
                    "+     * @return array<string>",
                    "+     *\/",
                    "+    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<mixed> $tokens",
                    "+     * @return array{fqcns: array<string>, 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 ?? $startIndex];",
                    "+    }",
                    "+",
                    "+    \/**",
                    "+     * @param array<mixed> $tokens",
                    "      * @return array<array{name: string, type: string, line: int}>",
                    "      *\/",
                    "     private function extractClasses(array $tokens): array"
                ]
            },
            {
                "oldStart": 113,
                "oldLines": 13,
                "newStart": 280,
                "newLines": 13,
                "lines": [
                    "                 continue;",
                    "             }",
                    " ",
                    "-            \/\/ Prüfen ob es kein anonymes Class-Statement ist",
                    "+            \/\/ Skip anonymous class statements",
                    "             $prevIndex = $this->findPrevNonWhitespace($tokens, $i);",
                    "             if ($prevIndex !== null && is_array($tokens[$prevIndex]) && $tokens[$prevIndex][0] === T_NEW) {",
                    "                 continue;",
                    "             }",
                    " ",
                    "-            \/\/ Klassennamen finden",
                    "+            \/\/ Find class name",
                    "             for ($j = $i + 1; $j < $count; $j++) {",
                    "                 if (is_array($tokens[$j]) && $tokens[$j][0] === T_STRING) {",
                    "                     $classes[] = ["
                ]
            },
            {
                "oldStart": 127,
                "oldLines": 6,
                "newStart": 294,
                "newLines": 7,
                "lines": [
                    "                         'type' => $type,",
                    "                         'line' => $token[2],",
                    "                     ];",
                    "+",
                    "                     break;",
                    "                 }",
                    "             }"
                ]
            },
            {
                "oldStart": 158,
                "oldLines": 10,
                "newStart": 326,
                "newLines": 10,
                "lines": [
                    "                         $inClass = false;",
                    "                     }",
                    "                 }",
                    "+",
                    "                 continue;",
                    "             }",
                    " ",
                    "-            \/\/ Klasse\/Interface\/Trait betreten",
                    "             if (in_array($token[0], [T_CLASS, T_INTERFACE, T_TRAIT, T_ENUM], true)) {",
                    "                 $inClass = true;",
                    "             }"
                ]
            },
            {
                "oldStart": 170,
                "oldLines": 7,
                "newStart": 338,
                "newLines": 6,
                "lines": [
                    "                 continue;",
                    "             }",
                    " ",
                    "-            \/\/ Visibility ermitteln",
                    "             $visibility = null;",
                    "             if ($inClass) {",
                    "                 for ($j = $i - 1; $j >= 0; $j--) {"
                ]
            },
            {
                "oldStart": 179,
                "oldLines": 14,
                "newStart": 346,
                "newLines": 17,
                "lines": [
                    "                     }",
                    "                     if ($tokens[$j][0] === T_PUBLIC) {",
                    "                         $visibility = 'public';",
                    "+",
                    "                         break;",
                    "                     }",
                    "                     if ($tokens[$j][0] === T_PROTECTED) {",
                    "                         $visibility = 'protected';",
                    "+",
                    "                         break;",
                    "                     }",
                    "                     if ($tokens[$j][0] === T_PRIVATE) {",
                    "                         $visibility = 'private';",
                    "+",
                    "                         break;",
                    "                     }",
                    "                     if ($tokens[$j][0] !== T_WHITESPACE && $tokens[$j][0] !== T_STATIC && $tokens[$j][0] !== T_FINAL && $tokens[$j][0] !== T_ABSTRACT) {"
                ]
            },
            {
                "oldStart": 195,
                "oldLines": 7,
                "newStart": 365,
                "newLines": 6,
                "lines": [
                    "                 }",
                    "             }",
                    " ",
                    "-            \/\/ Funktionsnamen finden",
                    "             for ($j = $i + 1; $j < $count; $j++) {",
                    "                 if (is_array($tokens[$j]) && $tokens[$j][0] === T_STRING) {",
                    "                     $functions[] = ["
                ]
            },
            {
                "oldStart": 203,
                "oldLines": 9,
                "newStart": 372,
                "newLines": 9,
                "lines": [
                    "                         'visibility' => $visibility,",
                    "                         'line' => $token[2],",
                    "                     ];",
                    "+",
                    "                     break;",
                    "                 }",
                    "-                \/\/ Anonyme Funktion (kein Name)",
                    "                 if ($tokens[$j] === '(') {",
                    "                     break;",
                    "                 }"
                ]
            },
            {
                "oldStart": 216,
                "oldLines": 8,
                "newStart": 385,
                "newLines": 351,
                "lines": [
                    "     }",
                    " ",
                    "     \/**",
                    "+     * Extract the parent class (extends clause).",
                    "+     *",
                    "      * @param array<mixed> $tokens",
                    "      *\/",
                    "+    private function extractExtends(array $tokens): ?string",
                    "+    {",
                    "+        $count = count($tokens);",
                    "+",
                    "+        for ($i = 0; $i < $count; $i++) {",
                    "+            $token = $tokens[$i];",
                    "+",
                    "+            if (!is_array($token) || $token[0] !== T_EXTENDS) {",
                    "+                continue;",
                    "+            }",
                    "+",
                    "+            \/\/ Find the class name after extends",
                    "+            $className = $this->extractTypeName($tokens, $i + 1);",
                    "+            if ($className !== null) {",
                    "+                return $this->resolveFqcn($className);",
                    "+            }",
                    "+        }",
                    "+",
                    "+        return null;",
                    "+    }",
                    "+",
                    "+    \/**",
                    "+     * Extract implemented interfaces.",
                    "+     *",
                    "+     * @param array<mixed> $tokens",
                    "+     * @return array<string>",
                    "+     *\/",
                    "+    private function extractImplements(array $tokens): array",
                    "+    {",
                    "+        $interfaces = [];",
                    "+        $count = count($tokens);",
                    "+",
                    "+        for ($i = 0; $i < $count; $i++) {",
                    "+            $token = $tokens[$i];",
                    "+",
                    "+            if (!is_array($token) || $token[0] !== T_IMPLEMENTS) {",
                    "+                continue;",
                    "+            }",
                    "+",
                    "+            \/\/ Collect interface names until { or extends",
                    "+            for ($j = $i + 1; $j < $count; $j++) {",
                    "+                if ($tokens[$j] === '{') {",
                    "+                    break;",
                    "+                }",
                    "+",
                    "+                if ($tokens[$j] === ',') {",
                    "+                    continue;",
                    "+                }",
                    "+",
                    "+                if (!is_array($tokens[$j])) {",
                    "+                    continue;",
                    "+                }",
                    "+",
                    "+                if ($tokens[$j][0] === T_EXTENDS) {",
                    "+                    break;",
                    "+                }",
                    "+",
                    "+                if (in_array($tokens[$j][0], [T_STRING, T_NAME_QUALIFIED, T_NAME_FULLY_QUALIFIED], true)) {",
                    "+                    $interfaces[] = $this->resolveFqcn($tokens[$j][1]);",
                    "+                }",
                    "+            }",
                    "+",
                    "+            break;",
                    "+        }",
                    "+",
                    "+        return array_unique($interfaces);",
                    "+    }",
                    "+",
                    "+    \/**",
                    "+     * Extract traits used inside class body.",
                    "+     *",
                    "+     * @param array<mixed> $tokens",
                    "+     * @return array<string>",
                    "+     *\/",
                    "+    private function extractTraits(array $tokens): array",
                    "+    {",
                    "+        $traits = [];",
                    "+        $count = count($tokens);",
                    "+        $braceDepth = 0;",
                    "+        $inClass = false;",
                    "+",
                    "+        for ($i = 0; $i < $count; $i++) {",
                    "+            $token = $tokens[$i];",
                    "+",
                    "+            if (!is_array($token)) {",
                    "+                if ($token === '{') {",
                    "+                    $braceDepth++;",
                    "+                } elseif ($token === '}') {",
                    "+                    $braceDepth--;",
                    "+                    if ($braceDepth === 0) {",
                    "+                        $inClass = false;",
                    "+                    }",
                    "+                }",
                    "+",
                    "+                continue;",
                    "+            }",
                    "+",
                    "+            if (in_array($token[0], [T_CLASS, T_TRAIT, T_ENUM], true)) {",
                    "+                $inClass = true;",
                    "+",
                    "+                continue;",
                    "+            }",
                    "+",
                    "+            \/\/ T_USE inside class body = trait use",
                    "+            if ($token[0] === T_USE && $inClass && $braceDepth === 1) {",
                    "+                \/\/ Collect trait names until ; or {",
                    "+                for ($j = $i + 1; $j < $count; $j++) {",
                    "+                    if ($tokens[$j] === ';' || $tokens[$j] === '{') {",
                    "+                        break;",
                    "+                    }",
                    "+",
                    "+                    if ($tokens[$j] === ',') {",
                    "+                        continue;",
                    "+                    }",
                    "+",
                    "+                    if (!is_array($tokens[$j])) {",
                    "+                        continue;",
                    "+                    }",
                    "+",
                    "+                    if (in_array($tokens[$j][0], [T_STRING, T_NAME_QUALIFIED, T_NAME_FULLY_QUALIFIED], true)) {",
                    "+                        $traits[] = $this->resolveFqcn($tokens[$j][1]);",
                    "+                    }",
                    "+                }",
                    "+            }",
                    "+        }",
                    "+",
                    "+        return array_unique($traits);",
                    "+    }",
                    "+",
                    "+    \/**",
                    "+     * Extract constructor parameter type hints (DI dependencies).",
                    "+     *",
                    "+     * @param array<mixed> $tokens",
                    "+     * @return array<string>",
                    "+     *\/",
                    "+    private function extractConstructorDeps(array $tokens): array",
                    "+    {",
                    "+        $deps = [];",
                    "+        $count = count($tokens);",
                    "+        $braceDepth = 0;",
                    "+        $inClass = false;",
                    "+",
                    "+        for ($i = 0; $i < $count; $i++) {",
                    "+            $token = $tokens[$i];",
                    "+",
                    "+            if (!is_array($token)) {",
                    "+                if ($token === '{') {",
                    "+                    $braceDepth++;",
                    "+                } elseif ($token === '}') {",
                    "+                    $braceDepth--;",
                    "+                    if ($braceDepth === 0) {",
                    "+                        $inClass = false;",
                    "+                    }",
                    "+                }",
                    "+",
                    "+                continue;",
                    "+            }",
                    "+",
                    "+            if (in_array($token[0], [T_CLASS, T_TRAIT, T_ENUM], true)) {",
                    "+                $inClass = true;",
                    "+",
                    "+                continue;",
                    "+            }",
                    "+",
                    "+            \/\/ Look for T_FUNCTION with name __construct",
                    "+            if ($token[0] !== T_FUNCTION) {",
                    "+                continue;",
                    "+            }",
                    "+",
                    "+            \/\/ Check if it's __construct",
                    "+            $funcName = $this->findNextString($tokens, $i);",
                    "+            if ($funcName !== '__construct') {",
                    "+                continue;",
                    "+            }",
                    "+",
                    "+            \/\/ Find opening parenthesis",
                    "+            $parenStart = null;",
                    "+            for ($j = $i + 1; $j < $count; $j++) {",
                    "+                if ($tokens[$j] === '(') {",
                    "+                    $parenStart = $j;",
                    "+",
                    "+                    break;",
                    "+                }",
                    "+            }",
                    "+",
                    "+            if ($parenStart === null) {",
                    "+                continue;",
                    "+            }",
                    "+",
                    "+            \/\/ Extract type hints from parameters",
                    "+            $parenDepth = 1;",
                    "+            $lastType = null;",
                    "+",
                    "+            for ($j = $parenStart + 1; $j < $count && $parenDepth > 0; $j++) {",
                    "+                $paramToken = $tokens[$j];",
                    "+",
                    "+                if ($paramToken === '(') {",
                    "+                    $parenDepth++;",
                    "+",
                    "+                    continue;",
                    "+                }",
                    "+                if ($paramToken === ')') {",
                    "+                    $parenDepth--;",
                    "+",
                    "+                    continue;",
                    "+                }",
                    "+",
                    "+                if (!is_array($paramToken)) {",
                    "+                    continue;",
                    "+                }",
                    "+",
                    "+                \/\/ Type hint tokens (skip built-in types)",
                    "+                if (in_array($paramToken[0], [T_STRING, T_NAME_QUALIFIED, T_NAME_FULLY_QUALIFIED], true)) {",
                    "+                    $typeName = $paramToken[1];",
                    "+",
                    "+                    \/\/ Skip built-in types",
                    "+                    if (!$this->isBuiltInType($typeName)) {",
                    "+                        $lastType = $typeName;",
                    "+                    }",
                    "+                }",
                    "+",
                    "+                \/\/ Variable = end of parameter, save type if found",
                    "+                if ($paramToken[0] === T_VARIABLE && $lastType !== null) {",
                    "+                    $deps[] = $this->resolveFqcn($lastType);",
                    "+                    $lastType = null;",
                    "+                }",
                    "+            }",
                    "+",
                    "+            \/\/ Only process first constructor found",
                    "+            break;",
                    "+        }",
                    "+",
                    "+        return array_unique($deps);",
                    "+    }",
                    "+",
                    "+    \/**",
                    "+     * Resolve a short or qualified name to FQCN.",
                    "+     *\/",
                    "+    private function resolveFqcn(string $name): string",
                    "+    {",
                    "+        \/\/ Already fully qualified",
                    "+        if (str_starts_with($name, '\\\\')) {",
                    "+            return ltrim($name, '\\\\');",
                    "+        }",
                    "+",
                    "+        \/\/ Already contains namespace separator (qualified name)",
                    "+        if (str_contains($name, '\\\\')) {",
                    "+            return $name;",
                    "+        }",
                    "+",
                    "+        \/\/ Check import map",
                    "+        if (isset($this->importMap[$name])) {",
                    "+            return $this->importMap[$name];",
                    "+        }",
                    "+",
                    "+        \/\/ Default to current namespace + name",
                    "+        if ($this->currentNamespace !== null) {",
                    "+            return $this->currentNamespace . '\\\\' . $name;",
                    "+        }",
                    "+",
                    "+        return $name;",
                    "+    }",
                    "+",
                    "+    \/**",
                    "+     * Check if a type name is a PHP built-in type.",
                    "+     *\/",
                    "+    private function isBuiltInType(string $type): bool",
                    "+    {",
                    "+        $builtIn = [",
                    "+            'int', 'float', 'bool', 'string', 'array', 'object', 'callable',",
                    "+            'iterable', 'void', 'null', 'mixed', 'never', 'true', 'false',",
                    "+            'self', 'static', 'parent',",
                    "+        ];",
                    "+",
                    "+        return in_array(strtolower($type), $builtIn, true);",
                    "+    }",
                    "+",
                    "+    \/**",
                    "+     * Extract a type name starting from a position.",
                    "+     *",
                    "+     * @param array<mixed> $tokens",
                    "+     *\/",
                    "+    private function extractTypeName(array $tokens, int $startIndex): ?string",
                    "+    {",
                    "+        $count = count($tokens);",
                    "+",
                    "+        for ($i = $startIndex; $i < $count; $i++) {",
                    "+            $token = $tokens[$i];",
                    "+",
                    "+            if (!is_array($token)) {",
                    "+                continue;",
                    "+            }",
                    "+",
                    "+            if ($token[0] === T_WHITESPACE) {",
                    "+                continue;",
                    "+            }",
                    "+",
                    "+            if (in_array($token[0], [T_STRING, T_NAME_QUALIFIED, T_NAME_FULLY_QUALIFIED], true)) {",
                    "+                return $token[1];",
                    "+            }",
                    "+",
                    "+            \/\/ Stop at unexpected token",
                    "+            break;",
                    "+        }",
                    "+",
                    "+        return null;",
                    "+    }",
                    "+",
                    "+    \/**",
                    "+     * Find next T_STRING token and return its value.",
                    "+     *",
                    "+     * @param array<mixed> $tokens",
                    "+     *\/",
                    "+    private function findNextString(array $tokens, int $startIndex): ?string",
                    "+    {",
                    "+        $count = count($tokens);",
                    "+",
                    "+        for ($i = $startIndex + 1; $i < $count; $i++) {",
                    "+            if (!is_array($tokens[$i])) {",
                    "+                if ($tokens[$i] === '(') {",
                    "+                    return null; \/\/ Anonymous function",
                    "+                }",
                    "+",
                    "+                continue;",
                    "+            }",
                    "+",
                    "+            if ($tokens[$i][0] === T_STRING) {",
                    "+                return $tokens[$i][1];",
                    "+            }",
                    "+",
                    "+            if ($tokens[$i][0] !== T_WHITESPACE) {",
                    "+                break;",
                    "+            }",
                    "+        }",
                    "+",
                    "+        return null;",
                    "+    }",
                    "+",
                    "+    \/**",
                    "+     * @param array<mixed> $tokens",
                    "+     *\/",
                    "     private function findPrevNonWhitespace(array $tokens, int $index): ?int",
                    "     {",
                    "         for ($i = $index - 1; $i >= 0; $i--) {"
                ]
            },
            {
                "oldStart": 228,
                "oldLines": 4,
                "newStart": 740,
                "newLines": 20,
                "lines": [
                    " ",
                    "         return null;",
                    "     }",
                    "+",
                    "+    \/**",
                    "+     * @param array<mixed> $tokens",
                    "+     *\/",
                    "+    private function findNextNonWhitespace(array $tokens, int $index): ?int",
                    "+    {",
                    "+        $count = count($tokens);",
                    "+",
                    "+        for ($i = $index + 1; $i < $count; $i++) {",
                    "+            if (!is_array($tokens[$i]) || $tokens[$i][0] !== T_WHITESPACE) {",
                    "+                return $i;",
                    "+            }",
                    "+        }",
                    "+",
                    "+        return null;",
                    "+    }",
                    " }"
                ]
            }
        ],
        "originalFile": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Infrastructure\\CodeAnalysis;\n\n\/\/ @responsibility: Tokenizer-basierte PHP-Dateianalyse\n\nfinal class PhpFileParser\n{\n    \/**\n     * Parst eine PHP-Datei und extrahiert Metadaten.\n     *\n     * @return array{\n     *     namespace: string|null,\n     *     classes: array<array{name: string, type: string, line: int}>,\n     *     functions: array<array{name: string, visibility: string|null, line: int}>,\n     *     error: string|null\n     * }\n     *\/\n    public function parse(string $filePath): array\n    {\n        $result = [\n            'namespace' => null,\n            'classes' => [],\n            'functions' => [],\n            'error' => null,\n        ];\n\n        if (!file_exists($filePath) || !is_readable($filePath)) {\n            $result['error'] = 'Datei nicht lesbar';\n\n            return $result;\n        }\n\n        $content = file_get_contents($filePath);\n        if ($content === false) {\n            $result['error'] = 'Datei konnte nicht gelesen werden';\n\n            return $result;\n        }\n\n        try {\n            $tokens = @token_get_all($content);\n        } catch (\\Throwable $e) {\n            $result['error'] = 'Token-Fehler: ' . $e->getMessage();\n\n            return $result;\n        }\n\n        $result['namespace'] = $this->extractNamespace($tokens);\n        $result['classes'] = $this->extractClasses($tokens);\n        $result['functions'] = $this->extractFunctions($tokens);\n\n        return $result;\n    }\n\n    \/**\n     * @param array<mixed> $tokens\n     *\/\n    private function extractNamespace(array $tokens): ?string\n    {\n        $namespace = '';\n        $capturing = false;\n\n        foreach ($tokens as $token) {\n            if (is_array($token)) {\n                if ($token[0] === T_NAMESPACE) {\n                    $capturing = true;\n                    continue;\n                }\n\n                if ($capturing) {\n                    if ($token[0] === T_NAME_QUALIFIED || $token[0] === T_STRING) {\n                        $namespace .= $token[1];\n                    } elseif ($token[0] === T_NS_SEPARATOR) {\n                        $namespace .= '\\\\';\n                    }\n                }\n            } elseif ($capturing && ($token === ';' || $token === '{')) {\n                break;\n            }\n        }\n\n        return $namespace !== '' ? $namespace : null;\n    }\n\n    \/**\n     * @param array<mixed> $tokens\n     * @return array<array{name: string, type: string, line: int}>\n     *\/\n    private function extractClasses(array $tokens): array\n    {\n        $classes = [];\n        $count = count($tokens);\n\n        for ($i = 0; $i < $count; $i++) {\n            $token = $tokens[$i];\n\n            if (!is_array($token)) {\n                continue;\n            }\n\n            $type = match ($token[0]) {\n                T_CLASS => 'class',\n                T_INTERFACE => 'interface',\n                T_TRAIT => 'trait',\n                T_ENUM => 'enum',\n                default => null,\n            };\n\n            if ($type === null) {\n                continue;\n            }\n\n            \/\/ Prüfen ob es kein anonymes Class-Statement ist\n            $prevIndex = $this->findPrevNonWhitespace($tokens, $i);\n            if ($prevIndex !== null && is_array($tokens[$prevIndex]) && $tokens[$prevIndex][0] === T_NEW) {\n                continue;\n            }\n\n            \/\/ Klassennamen finden\n            for ($j = $i + 1; $j < $count; $j++) {\n                if (is_array($tokens[$j]) && $tokens[$j][0] === T_STRING) {\n                    $classes[] = [\n                        'name' => $tokens[$j][1],\n                        'type' => $type,\n                        'line' => $token[2],\n                    ];\n                    break;\n                }\n            }\n        }\n\n        return $classes;\n    }\n\n    \/**\n     * @param array<mixed> $tokens\n     * @return array<array{name: string, visibility: string|null, line: int}>\n     *\/\n    private function extractFunctions(array $tokens): array\n    {\n        $functions = [];\n        $count = count($tokens);\n        $braceDepth = 0;\n        $inClass = false;\n\n        for ($i = 0; $i < $count; $i++) {\n            $token = $tokens[$i];\n\n            if (!is_array($token)) {\n                if ($token === '{') {\n                    $braceDepth++;\n                } elseif ($token === '}') {\n                    $braceDepth--;\n                    if ($braceDepth === 0) {\n                        $inClass = false;\n                    }\n                }\n                continue;\n            }\n\n            \/\/ Klasse\/Interface\/Trait betreten\n            if (in_array($token[0], [T_CLASS, T_INTERFACE, T_TRAIT, T_ENUM], true)) {\n                $inClass = true;\n            }\n\n            if ($token[0] !== T_FUNCTION) {\n                continue;\n            }\n\n            \/\/ Visibility ermitteln\n            $visibility = null;\n            if ($inClass) {\n                for ($j = $i - 1; $j >= 0; $j--) {\n                    if (!is_array($tokens[$j])) {\n                        break;\n                    }\n                    if ($tokens[$j][0] === T_PUBLIC) {\n                        $visibility = 'public';\n                        break;\n                    }\n                    if ($tokens[$j][0] === T_PROTECTED) {\n                        $visibility = 'protected';\n                        break;\n                    }\n                    if ($tokens[$j][0] === T_PRIVATE) {\n                        $visibility = 'private';\n                        break;\n                    }\n                    if ($tokens[$j][0] !== T_WHITESPACE && $tokens[$j][0] !== T_STATIC && $tokens[$j][0] !== T_FINAL && $tokens[$j][0] !== T_ABSTRACT) {\n                        break;\n                    }\n                }\n            }\n\n            \/\/ Funktionsnamen finden\n            for ($j = $i + 1; $j < $count; $j++) {\n                if (is_array($tokens[$j]) && $tokens[$j][0] === T_STRING) {\n                    $functions[] = [\n                        'name' => $tokens[$j][1],\n                        'visibility' => $visibility,\n                        'line' => $token[2],\n                    ];\n                    break;\n                }\n                \/\/ Anonyme Funktion (kein Name)\n                if ($tokens[$j] === '(') {\n                    break;\n                }\n            }\n        }\n\n        return $functions;\n    }\n\n    \/**\n     * @param array<mixed> $tokens\n     *\/\n    private function findPrevNonWhitespace(array $tokens, int $index): ?int\n    {\n        for ($i = $index - 1; $i >= 0; $i--) {\n            if (!is_array($tokens[$i]) || $tokens[$i][0] !== T_WHITESPACE) {\n                return $i;\n            }\n        }\n\n        return null;\n    }\n}\n"
    }
}
← Vorheriger Zur Liste Nächster →