code-graph.php

Code Hygiene Score: 99

Issues 1

Zeile Typ Beschreibung
30 magic_number Magic Number gefunden: 100

Versionen 2

Code

<?php

declare(strict_types=1);

// @responsibility: View für Dependency-Graph-Visualisierung

ob_start();
$shortPath = str_replace('/var/www/dev.campus.systemische-tools.de/', '', $file['file_path']);
?>

<nav class="breadcrumb">
    <a href="/docs/code-hygiene">Code Hygiene</a> &raquo;
    <a href="/docs/code-hygiene/<?= $file['id'] ?>"><?= htmlspecialchars($file['file_name']) ?></a> &raquo;
    <span>Graph</span>
</nav>

<h1>Dependency Graph</h1>
<p style="color: var(--text-muted); margin-bottom: 1rem;">
    <code><?= htmlspecialchars($fqcn) ?></code>
</p>

<div class="graph-legend" style="display: flex; gap: 1.5rem; flex-wrap: wrap; margin-bottom: 1rem; font-size: 0.875rem;">
    <span><span style="display: inline-block; width: 12px; height: 12px; background: #6366f1; border-radius: 50%; margin-right: 0.25rem;"></span> Zentral</span>
    <span><span style="display: inline-block; width: 12px; height: 12px; background: #f59e0b; border-radius: 50%; margin-right: 0.25rem;"></span> Extends</span>
    <span><span style="display: inline-block; width: 12px; height: 12px; background: #3b82f6; border-radius: 50%; margin-right: 0.25rem;"></span> Implements</span>
    <span><span style="display: inline-block; width: 12px; height: 12px; background: #8b5cf6; border-radius: 50%; margin-right: 0.25rem;"></span> Constructor</span>
    <span><span style="display: inline-block; width: 12px; height: 12px; background: #10b981; border-radius: 50%; margin-right: 0.25rem;"></span> Dependent</span>
</div>

<div id="graph-container" style="width: 100%; height: 600px; background: var(--bg-secondary); border-radius: 8px; overflow: hidden;"></div>

<div style="margin-top: 1.5rem;">
    <a href="/docs/code-hygiene/<?= $file['id'] ?>" class="btn btn--secondary">&larr; Zurück zu Details</a>
</div>

<script src="https://d3js.org/d3.v7.min.js"></script>
<script>
(function() {
    const container = document.getElementById('graph-container');
    const width = container.clientWidth;
    const height = container.clientHeight;

    const colors = {
        center: '#6366f1',
        extends: '#f59e0b',
        implements: '#3b82f6',
        constructor: '#8b5cf6',
        dependent: '#10b981',
        use: '#94a3b8'
    };

    const linkColors = {
        extends: '#f59e0b',
        implements: '#3b82f6',
        constructor: '#8b5cf6',
        use: '#94a3b8'
    };

    fetch('/docs/code-hygiene/<?= $file['id'] ?>/graph-data')
        .then(r => r.json())
        .then(data => {
            const svg = d3.select('#graph-container')
                .append('svg')
                .attr('width', width)
                .attr('height', height);

            // Arrow markers for directed edges
            svg.append('defs').selectAll('marker')
                .data(['extends', 'implements', 'constructor', 'use'])
                .enter().append('marker')
                .attr('id', d => 'arrow-' + d)
                .attr('viewBox', '0 -5 10 10')
                .attr('refX', 20)
                .attr('refY', 0)
                .attr('markerWidth', 6)
                .attr('markerHeight', 6)
                .attr('orient', 'auto')
                .append('path')
                .attr('d', 'M0,-5L10,0L0,5')
                .attr('fill', d => linkColors[d] || '#94a3b8');

            const simulation = d3.forceSimulation(data.nodes)
                .force('link', d3.forceLink(data.links).id(d => d.id).distance(120))
                .force('charge', d3.forceManyBody().strength(-400))
                .force('center', d3.forceCenter(width / 2, height / 2))
                .force('collision', d3.forceCollide().radius(50));

            const link = svg.append('g')
                .selectAll('line')
                .data(data.links)
                .enter().append('line')
                .attr('stroke', d => linkColors[d.type] || '#94a3b8')
                .attr('stroke-width', 2)
                .attr('stroke-opacity', 0.6)
                .attr('marker-end', d => 'url(#arrow-' + d.type + ')');

            const node = svg.append('g')
                .selectAll('g')
                .data(data.nodes)
                .enter().append('g')
                .attr('cursor', d => d.fileId ? 'pointer' : 'default')
                .on('click', (event, d) => {
                    if (d.fileId) {
                        window.location.href = '/docs/code-hygiene/' + d.fileId;
                    }
                })
                .call(d3.drag()
                    .on('start', dragstarted)
                    .on('drag', dragged)
                    .on('end', dragended));

            node.append('circle')
                .attr('r', d => d.type === 'center' ? 20 : 14)
                .attr('fill', d => colors[d.type] || '#94a3b8')
                .attr('stroke', '#fff')
                .attr('stroke-width', 2);

            node.append('text')
                .text(d => d.label)
                .attr('x', 0)
                .attr('y', d => d.type === 'center' ? 35 : 28)
                .attr('text-anchor', 'middle')
                .attr('fill', 'var(--text-primary)')
                .attr('font-size', d => d.type === 'center' ? '12px' : '10px')
                .attr('font-weight', d => d.type === 'center' ? 'bold' : 'normal');

            node.append('title')
                .text(d => d.id);

            simulation.on('tick', () => {
                link
                    .attr('x1', d => d.source.x)
                    .attr('y1', d => d.source.y)
                    .attr('x2', d => d.target.x)
                    .attr('y2', d => d.target.y);

                node.attr('transform', d => `translate(${d.x},${d.y})`);
            });

            function dragstarted(event) {
                if (!event.active) simulation.alphaTarget(0.3).restart();
                event.subject.fx = event.subject.x;
                event.subject.fy = event.subject.y;
            }

            function dragged(event) {
                event.subject.fx = event.x;
                event.subject.fy = event.y;
            }

            function dragended(event) {
                if (!event.active) simulation.alphaTarget(0);
                event.subject.fx = null;
                event.subject.fy = null;
            }
        })
        .catch(err => {
            container.innerHTML = '<p style="padding: 2rem; color: var(--text-muted);">Fehler beim Laden des Graphen: ' + err.message + '</p>';
        });
})();
</script>

<?php $content = ob_get_clean(); ?>
<?php require VIEW_PATH . '/layout.php'; ?>
← Übersicht