PythonFileParser.php
- Pfad:
src/Infrastructure/CodeAnalysis/PythonFileParser.php - Namespace: Infrastructure\CodeAnalysis
- Zeilen: 162 | Größe: 4,802 Bytes
- Geändert: 2025-12-25 09:23:49 | Gescannt: 2025-12-31 10:22:15
Code Hygiene Score: 100
- Dependencies: 100 (25%)
- LOC: 100 (20%)
- Methods: 100 (20%)
- Secrets: 100 (15%)
- Classes: 100 (10%)
- Magic Numbers: 100 (10%)
Keine Issues gefunden.
Klassen 1
-
PythonFileParserclass Zeile 9
Funktionen 2
-
parse()public Zeile 28 -
extractNamespace()private Zeile 129
Verwendet von 2
- CodeScanner.php constructor
- InfrastructureServiceProvider.php use
Versionen 1
-
v1
2025-12-25 09:23 | claude-code-hook | modified
Claude Code Pre-Hook Backup vor Edit-Operation
Code
<?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'])) {
$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;
}
}