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: |
|
|
|
|
107
|
2 |
|
return $this->guess($constraint->constraints); |
108
|
49 |
|
case $constraint instanceof Assert\Length:// << Applied on string only |
|
|
|
|
109
|
48 |
|
case $constraint instanceof Assert\Date: // << validator expect a string with specific format |
|
|
|
|
110
|
47 |
|
case $constraint instanceof Assert\Time: // << validator expect a string with specific format |
|
|
|
|
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)) |
|
|
|
|
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
|
|
|
|
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.
To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.