Passed
Push — master ( f4aea8...91cb90 )
by Alexander
01:38
created

Container::singleton()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
nc 1
nop 2
dl 0
loc 3
rs 10
c 1
b 0
f 0
1
<?php
2
3
namespace alkemann\h2l\util;
4
5
use Closure;
6
7
final class Container
8
{
9
    /** @var array<string, Closure> */
10
    private $_constructors = [];
11
    /** @var array<string, bool> */
12
    private $_is_singleton = [];
13
    /** @var array<string, object> */
14
    private $_singletons = [];
15
16
    /**
17
     * Set constructor callable for class creation
18
     *
19
     * @param string $name
20
     * @param callable $constructor
21
     */
22
    public function __set(string $name, callable $constructor): void
23
    {
24
        $this->_set_constructor($name, $constructor, false);
25
    }
26
27
    /**
28
     * Set constructor callable for singleton creation
29
     *
30
     * @param string $name
31
     * @param callable $constructor
32
     */
33
    public function singleton(string $name, callable $constructor): void
34
    {
35
        $this->_set_constructor($name, $constructor, true);
36
    }
37
38
    /**
39
     * @param string $name
40
     * @param callable $constructor
41
     * @param bool $is_singleton
42
     */
43
    private function _set_constructor(string $name, callable $constructor, bool $is_singleton): void
44
    {
45
        if ($constructor instanceof Closure) {
46
            $closure = $constructor;
47
        } else {
48
            $closure = Closure::fromCallable($constructor);
49
        }
50
        $this->_constructors[$name] = $closure;
51
        $this->_is_singleton[$name] = $is_singleton;
52
    }
53
54
    /**
55
     * Check if there is a constructor for `$name`
56
     *
57
     * @param string $name
58
     * @return bool
59
     */
60
    public function __isset(string $name): bool
61
    {
62
        return (isset($this->_constructors[$name]) && $this->_constructors[$name] instanceof Closure);
63
    }
64
65
    /**
66
     * Magic method for creating new objects or grabbing singleton
67
     *
68
     * @param string $name
69
     * @return object
70
     */
71
    public function __get(string $name): object
72
    {
73
        return $this->_call_or_return($name);
74
    }
75
76
    /**
77
     * Magic method for creating new objects or grabbing singleton, allows for args
78
     *
79
     * @param string $name
80
     * @param array<int, mixed> $args
81
     * @return object
82
     */
83
    public function __call(string $name, array $args): object
84
    {
85
        return $this->_call_or_return($name, $args);
86
    }
87
88
    /**
89
     * @param string $name
90
     * @param null|array<int, mixed> $args
91
     */
92
    private function _call_or_return(string $name, ?array $args = null): object
93
    {
94
95
        if (isset($this->_constructors[$name]) == false) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
96
            throw new \Exception("No constructor set for {$name}!");
97
        }
98
        if ($this->_is_singleton[$name] && isset($this->_singletons[$name])) {
99
            return $this->_singletons[$name];
100
        }
101
        if ($args) {
102
            array_unshift($args, $this);
103
            $object = call_user_func_array($this->_constructors[$name], $args);
104
        } else {
105
            $object = $this->_constructors[$name]($this);
106
        }
107
        if ($this->_is_singleton[$name]) {
108
            $this->_singletons[$name] = $object;
109
        }
110
        return $object;
111
    }
112
}
113