AnonymousGlobal::getGlobalKeyFromClassName()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 3
ccs 0
cts 0
cp 0
crap 2
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Gacela\Framework\ClassResolver\GlobalInstance;
6
7
use Gacela\Framework\ClassResolver\ClassInfo;
8
use Gacela\Framework\ClassResolver\GlobalKey;
9
use Gacela\Framework\ClassResolver\ResolvableType;
10
use RuntimeException;
11
12
use function in_array;
13
use function is_string;
14
use function sprintf;
15
16
/**
17
 * @internal
18
 */
19
final class AnonymousGlobal
20
{
21
    private const ALLOWED_TYPES_FOR_ANONYMOUS_GLOBAL = [
22
        'Config',
23
        'Factory',
24
        'Provider',
25
    ];
26 58
27
    /** @var array<string,object> */
28 58
    private static array $cachedGlobalInstances = [];
29
30
    /**
31
     * @internal
32
     */
33
    public static function resetCache(): void
34
    {
35
        self::$cachedGlobalInstances = [];
36
    }
37
38
    /**
39
     * @template T
40 11
     *
41
     * @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...
42 11
     *
43
     * @return ?T
44
     *
45 11
     * @internal so the Locator can access to the global instances before creating a new instance
46 11
     */
47 7
    public static function getByClassName(string $className)
48
    {
49 11
        $key = self::getGlobalKeyFromClassName($className);
50
51
        /** @var ?T $instance */
52 83
        $instance = self::getByKey($key); // @phpstan-ignore-line
53
54 83
        return $instance;
55
    }
56
57
    public static function getByKey(string $key): ?object
58
    {
59
        if (isset(self::$cachedGlobalInstances[$key])) {
60
            return self::$cachedGlobalInstances[$key];
61
        }
62 5
63
        $normalizedKey = '\\' . ltrim($key, '\\');
64 5
65 5
        return self::$cachedGlobalInstances[$normalizedKey] ?? null;
66
    }
67 5
68 4
    /**
69 1
     * Add an anonymous class as 'Config', 'Factory' or 'Provider' as a global resource
70
     * bound to the context that it's pass as first argument. It can be the string-key
71 5
     * (from a non-class/file context) or the class/object itself.
72
     */
73 4
    public static function addGlobal(object|string $context, object $resolvedClass): void
74 4
    {
75
        $contextName = self::extractContextNameFromContext($context);
76
        $parentClass = get_parent_class($resolvedClass);
77 6
        $type = is_string($parentClass)
0 ignored issues
show
introduced by
The condition is_string($parentClass) is always true.
Loading history...
78
            ? ResolvableType::fromClassName($parentClass)->resolvableType()
79 6
            : $contextName;
80
81 6
        self::validateTypeForAnonymousGlobalRegistration($type);
82
        $key = self::createCacheKey($contextName, $type);
83
        self::addCachedGlobalInstance($key, $resolvedClass);
84 5
    }
85
86 5
    public static function createCacheKey(string $contextName, string $type): string
87 1
    {
88
        return sprintf('\%s\%s\%s', ClassInfo::MODULE_NAME_ANONYMOUS, $contextName, $type);
89
    }
90 5
91
    public static function overrideExistingResolvedClass(string $className, object $resolvedClass): void
92 5
    {
93
        $key = self::getGlobalKeyFromClassName($className);
94 5
95
        self::addCachedGlobalInstance($key, $resolvedClass);
96 5
    }
97
98
    private static function extractContextNameFromContext(object|string $context): string
99 5
    {
100
        if (is_string($context)) {
101 5
            return $context;
102 1
        }
103 1
104 1
        $callerClass = $context::class;
105
        /** @var list<string> $callerClassParts */
106
        $callerClassParts = explode('\\', ltrim($callerClass, '\\'));
107
108 10
        $lastCallerClassParts = end($callerClassParts);
109
110 10
        return is_string($lastCallerClassParts) ? $lastCallerClassParts : '';
111
    }
112
113 13
    private static function validateTypeForAnonymousGlobalRegistration(string $type): void
114
    {
115 13
        if (!in_array($type, self::ALLOWED_TYPES_FOR_ANONYMOUS_GLOBAL, true)) {
116
            throw new RuntimeException(
117
                sprintf("Type '%s' not allowed. Valid types: ", $type) . implode(
118
                    ', ',
119
                    self::ALLOWED_TYPES_FOR_ANONYMOUS_GLOBAL,
120
                ),
121
            );
122
        }
123
    }
124
125
    private static function addCachedGlobalInstance(string $key, object $resolvedClass): void
126
    {
127
        self::$cachedGlobalInstances[$key] = $resolvedClass;
128
    }
129
130
    private static function getGlobalKeyFromClassName(string $className): string
131
    {
132
        return GlobalKey::fromClassName($className);
133
    }
134
}
135