Passed
Push — main ( dcc917...56ffb1 )
by Sammy
07:38 queued 05:57
created

LeMarchand   A

Complexity

Total Complexity 35

Size/Duplication

Total Lines 184
Duplicated Lines 0 %

Importance

Changes 27
Bugs 6 Features 2
Metric Value
wmc 35
eloc 79
c 27
b 6
f 2
dl 0
loc 184
rs 9.6

11 Methods

Rating   Name   Duplication   Size   Complexity  
A classification() 0 13 3
A cascadeNamespace() 0 23 6
A __debugInfo() 0 8 2
A has() 0 9 3
A getSettings() 0 14 3
A __construct() 0 6 1
A box() 0 10 3
A getInstance() 0 22 5
A get() 0 21 5
A wireInstance() 0 9 2
A put() 0 6 2
1
<?php
2
3
namespace HexMakina\LeMarchand;
4
5
use Psr\Container\{ContainerInterface, ContainerExceptionInterface, NotFoundExceptionInterface};
6
7
class LeMarchand implements ContainerInterface
8
{
9
    private static $instance = null;
10
    // stores all the settings
11
    private $configurations = [];
12
13
    // stores the namespace cascade
14
    private $namespace_cascade = [];
15
16
    // stores the interface to class wiring
17
    private $interface_wiring = [];
18
19
    // public const RX_CONTROLLER_NAME = '/([a-zA-Z]+)Controller$/';
20
    // public const RX_MODEL_CLASS = '/([a-zA-Z]+)(Class|Model)$/';
21
    const RX_SETTINGS = '/^settings\./';
22
    // public const RX_INTERFACE_NAME = '/([a-zA-Z]+)Interface$/';
23
    const RX_CLASS_NAME = '/([a-zA-Z]+)(Class|Model|Controller|Interface)$/';
24
25
26
    public static function box($settings = null): ContainerInterface
27
    {
28
        if (is_null(self::$instance)) {
29
            if (is_array($settings)) {
30
                return (self::$instance = new LeMarchand($settings));
31
            }
32
            throw new LamentException('UNABLE_TO_OPEN_BOX');
33
        }
34
35
        return self::$instance;
36
    }
37
38
39
    private function __construct($settings)
40
    {
41
        $this->configurations['settings'] = $settings;
42
        
43
        $this->namespace_cascade = $settings['LeMarchand']['cascade'];
44
        $this->interface_wiring = $settings['LeMarchand']['wiring'];
45
    }
46
47
    public function __debugInfo(): array
48
    {
49
        $dbg = get_object_vars($this);
50
        if (isset($dbg['configurations']['template_engine'])) {
51
          // way too long of an object
52
            $dbg['configurations']['template_engine'] = get_class($dbg['configurations']['template_engine']);
53
        }
54
        return $dbg;
55
    }
56
57
    public function put($configuration, $instance)
58
    {
59
        if (!is_string($configuration)) {
60
            throw new LamentException($configuration);
61
        }
62
        $this->configurations[$configuration] = $instance;
63
    }
64
65
    public function has($configuration)
66
    {
67
        try {
68
            $this->get($configuration);
69
            return true;
70
        } catch (NotFoundExceptionInterface $e) {
71
            return false;
72
        } catch (ContainerExceptionInterface $e) {
73
            return false;
74
        }
75
    }
76
77
78
    public function get($configuration)
79
    {
80
81
        if (!is_string($configuration)) {
82
            throw new LamentException($configuration);
83
        }
84
        // 1. is it a first level key ?
85
        if (isset($this->configurations[$configuration])) {
86
            return $this->configurations[$configuration];
87
        }
88
89
        // 2. is it configuration data ?
90
        if (preg_match(self::RX_SETTINGS, $configuration, $m) === 1) {
91
            return $this->getSettings($configuration);
92
        }
93
        // 3. is it a class
94
        if (preg_match(self::RX_CLASS_NAME, $configuration, $m) === 1) {
95
            return $this->classification($m[1], $m[2]);
96
        }
97
98
        throw new ConfigurationException($configuration);
99
    }
100
101
102
    private function getSettings($setting)
103
    {
104
      // vdt(__FUNCTION__);
105
        $ret = $this->configurations;
106
107
      //dot based hierarchy, parse and climb
108
        foreach (explode('.', $setting) as $k) {
109
            if (!isset($ret[$k])) {
110
                throw new ConfigurationException($setting);
111
            }
112
            $ret = $ret[$k];
113
        }
114
115
        return $ret;
116
    }
117
118
    private function classification($name, $type)
119
    {
120
        if ($type === 'Interface') {
121
          return $this->wireInstance($name.$type);
122
        }
123
124
        $class_name = $this->cascadeNamespace($name, $type);
125
126
        if ($type === 'Class') {
127
            return $class_name;
128
        }
129
130
        return $this->getInstance($class_name);
131
    }
132
133
    private function cascadeNamespace($class_name, $mvc_type = null)
134
    {
135
        // does the name already exists ?
136
        if (class_exists($class_name)) {
137
            return $class_name;
138
        }
139
140
        if ($mvc_type === 'Class') {
141
            $mvc_type = 'Model';
142
        }
143
144
        if ($mvc_type === 'Controller') {
145
            $class_name = $class_name . 'Controller';
146
        }
147
148
        // not fully namespaced, lets cascade
149
        foreach ($this->namespace_cascade as $ns) {
150
            if (class_exists($full_name = $ns . $mvc_type . 's\\' . $class_name)) {
151
                return $full_name;
152
            }
153
        }
154
155
        throw new ConfigurationException($class_name);
156
    }
157
158
    private function wireInstance($interface)
159
    {
160
        $wire = $this->getSettings('settings.interface_implementations');
161
162
        if (!isset($wire[$interface])) {
163
            throw new ConfigurationException($interface);
164
        }
165
166
        return $this->getInstance($wire[$interface]);
167
    }
168
169
    private function getInstance($class)
170
    {
171
        try {
172
            $rc = new \ReflectionClass($class);
173
            $instance = null;
174
            $construction_args = [];
175
            if (!is_null($rc->getConstructor())) {
176
                foreach ($rc->getConstructor()->getParameters() as $param) {
177
                    $construction_args [] = $this->get($param->getType() . '');
178
                }
179
                $instance = $rc->newInstanceArgs($construction_args);
180
            } else {
181
                $instance = $rc->newInstanceArgs();
182
            }
183
184
            if ($rc->hasMethod('set_container')) {
185
                $instance->set_container($this);
186
            }
187
188
            return $instance;
189
        } catch (\ReflectionException $e) {
190
            return null;
191
        }
192
    }
193
}
194