Test Failed
Push — feature/use-abstract-gacela-cl... ( fc7e58 )
by Chema
04:29
created

AbstractClassResolver   A

Complexity

Total Complexity 3

Size/Duplication

Total Lines 134
Duplicated Lines 0 %

Importance

Changes 8
Bugs 0 Features 0
Metric Value
eloc 49
c 8
b 0
f 0
dl 0
loc 134
rs 10
wmc 3

10 Methods

Rating   Name   Duplication   Size   Complexity  
A doResolve() 0 27 5
A resetCache() 0 3 1
A resolveCached() 0 5 1
A getClassNameFinder() 0 10 2
A createInstance() 0 7 2
A findClassName() 0 5 1
A hp$1 ➔ createDefaultGacelaClass() 0 9 3
A getPossibleResolvableTypes() 0 7 2
A getGacelaConfigFile() 0 9 2
createDefaultGacelaClass() 0 9 ?
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Gacela\Framework\ClassResolver;
6
7
use Gacela\Framework\AbstractConfig;
8
use Gacela\Framework\AbstractFactory;
9
use Gacela\Framework\ClassResolver\Cache\GacelaCache;
10
use Gacela\Framework\ClassResolver\ClassNameFinder\ClassNameFinderInterface;
11
use Gacela\Framework\ClassResolver\Config\ConfigResolver;
12
use Gacela\Framework\ClassResolver\Factory\FactoryResolver;
13
use Gacela\Framework\ClassResolver\GlobalInstance\AnonymousGlobal;
14
use Gacela\Framework\ClassResolver\InstanceCreator\InstanceCreator;
15
use Gacela\Framework\Config\Config;
16
use Gacela\Framework\Config\GacelaFileConfig\GacelaConfigFileInterface;
17
18
use function is_array;
19
use function is_object;
20
21
abstract class AbstractClassResolver
22
{
23
    /** @var array<string,null|object> */
24
    private static array $cachedInstances = [];
25
26
    private ?ClassNameFinderInterface $classNameFinder = null;
27
28
    private ?GacelaConfigFileInterface $gacelaFileConfig = null;
29
30
    private ?InstanceCreator $instanceCreator = null;
31
32
    /**
33
     * @internal remove all cached instances: facade, factory, config, dependency-provider
34
     */
35
    public static function resetCache(): void
36
    {
37
        self::$cachedInstances = [];
38
    }
39
40
    /**
41
     * @param object|class-string $caller
0 ignored issues
show
Documentation Bug introduced by
The doc comment object|class-string at position 2 could not be parsed: Unknown type name 'class-string' at position 2 in object|class-string.
Loading history...
42
     */
43
    abstract public function resolve($caller): ?object;
44
45
    /**
46
     * @param object|class-string $caller
0 ignored issues
show
Documentation Bug introduced by
The doc comment object|class-string at position 2 could not be parsed: Unknown type name 'class-string' at position 2 in object|class-string.
Loading history...
47
     */
48
    public function doResolve($caller): ?object
49
    {
50
        $classInfo = ClassInfo::from($caller, $this->getResolvableType());
51
52
        $cacheKey = $classInfo->getCacheKey();
53
54
        $resolvedClass = $this->resolveCached($cacheKey);
55
        if ($resolvedClass !== null) {
56
            return $resolvedClass;
57
        }
58
59
        $resolvedClassName = $this->findClassName($classInfo);
60
        if ($resolvedClassName === null) {
61
            // Try again with its parent class
62
            if (is_object($caller)) {
63
                $parentClass = get_parent_class($caller);
64
                if ($parentClass !== false) {
65
                    return $this->doResolve($parentClass);
66
                }
67
            }
68
69
            return $this->createDefaultGacelaClass();
70
        }
71
72
        self::$cachedInstances[$cacheKey] = $this->createInstance($resolvedClassName);
73
74
        return self::$cachedInstances[$cacheKey];
75
    }
76
77
    abstract protected function getResolvableType(): string;
78
79
    private function resolveCached(string $cacheKey): ?object
80
    {
81
        return AnonymousGlobal::getByKey($cacheKey)
82
            ?? self::$cachedInstances[$cacheKey]
83
            ?? null;
84
    }
85
86
    /**
87
     * @return class-string|null
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string|null at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string|null.
Loading history...
88
     */
89
    private function findClassName(ClassInfo $classInfo): ?string
90
    {
91
        return $this->getClassNameFinder()->findClassName(
92
            $classInfo,
93
            $this->getPossibleResolvableTypes()
94
        );
95
    }
96
97
    private function getClassNameFinder(): ClassNameFinderInterface
98
    {
99
        if ($this->classNameFinder === null) {
100
            $this->classNameFinder = (new ClassResolverFactory(
101
                new GacelaCache(Config::getInstance()),
102
                Config::getInstance()->getSetupGacela()
103
            ))->createClassNameFinder();
104
        }
105
106
        return $this->classNameFinder;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->classNameFinder could return the type null which is incompatible with the type-hinted return Gacela\Framework\ClassRe...lassNameFinderInterface. Consider adding an additional type-check to rule them out.
Loading history...
107
    }
108
109
    /**
110
     * Allow overriding gacela suffixes resolvable types.
111
     *
112
     * @return list<string>
113
     */
114
    private function getPossibleResolvableTypes(): array
115
    {
116
        $suffixTypes = $this->getGacelaConfigFile()->getSuffixTypes();
117
118
        $resolvableTypes = $suffixTypes[$this->getResolvableType()] ?? $this->getResolvableType();
119
120
        return is_array($resolvableTypes) ? $resolvableTypes : [$resolvableTypes];
121
    }
122
123
    /**
124
     * @param class-string $resolvedClassName
125
     */
126
    private function createInstance(string $resolvedClassName): ?object
127
    {
128
        if ($this->instanceCreator === null) {
129
            $this->instanceCreator = new InstanceCreator($this->getGacelaConfigFile());
130
        }
131
132
        return $this->instanceCreator->createByClassName($resolvedClassName);
0 ignored issues
show
Bug introduced by
The method createByClassName() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

132
        return $this->instanceCreator->/** @scrutinizer ignore-call */ createByClassName($resolvedClassName);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
133
    }
134
135
    private function getGacelaConfigFile(): GacelaConfigFileInterface
136
    {
137
        if ($this->gacelaFileConfig === null) {
138
            $this->gacelaFileConfig = Config::getInstance()
139
                ->getFactory()
140
                ->createGacelaFileConfig();
141
        }
142
143
        return $this->gacelaFileConfig;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->gacelaFileConfig could return the type null which is incompatible with the type-hinted return Gacela\Framework\Config\...celaConfigFileInterface. Consider adding an additional type-check to rule them out.
Loading history...
144
    }
145
146
    private function createDefaultGacelaClass(): ?object
147
    {
148
        switch ($this->getResolvableType()) {
149
            case FactoryResolver::TYPE:
150
                return new class() extends AbstractFactory {};
151
            case ConfigResolver::TYPE:
152
                return new class() extends AbstractConfig {};
153
            default:
154
                return null;
155
        }
156
    }
157
}
158