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

Container::setBubblePropagation()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
eloc 1
c 0
b 0
f 0
dl 0
loc 3
rs 10
ccs 0
cts 2
cp 0
cc 1
nc 1
nop 1
crap 2
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
}