AnnotationArgumentDeduction::shortNameOf()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 1
dl 0
loc 3
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
1
<?php declare(strict_types=1);
2
3
namespace Stratadox\PhpGenerics\Dissector;
4
5
use Doctrine\Common\Annotations\Reader;
6
use ReflectionClass;
7
use ReflectionException;
8
use Stratadox\PhpGenerics\Annotation\Generic;
9
use Stratadox\PhpGenerics\Dissector\Error\BaseClassIsNotGeneric;
10
use Stratadox\PhpGenerics\Dissector\Error\MissingTypeArgument;
11
use Stratadox\PhpGenerics\Dissector\Error\TypeArgumentNotFound;
12
use Stratadox\PhpGenerics\Dissector\Error\UnexpectedTypeArgument;
13
use Stratadox\PhpGenerics\Loader;
14
use Stratadox\PhpGenerics\Primitives;
15
use function assert;
16
use function class_exists;
17
use function count;
18
use function implode;
19
use function sprintf;
20
use function strrchr;
21
use function substr;
22
23
final class AnnotationArgumentDeduction implements ArgumentDeduction
24
{
25
    /** @var Reader */
26
    private $annotation;
27
    /** @var Primitives */
28
    private $primitives;
29
    /** @var string */
30
    private $separator;
31
    /** @var TypeDissector|null */
32
    private $dissect;
33
    /** @var Loader */
34
    private $loader;
35
36
    public function __construct(
37
        Reader $annotation,
38
        Primitives $primitives,
39
        string $separator,
40
        Loader $loader
41
    ) {
42
        $this->annotation = $annotation;
43
        $this->primitives = $primitives;
44
        $this->separator = $separator;
45
        $this->loader = $loader;
46
    }
47
48
    public function setDissector(TypeDissector $dissector): void
49
    {
50
        assert(null === $this->dissect);
51
        $this->dissect = $dissector;
52
    }
53
54
    public function for(
55
        string $namespace,
56
        string $baseClass,
57
        string $callingFile,
58
        string ...$types
59
    ): array {
60
        assert(null !== $this->dissect);
61
        $baseAnnotation = $this->annotationOf($baseClass);
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $baseAnnotation is correct as $this->annotationOf($baseClass) targeting Stratadox\PhpGenerics\Di...duction::annotationOf() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
62
        if ($baseAnnotation === null) {
0 ignored issues
show
introduced by
The condition $baseAnnotation === null is always true.
Loading history...
63
            throw BaseClassIsNotGeneric::cannotUse($baseClass);
64
        }
65
        $expectedCount = $baseAnnotation->count;
66
        $typeArguments = [];
67
        $skip = 0;
68
        $genericTypeArgument = [];
69
        foreach ($types as $type) {
70
            $annotation = $this->annotationOf($type);
71
            if ($annotation !== null) {
72
                $skip += $annotation->count + ($skip === 0 ? 1 : 0);
73
            }
74
            if ($skip > 0) {
75
                $genericTypeArgument[] = $this->shortNameOf($type);
76
                $skip--;
77
                if ($skip === 0) {
78
                    $genericType = sprintf(
79
                        '%s\\%s',
80
                        $namespace,
81
                        implode($this->separator, $genericTypeArgument)
82
                    );
83
                    $this->loader->generate($this->dissect->typesFromFile(
84
                        $genericType,
85
                        $callingFile
86
                    ));
87
                    // @todo reset $genericTypeArgument
88
                    $typeArguments[] = $genericType;
89
                }
90
                continue;
91
            }
92
            if (!$this->primitives->includes($type) && !class_exists($type)) {
93
                throw TypeArgumentNotFound::for($type, $baseClass, count($typeArguments));
94
            }
95
            $typeArguments[] = $type;
96
        }
97
        if (count($typeArguments) > $expectedCount) {
98
            throw UnexpectedTypeArgument::for($baseClass, count($typeArguments) - 1);
99
        }
100
        if (count($typeArguments) < $expectedCount) {
101
            throw MissingTypeArgument::for($baseClass, count($typeArguments));
102
        }
103
        return $typeArguments;
104
    }
105
106
    private function annotationOf(string $class): ?Generic
107
    {
108
        try {
109
            $annotation = $this->annotation->getClassAnnotation(
110
                new ReflectionClass($class),
111
                Generic::class
112
            );
113
        } catch (ReflectionException $e) {
114
            return null;
115
        }
116
        assert($annotation instanceof Generic || $annotation === null);
117
        return $annotation;
118
    }
119
120
    private function shortNameOf(string $fqcn): string
121
    {
122
        return substr(strrchr('\\' . $fqcn, '\\'), 1);
123
    }
124
}
125