Passed
Push — main ( 47d353...677be7 )
by Sammy
01:35
created

LeMarchand::resolver()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 1
c 0
b 0
f 0
dl 0
loc 2
rs 10
cc 1
nc 1
nop 0
1
<?php
2
3
namespace HexMakina\LeMarchand;
4
5
use Psr\Container\ContainerInterface;
6
use Psr\Container\ContainerExceptionInterface;
7
use Psr\Container\NotFoundExceptionInterface;
8
9
class LeMarchand implements ContainerInterface
10
{
11
    private static $instance = null;
12
13
    private $configurations = [];
14
15
    private $interface_wiring = [];
16
17
    private $resolver = null;
18
19
    public static function box($settings = null): ContainerInterface
20
    {
21
        if (is_null(self::$instance)) {
22
            if (is_array($settings)) {
23
                return (self::$instance = new LeMarchand($settings));
24
            }
25
            throw new ContainerException('UNABLE_TO_OPEN_BOX');
26
        }
27
28
        return self::$instance;
29
    }
30
31
32
    private function __construct($settings)
33
    {
34
        if (isset($settings[__CLASS__])) {
35
            $this->resolver = new Resolver($settings[__CLASS__]['cascade'] ?? []);
36
            $this->interface_wiring = $settings[__CLASS__]['wiring'] ?? [];
37
            unset($settings[__CLASS__]);
38
        }
39
        $this->configurations['settings'] = $settings;
40
    }
41
42
    public function __debugInfo(): array
43
    {
44
        $dbg = get_object_vars($this);
45
46
        foreach ($dbg['interface_wiring'] as $interface => $wire) {
47
            if (is_array($wire)) {
48
                $wire = array_shift($wire) . ' --array #' . count($wire);
49
            }
50
            $dbg['interface_wiring'][$interface] = $wire;
51
        }
52
53
        return $dbg;
54
    }
55
56
    public function has($configuration)
57
    {
58
        try {
59
            $this->get($configuration);
60
            return true;
61
        } catch (NotFoundExceptionInterface $e) {
62
            return false;
63
        } catch (ContainerExceptionInterface $e) {
64
            return false;
65
        }
66
        return false;
67
    }
68
69
70
    public function get($configuration_string)
71
    {
72
        if (!is_string($configuration_string)) {
73
            throw new ContainerException($configuration_string);
74
        }
75
76
        if ($this->isFirstLevelKey($configuration_string)) {
77
            return $this->configurations[$configuration_string];
78
        }
79
80
        // not a simple configuration string, it has meaning
81
        $res = $this->getComplexConfigurationString($configuration_string);
82
83
        if (is_null($res)) {
84
            throw new NotFoundException($configuration_string);
85
        }
86
87
        return $res;
88
    }
89
90
    public function resolver(){
91
        return $this->resolver;
92
    }
93
94
    private function getComplexConfigurationString($configuration_string)
95
    {
96
        $configuration = new Configuration($configuration_string);
97
98
        $ret = null;
99
100
        if ($configuration->isSettings()) {
101
            $ret = $this->getSettings($configuration);
102
        } elseif (class_exists($configuration_string)) {
103
            $ret = $this->getInstance($configuration_string);
104
        } elseif ($configuration->isInterface()) {
105
            $ret = $this->wireInstance($configuration);
106
        } elseif ($configuration->isModelOrController()) {
107
            $ret = $this->cascadeInstance($configuration);
108
        }
109
110
        return $ret;
111
    }
112
113
    private function isFirstLevelKey($configuration_string)
114
    {
115
        return isset($this->configurations[$configuration_string]);
116
    }
117
118
    private function getSettings($setting)
119
    {
120
        // vd(__FUNCTION__);
121
        $ret = $this->configurations;
122
123
      //dot based hierarchy, parse and climb
124
        foreach (explode('.', $setting) as $k) {
125
            if (!isset($ret[$k])) {
126
                throw new NotFoundException($setting);
127
            }
128
            $ret = $ret[$k];
129
        }
130
131
        return $ret;
132
    }
133
134
    private function cascadeInstance(Configuration $configuration)
135
    {
136
        $class_name = $configuration->getModelOrControllerName();
137
        $class_name = $this->resolver->cascadeNamespace($class_name);
138
139
        if ($configuration->hasClassNameModifier()) {
140
            $ret = $class_name;
141
        } elseif ($configuration->hasNewInstanceModifier()) {
142
            $ret = $this->makeInstance($class_name);
143
        } else {
144
            $ret = $this->getInstance($class_name);
145
        }
146
147
        return $ret;
148
    }
149
150
    private function wireInstance(Configuration $configuration)
151
    {
152
        $interface = $configuration->configurationString();
153
154
        if (!isset($this->interface_wiring[$interface])) {
155
            throw new NotFoundException($interface);
156
        }
157
158
        $wire = $this->interface_wiring[$interface];
159
160
        // interface + constructor params
161
        if ($this->hasEmbeddedConstructorParameters($wire)) {
162
            $class = array_shift($wire);
163
            $args = $wire;
164
        } else {
165
            $class = $wire;
166
            $args = null;
167
        }
168
169
        if ($this->resolver->isResolved($class) && $this->hasPrivateContructor($class)) {
170
            return $this->resolver->resolved($class);
171
        }
172
173
        return $this->getInstance($class, $args);
174
    }
175
176
    private function hasPrivateContructor($class_name): bool
177
    {
178
        $rc = new \ReflectionClass($class_name);
179
        return !is_null($constructor = $rc->getConstructor()) && $constructor->isPrivate();
180
    }
181
182
    private function hasEmbeddedConstructorParameters($wire)
183
    {
184
        return is_array($wire);
185
    }
186
187
    private function getInstance($class, $construction_args = [])
188
    {
189
        if (ReflectionFactory::hasCacheFor($class)) {
190
            return ReflectionFactory::getCacheFor($class);
191
        }
192
193
        return $this->makeInstance($class, $construction_args);
194
    }
195
196
    private function makeInstance($class, $construction_args = [])
197
    {
198
        $instance = ReflectionFactory::make($class, $construction_args, $this);
199
        return $instance;
200
    }
201
}
202