PhpDocTypeGuesser::guessType()   B
last analyzed

Complexity

Conditions 5
Paths 5

Size

Total Lines 33
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 5.0488

Importance

Changes 0
Metric Value
dl 0
loc 33
ccs 14
cts 16
cp 0.875
rs 8.439
c 0
b 0
f 0
cc 5
eloc 18
nc 5
nop 2
crap 5.0488
1
<?php
2
3
namespace ScayTrase\Api\Cruds\Adaptors\Symfony;
4
5
use phpDocumentor\Reflection\DocBlock\Tags\Var_;
6
use phpDocumentor\Reflection\DocBlockFactory;
7
use phpDocumentor\Reflection\Types\Array_;
8
use phpDocumentor\Reflection\Types\Mixed_;
9
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
10
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
11
use Symfony\Component\Form\Extension\Core\Type\IntegerType;
12
use Symfony\Component\Form\Extension\Core\Type\NumberType;
13
use Symfony\Component\Form\Extension\Core\Type\TextType;
14
use Symfony\Component\Form\FormTypeGuesserInterface;
15
use Symfony\Component\Form\Guess\Guess;
16
use Symfony\Component\Form\Guess\TypeGuess;
17
use Symfony\Component\Form\Guess\ValueGuess;
18
19
final class PhpDocTypeGuesser implements FormTypeGuesserInterface
20
{
21
    /** @var string[] */
22
    private static $typeMap = [
23
        'int'     => IntegerType::class,
24
        'integer' => IntegerType::class,
25
        'string'  => TextType::class,
26
        'float'   => NumberType::class,
27
        'double'  => NumberType::class,
28
        'real'    => NumberType::class,
29
        'boolean' => CheckboxType::class,
30
        'bool'    => CheckboxType::class,
31
        'array'   => CollectionType::class,
32
    ];
33
    /** @var DocBlockFactory */
34
    private $factory;
35
36
    /**
37
     * PhpDocTypeGuesser constructor.
38
     */
39 6
    public function __construct()
40
    {
41 6
        $this->factory = DocBlockFactory::createInstance();
42 6
    }
43
44
    /** {@inheritdoc} */
45 6
    public function guessType($class, $property)
46
    {
47 6
        $annotation = $this->readPhpDocAnnotations($class, $property);
48
49 6
        if (null === $annotation) {
50
            return; // guess nothing if the @var annotation is not available
51
        }
52
53 6
        $type = $annotation->getType();
54
55 6
        if ($type instanceof Array_) {
56 6
            $entryType = TextType::class;
57 6
            if (!$type->getValueType() instanceof Mixed_) {
58 6
                $entryType = $this->replaceType((string)$type->getValueType());
59
            }
60
61 6
            return new TypeGuess(
62 6
                CollectionType::class,
63
                [
64 6
                    'entry_type'   => $entryType,
65
                    'allow_add'    => true,
66
                    'allow_delete' => true,
67
                ],
68 6
                Guess::MEDIUM_CONFIDENCE
69
            );
70
        }
71
72 6
        if (!$this->exists((string)$type)) {
73
            return new TypeGuess(TextType::class, [], Guess::LOW_CONFIDENCE);
74
        }
75
76 6
        return new TypeGuess($this->replaceType((string)$type), [], Guess::MEDIUM_CONFIDENCE);
77
    }
78
79
    /** {@inheritdoc} */
80 6
    public function guessRequired($class, $property)
81
    {
82 6
        return new ValueGuess(false, Guess::LOW_CONFIDENCE);
83
    }
84
85
    /** {@inheritdoc} */
86 6
    public function guessMaxLength($class, $property)
87
    {
88 6
    }
89
90
    /** {@inheritdoc} */
91 6
    public function guessPattern($class, $property)
92
    {
93 6
    }
94
95
    /**
96
     * @param string $class
97
     * @param string $property
98
     *
99
     * @return Var_
100
     */
101 6
    private function readPhpDocAnnotations($class, $property)
102
    {
103 6
        $reflectionProperty = new \ReflectionProperty($class, $property);
104 6
        $phpdoc             = $reflectionProperty->getDocComment();
105
106 6
        $docblock = $this->factory->create($phpdoc);
107
108 6
        $tags = $docblock->getTagsByName('var');
109
110 6
        return array_shift($tags);
111
    }
112
113
    /**
114
     * @param string $type
115
     *
116
     * @return string
117
     */
118 6
    private function replaceType($type)
119
    {
120 6
        if (!$this->exists($type)) {
121
            return TextType::class;
122
        }
123
124 6
        return self::$typeMap[$type];
125
    }
126
127
    /**
128
     * @param string $type
129
     *
130
     * @return bool
131
     */
132 6
    private function exists($type)
133
    {
134 6
        return array_key_exists($type, self::$typeMap);
135
    }
136
}
137