Passed
Push — feat/getRequired ( 30d2e8 )
by Chema
24:24 queued 09:04
created

Locator::getRequiredSingleton()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 2
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Gacela\Framework\Container;
6
7
use Gacela\Framework\ClassResolver\GlobalInstance\AnonymousGlobal;
8
use Gacela\Framework\Exception\ServiceNotFoundException;
9
10
/**
11
 * @internal
12
 */
13
final class Locator implements LocatorInterface
14
{
15
    private static ?Locator $instance = null;
16
17
    /** @var array<string, mixed> */
18
    private array $instanceCache = [];
19
20
    private function __construct(
21
        private readonly ContainerInterface $container = new Container(),
22
    ) {
23
    }
24
25
    public static function resetInstance(): void
26
    {
27
        self::$instance = null;
28
    }
29
30
    /**
31
     * @template T
32
     *
33
     * @param class-string<T> $key
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string<T> at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string<T>.
Loading history...
34
     * @param T $value
35
     */
36
    public static function addSingleton(string $key, mixed $value): void
37
    {
38
        self::getInstance()->add($key, $value);
39
    }
40
41
    /**
42
     * @template T
43
     *
44
     * @param class-string<T> $className
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string<T> at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string<T>.
Loading history...
45
     *
46
     * @return T|null
47
     */
48
    public static function getSingleton(string $className, ?Container $container = null)
49
    {
50
        return self::getInstance($container)->get($className);
51
    }
52
53
    /**
54
     * Get a singleton from the container, throwing an exception if not found.
55
     * Use this when you expect the service to exist and want type-safe returns.
56
     *
57
     * @template T of object
58
     *
59
     * @param class-string<T> $className
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string<T> at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string<T>.
Loading history...
60
     *
61
     * @throws ServiceNotFoundException
62
     *
63
     * @return T
64
     */
65
    public static function getRequiredSingleton(string $className, ?Container $container = null): object
66
    {
67
        return self::getInstance($container)->getRequired($className);
68
    }
69
70
    public static function getInstance(?Container $container = null): self
71
    {
72
        if (!self::$instance instanceof self) {
73
            self::$instance = new self($container ?? new Container());
74
        }
75
76
        return self::$instance;
0 ignored issues
show
Bug Best Practice introduced by
The expression return self::instance could return the type null which is incompatible with the type-hinted return Gacela\Framework\Container\Locator. Consider adding an additional type-check to rule them out.
Loading history...
77
    }
78
79
    /**
80
     * @template T
81
     *
82
     * @param class-string<T> $className
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string<T> at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string<T>.
Loading history...
83
     *
84
     * @return T|null
85
     */
86
    public function get(string $className)
87
    {
88
        if (isset($this->instanceCache[$className])) {
89
            /** @var T $instance */
90
            $instance = $this->instanceCache[$className];
91
92
            return $instance;
93
        }
94
95
        /** @var T|null $locatedInstance */
96
        $locatedInstance = AnonymousGlobal::getByClassName($className)
97
            ?? $this->container->get($className);
98
99
        $this->add($className, $locatedInstance);
100
101
        return $locatedInstance;
102
    }
103
104
    /**
105
     * @template T of object
106
     *
107
     * @param class-string<T> $className
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string<T> at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string<T>.
Loading history...
108
     *
109
     * @throws ServiceNotFoundException
110
     *
111
     * @return T
112
     */
113
    public function getRequired(string $className): object
114
    {
115
        $instance = $this->get($className);
116
117
        if ($instance === null) {
118
            throw new ServiceNotFoundException($className);
119
        }
120
121
        return $instance;
122
    }
123
124
    /**
125
     * @template T
126
     *
127
     * @param class-string<T> $key
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string<T> at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string<T>.
Loading history...
128
     * @param T|null $value
129
     */
130
    private function add(string $key, mixed $value = null): void
131
    {
132
        $this->instanceCache[$key] = $value;
133
    }
134
}
135