DependencyGraphRepository.php

Code Hygiene Score: 100

Keine Issues gefunden.

Dependencies 4

Klassen 1

Funktionen 5

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,
        ];
    }
}
← Übersicht Graph