Passed
Push — main ( d186ad...4a5d16 )
by ANDREY
02:20 queued 13s
created

Container::reloadContainers()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

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