DependencyGraphRepository.php
- Pfad:
src/Infrastructure/Persistence/DependencyGraphRepository.php - Namespace: Infrastructure\Persistence
- Zeilen: 192 | Größe: 6,267 Bytes
- Geändert: 2025-12-25 12:44:13 | 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.
Dependencies 4
- implements Domain\Repository\DependencyGraphRepositoryInterface
- constructor PDO
- use Domain\Repository\DependencyGraphRepositoryInterface
- use PDO
Klassen 1
-
DependencyGraphRepositoryclass Zeile 12
Funktionen 5
-
__construct()public Zeile 16 -
findDependents()public Zeile 28 -
getDependencies()public Zeile 56 -
getDependencyStatistics()public Zeile 74 -
getGlobalGraphData()public Zeile 113
Verwendet von 1
Code
<?php
declare(strict_types=1);
namespace Infrastructure\Persistence;
// @responsibility: Persistenz für Dependency-Graph (code_dependencies Tabelle)
use Domain\Repository\DependencyGraphRepositoryInterface;
use PDO;
class DependencyGraphRepository implements DependencyGraphRepositoryInterface
{
private PDO $pdo;
public function __construct(PDO $pdo)
{
$this->pdo = $pdo;
}
/**
* Find all files that depend on a given FQCN (reverse lookup for impact analysis).
*
* @param string $fqcn The fully qualified class name to search for
* @param string|null $dependencyType Filter by type (use, extends, implements, trait, constructor)
* @return array<array{id: int, file_name: string, file_path: string, namespace: string|null, dependency_type: string}>
*/
public function findDependents(string $fqcn, ?string $dependencyType = null): array
{
$sql = '
SELECT ca.id, ca.file_name, ca.file_path, ca.namespace, cd.dependency_type
FROM code_dependencies cd
JOIN code_analysis ca ON cd.analysis_id = ca.id
WHERE cd.target_fqcn = :fqcn
';
$params = ['fqcn' => $fqcn];
if ($dependencyType !== null) {
$sql .= ' AND cd.dependency_type = :dep_type';
$params['dep_type'] = $dependencyType;
}
$sql .= ' ORDER BY ca.file_name';
$stmt = $this->pdo->prepare($sql);
$stmt->execute($params);
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
/**
* Get all dependencies for a given analysis ID.
*
* @return array<array{dependency_type: string, target_fqcn: string}>
*/
public function getDependencies(int $analysisId): array
{
$stmt = $this->pdo->prepare('
SELECT dependency_type, target_fqcn
FROM code_dependencies
WHERE analysis_id = :id
ORDER BY dependency_type, target_fqcn
');
$stmt->execute(['id' => $analysisId]);
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
/**
* Get dependency statistics.
*
* @return array{total_dependencies: int, by_type: array<string, int>, most_used: array<array{fqcn: string, count: int}>}
*/
public function getDependencyStatistics(): array
{
// Total count
$stmt = $this->pdo->query('SELECT COUNT(*) FROM code_dependencies');
$total = (int) $stmt->fetchColumn();
// Count by type
$stmt = $this->pdo->query('
SELECT dependency_type, COUNT(*) as cnt
FROM code_dependencies
GROUP BY dependency_type
');
$byType = [];
foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
$byType[$row['dependency_type']] = (int) $row['cnt'];
}
// Most used classes (top 20)
$stmt = $this->pdo->query('
SELECT target_fqcn as fqcn, COUNT(*) as count
FROM code_dependencies
GROUP BY target_fqcn
ORDER BY count DESC
LIMIT 20
');
$mostUsed = $stmt->fetchAll(PDO::FETCH_ASSOC);
return [
'total_dependencies' => $total,
'by_type' => $byType,
'most_used' => $mostUsed,
];
}
/**
* Get global graph data for entire project.
*
* @return array{nodes: array<array<string, mixed>>, links: array<array<string, mixed>>, stats: array<string, int>}
*/
public function getGlobalGraphData(): array
{
$nodes = [];
$links = [];
$nodeIndex = [];
// Get all files with classes
$stmt = $this->pdo->query('
SELECT id, file_name, namespace, classes, extends_class, extension
FROM code_analysis
WHERE classes IS NOT NULL AND classes != "[]"
ORDER BY namespace, file_name
');
foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
$classes = json_decode($row['classes'], true);
if (empty($classes)) {
continue;
}
$className = $classes[0]['name'];
$fqcn = $row['namespace'] ? $row['namespace'] . '\\' . $className : $className;
$namespace = $row['namespace'] ?? 'global';
if (!isset($nodeIndex[$fqcn])) {
$nodeIndex[$fqcn] = count($nodes);
$nodes[] = [
'id' => $fqcn,
'label' => $className,
'namespace' => $namespace,
'fileId' => $row['id'],
'type' => $classes[0]['type'] ?? 'class',
'extension' => $row['extension'] ?? 'php',
];
}
}
// Get all dependency relationships (internal only)
$stmt = $this->pdo->query('
SELECT cd.target_fqcn, cd.dependency_type, ca.namespace, ca.classes
FROM code_dependencies cd
JOIN code_analysis ca ON cd.analysis_id = ca.id
WHERE cd.dependency_type IN ("extends", "implements", "constructor", "use", "trait")
');
foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
$classes = json_decode($row['classes'], true);
if (empty($classes)) {
continue;
}
$sourceClass = $classes[0]['name'];
$sourceFqcn = $row['namespace'] ? $row['namespace'] . '\\' . $sourceClass : $sourceClass;
$targetFqcn = $row['target_fqcn'];
// Only include if both nodes exist in project
if (isset($nodeIndex[$sourceFqcn]) && isset($nodeIndex[$targetFqcn])) {
$links[] = [
'source' => $nodeIndex[$sourceFqcn],
'target' => $nodeIndex[$targetFqcn],
'type' => $row['dependency_type'],
];
}
}
// Stats
$stats = [
'nodes' => count($nodes),
'links' => count($links),
'namespaces' => count(array_unique(array_column($nodes, 'namespace'))),
];
return [
'nodes' => $nodes,
'links' => $links,
'stats' => $stats,
];
}
}