Passed
Push — main ( 8c175e...8b738d )
by Sammy
01:18
created

LeMarchand.php (1 issue)

Labels
Severity
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
18
19
    private $resolver = null;
20
21
    public static function box($settings = null): ContainerInterface
22
    {
23
        if (is_null(self::$instance)) {
24
            if (is_array($settings)) {
25
                return (self::$instance = new LeMarchand($settings));
26
            }
27
            throw new ContainerException('UNABLE_TO_OPEN_BOX');
28
        }
29
30
        return self::$instance;
31
    }
32
33
34
    private function __construct($settings)
35
    {
36
        if (isset($settings[__CLASS__])) {
37
            $this->resolver = new Resolver($settings[__CLASS__]['cascade'] ?? []);
38
            $this->interface_wiring = $settings[__CLASS__]['wiring'] ?? [];
39
            unset($settings[__CLASS__]);
40
        }
41
        $this->configurations['settings'] = $settings;
42
    }
43
44
    public function __debugInfo(): array
45
    {
46
        $dbg = get_object_vars($this);
47
48
        foreach ($dbg['interface_wiring'] as $interface => $wire) {
49
            if (is_array($wire)) {
50
                $wire = array_shift($wire) . ' --array #' . count($wire);
51
            }
52
            $dbg['interface_wiring'][$interface] = $wire;
53
        }
54
55
        return $dbg;
56
    }
57
58
    public function has($configuration)
59
    {
60
        try {
61
            $this->get($configuration);
62
            return true;
63
        } catch (NotFoundExceptionInterface $e) {
64
            return false;
65
        } catch (ContainerExceptionInterface $e) {
66
            return false;
67
        }
68
        return false;
69
    }
70
71
72
    public function get($configuration_string)
73
    {
74
        if (!is_string($configuration_string)) {
75
            throw new ContainerException($configuration_string);
76
        }
77
78
        if ($this->isFirstLevelKey($configuration_string)) {
79
            return $this->configurations[$configuration_string];
80
        }
81
82
        // not a simple configuration string, it has meaning
83
        $res = $this->getComplexConfigurationString($configuration_string);
84
85
        if (!is_null($res)) {
86
            throw new NotFoundException($configuration_string);
87
        }
88
89
        return $res;
90
    }
91
92
    private function getComplexConfigurationString($configuration_string)
93
    {
94
        $configuration = new Configuration($configuration_string);
95
96
        $ret = null;
97
98
        if ($configuration->isSettings()) {
99
            $ret = $this->getSettings($configuration);
100
        } elseif (class_exists($configuration_string)) {
101
            $ret = $this->getInstance($configuration);
102
        } elseif ($configuration->isInterface()) {
103
            $ret = $this->wireInstance($configuration);
104
        } elseif ($configuration->isModelOrController()) {
105
            $ret = $this->cascadeInstance($configuration);
106
        }
107
108
        return $ret;
109
    }
110
111
    private function isFirstLevelKey($configuration_string)
112
    {
113
        return isset($this->configurations[$configuration_string]);
114
    }
115
116
    private function getSettings($setting)
117
    {
118
        // vd(__FUNCTION__);
119
        $ret = $this->configurations;
120
121
      //dot based hierarchy, parse and climb
122
        foreach (explode('.', $setting) as $k) {
123
            if (!isset($ret[$k])) {
124
                throw new NotFoundException($setting);
125
            }
126
            $ret = $ret[$k];
127
        }
128
129
        return $ret;
130
    }
131
132
    private function cascadeInstance(Configuration $configuration)
133
    {
134
        $class_name = $configuration->getModelOrControllerName();
135
        $class_name = $this->resolver->cascadeNamespace($class_name);
136
137
        if ($configuration->hasClassNameModifier()) {
138
            $ret = $class_name;
139
        } elseif ($configuration->hasNewInstanceModifier()) {
140
            $ret = $this->makeInstance($class_name);
141
        } else {
142
            $ret = $this->getInstance($class_name);
143
        }
144
145
        return $ret;
146
    }
147
148
    private function wireInstance($interface)
149
    {
150
        if (!isset($this->interface_wiring[$interface])) {
151
            throw new NotFoundException($interface);
152
        }
153
154
        $wire = $this->interface_wiring[$interface];
155
156
        // interface + constructor params
157
        if ($this->hasEmbeddedConstructorParameters($wire)) {
158
            $class = array_shift($wire);
159
            $args = $wire;
160
        } else {
161
            $class = $wire;
162
            $args = null;
163
        }
164
165
        if ($this->resolver->isResolved($class) && $this->hasPrivateContructor($class)) {
166
            return $this->resolver->resolved($class);
167
        }
168
169
        return $this->getInstance($class, $args);
170
    }
171
172
    private function hasPrivateContructor($class_name): bool
173
    {
174
        $rc = new \ReflectionClass($class_name);
175
        return !is_null($constructor = $rc->getConstructor()) && $constructor->isPrivate();
176
    }
177
178
    private function hasEmbeddedConstructorParameters($wire)
179
    {
180
        return is_array($wire);
181
    }
182
183
    private function getInstance($class, $construction_args = [])
184
    {
185
        if (ReflectionFactory::hasCacheFor($class)) {
0 ignored issues
show
The type HexMakina\LeMarchand\ReflectionFactory was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
186
            return ReflectionFactory::getCacheFor($class);
187
        }
188
189
        return $this->makeInstance($class, $construction_args);
190
    }
191
192
    private function makeInstance($class, $construction_args = [])
193
    {
194
        $instance = ReflectionFactory::make($class, $construction_args, $this);
195
        return $instance;
196
    }
197
}
198