Backup #878

ID878
Dateipfad/var/www/dev.campus.systemische-tools.de/src/Infrastructure/CodeAnalysis/PhpFileParser.php
Version1
Typ modified
Größe6.6 KB
Hash242080786579e14796dae0cf7c6c46cbefe690d41529f10a445bc359c643ccac
Datum2025-12-23 14:14:46
Geändert vonclaude-code-hook
GrundClaude Code Pre-Hook Backup vor Write-Operation
Datei existiert Ja

Dateiinhalt

<?php

declare(strict_types=1);

namespace Infrastructure\CodeAnalysis;

// @responsibility: Tokenizer-basierte PHP-Dateianalyse

final class PhpFileParser
{
    /**
     * Parst eine PHP-Datei und extrahiert Metadaten.
     *
     * @return array{
     *     namespace: string|null,
     *     classes: array<array{name: string, type: string, line: int}>,
     *     functions: array<array{name: string, visibility: string|null, line: int}>,
     *     error: string|null
     * }
     */
    public function parse(string $filePath): array
    {
        $result = [
            'namespace' => null,
            'classes' => [],
            'functions' => [],
            'error' => null,
        ];

        if (!file_exists($filePath) || !is_readable($filePath)) {
            $result['error'] = 'Datei nicht lesbar';

            return $result;
        }

        $content = file_get_contents($filePath);
        if ($content === false) {
            $result['error'] = 'Datei konnte nicht gelesen werden';

            return $result;
        }

        try {
            $tokens = @token_get_all($content);
        } catch (\Throwable $e) {
            $result['error'] = 'Token-Fehler: ' . $e->getMessage();

            return $result;
        }

        $result['namespace'] = $this->extractNamespace($tokens);
        $result['classes'] = $this->extractClasses($tokens);
        $result['functions'] = $this->extractFunctions($tokens);

        return $result;
    }

    /**
     * @param array<mixed> $tokens
     */
    private function extractNamespace(array $tokens): ?string
    {
        $namespace = '';
        $capturing = false;

        foreach ($tokens as $token) {
            if (is_array($token)) {
                if ($token[0] === T_NAMESPACE) {
                    $capturing = true;
                    continue;
                }

                if ($capturing) {
                    if ($token[0] === T_NAME_QUALIFIED || $token[0] === T_STRING) {
                        $namespace .= $token[1];
                    } elseif ($token[0] === T_NS_SEPARATOR) {
                        $namespace .= '\\';
                    }
                }
            } elseif ($capturing && ($token === ';' || $token === '{')) {
                break;
            }
        }

        return $namespace !== '' ? $namespace : null;
    }

    /**
     * @param array<mixed> $tokens
     * @return array<array{name: string, type: string, line: int}>
     */
    private function extractClasses(array $tokens): array
    {
        $classes = [];
        $count = count($tokens);

        for ($i = 0; $i < $count; $i++) {
            $token = $tokens[$i];

            if (!is_array($token)) {
                continue;
            }

            $type = match ($token[0]) {
                T_CLASS => 'class',
                T_INTERFACE => 'interface',
                T_TRAIT => 'trait',
                T_ENUM => 'enum',
                default => null,
            };

            if ($type === null) {
                continue;
            }

            // Prüfen ob es kein anonymes Class-Statement ist
            $prevIndex = $this->findPrevNonWhitespace($tokens, $i);
            if ($prevIndex !== null && is_array($tokens[$prevIndex]) && $tokens[$prevIndex][0] === T_NEW) {
                continue;
            }

            // Klassennamen finden
            for ($j = $i + 1; $j < $count; $j++) {
                if (is_array($tokens[$j]) && $tokens[$j][0] === T_STRING) {
                    $classes[] = [
                        'name' => $tokens[$j][1],
                        'type' => $type,
                        'line' => $token[2],
                    ];
                    break;
                }
            }
        }

        return $classes;
    }

    /**
     * @param array<mixed> $tokens
     * @return array<array{name: string, visibility: string|null, line: int}>
     */
    private function extractFunctions(array $tokens): array
    {
        $functions = [];
        $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;
            }

            // Klasse/Interface/Trait betreten
            if (in_array($token[0], [T_CLASS, T_INTERFACE, T_TRAIT, T_ENUM], true)) {
                $inClass = true;
            }

            if ($token[0] !== T_FUNCTION) {
                continue;
            }

            // Visibility ermitteln
            $visibility = null;
            if ($inClass) {
                for ($j = $i - 1; $j >= 0; $j--) {
                    if (!is_array($tokens[$j])) {
                        break;
                    }
                    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) {
                        break;
                    }
                }
            }

            // Funktionsnamen finden
            for ($j = $i + 1; $j < $count; $j++) {
                if (is_array($tokens[$j]) && $tokens[$j][0] === T_STRING) {
                    $functions[] = [
                        'name' => $tokens[$j][1],
                        'visibility' => $visibility,
                        'line' => $token[2],
                    ];
                    break;
                }
                // Anonyme Funktion (kein Name)
                if ($tokens[$j] === '(') {
                    break;
                }
            }
        }

        return $functions;
    }

    /**
     * @param array<mixed> $tokens
     */
    private function findPrevNonWhitespace(array $tokens, int $index): ?int
    {
        for ($i = $index - 1; $i >= 0; $i--) {
            if (!is_array($tokens[$i]) || $tokens[$i][0] !== T_WHITESPACE) {
                return $i;
            }
        }

        return null;
    }
}

Vollständig herunterladen

Aktionen

Herunterladen

Andere Versionen dieser Datei

ID Version Typ Größe Datum
2021 12 modified 8.0 KB 2025-12-28 23:20
1039 11 modified 11.7 KB 2025-12-25 01:51
1038 10 modified 11.7 KB 2025-12-25 01:51
1037 9 modified 15.2 KB 2025-12-25 01:50
1036 8 modified 15.2 KB 2025-12-25 01:50
1034 7 modified 18.9 KB 2025-12-25 01:48
1033 6 modified 18.8 KB 2025-12-25 01:48
1032 5 modified 20.9 KB 2025-12-25 01:45
1031 4 modified 20.9 KB 2025-12-25 01:44
1028 3 modified 20.9 KB 2025-12-25 00:18
1027 2 modified 20.9 KB 2025-12-25 00:17
878 1 modified 6.6 KB 2025-12-23 14:14

← Zurück zur Übersicht