Passed
Push — feature/reset-cache-from-class... ( b5ffe1 )
by Chema
03:59
created

AbstractClassResolver   A

Complexity

Total Complexity 16

Size/Duplication

Total Lines 115
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 8
Bugs 0 Features 0
Metric Value
eloc 40
c 8
b 0
f 0
dl 0
loc 115
ccs 42
cts 42
cp 1
rs 10
wmc 16

8 Methods

Rating   Name   Duplication   Size   Complexity  
A doResolve() 0 27 5
A resolveCached() 0 5 1
A createInstance() 0 7 2
A getClassNameFinder() 0 8 2
A findClassName() 0 5 1
A getPossibleResolvableTypes() 0 7 2
A getGacelaConfigFile() 0 9 2
A resetCache() 0 3 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Gacela\Framework\ClassResolver;
6
7
use Gacela\Framework\ClassResolver\ClassNameFinder\ClassNameFinderInterface;
8
use Gacela\Framework\ClassResolver\GlobalInstance\AnonymousGlobal;
9
use Gacela\Framework\ClassResolver\InstanceCreator\InstanceCreator;
10
use Gacela\Framework\Config\Config;
11
use Gacela\Framework\Config\GacelaFileConfig\GacelaConfigFileInterface;
12
13
use function is_array;
14
use function is_object;
15
16
abstract class AbstractClassResolver
17
{
18
    /** @var array<string,null|object> */
19
    private static array $cachedInstances = [];
20
21
    private ?ClassNameFinderInterface $classNameFinder = null;
22
23
    private ?GacelaConfigFileInterface $gacelaFileConfig = null;
24
25
    private ?InstanceCreator $instanceCreator = null;
26
27
    /**
28
     * @internal remove all cached instances: facade, factory, config, dependency-provider
29
     */
30 26
    public static function resetCache(): void
31
    {
32 26
        self::$cachedInstances = [];
33
    }
34
35
    /**
36
     * @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...
37
     */
38
    abstract public function resolve($caller): ?object;
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 28
    public function doResolve($caller): ?object
44
    {
45 28
        $classInfo = ClassInfo::from($caller, $this->getResolvableType());
46
47 28
        $cacheKey = $classInfo->getCacheKey();
48
49 28
        $resolvedClass = $this->resolveCached($cacheKey);
50 28
        if ($resolvedClass !== null) {
51 2
            return $resolvedClass;
52
        }
53
54 27
        $resolvedClassName = $this->findClassName($classInfo);
55 27
        if ($resolvedClassName === null) {
56
            // Try again with its parent class
57 4
            if (is_object($caller)) {
58 4
                $parentClass = get_parent_class($caller);
59 4
                if ($parentClass !== false) {
60 4
                    return $this->doResolve($parentClass);
61
                }
62
            }
63
64 3
            return null;
65
        }
66
67 26
        self::$cachedInstances[$cacheKey] = $this->createInstance($resolvedClassName);
68
69 26
        return self::$cachedInstances[$cacheKey];
70
    }
71
72
    abstract protected function getResolvableType(): string;
73
74 28
    private function resolveCached(string $cacheKey): ?object
75
    {
76 28
        return AnonymousGlobal::getByKey($cacheKey)
77 27
            ?? self::$cachedInstances[$cacheKey]
78
            ?? null;
79
    }
80
81 27
    private function findClassName(ClassInfo $classInfo): ?string
82
    {
83 27
        return $this->getClassNameFinder()->findClassName(
84
            $classInfo,
85 27
            $this->getPossibleResolvableTypes()
86
        );
87
    }
88
89 27
    private function getClassNameFinder(): ClassNameFinderInterface
90
    {
91 27
        if ($this->classNameFinder === null) {
92 27
            $this->classNameFinder = (new ClassResolverFactory())
93 27
                ->createClassNameFinder();
94
        }
95
96 27
        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...
97
    }
98
99
    /**
100
     * Allow overriding gacela suffixes resolvable types.
101
     *
102
     * @return list<string>
103
     */
104 27
    private function getPossibleResolvableTypes(): array
105
    {
106 27
        $suffixTypes = $this->getGacelaConfigFile()->getSuffixTypes();
107
108 27
        $resolvableTypes = $suffixTypes[$this->getResolvableType()] ?? $this->getResolvableType();
109
110 27
        return is_array($resolvableTypes) ? $resolvableTypes : [$resolvableTypes];
111
    }
112
113 26
    private function createInstance(string $resolvedClassName): ?object
114
    {
115 26
        if ($this->instanceCreator === null) {
116 26
            $this->instanceCreator = new InstanceCreator($this->getGacelaConfigFile());
117
        }
118
119 26
        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

119
        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...
120
    }
121
122 27
    private function getGacelaConfigFile(): GacelaConfigFileInterface
123
    {
124 27
        if ($this->gacelaFileConfig === null) {
125 27
            $this->gacelaFileConfig = Config::getInstance()
126 27
                ->getFactory()
127 27
                ->createGacelaFileConfig();
128
        }
129
130 27
        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...
131
    }
132
}
133