docs: consolidate documentation into organized structure

- 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
This commit is contained in:
2025-10-05 11:05:04 +02:00
parent 887847dde6
commit 5050c7d73a
36686 changed files with 196456 additions and 12398919 deletions

View File

@@ -0,0 +1,80 @@
<?php
declare(strict_types=1);
namespace App\Framework\Discovery\Analysis;
use App\Framework\Discovery\ValueObjects\DependencyGraph;
final readonly class DependencyAnalysisResult
{
/**
* @param array<array<string>> $circularDependencies
* @param array<string, mixed> $statistics
*/
public function __construct(
private DependencyGraph $graph,
private array $circularDependencies,
private array $statistics
) {}
/**
* Get the dependency graph
*/
public function getGraph(): DependencyGraph
{
return $this->graph;
}
/**
* Get circular dependencies
*
* @return array<array<string>>
*/
public function getCircularDependencies(): array
{
return $this->circularDependencies;
}
/**
* Get analysis statistics
*
* @return array<string, mixed>
*/
public function getStatistics(): array
{
return $this->statistics;
}
/**
* Check if there are any circular dependencies
*/
public function hasCircularDependencies(): bool
{
return !empty($this->circularDependencies);
}
/**
* Get the number of circular dependency cycles
*/
public function getCircularDependencyCount(): int
{
return count($this->circularDependencies);
}
/**
* Convert to array for serialization
*
* @return array<string, mixed>
*/
public function toArray(): array
{
return [
'statistics' => $this->statistics,
'circular_dependencies' => $this->circularDependencies,
'has_circular_dependencies' => $this->hasCircularDependencies(),
'circular_dependency_count' => $this->getCircularDependencyCount(),
'graph' => $this->graph->toArray(),
];
}
}

View File

@@ -0,0 +1,456 @@
<?php
declare(strict_types=1);
namespace App\Framework\Discovery\Analysis;
use App\Framework\Core\ValueObjects\ClassName;
use App\Framework\Discovery\ValueObjects\DependencyEdge;
use App\Framework\Discovery\ValueObjects\DependencyGraph;
use App\Framework\Discovery\ValueObjects\DependencyNode;
use App\Framework\Discovery\ValueObjects\DependencyRelation;
use App\Framework\Discovery\ValueObjects\DependencyType;
use App\Framework\Reflection\ReflectionProvider;
use App\Framework\Logging\Logger;
use App\Framework\Logging\ValueObjects\LogContext;
use App\Framework\Reflection\WrappedReflectionClass;
use ReflectionClass;
use ReflectionMethod;
use ReflectionParameter;
use ReflectionProperty;
use ReflectionException;
use Throwable;
final readonly class DependencyAnalyzer
{
public function __construct(
private ReflectionProvider $reflectionProvider,
private ?Logger $logger = null
) {}
/**
* Analyze dependencies for a list of classes
*
* @param array<string> $classNames
*/
public function analyze(array $classNames): DependencyGraph
{
$this->logger?->info('Starting dependency analysis', LogContext::withData([
'class_count' => count($classNames),
]));
$graph = DependencyGraph::empty();
$processedClasses = [];
foreach ($classNames as $className) {
try {
if (isset($processedClasses[$className])) {
continue;
}
$graph = $this->analyzeClass($className, $graph);
$processedClasses[$className] = true;
} catch (Throwable $e) {
$this->logger?->warning('Failed to analyze class dependencies', LogContext::withData([
'class' => $className,
'error' => $e->getMessage(),
]));
}
}
$this->logger?->info('Dependency analysis completed', LogContext::withData([
'nodes' => $graph->getNodeCount(),
'edges' => $graph->getEdgeCount(),
'circular_dependencies' => count($graph->findCircularDependencies()),
]));
return $graph;
}
/**
* Analyze dependencies for a single class
*/
public function analyzeClass(string $className, DependencyGraph $graph): DependencyGraph
{
try {
$reflectionClass = $this->reflectionProvider->getClass(ClassName::create($className));
// Create or update the node for this class
$dependencyType = $this->determineDependencyType($reflectionClass);
$node = DependencyNode::create(ClassName::create($className), $dependencyType);
$graph = $graph->addNode($node);
$this->logger?->debug('Analyzing class', ['class' => $className, 'type' => $dependencyType->value]);
// Analyze inheritance
$graph = $this->analyzeInheritance($reflectionClass, $graph);
// Analyze interfaces
$graph = $this->analyzeInterfaces($reflectionClass, $graph);
// Analyze traits
$graph = $this->analyzeTraits($reflectionClass, $graph);
// Analyze constructor dependencies
$graph = $this->analyzeConstructorDependencies($reflectionClass, $graph);
// Analyze method dependencies
$graph = $this->analyzeMethodDependencies($reflectionClass, $graph);
// Analyze property dependencies
$graph = $this->analyzePropertyDependencies($reflectionClass, $graph);
} catch (ReflectionException $e) {
$this->logger?->warning('Reflection failed for class', LogContext::withData([
'class' => $className,
'error' => $e->getMessage(),
]));
}
return $graph;
}
/**
* Determine the dependency type of a class
*/
private function determineDependencyType(WrappedReflectionClass $class): DependencyType
{
return DependencyType::fromClassName(
$class->getName(),
$class->isInterface(),
$class->isAbstract(),
$class->isTrait(),
$class->isEnum()
);
}
/**
* Analyze inheritance relationships
*/
private function analyzeInheritance(WrappedReflectionClass $class, DependencyGraph $graph): DependencyGraph
{
$parentClass = $class->getParentClass();
if ($parentClass === false) {
return $graph;
}
$edge = DependencyEdge::create(
ClassName::create($class->getName()),
ClassName::create($parentClass->getName()),
DependencyRelation::EXTENDS,
10 // High weight for inheritance
);
return $graph->addEdge($edge);
}
/**
* Analyze interface implementations
*/
private function analyzeInterfaces(WrappedReflectionClass $class, DependencyGraph $graph): DependencyGraph
{
$interfaces = $class->getInterfaceNames();
foreach ($interfaces as $interfaceName) {
$edge = DependencyEdge::create(
ClassName::create($class->getName()),
ClassName::create($interfaceName),
DependencyRelation::IMPLEMENTS,
8 // High weight for interface implementation
);
$graph = $graph->addEdge($edge);
}
return $graph;
}
/**
* Analyze trait usage
*/
private function analyzeTraits(WrappedReflectionClass $class, DependencyGraph $graph): DependencyGraph
{
$traits = $class->getTraitNames();
foreach ($traits as $traitName) {
$edge = DependencyEdge::create(
ClassName::create($class->getName()),
ClassName::create($traitName),
DependencyRelation::USES_TRAIT,
6 // Medium weight for trait usage
);
$graph = $graph->addEdge($edge);
}
return $graph;
}
/**
* Analyze constructor dependencies
*/
private function analyzeConstructorDependencies(WrappedReflectionClass $class, DependencyGraph $graph): DependencyGraph
{
$constructor = $class->getConstructor();
if ($constructor === null) {
return $graph;
}
$parameters = $constructor->getParameters();
foreach ($parameters as $parameter) {
$type = $parameter->getType();
if ($type === null || $type->isBuiltin()) {
continue;
}
$typeNames = $this->extractTypeNames($type);
foreach ($typeNames as $typeName) {
$edge = DependencyEdge::create(
ClassName::create($class->getName()),
ClassName::create($typeName),
DependencyRelation::CONSTRUCTOR_INJECTION,
10 // Highest weight for constructor injection
);
$graph = $graph->addEdge($edge);
}
}
return $graph;
}
/**
* Analyze method parameter dependencies
*/
private function analyzeMethodDependencies(WrappedReflectionClass $class, DependencyGraph $graph): DependencyGraph
{
$methods = $class->getMethodsRaw(ReflectionMethod::IS_PUBLIC | ReflectionMethod::IS_PROTECTED);
foreach ($methods as $method) {
// Skip constructor (already analyzed)
if ($method->isConstructor()) {
continue;
}
// Analyze parameters
$graph = $this->analyzeMethodParameters($class, $method, $graph);
// Analyze return type
$graph = $this->analyzeMethodReturnType($class, $method, $graph);
}
return $graph;
}
/**
* Analyze method parameters
*/
private function analyzeMethodParameters(WrappedReflectionClass $class, ReflectionMethod $method, DependencyGraph $graph): DependencyGraph
{
$parameters = $method->getParameters();
foreach ($parameters as $parameter) {
$type = $parameter->getType();
if ($type === null || $type->isBuiltin()) {
continue;
}
$typeNames = $this->extractTypeNames($type);
foreach ($typeNames as $typeName) {
$edge = DependencyEdge::create(
ClassName::create($class->getName()),
ClassName::create($typeName),
DependencyRelation::METHOD_PARAMETER,
5 // Medium weight for method parameters
);
$graph = $graph->addEdge($edge);
}
}
return $graph;
}
/**
* Analyze method return types
*/
private function analyzeMethodReturnType(WrappedReflectionClass $class, ReflectionMethod $method, DependencyGraph $graph): DependencyGraph
{
$returnType = $method->getReturnType();
if ($returnType === null || $returnType->isBuiltin()) {
return $graph;
}
$typeNames = $this->extractTypeNames($returnType);
foreach ($typeNames as $typeName) {
$edge = DependencyEdge::create(
ClassName::create($class->getName()),
ClassName::create($typeName),
DependencyRelation::RETURN_TYPE,
3 // Lower weight for return types
);
$graph = $graph->addEdge($edge);
}
return $graph;
}
/**
* Analyze property type dependencies
*/
private function analyzePropertyDependencies(WrappedReflectionClass $class, DependencyGraph $graph): DependencyGraph
{
$properties = $class->getPropertiesRaw(ReflectionProperty::IS_PUBLIC | ReflectionProperty::IS_PROTECTED | ReflectionProperty::IS_PRIVATE);
foreach ($properties as $property) {
$type = $property->getType();
if ($type === null || $type->isBuiltin()) {
continue;
}
$typeNames = $this->extractTypeNames($type);
foreach ($typeNames as $typeName) {
$edge = DependencyEdge::create(
ClassName::create($class->getName()),
ClassName::create($typeName),
DependencyRelation::PROPERTY_TYPE,
4 // Medium-low weight for property types
);
$graph = $graph->addEdge($edge);
}
}
return $graph;
}
/**
* Analyze dependencies with circular detection
*
* @param array<string> $classNames
*/
public function analyzeWithCircularDetection(array $classNames): DependencyAnalysisResult
{
$graph = $this->analyze($classNames);
$circularDependencies = $graph->findCircularDependencies();
$statistics = $graph->getStatistics();
return new DependencyAnalysisResult(
graph: $graph,
circularDependencies: $circularDependencies,
statistics: $statistics
);
}
/**
* Get dependency recommendations
*
* @return array<string, mixed>
*/
public function getRecommendations(DependencyGraph $graph): array
{
$recommendations = [];
$statistics = $graph->getStatistics();
// Check for high complexity nodes
$highComplexityNodes = [];
foreach ($graph->getNodes() as $node) {
$expectedRange = $node->getType()->getExpectedComplexityRange();
if ($node->getComplexityScore() > $expectedRange['max']) {
$highComplexityNodes[] = [
'class' => $node->getClassName()->getShortName(),
'current' => $node->getComplexityScore(),
'expected_max' => $expectedRange['max'],
'suggestion' => 'Consider breaking down this class into smaller components',
];
}
}
if (!empty($highComplexityNodes)) {
$recommendations['high_complexity'] = $highComplexityNodes;
}
// Check for circular dependencies
$circularDependencies = $graph->findCircularDependencies();
if (!empty($circularDependencies)) {
$recommendations['circular_dependencies'] = [
'count' => count($circularDependencies),
'cycles' => $circularDependencies,
'suggestion' => 'Break circular dependencies using interfaces, events, or dependency inversion',
];
}
// Check for highly coupled classes
$highDependencyNodes = $graph->getHighestDependencyNodes(5);
if (!empty($highDependencyNodes)) {
$recommendations['high_dependencies'] = array_map(
fn(DependencyNode $node) => [
'class' => $node->getClassName()->getShortName(),
'dependency_count' => $node->getDependencyCount(),
'suggestion' => 'Consider using dependency injection or factory patterns',
],
$highDependencyNodes
);
}
// Check for unused classes (leaf nodes with no dependents)
$unusedClasses = array_filter(
$graph->getLeafNodes(),
fn(DependencyNode $node) => $node->getDependentCount() === 0
);
if (!empty($unusedClasses)) {
$recommendations['potentially_unused'] = array_map(
fn(DependencyNode $node) => [
'class' => $node->getClassName()->getShortName(),
'suggestion' => 'Consider removing if truly unused',
],
array_slice($unusedClasses, 0, 10)
);
}
return $recommendations;
}
/**
* Extract type names from reflection type, handling union types
*
* @return array<string>
*/
private function extractTypeNames(\ReflectionType $type): array
{
if ($type instanceof \ReflectionNamedType) {
return [$type->getName()];
}
if ($type instanceof \ReflectionUnionType) {
$types = [];
foreach ($type->getTypes() as $subType) {
if ($subType instanceof \ReflectionNamedType && !$subType->isBuiltin()) {
$types[] = $subType->getName();
}
}
return $types;
}
if ($type instanceof \ReflectionIntersectionType) {
$types = [];
foreach ($type->getTypes() as $subType) {
if ($subType instanceof \ReflectionNamedType && !$subType->isBuiltin()) {
$types[] = $subType->getName();
}
}
return $types;
}
// Fallback for unknown types
return [];
}
}