Passed
Push — feature/custom-services-v2 ( 14ad97...35de26 )
by Chema
03:35
created

searchClassOverDocBlock()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 2
c 0
b 0
f 0
nc 1
nop 2
dl 0
loc 5
rs 10
ccs 3
cts 3
cp 1
crap 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Gacela\Framework;
6
7
use Gacela\Framework\ClassResolver\DocBlockService\DocBlockParser;
8
use Gacela\Framework\ClassResolver\DocBlockService\DocBlockServiceResolver;
9
use Gacela\Framework\ClassResolver\DocBlockService\MissingClassDefinitionException;
10
use Gacela\Framework\ClassResolver\DocBlockService\UseBlockParser;
11
use ReflectionClass;
12
13
use function is_string;
14
15
trait DocBlockResolverAwareTrait
16
{
17
    /** @var array<string,string> */
18
    protected static array $fileContentCache = [];
19
20
    /** @var array<string,?object> */
21
    private array $customServices = [];
22
23 2
    public function __call(string $method, array $arguments = []): ?object
24
    {
25 2
        if (!isset($this->customServices[$method])) {
26 2
            $className = $this->getClassFromDoc($method);
27 2
            $resolvableType = $this->normalizeResolvableType($className);
28
29 2
            $this->customServices[$method] = (new DocBlockServiceResolver($resolvableType))
30 2
                ->resolve($className);
31
        }
32
33 2
        return $this->customServices[$method];
34
    }
35
36
    /**
37
     * @return class-string
1 ignored issue
show
Documentation Bug introduced by
The doc comment class-string at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string.
Loading history...
38
     */
39 2
    private function getClassFromDoc(string $method): string
40
    {
41 2
        $reflectionClass = new ReflectionClass(static::class);
42 2
        $className = $this->searchClassOverDocBlock($reflectionClass, $method);
43 2
        if (class_exists($className)) {
44
            return $className;
45
        }
46
47 2
        $className = $this->searchClassOverUseStatements($reflectionClass, $className);
48 2
        if (class_exists($className)) {
49 2
            return $className;
50
        }
51
52
        throw MissingClassDefinitionException::missingDefinition(static::class, $method, $className);
53
    }
54
55 2
    private function searchClassOverDocBlock(ReflectionClass $reflectionClass, string $method): string
56
    {
57 2
        $docBlock = (string)$reflectionClass->getDocComment();
58
59 2
        return (new DocBlockParser())->getClassFromMethod($docBlock, $method);
60
    }
61
62
    /**
63
     * Look the uses, to find the fully-qualified class name for the className.
64
     */
65 2
    private function searchClassOverUseStatements(ReflectionClass $reflectionClass, string $className): string
66
    {
67 2
        $fileName = $reflectionClass->getFileName();
68 2
        if (!isset(static::$fileContentCache[$fileName])) {
69 2
            static::$fileContentCache[$fileName] = file_get_contents($fileName);
70
        }
71 2
        $phpFile = static::$fileContentCache[$fileName];
72
73 2
        return (new UseBlockParser())->getUseStatement($className, $phpFile);
74
    }
75
76 2
    private function normalizeResolvableType(string $resolvableType): string
77
    {
78
        /** @var list<string> $resolvableTypeParts */
79 2
        $resolvableTypeParts = explode('\\', ltrim($resolvableType, '\\'));
80 2
        $normalizedResolvableType = end($resolvableTypeParts);
81
82 2
        return is_string($normalizedResolvableType)
83 2
            ? $normalizedResolvableType
84 2
            : $resolvableType;
85
    }
86
}
87