Passed
Push — master ( bfac15...293213 )
by Chema
01:31 queued 16s
created

AbstractClassResolver   A

Complexity

Total Complexity 3

Size/Duplication

Total Lines 134
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 8
Bugs 0 Features 0
Metric Value
eloc 49
dl 0
loc 134
ccs 51
cts 51
cp 1
rs 10
c 8
b 0
f 0
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 9
    public static function resetCache(): void
36
    {
37 9
        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 42
    public function doResolve($caller): ?object
49
    {
50 42
        $classInfo = ClassInfo::from($caller, $this->getResolvableType());
51
52 42
        $cacheKey = $classInfo->getCacheKey();
53
54 42
        $resolvedClass = $this->resolveCached($cacheKey);
55 42
        if ($resolvedClass !== null) {
56 15
            return $resolvedClass;
57
        }
58
59 28
        $resolvedClassName = $this->findClassName($classInfo);
60 28
        if ($resolvedClassName === null) {
61
            // Try again with its parent class
62 5
            if (is_object($caller)) {
63 5
                $parentClass = get_parent_class($caller);
64 5
                if ($parentClass !== false) {
65 5
                    return $this->doResolve($parentClass);
66
                }
67
            }
68
69 4
            return $this->createDefaultGacelaClass();
70
        }
71
72 25
        self::$cachedInstances[$cacheKey] = $this->createInstance($resolvedClassName);
73
74 25
        return self::$cachedInstances[$cacheKey];
75
    }
76
77
    abstract protected function getResolvableType(): string;
78
79 42
    private function resolveCached(string $cacheKey): ?object
80
    {
81 42
        return AnonymousGlobal::getByKey($cacheKey)
82 41
            ?? 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 28
    private function findClassName(ClassInfo $classInfo): ?string
90
    {
91 28
        return $this->getClassNameFinder()->findClassName(
92
            $classInfo,
93 28
            $this->getPossibleResolvableTypes()
94
        );
95
    }
96
97 28
    private function getClassNameFinder(): ClassNameFinderInterface
98
    {
99 28
        if ($this->classNameFinder === null) {
100 28
            $this->classNameFinder = (new ClassResolverFactory(
101 28
                new GacelaCache(Config::getInstance()),
102 28
                Config::getInstance()->getSetupGacela()
103 28
            ))->createClassNameFinder();
104
        }
105
106 28
        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 28
    private function getPossibleResolvableTypes(): array
115
    {
116 28
        $suffixTypes = $this->getGacelaConfigFile()->getSuffixTypes();
117
118 28
        $resolvableTypes = $suffixTypes[$this->getResolvableType()] ?? $this->getResolvableType();
119
120 28
        return is_array($resolvableTypes) ? $resolvableTypes : [$resolvableTypes];
121
    }
122
123
    /**
124
     * @param class-string $resolvedClassName
125
     */
126 25
    private function createInstance(string $resolvedClassName): ?object
127
    {
128 25
        if ($this->instanceCreator === null) {
129 25
            $this->instanceCreator = new InstanceCreator($this->getGacelaConfigFile());
130
        }
131
132 25
        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 28
    private function getGacelaConfigFile(): GacelaConfigFileInterface
136
    {
137 28
        if ($this->gacelaFileConfig === null) {
138 28
            $this->gacelaFileConfig = Config::getInstance()
139 28
                ->getFactory()
140 28
                ->createGacelaFileConfig();
141
        }
142
143 28
        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 4
    private function createDefaultGacelaClass(): ?object
147
    {
148 4
        switch ($this->getResolvableType()) {
149 4
            case FactoryResolver::TYPE:
150 3
                return new class() extends AbstractFactory {};
151 3
            case ConfigResolver::TYPE:
152 2
                return new class() extends AbstractConfig {};
153
            default:
154 1
                return null;
155
        }
156
    }
157
}
158