Backup #1314

ID1314
Dateipfad/var/www/dev.campus.systemische-tools.de/src/Infrastructure/Persistence/FileAnalysisRepository.php
Version3
Typ modified
Größe11.1 KB
Hashceb32b9da304679297aae588bc3b93124a08013c3c930119ed1a91935439babc
Datum2025-12-25 16:16:20
Geändert vonclaude-code-hook
GrundClaude Code Pre-Hook Backup vor Edit-Operation
Datei existiert Ja

Dateiinhalt

<?php

declare(strict_types=1);

namespace Infrastructure\Persistence;

// @responsibility: Persistenz für Datei-Analyse (code_analysis Tabelle)

use Domain\Repository\FileAnalysisRepositoryInterface;
use PDO;

class FileAnalysisRepository implements FileAnalysisRepositoryInterface
{
    private PDO $pdo;

    public function __construct(PDO $pdo)
    {
        $this->pdo = $pdo;
    }

    /**
     * @param array<string, mixed> $filters
     * @return array<array<string, mixed>>
     */
    public function findAll(array $filters = [], int $limit = 100, int $offset = 0): array
    {
        $sql = '
            SELECT ca.*,
                   cq.dependency_score, cq.hygiene_score, cq.hygiene_status,
                   cq.factor_scores, cq.hardcoded_count, cq.issues_count,
                   cq.warnings_count, cq.issues_json
            FROM code_analysis ca
            LEFT JOIN code_quality cq ON ca.id = cq.analysis_id
            WHERE 1=1
        ';
        $params = [];

        if (!empty($filters['scan_id'])) {
            $sql .= ' AND ca.scan_id = :scan_id';
            $params['scan_id'] = $filters['scan_id'];
        }

        if (!empty($filters['directory'])) {
            $sql .= ' AND ca.directory = :directory';
            $params['directory'] = $filters['directory'];
        }

        if (!empty($filters['namespace'])) {
            $sql .= ' AND ca.namespace = :namespace';
            $params['namespace'] = $filters['namespace'];
        }

        if (!empty($filters['extension'])) {
            $sql .= ' AND ca.extension = :extension';
            $params['extension'] = $filters['extension'];
        }

        if (!empty($filters['search'])) {
            $sql .= ' AND (ca.file_name LIKE :search OR ca.namespace LIKE :search2)';
            $params['search'] = '%' . $filters['search'] . '%';
            $params['search2'] = '%' . $filters['search'] . '%';
        }

        if (isset($filters['has_classes']) && $filters['has_classes'] !== '') {
            if ($filters['has_classes'] === '1') {
                $sql .= " AND ca.classes IS NOT NULL AND ca.classes != '[]'";
            } else {
                $sql .= " AND (ca.classes IS NULL OR ca.classes = '[]')";
            }
        }

        if (isset($filters['has_error']) && $filters['has_error'] === '1') {
            $sql .= ' AND ca.parse_error IS NOT NULL';
        }

        if (!empty($filters['hygiene_status'])) {
            $sql .= ' AND cq.hygiene_status = :hygiene_status';
            $params['hygiene_status'] = $filters['hygiene_status'];
        }

        if (!empty($filters['has_issues'])) {
            $sql .= ' AND cq.issues_count > 0';
        }

        $sql .= ' ORDER BY ca.directory, ca.file_name LIMIT :limit OFFSET :offset';

        $stmt = $this->pdo->prepare($sql);
        foreach ($params as $key => $value) {
            $stmt->bindValue(':' . $key, $value);
        }
        $stmt->bindValue(':limit', $limit, PDO::PARAM_INT);
        $stmt->bindValue(':offset', $offset, PDO::PARAM_INT);
        $stmt->execute();

        return $stmt->fetchAll(PDO::FETCH_ASSOC);
    }

    /**
     * @return array<string, mixed>|null
     */
    public function findById(int $id): ?array
    {
        $stmt = $this->pdo->prepare('
            SELECT ca.*,
                   cq.complexity_score, cq.loc_score, cq.dependency_score,
                   cq.hardcoded_count, cq.issues_count, cq.warnings_count,
                   cq.quality_grade, cq.issues_json
            FROM code_analysis ca
            LEFT JOIN code_quality cq ON ca.id = cq.analysis_id
            WHERE ca.id = :id
        ');
        $stmt->execute(['id' => $id]);
        $result = $stmt->fetch(PDO::FETCH_ASSOC);

        return $result ?: null;
    }

    /**
     * @param array<array<string, mixed>> $items
     */
    public function saveBatch(array $items, string $scanId): int
    {
        if (empty($items)) {
            return 0;
        }

        $this->pdo->beginTransaction();

        try {
            $analysisStmt = $this->pdo->prepare('
                INSERT INTO code_analysis
                (scan_id, file_path, file_name, extension, directory, file_size, line_count,
                 file_content, modified_at, namespace, classes, functions, uses, extends_class,
                 implements_interfaces, traits_used, constructor_deps, parse_error, triggered_by)
                VALUES
                (:scan_id, :file_path, :file_name, :extension, :directory, :file_size, :line_count,
                 :file_content, :modified_at, :namespace, :classes, :functions, :uses, :extends_class,
                 :implements_interfaces, :traits_used, :constructor_deps, :parse_error, :triggered_by)
            ');

            $depStmt = $this->pdo->prepare('
                INSERT INTO code_dependencies (analysis_id, dependency_type, target_fqcn)
                VALUES (:analysis_id, :dep_type, :target_fqcn)
            ');

            $count = 0;
            foreach ($items as $item) {
                $analysisStmt->execute([
                    'scan_id' => $scanId,
                    'file_path' => $item['file_path'],
                    'file_name' => $item['file_name'],
                    'extension' => $item['extension'],
                    'directory' => $item['directory'],
                    'file_size' => $item['file_size'],
                    'line_count' => $item['line_count'],
                    'file_content' => $item['file_content'] ?? '',
                    'modified_at' => $item['modified_at'],
                    'namespace' => $item['namespace'],
                    'classes' => json_encode($item['classes'] ?? [], JSON_UNESCAPED_UNICODE),
                    'functions' => json_encode($item['functions'] ?? [], JSON_UNESCAPED_UNICODE),
                    'uses' => json_encode($item['uses'] ?? [], JSON_UNESCAPED_UNICODE),
                    'extends_class' => $item['extends_class'],
                    'implements_interfaces' => json_encode($item['implements_interfaces'] ?? [], JSON_UNESCAPED_UNICODE),
                    'traits_used' => json_encode($item['traits_used'] ?? [], JSON_UNESCAPED_UNICODE),
                    'constructor_deps' => json_encode($item['constructor_deps'] ?? [], JSON_UNESCAPED_UNICODE),
                    'parse_error' => $item['parse_error'],
                    'triggered_by' => $item['triggered_by'] ?? 'web',
                ]);

                $analysisId = (int) $this->pdo->lastInsertId();

                // Save dependencies to normalized table
                $this->saveDependencies($depStmt, $analysisId, $item);

                $count++;
            }

            $this->pdo->commit();

            return $count;
        } catch (\Throwable $e) {
            $this->pdo->rollBack();

            throw $e;
        }
    }

    /**
     * @param array<string, mixed> $item
     */
    private function saveDependencies(\PDOStatement $stmt, int $analysisId, array $item): void
    {
        // Use statements
        foreach ($item['uses'] ?? [] as $fqcn) {
            $stmt->execute(['analysis_id' => $analysisId, 'dep_type' => 'use', 'target_fqcn' => $fqcn]);
        }

        // Extends
        if (!empty($item['extends_class'])) {
            $stmt->execute(['analysis_id' => $analysisId, 'dep_type' => 'extends', 'target_fqcn' => $item['extends_class']]);
        }

        // Implements
        foreach ($item['implements_interfaces'] ?? [] as $fqcn) {
            $stmt->execute(['analysis_id' => $analysisId, 'dep_type' => 'implements', 'target_fqcn' => $fqcn]);
        }

        // Traits
        foreach ($item['traits_used'] ?? [] as $fqcn) {
            $stmt->execute(['analysis_id' => $analysisId, 'dep_type' => 'trait', 'target_fqcn' => $fqcn]);
        }

        // Constructor dependencies
        foreach ($item['constructor_deps'] ?? [] as $fqcn) {
            $stmt->execute(['analysis_id' => $analysisId, 'dep_type' => 'constructor', 'target_fqcn' => $fqcn]);
        }
    }

    public function deleteByNotScanId(string $currentScanId): int
    {
        $stmt = $this->pdo->prepare('DELETE FROM code_analysis WHERE scan_id != :scan_id');
        $stmt->execute(['scan_id' => $currentScanId]);

        return $stmt->rowCount();
    }

    public function getLatestScanId(): ?string
    {
        $stmt = $this->pdo->query('SELECT scan_id FROM code_analysis ORDER BY scanned_at DESC LIMIT 1');
        $result = $stmt->fetchColumn();

        return $result ?: null;
    }

    /**
     * @return array<array<string, mixed>>
     */
    public function findByScanId(string $scanId): array
    {
        $stmt = $this->pdo->prepare('
            SELECT * FROM code_analysis WHERE scan_id = :scan_id ORDER BY file_path
        ');
        $stmt->execute(['scan_id' => $scanId]);

        return $stmt->fetchAll(PDO::FETCH_ASSOC);
    }

    /**
     * @return array<string, mixed>
     */
    public function getStatistics(?string $scanId = null): array
    {
        $where = $scanId ? 'WHERE scan_id = :scan_id' : '';
        $params = $scanId ? ['scan_id' => $scanId] : [];

        $stmt = $this->pdo->prepare("
            SELECT
                COUNT(*) as total_files,
                SUM(line_count) as total_lines,
                SUM(file_size) as total_size,
                COUNT(CASE WHEN parse_error IS NOT NULL THEN 1 END) as files_with_errors,
                MAX(scanned_at) as last_scan
            FROM code_analysis {$where}
        ");
        $stmt->execute($params);
        $stats = $stmt->fetch(PDO::FETCH_ASSOC);

        $stmt = $this->pdo->prepare("SELECT classes, functions FROM code_analysis {$where}");
        $stmt->execute($params);
        $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);

        $totalClasses = 0;
        $totalFunctions = 0;
        foreach ($rows as $row) {
            $classes = json_decode($row['classes'] ?? '[]', true);
            $functions = json_decode($row['functions'] ?? '[]', true);
            $totalClasses += count($classes);
            $totalFunctions += count($functions);
        }

        return [
            'total_files' => (int) ($stats['total_files'] ?? 0),
            'total_lines' => (int) ($stats['total_lines'] ?? 0),
            'total_size' => (int) ($stats['total_size'] ?? 0),
            'total_classes' => $totalClasses,
            'total_functions' => $totalFunctions,
            'files_with_errors' => (int) ($stats['files_with_errors'] ?? 0),
            'last_scan' => $stats['last_scan'],
        ];
    }

    /**
     * @return array<string>
     */
    public function getDistinctDirectories(): array
    {
        $stmt = $this->pdo->query('SELECT DISTINCT directory FROM code_analysis ORDER BY directory');

        return $stmt->fetchAll(PDO::FETCH_COLUMN);
    }

    /**
     * @return array<string>
     */
    public function getDistinctNamespaces(): array
    {
        $stmt = $this->pdo->query('SELECT DISTINCT namespace FROM code_analysis WHERE namespace IS NOT NULL ORDER BY namespace');

        return $stmt->fetchAll(PDO::FETCH_COLUMN);
    }

    /**
     * @return array<array<string, mixed>>
     */
    public function getConfiguredDirectories(): array
    {
        $stmt = $this->pdo->query('SELECT * FROM code_scan_config WHERE enabled = 1 ORDER BY label');

        return $stmt->fetchAll(PDO::FETCH_ASSOC);
    }
}

Vollständig herunterladen

Aktionen

Herunterladen

Andere Versionen dieser Datei

ID Version Typ Größe Datum
1870 8 modified 10.9 KB 2025-12-27 23:50
1866 7 modified 10.9 KB 2025-12-27 23:49
1334 6 modified 10.9 KB 2025-12-25 16:26
1333 5 modified 11.1 KB 2025-12-25 16:26
1332 4 modified 11.1 KB 2025-12-25 16:26
1314 3 modified 11.1 KB 2025-12-25 16:16
1313 2 modified 11.1 KB 2025-12-25 16:16
1312 1 modified 11.1 KB 2025-12-25 16:16

← Zurück zur Übersicht