Backup #1313
| ID | 1313 |
| Dateipfad | /var/www/dev.campus.systemische-tools.de/src/Infrastructure/Persistence/FileAnalysisRepository.php |
| Version | 2 |
| Typ |
modified |
| Größe | 11.1 KB |
| Hash | bf604a582716ff8d42ecad3104b401bbc12f8a2f3769f8901a28c0358606ec37 |
| Datum | 2025-12-25 16:16:20 |
| Geändert von | claude-code-hook |
| Grund | Claude 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['quality_grade'])) {
$sql .= ' AND cq.quality_grade = :quality_grade';
$params['quality_grade'] = $filters['quality_grade'];
}
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
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