• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

overblog / GraphQLBundle / 4648668349

pending completion
4648668349

push

github

GitHub
Merge pull request #1113 from overblog/Vincz-patch-1

4256 of 4323 relevant lines covered (98.45%)

39.09 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

95.16
/src/Config/Parser/MetadataParser/TypeGuesser/DocBlockTypeGuesser.php
1
<?php
2

3
declare(strict_types=1);
4

5
namespace Overblog\GraphQLBundle\Config\Parser\MetadataParser\TypeGuesser;
6

7
use Exception;
8
use phpDocumentor\Reflection\DocBlock\Tags\TagWithType;
9
use phpDocumentor\Reflection\DocBlockFactory;
10
use phpDocumentor\Reflection\Type;
11
use phpDocumentor\Reflection\Types\AbstractList;
12
use phpDocumentor\Reflection\Types\Compound;
13
use phpDocumentor\Reflection\Types\ContextFactory;
14
use phpDocumentor\Reflection\Types\Mixed_;
15
use phpDocumentor\Reflection\Types\Null_;
16
use phpDocumentor\Reflection\Types\Nullable;
17
use phpDocumentor\Reflection\Types\Object_;
18
use ReflectionClass;
19
use ReflectionMethod;
20
use ReflectionProperty;
21
use Reflector;
22

23
use function sprintf;
24

25
final class DocBlockTypeGuesser extends PhpTypeGuesser
26
{
27
    protected ?DocBlockFactory $factory;
28

29
    public function getName(): string
30
    {
31
        return 'Dock block';
48✔
32
    }
33

34
    public function supports(Reflector $reflector): bool
35
    {
36
        return $reflector instanceof ReflectionProperty || $reflector instanceof ReflectionMethod;
48✔
37
    }
38

39
    /**
40
     * @param ReflectionProperty|ReflectionMethod $reflector
41
     */
42
    public function guessType(ReflectionClass $reflectionClass, Reflector $reflector, array $filterGraphQLTypes = []): ?string
43
    {
44
        $contextFactory = new ContextFactory();
95✔
45
        $context = $contextFactory->createFromReflector($reflectionClass);
95✔
46
        $docBlockComment = $reflector->getDocComment();
95✔
47
        if (!$docBlockComment) {
95✔
48
            throw new TypeGuessingException(sprintf('Doc Block not found'));
1✔
49
        }
50

51
        try {
52
            $docBlock = $this->getParser()->create($docBlockComment, $context);
94✔
53
        } catch (Exception $e) {
×
54
            throw new TypeGuessingException(sprintf('Doc Block parsing failed with error: %s', $e->getMessage()));
×
55
        }
56
        $tagName = $reflector instanceof ReflectionProperty ? 'var' : 'return';
94✔
57
        $tags = $docBlock->getTagsByName($tagName);
94✔
58
        $tag = $tags[0] ?? null;
94✔
59
        if (!$tag || !$tag instanceof TagWithType) {
94✔
60
            throw new TypeGuessingException(sprintf('No @%s tag found in doc block or tag has no type', $tagName));
52✔
61
        }
62
        $type = $tag->getType();
90✔
63
        $isNullable = false;
90✔
64
        $isList = false;
90✔
65
        $isListNullable = false;
90✔
66
        $exceptionPrefix = sprintf('Tag @%s found', $tagName);
90✔
67

68
        if ($type instanceof Compound) {
90✔
69
            $type = $this->resolveCompound($type);
9✔
70
            if (!$type) {
9✔
71
                throw new TypeGuessingException(sprintf('%s, but composite types are only allowed with null. Ex: string|null.', $exceptionPrefix));
2✔
72
            }
73
            $isNullable = true;
7✔
74
        } elseif ($type instanceof Nullable) {
81✔
75
            $isNullable = true;
2✔
76
            $type = $type->getActualType();
2✔
77
        }
78

79
        if ($type instanceof AbstractList) {
88✔
80
            $isList = true;
65✔
81
            $isListNullable = $isNullable;
65✔
82
            $isNullable = false;
65✔
83
            $type = $type->getValueType();
65✔
84
            if ($type instanceof Compound) {
65✔
85
                $type = $this->resolveCompound($type);
4✔
86
                if (!$type) {
4✔
87
                    throw new TypeGuessingException(sprintf('%s, but composite types in array or iterable are only allowed with null. Ex: string|null.', $exceptionPrefix));
2✔
88
                }
89
                $isNullable = true;
2✔
90
            } elseif ($type instanceof Mixed_) {
61✔
91
                throw new TypeGuessingException(sprintf('%s, but the array values cannot be mixed type', $exceptionPrefix));
4✔
92
            }
93
        }
94

95
        if ($type instanceof Object_) {
82✔
96
            $className = $type->getFqsen();
6✔
97
            if (!$className) {
6✔
98
                throw new TypeGuessingException(sprintf('%s, but type "object" is too generic.', $exceptionPrefix));
2✔
99
            }
100
            // Remove first '\' from returned class name
101
            $className = substr((string) $className, 1);
4✔
102
            $gqlType = $this->map->resolveType((string) $className, $filterGraphQLTypes);
4✔
103
            if (!$gqlType) {
4✔
104
                throw new TypeGuessingException(sprintf('%s, but target object "%s" is not a GraphQL Type class.', $exceptionPrefix, $className));
4✔
105
            }
106
        } else {
107
            $gqlType = $this->resolveTypeFromPhpType((string) $type);
76✔
108
            if (!$gqlType) {
76✔
109
                throw new TypeGuessingException(sprintf('%s, but unable to resolve type "%s" to a GraphQL scalar.', $exceptionPrefix, (string) $type));
2✔
110
            }
111
        }
112

113
        return $isList ? sprintf('[%s%s]%s', $gqlType, $isNullable ? '' : '!', $isListNullable ? '' : '!') : sprintf('%s%s', $gqlType, $isNullable ? '' : '!');
78✔
114
    }
115

116
    protected function resolveCompound(Compound $compound): ?Type
117
    {
118
        $typeNull = new Null_();
11✔
119
        if (2 !== $compound->getIterator()->count() || !$compound->contains($typeNull)) {
11✔
120
            return null;
4✔
121
        }
122
        foreach ($compound as $type) {
7✔
123
            if (!$type instanceof Null_) {
7✔
124
                return $type;
7✔
125
            }
126
        }
127

128
        return null;
×
129
    }
130

131
    private function getParser(): DocBlockFactory
132
    {
133
        if (!isset($this->factory)) {
94✔
134
            $this->factory = DocBlockFactory::createInstance();
94✔
135
        }
136

137
        return $this->factory;
94✔
138
    }
139
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc