Passed
Push — main ( 1d9848...0ea40c )
by Sammy
14:36 queued 05:17
created

Solver   A

Complexity

Total Complexity 25

Size/Duplication

Total Lines 162
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 25
eloc 71
c 1
b 0
f 0
dl 0
loc 162
rs 10

11 Methods

Rating   Name   Duplication   Size   Complexity  
A probeClasses() 0 6 2
A cascadeNamespace() 0 23 4
A __construct() 0 4 1
A hasClassNameModifier() 0 3 1
A isInterface() 0 3 1
A solve() 0 6 1
A probeInterface() 0 24 4
A probeCascade() 0 24 5
A probeSettings() 0 26 4
A hasNewInstanceModifier() 0 3 1
A isSettings() 0 3 1
1
<?php
2
3
namespace HexMakina\LeMarchand;
4
5
use Psr\Container\ContainerInterface;
6
7
class Solver
8
{
9
    public const RX_SETTINGS = '/^settings\./';
10
11
    public const RX_INTERFACE = '/([a-zA-Z]+)Interface$/';
12
13
    public const RX_MVC = '/(Models|Controllers)\\\([a-zA-Z\\\]+)(::class|::new)?$/';
14
15
    private static array $cascade_cache = [];
16
17
    private ContainerInterface $container;
18
    private Factory $factory;
19
20
21
    public function __construct(ContainerInterface $c)
22
    {
23
        $this->container = $c;
24
        $this->factory = new Factory($this->container);
25
    }
26
27
28
29
    public function solve(string $lamentation)
30
    {
31
        return $this->probeSettings ($lamentation)
32
            ?? $this->probeClasses  ($lamentation)
33
            ?? $this->probeInterface($lamentation)
34
            ?? $this->probeCascade  ($lamentation);
35
    }
36
37
    //dot based hierarchy, parse and climb
38
    public function probeSettings(string $lamentation)
39
    {
40
        if (!self::isSettings($lamentation)) {
41
            return null;
42
        }
43
44
        $settings = $this->container->get('settings') ?? [];
45
46
        $path = explode('.', $lamentation);
47
        array_shift($path); // remove 'settings' from the path
48
        $walked = [];
49
        foreach ($path as $k) {
50
            
51
            $walked []= $k;
52
            
53
            if (isset($settings[$k])) {
54
                $settings = $settings[$k];
55
                continue;
56
            }
57
58
            // we didn't continue; we failed
59
            $walked = 'settings.' . implode('.', $walked);
60
            throw new NotFoundException(__FUNCTION__."($lamentation) failed at $walked");
61
        }
62
63
        return $settings;
64
    }
65
66
    public function probeClasses(string $className, array $construction_args = []): ?object
67
    {
68
        if(class_exists($className))
69
            return $this->factory->serve($className, $construction_args);
70
71
        return null;
72
    }
73
74
    public function probeInterface(string $lamentation): ?object
75
    {
76
        if (!self::isInterface($lamentation)) {
77
            return null;
78
        }
79
80
        $wires = $this->container->get('wiring') ?? [];
81
82
        if (!isset($wires[$lamentation]))
83
            throw new NotFoundException(__FUNCTION__."($lamentation) is not wired to a class");
84
85
        $wire = $wires[$lamentation];
86
87
        // interface + constructor params
88
89
        if (is_array($wire)) { // hasEmbeddedConstructorParameters, [0] is class, then args
90
            $class = array_shift($wire);
91
            $args = $wire;
92
        } else { // simple instantiation
93
            $class = $wire;
94
            $args = null;
95
        }
96
97
        return $this->factory->serve($class, $args);
98
    }
99
100
    public function probeCascade(string $lamentation)
101
    {
102
        $ret = null;
103
104
        $m = [];
105
        if (preg_match(self::RX_MVC, $lamentation, $m) !== 1) {
106
            return null;
107
        }
108
        
109
        $class_name = $m[1] . '\\' . $m[2];
110
111
        $class_name = $this->cascadeNamespace($class_name);
112
113
        if(is_null($class_name))
114
            $ret = null;
115
        elseif (self::hasClassNameModifier($lamentation)) {
116
            $ret = $class_name;
117
        } elseif (self::hasNewInstanceModifier($lamentation)) {
118
            $ret = $this->factory->build($class_name, []);
119
        } else {
120
            $ret = $this->factory->serve($class_name, []);
121
        }
122
123
        return $ret;
124
    }
125
126
    private function cascadeNamespace(string $class_name): ?string
127
    {
128
        // is it cached ?
129
        if (isset(self::$cascade_cache[$class_name])) {
130
            return self::$cascade_cache[$class_name];
131
        }
132
133
        // no cache lets cascade
134
        $cascade = $this->container->get('cascade') ?? [];
135
136
        foreach ($cascade as $namespace) {
137
138
            $fully_namespaced = $namespace . $class_name;
139
            
140
            if (class_exists($fully_namespaced)) {
141
142
                self::$cascade_cache[$class_name] = $fully_namespaced;
143
144
                return $fully_namespaced;
145
            }
146
        }
147
148
        return null;
149
    }
150
151
    private static function isSettings($lamentation): bool
152
    {
153
        return preg_match(self::RX_SETTINGS, $lamentation) === 1;
154
    }
155
156
    private static function isInterface($lamentation): bool
157
    {
158
        return preg_match(self::RX_INTERFACE, $lamentation) === 1;
159
    }
160
161
    private static function hasClassNameModifier($lamentation)
162
    {
163
        return strpos($lamentation, '::class') !== false;
164
    }
165
166
    private static function hasNewInstanceModifier($lamentation)
167
    {
168
        return strpos($lamentation, '::new') !== false;
169
    }
170
}