Failed Conditions
Pull Request — release/1.0.0 (#2)
by Yo
03:36
created

DocTypeHelper::isAbstractType()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 3

Importance

Changes 0
Metric Value
cc 3
eloc 4
nc 3
nop 1
dl 0
loc 8
ccs 5
cts 5
cp 1
crap 3
rs 9.4285
c 0
b 0
f 0
1
<?php
2
namespace Yoanm\JsonRpcParamsSymfonyConstraintDoc\App\Helper;
3
4
use Symfony\Component\Validator\Constraint;
5
use Symfony\Component\Validator\Constraints as Assert;
6
use Yoanm\JsonRpcServerDoc\Domain\Model\Type\ArrayDoc;
7
use Yoanm\JsonRpcServerDoc\Domain\Model\Type\BooleanDoc;
8
use Yoanm\JsonRpcServerDoc\Domain\Model\Type\CollectionDoc;
9
use Yoanm\JsonRpcServerDoc\Domain\Model\Type\FloatDoc;
10
use Yoanm\JsonRpcServerDoc\Domain\Model\Type\IntegerDoc;
11
use Yoanm\JsonRpcServerDoc\Domain\Model\Type\NumberDoc;
12
use Yoanm\JsonRpcServerDoc\Domain\Model\Type\ObjectDoc;
13
use Yoanm\JsonRpcServerDoc\Domain\Model\Type\ScalarDoc;
14
use Yoanm\JsonRpcServerDoc\Domain\Model\Type\StringDoc;
15
use Yoanm\JsonRpcServerDoc\Domain\Model\Type\TypeDoc;
16
17
/**
18
 * Class DocTypeHelper
19
 */
20
class DocTypeHelper
21
{
22
    /** @var ConstraintPayloadDocHelper */
23
    private $constraintPayloadDocHelper;
24
25
    /**
26
     * @param ConstraintPayloadDocHelper $constraintPayloadDocHelper
27
     */
28 69
    public function __construct(ConstraintPayloadDocHelper $constraintPayloadDocHelper)
29
    {
30 69
        $this->constraintPayloadDocHelper = $constraintPayloadDocHelper;
31 69
    }
32
33
    /**
34
     * @param Constraint[] $constraintList
35
     *
36
     * @return TypeDoc
37
     */
38 69
    public function guess(array $constraintList) : TypeDoc
39
    {
40 69
        return $this->getDocFromTypeConstraintOrPayloadDocIfExist($constraintList)
41 52
        ?? $this->guessTypeFromConstraintList($constraintList)
42 69
        ?? new TypeDoc();
43
    }
44
45
    /**
46
     * @param Constraint[] $constraintList
47
     *
48
     * @return TypeDoc|null
49
     */
50 69
    private function getDocFromTypeConstraintOrPayloadDocIfExist(array $constraintList)
51
    {
52 69
        $doc = null;
53
        // Check if a Type constraint exist or if a constraint have a type documentation
54 69
        foreach ($constraintList as $constraint) {
55 68
            if (null !== ($typeFromPayload = $this->constraintPayloadDocHelper->getTypeIfExist($constraint))) {
56 1
                $doc = $this->getDocFromType($typeFromPayload);
57 67
            } elseif ($constraint instanceof Assert\Type) {
58 19
                $doc = $this->getDocFromType(strtolower($constraint->type));
59
            }
60
61 68
            if (null !== $doc) {
62 68
                break;
63
            }
64
        }
65
66 69
        return $doc;
67
    }
68
69
    /**
70
     * @param array $constraintList
71
     *
72
     * @return TypeDoc|null
73
     */
74 52
    private function guessTypeFromConstraintList(array $constraintList)
75
    {
76 52
        $doc = $abstractTypeFound = null;
77 52
        foreach ($constraintList as $constraint) {
78 51
            $doc = $this->guessTypeFromConstraint($constraint);
79 51
            if (null !== $doc) {
80 50
                if ($this->isAbstractType($doc)) {
81
                     // Abstract type => continue to see if better type can be found
82 14
                     $abstractTypeFound = $doc;
83 14
                     $doc = null;
84
                } else {
85 51
                     break;
86
                }
87
            }
88
        }
89
        // Try to fallback on abstractType if found
90 52
        if (null === $doc && null !== $abstractTypeFound) {
91 10
            $doc = $abstractTypeFound;
92
        }
93
94 52
        return $doc;
95
    }
96
97
    /**
98
     * @param Constraint $constraint
99
     *
100
     * @return TypeDoc|null
101
     */
102 51
    private function guessTypeFromConstraint(Constraint $constraint)
103
    {
104
        // Try to guess primary types
105
        switch (true) {
106 51
            case $constraint instanceof Assert\Existence:
0 ignored issues
show
Coding Style introduced by
case statements should be defined using a colon.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B"; //wrong
        doSomething();
        break;
    case "C": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
107 2
                return $this->guess($constraint->constraints);
108 49
            case $constraint instanceof Assert\Length:// << Applied on string only
0 ignored issues
show
Coding Style introduced by
case statements should be defined using a colon.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B"; //wrong
        doSomething();
        break;
    case "C": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
109 48
            case $constraint instanceof Assert\Date: // << validator expect a string with specific format
0 ignored issues
show
Coding Style introduced by
case statements should be defined using a colon.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B"; //wrong
        doSomething();
        break;
    case "C": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
110 47
            case $constraint instanceof Assert\Time: // << validator expect a string with specific format
0 ignored issues
show
Coding Style introduced by
case statements should be defined using a colon.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B"; //wrong
        doSomething();
        break;
    case "C": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
111 46
            case $constraint instanceof Assert\Bic:
112 45
            case $constraint instanceof Assert\CardScheme:
113 44
            case $constraint instanceof Assert\Country:
114 43
            case $constraint instanceof Assert\Currency:
115 42
            case $constraint instanceof Assert\Email:
116 41
            case $constraint instanceof Assert\File:
117 40
            case $constraint instanceof Assert\Iban:
118 39
            case $constraint instanceof Assert\Ip:
119 38
            case $constraint instanceof Assert\Isbn:
120 37
            case $constraint instanceof Assert\Issn:
121 36
            case $constraint instanceof Assert\Language:
122 35
            case $constraint instanceof Assert\Locale:
123 34
            case $constraint instanceof Assert\Luhn:
124 33
            case $constraint instanceof Assert\Regex:
125 32
            case $constraint instanceof Assert\Url:
126 31
            case $constraint instanceof Assert\Uuid:
127 20
                return new StringDoc();
128 30
            case $constraint instanceof Assert\DateTime:
129 6
                if ('U' === $constraint->format) {
130 5
                    return new ScalarDoc();// Don't know if value will be an number as string or as integer
131
                }
132
133 1
                return new StringDoc();
134 26
            case $constraint instanceof Assert\IsTrue:
135 24
            case $constraint instanceof Assert\IsFalse:
136 3
                return new BooleanDoc();
137 23
            case $constraint instanceof Assert\Collection:
138
                // If only integer => array, else object
139 2
                $integerKeyList = array_filter(array_keys($constraint->fields), 'is_int');
140 2
                if (count($constraint->fields) === count($integerKeyList)) {
141 1
                    return new ArrayDoc();
142
                }
143
144 1
                return new ObjectDoc();
145 21
            case $constraint instanceof Assert\Choice
146 21
                && true === $constraint->multiple: // << expect an array multiple choices
147 20
            case $constraint instanceof Assert\All: // << Applied only on array
148 2
                return new ArrayDoc();
149
        }
150
151
        // If primary type is still not defined
152
        switch (true) {
153 19
            case $constraint instanceof Assert\Range:
154 8
                if ((null !== $constraint->min && is_float($constraint->min))
0 ignored issues
show
introduced by
The condition is_float($constraint->min) is always false.
Loading history...
155 8
                    || (null !== $constraint->max && is_float($constraint->max))
156
                ) {
157 4
                    return new FloatDoc();
158
                }
159
160 4
                return new NumberDoc();
161 13
            case $constraint instanceof Assert\GreaterThan:
162 9
            case $constraint instanceof Assert\GreaterThanOrEqual:
163 7
            case $constraint instanceof Assert\LessThan:
164 5
            case $constraint instanceof Assert\LessThanOrEqual:
165 10
                if (null !== $constraint->value && is_float($constraint->value)) {
166 6
                    return new FloatDoc();
167
                }
168
169 4
                return new NumberDoc();
170 3
            case $constraint instanceof Assert\Count:
171 2
                return new CollectionDoc();
172
        }
173
174 1
        return null;
175
    }
176
177
    /**
178
     * @param string $type
179
     *
180
     * @return TypeDoc|null
181
     */
182 20
    private function getDocFromType(string $type)
183
    {
184
        switch (true) {
185 20
            case 'scalar' === $type:
186 1
                return new ScalarDoc();
187 19
            case 'string' === $type:
188 1
                return new StringDoc();
189 18
            case 'bool' === $type || 'boolean' === $type:
190 2
                return new BooleanDoc();
191 16
            case 'int' === $type || 'integer' === $type:
192 3
                return new IntegerDoc();
193 13
            case in_array($type, ['float', 'long', 'double', 'real', 'numeric']):
194 7
                return new FloatDoc();
195 6
            case 'array' === $type:
196 3
                return new ArrayDoc();
197 3
            case 'object' === $type:
198 2
                return new ObjectDoc();
199
        }
200
201 1
        return null;
202
    }
203
204
    /**
205
     * @param TypeDoc $doc
206
     *
207
     * @return bool
208
     */
209 50
    private function isAbstractType(TypeDoc $doc) : bool
210
    {
211
        // use get_class to avoid inheritance issue
212 50
        $class = get_class($doc);
213
214 50
        return CollectionDoc::class === $class
215 48
            || NumberDoc::class === $class
216 50
            || ScalarDoc::class === $class
217
        ;
218
    }
219
}
220