- Move 12 markdown files from root to docs/ subdirectories - Organize documentation by category: • docs/troubleshooting/ (1 file) - Technical troubleshooting guides • docs/deployment/ (4 files) - Deployment and security documentation • docs/guides/ (3 files) - Feature-specific guides • docs/planning/ (4 files) - Planning and improvement proposals Root directory cleanup: - Reduced from 16 to 4 markdown files in root - Only essential project files remain: • CLAUDE.md (AI instructions) • README.md (Main project readme) • CLEANUP_PLAN.md (Current cleanup plan) • SRC_STRUCTURE_IMPROVEMENTS.md (Structure improvements) This improves: ✅ Documentation discoverability ✅ Logical organization by purpose ✅ Clean root directory ✅ Better maintainability
252 lines
9.4 KiB
PHP
252 lines
9.4 KiB
PHP
<?php
|
||
|
||
declare(strict_types=1);
|
||
|
||
require_once __DIR__ . '/../../vendor/autoload.php';
|
||
|
||
use App\Framework\Discovery\Analysis\DependencyAnalyzer;
|
||
use App\Framework\Discovery\Plugins\DependencyAnalysisPlugin;
|
||
use App\Framework\Reflection\CachedReflectionProvider;
|
||
use App\Framework\Core\ValueObjects\ClassName;
|
||
|
||
echo "=== Testing Dependency Graph Analysis System ===\n\n";
|
||
|
||
$reflectionProvider = new CachedReflectionProvider();
|
||
|
||
echo "1. Testing DependencyAnalyzer with sample framework classes:\n";
|
||
try {
|
||
$analyzer = new DependencyAnalyzer($reflectionProvider);
|
||
|
||
// Test with simpler, existing classes first
|
||
$classesToAnalyze = [
|
||
'App\\Framework\\Core\\ValueObjects\\ClassName',
|
||
'App\\Framework\\Http\\Method',
|
||
'App\\Framework\\Router\\ValueObjects\\RoutePath',
|
||
'App\\Framework\\Router\\ValueObjects\\Placeholder',
|
||
];
|
||
|
||
echo " 🔍 Analyzing classes: " . implode(', ', array_map(fn($c) => basename(str_replace('\\', '/', $c)), $classesToAnalyze)) . "\n";
|
||
|
||
$analysisResult = $analyzer->analyzeWithCircularDetection($classesToAnalyze);
|
||
$graph = $analysisResult->getGraph();
|
||
$statistics = $analysisResult->getStatistics();
|
||
|
||
echo " ✅ Analysis completed successfully\n";
|
||
echo " 📊 Statistics:\n";
|
||
echo " • Total nodes: {$statistics['node_count']}\n";
|
||
echo " • Total edges: {$statistics['edge_count']}\n";
|
||
echo " • Circular dependencies: {$statistics['circular_dependencies']}\n";
|
||
echo " • Leaf nodes: {$statistics['leaf_nodes']}\n";
|
||
echo " • Root nodes: {$statistics['root_nodes']}\n";
|
||
echo " • Average complexity: {$statistics['average_complexity']}\n";
|
||
echo " • Max dependencies: {$statistics['max_dependency_count']}\n\n";
|
||
|
||
echo " 📈 Type distribution:\n";
|
||
foreach ($statistics['type_distribution'] as $type => $count) {
|
||
echo " • {$type}: {$count}\n";
|
||
}
|
||
echo "\n";
|
||
|
||
if ($analysisResult->hasCircularDependencies()) {
|
||
echo " ⚠️ Circular dependencies found:\n";
|
||
foreach ($analysisResult->getCircularDependencies() as $cycle) {
|
||
echo " • " . implode(' -> ', $cycle) . "\n";
|
||
}
|
||
echo "\n";
|
||
} else {
|
||
echo " ✅ No circular dependencies found\n\n";
|
||
}
|
||
|
||
} catch (\Throwable $e) {
|
||
echo " ❌ Error: {$e->getMessage()}\n\n";
|
||
}
|
||
|
||
echo "2. Testing specific class analysis (DefaultContainer):\n";
|
||
try {
|
||
$containerClass = 'App\\Framework\\DI\\DefaultContainer';
|
||
$analyzer = new DependencyAnalyzer($reflectionProvider);
|
||
|
||
$analysisResult = $analyzer->analyzeWithCircularDetection([$containerClass]);
|
||
$graph = $analysisResult->getGraph();
|
||
|
||
$node = $graph->getNode(ClassName::create($containerClass));
|
||
|
||
if ($node !== null) {
|
||
echo " ✅ DefaultContainer analysis:\n";
|
||
echo " • Type: {$node->getType()->value}\n";
|
||
echo " • Dependencies: {$node->getDependencyCount()}\n";
|
||
echo " • Dependents: {$node->getDependentCount()}\n";
|
||
echo " • Complexity score: " . round($node->getComplexityScore(), 2) . "\n";
|
||
echo " • Is leaf: " . ($node->isLeaf() ? 'Yes' : 'No') . "\n";
|
||
echo " • Is root: " . ($node->isRoot() ? 'Yes' : 'No') . "\n\n";
|
||
|
||
if (!empty($node->getDependencies())) {
|
||
echo " 📋 Dependencies:\n";
|
||
foreach ($node->getDependencies() as $edge) {
|
||
echo " • {$edge->getTarget()->getShortName()} ({$edge->getRelation()->value}, weight: {$edge->getWeight()})\n";
|
||
}
|
||
echo "\n";
|
||
}
|
||
|
||
// Test dependency depth
|
||
$depth = $graph->getDependencyDepth($node->getClassName());
|
||
echo " 📏 Dependency depth: {$depth}\n\n";
|
||
}
|
||
|
||
} catch (\Throwable $e) {
|
||
echo " ❌ Error: {$e->getMessage()}\n\n";
|
||
}
|
||
|
||
echo "3. Testing dependency recommendations:\n";
|
||
try {
|
||
$analyzer = new DependencyAnalyzer($reflectionProvider);
|
||
|
||
// Analyze a larger set of framework classes
|
||
$frameworkClasses = [
|
||
'App\\Framework\\DI\\DefaultContainer',
|
||
'App\\Framework\\Router\\ValueObjects\\RoutePath',
|
||
'App\\Framework\\Router\\ValueObjects\\RouteGroup',
|
||
'App\\Framework\\TypeCaster\\TypeCasterRegistry',
|
||
'App\\Framework\\Discovery\\UnifiedDiscoveryService',
|
||
'App\\Framework\\Core\\Application',
|
||
'App\\Framework\\Http\\Request',
|
||
'App\\Framework\\Database\\EntityManager',
|
||
];
|
||
|
||
$analysisResult = $analyzer->analyzeWithCircularDetection($frameworkClasses);
|
||
$recommendations = $analyzer->getRecommendations($analysisResult->getGraph());
|
||
|
||
echo " 📝 Recommendations:\n";
|
||
|
||
if (isset($recommendations['high_complexity'])) {
|
||
echo " 🔺 High Complexity Classes:\n";
|
||
foreach ($recommendations['high_complexity'] as $item) {
|
||
echo " • {$item['class']}: {$item['current']} (expected max: {$item['expected_max']})\n";
|
||
echo " {$item['suggestion']}\n";
|
||
}
|
||
echo "\n";
|
||
}
|
||
|
||
if (isset($recommendations['high_dependencies'])) {
|
||
echo " 🔗 High Dependency Classes:\n";
|
||
foreach ($recommendations['high_dependencies'] as $item) {
|
||
echo " • {$item['class']}: {$item['dependency_count']} dependencies\n";
|
||
echo " {$item['suggestion']}\n";
|
||
}
|
||
echo "\n";
|
||
}
|
||
|
||
if (isset($recommendations['circular_dependencies'])) {
|
||
echo " 🔄 Circular Dependencies:\n";
|
||
echo " • Count: {$recommendations['circular_dependencies']['count']}\n";
|
||
echo " • {$recommendations['circular_dependencies']['suggestion']}\n";
|
||
foreach ($recommendations['circular_dependencies']['cycles'] as $cycle) {
|
||
echo " Cycle: " . implode(' -> ', $cycle) . "\n";
|
||
}
|
||
echo "\n";
|
||
}
|
||
|
||
if (isset($recommendations['potentially_unused'])) {
|
||
echo " 🗑️ Potentially Unused Classes:\n";
|
||
foreach ($recommendations['potentially_unused'] as $item) {
|
||
echo " • {$item['class']}: {$item['suggestion']}\n";
|
||
}
|
||
echo "\n";
|
||
}
|
||
|
||
if (empty($recommendations)) {
|
||
echo " ✅ No major issues found!\n\n";
|
||
}
|
||
|
||
} catch (\Throwable $e) {
|
||
echo " ❌ Error: {$e->getMessage()}\n\n";
|
||
}
|
||
|
||
echo "4. Testing dependency path finding:\n";
|
||
try {
|
||
$analyzer = new DependencyAnalyzer($reflectionProvider);
|
||
|
||
$analysisResult = $analyzer->analyzeWithCircularDetection([
|
||
'App\\Framework\\DI\\DefaultContainer',
|
||
'App\\Framework\\Reflection\\ReflectionProvider',
|
||
'App\\Framework\\Reflection\\CachedReflectionProvider',
|
||
]);
|
||
|
||
$graph = $analysisResult->getGraph();
|
||
|
||
$fromClass = ClassName::create('App\\Framework\\DI\\DefaultContainer');
|
||
$toClass = ClassName::create('App\\Framework\\Reflection\\ReflectionProvider');
|
||
|
||
$path = $graph->getDependencyPath($fromClass, $toClass);
|
||
|
||
if ($path !== null) {
|
||
echo " ✅ Dependency path found:\n";
|
||
echo " 📍 From: {$fromClass->getShortName()}\n";
|
||
echo " 📍 To: {$toClass->getShortName()}\n";
|
||
echo " 🛤️ Path: " . implode(' -> ', array_map(fn($class) => basename(str_replace('\\', '/', $class)), $path)) . "\n";
|
||
echo " 📏 Length: " . count($path) . "\n\n";
|
||
} else {
|
||
echo " ℹ️ No dependency path found between DefaultContainer and ReflectionProvider\n\n";
|
||
}
|
||
|
||
} catch (\Throwable $e) {
|
||
echo " ❌ Error: {$e->getMessage()}\n\n";
|
||
}
|
||
|
||
echo "5. Testing graph statistics and analysis:\n";
|
||
try {
|
||
$analyzer = new DependencyAnalyzer($reflectionProvider);
|
||
|
||
// Test with more comprehensive class set
|
||
$allClasses = [
|
||
'App\\Framework\\DI\\DefaultContainer',
|
||
'App\\Framework\\Router\\ValueObjects\\RoutePath',
|
||
'App\\Framework\\Router\\ValueObjects\\RouteGroup',
|
||
'App\\Framework\\Router\\ValueObjects\\GroupRoute',
|
||
'App\\Framework\\TypeCaster\\TypeCasterRegistry',
|
||
'App\\Framework\\Core\\ValueObjects\\ClassName',
|
||
'App\\Framework\\Http\\Method',
|
||
];
|
||
|
||
$analysisResult = $analyzer->analyzeWithCircularDetection($allClasses);
|
||
$graph = $analysisResult->getGraph();
|
||
|
||
echo " 📊 Detailed Graph Analysis:\n";
|
||
|
||
// Top complex nodes
|
||
$topComplex = $graph->getHighestDependencyNodes(3);
|
||
echo " 🔺 Most Complex Classes:\n";
|
||
foreach ($topComplex as $node) {
|
||
echo " • {$node->getClassName()->getShortName()}: complexity {$node->getComplexityScore()}\n";
|
||
}
|
||
echo "\n";
|
||
|
||
// Most used nodes
|
||
$mostUsed = $graph->getMostUsedNodes(3);
|
||
echo " 🌟 Most Used Classes:\n";
|
||
foreach ($mostUsed as $node) {
|
||
echo " • {$node->getClassName()->getShortName()}: {$node->getDependentCount()} dependents\n";
|
||
}
|
||
echo "\n";
|
||
|
||
// Leaf nodes
|
||
$leafNodes = $graph->getLeafNodes();
|
||
echo " 🍃 Leaf Nodes (no dependencies):\n";
|
||
foreach (array_slice($leafNodes, 0, 5) as $node) {
|
||
echo " • {$node->getClassName()->getShortName()} ({$node->getType()->value})\n";
|
||
}
|
||
echo "\n";
|
||
|
||
// Root nodes
|
||
$rootNodes = $graph->getRootNodes();
|
||
echo " 🌳 Root Nodes (no dependents):\n";
|
||
foreach (array_slice($rootNodes, 0, 5) as $node) {
|
||
echo " • {$node->getClassName()->getShortName()} ({$node->getType()->value})\n";
|
||
}
|
||
echo "\n";
|
||
|
||
} catch (\Throwable $e) {
|
||
echo " ❌ Error: {$e->getMessage()}\n\n";
|
||
}
|
||
|
||
echo "=== Dependency Graph Analysis Test Completed ===\n"; |