Passed
Push — main ( 1b4087...ef945e )
by ANDREY
02:43 queued 12s
created

Container   A

Complexity

Total Complexity 22

Size/Duplication

Total Lines 104
Duplicated Lines 0 %

Test Coverage

Coverage 92.45%

Importance

Changes 0
Metric Value
wmc 22
eloc 56
c 0
b 0
f 0
dl 0
loc 104
rs 10
ccs 49
cts 53
cp 0.9245

8 Methods

Rating   Name   Duplication   Size   Complexity  
A setBubblePropagation() 0 3 1
A classIsInjectable() 0 12 3
A has() 0 3 1
A prepareObject() 0 6 3
A getObject() 0 27 5
A get() 0 5 1
A __construct() 0 2 1
B registerContainers() 0 26 7
1
<?php
2
declare(strict_types=1);
3
4
5
namespace VPA\DI;
6
7
use Psr\Container\ContainerInterface;
8
9
class Container implements ContainerInterface
10
{
11
    private static array $classes = [];
12
    private static bool $bubblePropagation = true;
13
14
    function __construct()
15
    {
16
    }
17
18
    public function setBubblePropagation(bool $bubblePropagation): void
19
    {
20
        self::$bubblePropagation = $bubblePropagation;
21
    }
22
23 9
    public function registerContainers(array $manualConfig = []): void
24
    {
25 9
        $injectedClasses = [];
26 9
        $classes = get_declared_classes();
27 9
        $loadedClasses = array_combine($classes, $classes);
28 9
        $classesNeedCheck = array_merge($loadedClasses, $manualConfig);
29 9
        foreach ($classesNeedCheck as $alias => $class) {
30
            assert(is_string($class));
31 9
            if (class_exists($class)) {
32 9
                if ($this->classIsInjectable($class)) {
33 9
                    $injectedClasses[$alias] = $class;
34
                }
35 9
                if (self::$bubblePropagation) {
36 9
                    $parents = class_parents($class);
37 9
                    foreach ($parents as $parent) {
38 9
                        if ($this->classIsInjectable($parent)) {
39 9
                            $injectedClasses[$alias] = $class;
40 9
                            break;
41
                        }
42
                    }
43
                }
44
            } else {
45
                throw new NotFoundException("VPA\DI\Container::registerClasses: Class $class not found");
46
            }
47
        }
48 9
        self::$classes = $injectedClasses;
49 9
    }
50
51
    private function classIsInjectable(string $class): bool
52 9
    {
53
        assert(class_exists($class));
54
        $reflectionClass = new \ReflectionClass($class);
55 9
        $attributes = $reflectionClass->getAttributes();
56 9
        foreach ($attributes as $attribute) {
57 9
            $typeOfEntity = $attribute->getName();
58 9
            if ($typeOfEntity === 'VPA\DI\Injectable') {
59 9
                return true;
60 9
            }
61
        }
62
        return false;
63 9
    }
64
65
    private function prepareObject(string $aliasName, string $className, array $params = []): object
66 9
    {
67
        if ($this->has($className) || $this->classIsInjectable($className)) {
68 9
            return $this->getObject($className, $params);
69 8
        }
70
        throw new NotFoundException("VPA\DI\Container::get('$aliasName->$className'): Class with attribute Injectable not found. Check what class exists and attribute Injectable is set");
71 1
    }
72
73
    private function getObject(string $className, array $params): object
74 8
    {
75
        assert(class_exists($className));
76
        $reflectionClass = new \ReflectionClass($className);
77 8
        $constructReflector = $reflectionClass->getConstructor();
78 8
        if (empty($constructReflector)) {
79 8
            return new $className;
80 8
        }
81
82
        $constructArguments = $constructReflector->getParameters();
83 4
        if (empty($constructArguments)) {
84 4
            return new $className;
85
        }
86
        $args = [];
87 4
        foreach ($constructArguments as $argument) {
88 4
            $argumentType = $argument->getType();
89 4
            $argumentName = $argument->getName();
90 4
            assert($argumentType instanceof \ReflectionNamedType);
91
            $argumentTypeName = $argumentType->getName();
92 4
            if (class_exists($argumentTypeName)) {
93 4
                $args[$argumentName] = $this->get($argumentTypeName);
94 4
            } else {
95
                $args[$argumentName] = $params[$argumentName] ?? null;
96 1
            }
97
        }
98
99
        return new $className(...$args);
100 4
    }
101
102
103
    public function get(string $id, array $params = []): object
104 9
    {
105
        $class = self::$classes[$id] ?? $id;
106 9
        assert(is_string($class));
107
        return $this->prepareObject($id, $class, $params);
108 9
    }
109
110
    public function has(string $id): bool
111 9
    {
112
        return isset(self::$classes[$id]);
113
    }
114
}