Passed
Push — main ( cb46db...03ea74 )
by Sammy
01:39
created

LeMarchand::cascadeNamespace()   A

Complexity

Conditions 6
Paths 13

Size

Total Lines 19
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
eloc 10
c 3
b 0
f 0
dl 0
loc 19
rs 9.2222
cc 6
nc 13
nop 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
    private $configurations = [];
11
12
    // public const RX_CONTROLLER_NAME = '/([a-zA-Z]+)Controller$/';
13
    // public const RX_MODEL_CLASS = '/([a-zA-Z]+)(Class|Model)$/';
14
    public const RX_SETTINGS = '/^settings\./';
15
    public const RX_INTERFACE_NAME = '/([a-zA-Z]+)Interface$/';
16
    public const RX_CLASS_NAME = '/([a-zA-Z]+)(Class|Model|Controller)$/';
17
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
        throw new LamentException('UNABLE_TO_OPEN_BOX');
25
      }
26
27
      return self::$instance;
28
    }
29
30
31
    private function __construct($settings)
32
    {
33
        $this->configurations['settings'] = $settings;
34
    }
35
36
    public function __debugInfo(): array
37
    {
38
        $dbg = get_object_vars($this);
39
        if (isset($dbg['configurations']['template_engine'])) {
40
          // way too long of an object
41
            $dbg['configurations']['template_engine'] = get_class($dbg['configurations']['template_engine']);
42
        }
43
        return $dbg;
44
    }
45
46
    public function register($configuration, $instance)
47
    {
48
        $this->configurations[$configuration] = $instance;
49
    }
50
51
    public function has($configuration)
52
    {
53
      try{
54
        $this->get($configuration);
55
        return true;
56
      }
57
      catch(NotFoundExceptionInterface $e){
58
        return false;
59
      }
60
      catch(ContainerExceptionInterface $e){
61
        return false;
62
      }
63
    }
64
65
66
    public function get($configuration)
67
    {
68
        if (!is_string($configuration)) {
69
            throw new LamentException($configuration);
70
        }
71
        // 1. is it a first level key ?
72
        if (isset($this->configurations[$configuration])) {
73
            return $this->configurations[$configuration];
74
        }
75
76
        // 2. is it configuration data ?
77
        if (preg_match(self::RX_SETTINGS, $configuration, $m) === 1) {
78
            return $this->getSettings($configuration);
79
        }
80
        // 3. is it a class
81
        if (preg_match(self::RX_CLASS_NAME, $configuration, $m) === 1) {
82
            $class_name = $this->cascadeNamespace($m[1], $m[2]);
83
            if($m[2] === 'Class')
84
              return $class_name;
85
86
            return $this->getInstance($class_name);
87
        }
88
89
        if (preg_match(self::RX_INTERFACE_NAME, $configuration, $m) === 1) {
90
            $wire = $this->get('settings.interface_implementations');
91
            if(isset($wire[$configuration]))
92
              return $this->getInstance($wire[$configuration]);
93
        }
94
95
        throw new ConfigurationException($configuration);
96
    }
97
98
    // private function cascadeControllers($controller_name)
99
    // {
100
    //     // is the controller name already instantiable ?
101
    //     if (!is_null($instance = $this->getInstance($controller_name))) {
102
    //         return $instance;
103
    //     }
104
    //     // not fully namespaced, lets cascade
105
    //     foreach ($this->getSettings('settings.controller_namespaces') as $cns) {
106
    //         if (!is_null($instance = $this->getInstance($cns . $controller_name))) {
107
    //             return $instance;
108
    //         }
109
    //     }
110
    //     throw new ConfigurationException($controller_name);
111
    // }
112
113
    private function cascadeNamespace($class_name, $mvc_type=null)
114
    {
115
        // does the name already exists ?
116
        if(class_exists($class_name))
117
          return $class_name;
118
119
        if($mvc_type === 'Class')
120
          $mvc_type = 'Model';
121
122
        if($mvc_type === 'Controller')
123
          $class_name = $class_name.'Controller';
124
125
        // not fully namespaced, lets cascade
126
        foreach ($this->getSettings('settings.namespaces') as $ns) {
127
            if(class_exists($full_name = $ns . $mvc_type . 's\\' . $class_name))
128
              return $full_name;
129
        }
130
131
        throw new ConfigurationException($class_name);
132
    }
133
134
    private function getInstance($class)
135
    {
136
        try {
137
            $rc = new \ReflectionClass($class);
138
            $instance = null;
139
            $construction_args = [];
140
            if (!is_null($rc->getConstructor())) {
141
                foreach ($rc->getConstructor()->getParameters() as $param) {
142
                    $construction_args [] = $this->get($param->getType() . '');
143
                }
144
                $instance = $rc->newInstanceArgs($construction_args);
145
            } else {
146
                $instance = $rc->newInstanceArgs();
147
            }
148
149
            if ($rc->hasMethod('set_container')) {
150
                $instance->set_container($this);
151
            }
152
153
            return $instance;
154
        } catch (\ReflectionException $e) {
155
            return null;
156
        }
157
    }
158
159
160
    private function getSettings($setting)
161
    {
162
        $ret = $this->configurations;
163
164
      //dot based hierarchy, parse and climb
165
        foreach (explode('.', $setting) as $k) {
166
            if (!isset($ret[$k])) {
167
                throw new ConfigurationException($setting);
168
            }
169
            $ret = $ret[$k];
170
        }
171
172
        return $ret;
173
    }
174
}
175