Passed
Push — feature/add-container-protect-... ( b82e87 )
by Chema
04:32
created

Locator::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 0

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 0
nc 1
nop 0
dl 0
loc 2
ccs 1
cts 1
cp 1
crap 1
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
9
final class Locator
10
{
11
    private const INTERFACE_SUFFIX = 'Interface';
12
13
    private static ?Locator $instance = null;
14
15
    /** @var array<string, mixed> */
16
    private array $instanceCache = [];
17
18 4
    private function __construct()
19
    {
20 4
    }
21
22
    /**
23
     * @codeCoverageIgnore
24
     */
25
    private function __clone()
26
    {
27
    }
28
29 4
    public static function getInstance(): self
30
    {
31 4
        if (self::$instance === null) {
32 4
            self::$instance = new self();
33
        }
34
35 4
        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...
36
    }
37
38 3
    public static function resetInstance(): void
39
    {
40 3
        self::$instance = null;
41
    }
42
43
    /**
44
     * @template T
45
     *
46
     * @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...
47
     *
48
     * @return T|null
49
     */
50 4
    public function get(string $className)
51
    {
52
        /** @var class-string<T> $concreteClass */
53 4
        $concreteClass = $this->getConcreteClass($className);
54
55 4
        if (isset($this->instanceCache[$concreteClass])) {
56
            /** @var T $instance */
57 2
            $instance = $this->instanceCache[$concreteClass];
58
59 2
            return $instance;
60
        }
61
62 4
        $locatedInstance = AnonymousGlobal::getByClassName($concreteClass)
63 4
            ?? $this->newInstance($concreteClass);
64
65
        /** @psalm-suppress MixedAssignment */
66 4
        $this->instanceCache[$concreteClass] = $locatedInstance;
67
68 4
        return $locatedInstance;
69
    }
70
71 4
    private function getConcreteClass(string $className): string
72
    {
73 4
        if ($this->isInterface($className)) {
74 1
            return $this->getConcreteClassFromInterface($className);
75
        }
76
77 3
        return $className;
78
    }
79
80
    /**
81
     * @template T
82
     *
83
     * @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...
84
     *
85
     * @return T|null
86
     */
87 4
    private function newInstance(string $className)
88
    {
89 4
        if (class_exists($className)) {
90
            /** @psalm-suppress MixedMethodCall */
91 3
            return new $className();
92
        }
93
94 1
        return null;
95
    }
96
97 4
    private function isInterface(string $className): bool
98
    {
99 4
        return mb_strpos($className, self::INTERFACE_SUFFIX) !== false;
100
    }
101
102 1
    private function getConcreteClassFromInterface(string $interface): string
103
    {
104 1
        return str_replace(self::INTERFACE_SUFFIX, '', $interface);
105
    }
106
}
107