Passed
Push — master ( e047bd...480ec5 )
by Korotkov
07:04 queued 14s
created

Rudra::session()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 1
c 0
b 0
f 0
dl 0
loc 3
rs 10
cc 1
nc 1
nop 0
1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * @author    : Jagepard <[email protected]">
7
 * @license   https://mit-license.org/ MIT
8
 */
9
10
namespace Rudra\Container;
11
12
use Rudra\Container\Interfaces\RudraInterface;
13
use Rudra\Container\Interfaces\RequestInterface;
14
use Rudra\Container\Interfaces\ResponseInterface;
15
use Rudra\Container\Interfaces\ContainerInterface;
16
use Rudra\Container\Traits\InstantiationsTrait;
17
18
class Rudra implements RudraInterface, ContainerInterface
19
{
20
    use InstantiationsTrait;
21
22
    public static ?RudraInterface $rudra = null;
23
    private array $data = [];
24
25
    public function binding(array $contracts = []): ContainerInterface
26
    {
27
        if (!$this->has("binding")) $this->set(["binding", new Container($contracts)]);
28
29
        return $this->get("binding");
30
    }
31
32
    public function services(array $services = []): ContainerInterface
33
    {
34
        if (!$this->has("services")) $this->set(["services", new Container($services)]);
35
36
        return $this->get("services");
37
    }
38
    
39
    public function config(array $config = []): ContainerInterface
40
    {
41
        if (!$this->has("config")) $this->set(["config", new Container($config)]);
42
43
        return $this->get("config");
44
    }
45
46
    public function data(array $data = []): ContainerInterface
47
    {
48
        if (!$this->has("data")) $this->set(["data", new Container($data)]);
49
50
        return $this->get("data");
51
    }
52
    
53
    public function request(): RequestInterface
54
    {
55
        return $this->containerize(Request::class);
56
    }
57
58
    public function response(): ResponseInterface
59
    {
60
        return $this->containerize(Response::class);
61
    }
62
63
    public function cookie(): Cookie
64
    {
65
        return $this->containerize(Cookie::class);
66
    }
67
68
    public function session(): Session
69
    {
70
        return $this->containerize(Session::class);
71
    }
72
73
    /*
74
     | Creates an object without adding to the container
75
     */
76
    public function new($object, $params = null)
77
    {
78
        $reflection = new \ReflectionClass($object);
79
        $constructor = $reflection->getConstructor();
80
81
        if ($constructor && $constructor->getNumberOfParameters()) {
82
            $paramsIoC = $this->getParamsIoC($constructor, $params);
83
84
            return $reflection->newInstanceArgs($paramsIoC);
85
        }
86
87
        return new $object();
88
    }
89
90
    public static function run(): RudraInterface
91
    {
92
        if (!static::$rudra instanceof static) {
93
            static::$rudra = new static();
94
        }
95
96
        return static::$rudra;
0 ignored issues
show
Bug Best Practice introduced by
The expression return static::rudra could return the type null which is incompatible with the type-hinted return Rudra\Container\Interfaces\RudraInterface. Consider adding an additional type-check to rule them out.
Loading history...
97
    }
98
99
    public function get(string $key = null)
100
    {
101
        if (isset($key) && !$this->has($key)) {
102
            if (!$this->services()->has($key)) {
103
                throw new \InvalidArgumentException("Service '$key' is not installed");
104
            }
105
106
            $this->set([$key, $this->services()->get($key)]);
107
        }
108
109
        if (empty($key)) {
110
            return $this->data;
111
        }
112
113
        return ($this->data[$key] instanceof \Closure) ? $this->data[$key]() : $this->data[$key];
114
    }
115
116
    public function set(array $data): void
117
    {
118
        list($key, $object) = $data;
119
120
        if (is_array($object)) {
121
            if (array_key_exists(1, $object) && !is_object($object[0])) {
122
                $this->iOc($key, $object[0], $object[1]);
123
                return;
124
            }
125
126
            $this->setObject($object[0], $key);
127
            return;
128
        }
129
130
        $this->setObject($object, $key);
131
    }
132
133
    public function has(string $key): bool
134
    {
135
        return array_key_exists($key, $this->data);
136
    }
137
138
    private function setObject($object, $key): void
139
    {
140
        (is_object($object)) ? $this->mergeData($key, $object) : $this->iOc($key, $object);
141
    }
142
143
    private function mergeData(string $key, $object)
144
    {
145
        $this->data = array_merge([$key => $object], $this->data);
146
    }
147
148
    private function iOc(string $key, $object, $params = null): void
149
    {
150
        $reflection = new \ReflectionClass($object);
151
        $constructor = $reflection->getConstructor();
152
153
        if ($constructor && $constructor->getNumberOfParameters()) {
154
            $paramsIoC = $this->getParamsIoC($constructor, $params);
155
            $this->mergeData($key, $reflection->newInstanceArgs($paramsIoC));
156
            return;
157
        }
158
159
        $this->mergeData($key, new $object());
160
    }
161
162
    private function getParamsIoC(\ReflectionMethod $constructor, $params): array
163
    {
164
        $i = 0;
165
        $paramsIoC = [];
166
        $params = (is_array($params) && array_key_exists(0, $params)) ? $params : [$params];
167
168
        foreach ($constructor->getParameters() as $value) {
169
            /*
170
             | If in the constructor expects the implementation of interface,
171
             | so that the container automatically created the necessary object and substituted as an argument,
172
             | we need to bind the interface with the implementation.
173
             */
174
            if (isset($value->getClass()->name) && $this->binding()->has($value->getClass()->name)) {
175
                $className = $this->binding()->get($value->getClass()->name);
176
                $paramsIoC[] = (is_object($className)) ? $className : new $className;
177
                continue;
178
            }
179
180
            /*
181
             | If the class constructor contains arguments with default values,
182
             | then if no arguments are passed,
183
             | values will be added by default by container
184
             */
185
            if ($value->isDefaultValueAvailable() && !isset($params[$i])) {
186
                $paramsIoC[] = $value->getDefaultValue();
187
                continue;
188
            }
189
190
            $paramsIoC[] = $params[$i++];
191
        }
192
193
        return $paramsIoC;
194
    }
195
}
196