Container::getDefaultDefinition()   A
last analyzed

Complexity

Conditions 3
Paths 3

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 5
c 1
b 0
f 0
dl 0
loc 9
rs 10
cc 3
nc 3
nop 2
1
<?php
2
3
namespace PHPWatch\SimpleContainer;
4
5
use ArrayAccess;
6
use PHPWatch\SimpleContainer\Exception\BadMethodCallException;
7
use PHPWatch\SimpleContainer\Exception\NotFoundException;
8
use Psr\Container\ContainerInterface;
9
use function array_key_exists;
10
use function is_callable;
11
use function sprintf;
12
13
class Container implements ArrayAccess, ContainerInterface {
14
    private array $definitions;
15
    private array $generated = [];
16
    private array $protected = [];
17
    private array $factories = [];
18
19
    public function __construct(array $definitions = []) {
20
        $this->definitions = $definitions;
21
    }
22
23
    private function getService(string $id) {
24
        if (!$this->has($id)) {
25
            throw new NotFoundException(sprintf('Container key "%s" is not defined', $id));
26
        }
27
28
        if (array_key_exists($id, $this->generated)) {
29
            return $this->generated[$id];
30
        }
31
32
        if (!is_callable($this->definitions[$id]) || isset($this->protected[$id])) {
33
            return $this->definitions[$id];
34
        }
35
36
        if (isset($this->factories[$id])) {
37
            return $this->definitions[$id]($this);
38
        }
39
40
        return $this->generated[$id] = $this->definitions[$id]($this);
41
    }
42
43
    public function set(string $id, $value): void {
44
        if (array_key_exists($id, $this->definitions)) {
45
            unset($this->generated[$id], $this->factories[$id], $this->protected[$id]);
46
        }
47
        $this->definitions[$id] = $value;
48
    }
49
50
    public function setProtected(string $id, ?callable $value = null): void {
51
        if ($value === null) {
52
            $value = $this->getDefaultDefinition($id, sprintf('Attempt to set container ID "%s" as protected, but it is not already set nor provided in the function call.', $id));
53
        }
54
55
        $this->set($id, $value);
56
        $this->protected[$id] = true;
57
    }
58
59
    public function setFactory(string $id, ?callable $value = null): void {
60
        if ($value === null) {
61
            $value = $this->getDefaultDefinition($id, sprintf('Attempt to set container ID "%s" as factory, but it is not already set nor provided in the function call', $id));
62
        }
63
64
        $this->set($id, $value);
65
        $this->factories[$id] = true;
66
    }
67
68
    private function getDefaultDefinition(string $id, string $exception_message): callable {
69
        if (!$this->has($id)) {
70
            throw new BadMethodCallException($exception_message);
71
        }
72
        if (!is_callable($this->definitions[$id])) {
73
            throw new BadMethodCallException(sprintf('Definition for "%s" expected to be a callable, "%s" found', $id, \gettype($this->definitions[$id])));
74
        }
75
76
        return $this->definitions[$id];
77
    }
78
79
    public function offsetSet($id, $value): void {
80
        $this->set($id, $value);
81
    }
82
83
    public function offsetUnset($id): void {
84
        unset($this->definitions[$id], $this->generated[$id], $this->factories[$id], $this->protected[$id]);
85
    }
86
87
    public function offsetExists($id): bool {
88
        return array_key_exists($id, $this->definitions);
89
    }
90
91
    public function offsetGet($id) {
92
        return $this->getService($id);
93
    }
94
95
    /**
96
     * @inheritDoc
97
     */
98
    public function get($id) {
99
        return $this->getService($id);
100
    }
101
102
    /**
103
     * @inheritDoc
104
     */
105
    public function has($id): bool {
106
        return array_key_exists($id, $this->definitions);
107
    }
108
}
109