Passed
Branch main (8d1023)
by Chema
01:53
created

DependencyTreeAnalyzer::collectDependencies()   B

Complexity

Conditions 7
Paths 6

Size

Total Lines 29
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 16
CRAP Score 7

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 15
c 1
b 0
f 0
dl 0
loc 29
ccs 16
cts 16
cp 1
rs 8.8333
cc 7
nc 6
nop 2
crap 7
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Gacela\Container;
6
7
use ReflectionClass;
8
use ReflectionNamedType;
9
10
use function class_exists;
11
12
/**
13
 * Analyzes and reports dependency trees for classes.
14
 */
15
final class DependencyTreeAnalyzer
16
{
17 61
    public function __construct(
18
        private BindingResolver $bindingResolver,
19
    ) {
20 61
    }
21
22
    /**
23
     * Get all dependencies for a class as a flat list.
24
     *
25
     * @param class-string $className
26
     *
27
     * @return list<string>
28
     */
29 4
    public function analyze(string $className): array
30
    {
31 4
        if (!class_exists($className)) {
32
            return [];
33
        }
34
35 4
        $dependencies = [];
36 4
        $this->collectDependencies($className, $dependencies);
37
38
        /** @var list<string> */
39 4
        return array_keys($dependencies);
40
    }
41
42
    /**
43
     * Recursively collect dependencies for a class.
44
     *
45
     * @param class-string $className
46
     * @param array<string, true> $dependencies
47
     */
48 4
    private function collectDependencies(string $className, array &$dependencies): void
49
    {
50 4
        $reflection = new ReflectionClass($className);
51
52 4
        $constructor = $reflection->getConstructor();
53 4
        if ($constructor === null) {
54 2
            return;
55
        }
56
57 3
        foreach ($constructor->getParameters() as $parameter) {
58 3
            $type = $parameter->getType();
59 3
            if (!$type instanceof ReflectionNamedType || $type->isBuiltin()) {
60 3
                continue;
61
            }
62
63
            /** @var class-string $paramTypeName */
64 3
            $paramTypeName = $type->getName();
65
66
            // Resolve binding if it's an interface
67 3
            $paramTypeName = $this->bindingResolver->resolveType($paramTypeName);
68
69 3
            if (isset($dependencies[$paramTypeName])) {
70 1
                continue; // Already processed
71
            }
72
73 3
            $dependencies[$paramTypeName] = true;
74
75 3
            if (class_exists($paramTypeName)) {
76 3
                $this->collectDependencies($paramTypeName, $dependencies);
77
            }
78
        }
79
    }
80
}
81