Backup #1114

ID1114
Dateipfad/var/www/dev.campus.systemische-tools.de/src/Infrastructure/CodeAnalysis/PythonFileParser.php
Version1
Typ modified
Größe4.7 KB
Hash73e1e090e4fe45fdd5b3493bb39f80648307ddcab012f933d8f8649f8aa6db63
Datum2025-12-25 09:23:49
Geändert vonclaude-code-hook
GrundClaude Code Pre-Hook Backup vor Edit-Operation
Datei existiert Ja

Dateiinhalt

<?php

declare(strict_types=1);

namespace Infrastructure\CodeAnalysis;

// @responsibility: Python-Dateianalyse via AST-basiertem Python-Script

final class PythonFileParser
{
    private const SCRIPT_PATH = __DIR__ . '/scripts/python_parser.py';

    /**
     * Parst eine Python-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}>,
     *     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
    {
        $result = [
            'namespace' => null,
            'classes' => [],
            'functions' => [],
            'uses' => [],
            'extends_class' => null,
            'implements_interfaces' => [],
            'traits_used' => [],
            'constructor_deps' => [],
            'error' => null,
        ];

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

            return $result;
        }

        // Python-Script ausführen
        $command = sprintf(
            'python3 %s %s 2>&1',
            escapeshellarg(self::SCRIPT_PATH),
            escapeshellarg($filePath)
        );

        $output = shell_exec($command);

        if ($output === null) {
            $result['error'] = 'Python-Parser konnte nicht ausgeführt werden';

            return $result;
        }

        $parsed = json_decode($output, true);

        if ($parsed === null) {
            $result['error'] = 'JSON-Parsing fehlgeschlagen: ' . $output;

            return $result;
        }

        if (isset($parsed['error']) && $parsed['error'] !== null) {
            $result['error'] = $parsed['error'];

            return $result;
        }

        // Namespace aus Verzeichnisstruktur ableiten
        $result['namespace'] = $this->extractNamespace($filePath);

        // Klassen konvertieren
        foreach ($parsed['classes'] ?? [] as $class) {
            $result['classes'][] = [
                'name' => $class['name'],
                'type' => 'class',
                'line' => $class['line'],
            ];

            // Erste Basisklasse als extends_class
            if (!empty($class['bases']) && $result['extends_class'] === null) {
                $result['extends_class'] = $class['bases'][0];
            }

            // Weitere Basisklassen als "Mixins" (ähnlich Traits)
            if (count($class['bases'] ?? []) > 1) {
                $result['traits_used'] = array_merge(
                    $result['traits_used'],
                    array_slice($class['bases'], 1)
                );
            }
        }

        // Funktionen konvertieren
        foreach ($parsed['functions'] ?? [] as $func) {
            $result['functions'][] = [
                'name' => $func['name'],
                'visibility' => null,
                'line' => $func['line'],
            ];
        }

        // Imports als uses
        foreach ($parsed['imports'] ?? [] as $import) {
            if ($import['type'] === 'import') {
                $result['uses'][] = $import['module'];
            } else {
                // from X import Y -> X.Y
                $result['uses'][] = $import['full'] ?? $import['module'];
            }
        }

        $result['uses'] = array_unique($result['uses']);

        return $result;
    }

    /**
     * Extrahiert einen pseudo-Namespace aus dem Dateipfad.
     */
    private function extractNamespace(string $filePath): ?string
    {
        // /opt/mcp-servers/mcp-db/tools/select_tool.py -> mcp-db.tools
        if (preg_match('#/opt/mcp-servers/([^/]+)/(.+)\.py$#', $filePath, $matches)) {
            $server = $matches[1];
            $subPath = dirname($matches[2]);

            if ($subPath === '.') {
                return $server;
            }

            return $server . '.' . str_replace('/', '.', $subPath);
        }

        // /var/www/scripts/pipeline/detect.py -> pipeline
        if (preg_match('#/var/www/scripts/([^/]+)#', $filePath, $matches)) {
            return $matches[1];
        }

        // /var/www/tools/ki-protokoll/claude-hook/quality/pre_rules.py -> claude-hook.quality
        if (preg_match('#/claude-hook/(.+)\.py$#', $filePath, $matches)) {
            $subPath = dirname($matches[1]);

            if ($subPath === '.') {
                return 'claude-hook';
            }

            return 'claude-hook.' . str_replace('/', '.', $subPath);
        }

        return null;
    }
}

Vollständig herunterladen

Aktionen

Herunterladen

← Zurück zur Übersicht