1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace SwaggerGen\Swagger\Type; |
4
|
|
|
|
5
|
|
|
/** |
6
|
|
|
* Basic object type definition. |
7
|
|
|
* |
8
|
|
|
* @package SwaggerGen |
9
|
|
|
* @author Martijn van der Lee <[email protected]> |
10
|
|
|
* @copyright 2014-2015 Martijn van der Lee |
11
|
|
|
* @license https://opensource.org/licenses/MIT MIT |
12
|
|
|
*/ |
13
|
|
|
class ObjectType extends AbstractType |
14
|
|
|
{ |
15
|
|
|
|
16
|
|
|
const REGEX_OBJECT_CONTENT = '(?:(\{)(.*)\})?'; |
17
|
|
|
const REGEX_PROP_START = '/^'; |
18
|
|
|
const REGEX_PROP_NAME = '([^?!:]+)'; |
19
|
|
|
const REGEX_PROP_REQUIRED = '([\?!])?'; |
20
|
|
|
const REGEX_PROP_ASSIGN = ':'; |
21
|
|
|
const REGEX_PROP_DEFINITION = '(.+)'; |
22
|
|
|
const REGEX_PROP_ADDITIONAL = '\.\.\.(!|.+)?'; |
23
|
|
|
const REGEX_PROP_END = '$/'; |
24
|
|
|
|
25
|
|
|
private $minProperties = null; |
26
|
|
|
private $maxProperties = null; |
27
|
|
|
private $discriminator = null; |
28
|
|
|
private $additionalProperties = null; |
29
|
|
|
private $required = array(); |
30
|
|
|
|
31
|
|
|
/** |
32
|
|
|
* @var Property[] |
33
|
|
|
*/ |
34
|
|
|
private $properties = array(); |
35
|
|
|
|
36
|
|
|
/** |
37
|
|
|
* @var Property |
38
|
|
|
*/ |
39
|
|
|
private $mostRecentProperty = null; |
40
|
|
|
|
41
|
|
|
protected function parseDefinition($definition) |
42
|
|
|
{ |
43
|
|
|
$definition = self::trim($definition); |
44
|
|
|
|
45
|
|
|
$match = array(); |
46
|
|
|
if (preg_match(self::REGEX_START . self::REGEX_FORMAT . self::REGEX_CONTENT . self::REGEX_RANGE . self::REGEX_END, $definition, $match) === 1) { |
|
|
|
|
47
|
|
|
// recognized format |
48
|
|
|
} elseif (preg_match(self::REGEX_START . self::REGEX_OBJECT_CONTENT . self::REGEX_RANGE . self::REGEX_END, $definition, $match) === 1) { |
49
|
|
|
$match[1] = 'object'; |
50
|
|
|
} else { |
51
|
|
|
throw new \SwaggerGen\Exception("Unparseable object definition: '{$definition}'"); |
52
|
|
|
} |
53
|
|
|
|
54
|
|
|
|
55
|
|
|
$this->parseFormat($definition, $match); |
56
|
|
|
$this->parseProperties($definition, $match); |
57
|
|
|
$this->parseRange($definition, $match); |
58
|
|
|
} |
59
|
|
|
|
60
|
|
|
private function parseFormat($definition, $match) |
61
|
|
|
{ |
62
|
|
|
if (strtolower($match[1]) !== 'object') { |
63
|
|
|
throw new \SwaggerGen\Exception("Not an object: '{$definition}'"); |
64
|
|
|
} |
65
|
|
|
} |
66
|
|
|
|
67
|
|
|
/** |
68
|
|
|
* @param AbstractType|bool $type |
69
|
|
|
* @return void |
70
|
|
|
*/ |
71
|
|
|
private function setAdditionalProperties($type) |
72
|
|
|
{ |
73
|
|
|
if ($this->additionalProperties !== null) { |
74
|
|
|
throw new \SwaggerGen\Exception('Additional properties may only be set once'); |
75
|
|
|
} |
76
|
|
|
$this->additionalProperties = $type; |
77
|
|
|
} |
78
|
|
|
|
79
|
|
|
private function parseProperties($definition, $match) |
|
|
|
|
80
|
|
|
{ |
81
|
|
|
if (empty($match[2])) { |
82
|
|
|
return; |
83
|
|
|
} |
84
|
|
|
|
85
|
|
|
while (($property = self::parseListItem($match[2])) !== '') { |
86
|
|
|
$prop_match = array(); |
87
|
|
|
if (preg_match(self::REGEX_PROP_START . self::REGEX_PROP_ADDITIONAL . self::REGEX_PROP_END, $property, $prop_match) === 1) { |
88
|
|
|
if (empty($prop_match[1])) { |
89
|
|
|
$this->setAdditionalProperties(true); |
90
|
|
|
} else if ($prop_match[1] === '!') { |
91
|
|
|
$this->setAdditionalProperties(false); |
92
|
|
|
} else { |
93
|
|
|
$this->setAdditionalProperties(self::typeFactory($this, $prop_match[1], "Unparseable additional properties definition: '...%s'")); |
|
|
|
|
94
|
|
|
} |
95
|
|
|
continue; |
96
|
|
|
} |
97
|
|
|
if (preg_match(self::REGEX_PROP_START . self::REGEX_PROP_NAME . self::REGEX_PROP_REQUIRED . self::REGEX_PROP_ASSIGN . self::REGEX_PROP_DEFINITION . self::REGEX_PROP_END, $property, $prop_match) !== 1) { |
98
|
|
|
throw new \SwaggerGen\Exception("Unparseable property definition: '{$property}'"); |
99
|
|
|
} |
100
|
|
|
$this->properties[$prop_match[1]] = new Property($this, $prop_match[3]); |
101
|
|
|
if ($prop_match[2] !== '!' && $prop_match[2] !== '?') { |
102
|
|
|
$this->required[$prop_match[1]] = true; |
103
|
|
|
} |
104
|
|
|
} |
105
|
|
|
|
106
|
|
|
} |
107
|
|
|
|
108
|
|
|
private function parseRange($definition, $match) |
109
|
|
|
{ |
110
|
|
|
if (!empty($match[3])) { |
111
|
|
|
if ($match[4] === '' && $match[5] === '') { |
112
|
|
|
throw new \SwaggerGen\Exception("Empty object range: '{$definition}'"); |
113
|
|
|
} |
114
|
|
|
|
115
|
|
|
$exclusiveMinimum = isset($match[3]) ? ($match[3] == '<') : null; |
116
|
|
|
$this->minProperties = $match[4] === '' ? null : intval($match[4]); |
117
|
|
|
$this->maxProperties = $match[5] === '' ? null : intval($match[5]); |
118
|
|
|
$exclusiveMaximum = isset($match[6]) ? ($match[6] == '>') : null; |
119
|
|
|
if ($this->minProperties && $this->maxProperties && $this->minProperties > $this->maxProperties) { |
120
|
|
|
self::swap($this->minProperties, $this->maxProperties); |
121
|
|
|
self::swap($exclusiveMinimum, $exclusiveMaximum); |
122
|
|
|
} |
123
|
|
|
$this->minProperties = $this->minProperties === null ? null : max(0, $exclusiveMinimum ? $this->minProperties + 1 : $this->minProperties); |
124
|
|
|
$this->maxProperties = $this->maxProperties === null ? null : max(0, $exclusiveMaximum ? $this->maxProperties - 1 : $this->maxProperties); |
125
|
|
|
} |
126
|
|
|
} |
127
|
|
|
|
128
|
|
|
private function setDiscriminator($discriminator) |
129
|
|
|
{ |
130
|
|
|
if (!empty($this->discriminator)) { |
131
|
|
|
throw new \SwaggerGen\Exception("Discriminator may only be set once, " |
132
|
|
|
. "trying to change it " |
133
|
|
|
. "from '{$this->discriminator}' " |
134
|
|
|
. "to '{$discriminator}'"); |
135
|
|
|
} |
136
|
|
|
if (isset($this->properties[$discriminator]) && empty($this->required[$discriminator])) { |
137
|
|
|
throw new \SwaggerGen\Exception("Discriminator must be a required property, " |
138
|
|
|
. "property '{$discriminator}' is not required"); |
139
|
|
|
} |
140
|
|
|
$this->discriminator = $discriminator; |
141
|
|
|
} |
142
|
|
|
|
143
|
|
|
/** |
144
|
|
|
* @param string $command The comment command |
145
|
|
|
* @param string $data Any data added after the command |
146
|
|
|
* @return \SwaggerGen\Swagger\Type\AbstractType|boolean |
147
|
|
|
*/ |
148
|
|
|
public function handleCommand($command, $data = null) |
149
|
|
|
{ |
150
|
|
|
switch (strtolower($command)) { |
151
|
|
|
// type name description... |
152
|
|
|
case 'additionalproperties': |
153
|
|
|
$value = self::wordShift($data); |
154
|
|
|
if ($value === 'false') { |
155
|
|
|
$type = false; |
156
|
|
|
} else if ($value === 'true') { |
157
|
|
|
$type = true; |
158
|
|
|
} else { |
159
|
|
|
$type = self::typeFactory($this, $value, "Unparseable additional properties definition: '%s'"); |
|
|
|
|
160
|
|
|
} |
161
|
|
|
$this->setAdditionalProperties($type); |
162
|
|
|
return $this; |
163
|
|
|
case 'discriminator': |
164
|
|
|
$discriminator = self::wordShift($data); |
165
|
|
|
$this->setDiscriminator($discriminator); |
166
|
|
|
return $this; |
167
|
|
|
case 'property': |
168
|
|
|
case 'property?': |
169
|
|
|
case 'property!': |
170
|
|
|
$definition = self::wordShift($data); |
171
|
|
|
if (empty($definition)) { |
172
|
|
|
throw new \SwaggerGen\Exception("Missing property definition"); |
173
|
|
|
} |
174
|
|
|
|
175
|
|
|
$name = self::wordShift($data); |
176
|
|
|
if (empty($name)) { |
177
|
|
|
throw new \SwaggerGen\Exception("Missing property name: '{$definition}'"); |
178
|
|
|
} |
179
|
|
|
|
180
|
|
|
$readOnly = null; |
181
|
|
|
$required = false; |
182
|
|
|
$propertySuffix = substr($command, -1); |
183
|
|
|
if ($propertySuffix === '!') { |
184
|
|
|
$readOnly = true; |
185
|
|
|
} else if ($propertySuffix !== '?') { |
186
|
|
|
$required = true; |
187
|
|
|
} |
188
|
|
|
|
189
|
|
|
if (($name === $this->discriminator) && !$required) { |
190
|
|
|
throw new \SwaggerGen\Exception("Discriminator must be a required property, " |
191
|
|
|
. "property '{$name}' is not required"); |
192
|
|
|
} |
193
|
|
|
|
194
|
|
|
unset($this->required[$name]); |
195
|
|
|
if ($required) { |
196
|
|
|
$this->required[$name] = true; |
197
|
|
|
} |
198
|
|
|
|
199
|
|
|
$this->mostRecentProperty = new Property($this, $definition, $data, $readOnly); |
200
|
|
|
$this->properties[$name] = $this->mostRecentProperty; |
201
|
|
|
return $this; |
202
|
|
|
|
203
|
|
|
case 'min': |
204
|
|
|
$this->minProperties = intval($data); |
205
|
|
|
if ($this->minProperties < 0) { |
206
|
|
|
throw new \SwaggerGen\Exception("Minimum less than zero: '{$data}'"); |
207
|
|
|
} |
208
|
|
|
if ($this->maxProperties !== null && $this->minProperties > $this->maxProperties) { |
209
|
|
|
throw new \SwaggerGen\Exception("Minimum greater than maximum: '{$data}'"); |
210
|
|
|
} |
211
|
|
|
$this->minProperties = intval($data); |
212
|
|
|
return $this; |
213
|
|
|
|
214
|
|
|
case 'max': |
215
|
|
|
$this->maxProperties = intval($data); |
216
|
|
|
if ($this->minProperties !== null && $this->minProperties > $this->maxProperties) { |
217
|
|
|
throw new \SwaggerGen\Exception("Maximum less than minimum: '{$data}'"); |
218
|
|
|
} |
219
|
|
|
if ($this->maxProperties < 0) { |
220
|
|
|
throw new \SwaggerGen\Exception("Maximum less than zero: '{$data}'"); |
221
|
|
|
} |
222
|
|
|
return $this; |
223
|
|
|
} |
224
|
|
|
|
225
|
|
|
// Pass through to most recent Property |
226
|
|
|
if ($this->mostRecentProperty && $this->mostRecentProperty->handleCommand($command, $data)) { |
227
|
|
|
return $this; |
228
|
|
|
} |
229
|
|
|
|
230
|
|
|
return parent::handleCommand($command, $data); |
231
|
|
|
} |
232
|
|
|
|
233
|
|
|
public function toArray() |
234
|
|
|
{ |
235
|
|
|
return self::arrayFilterNull(array_merge(array( |
236
|
|
|
'type' => 'object', |
237
|
|
|
'required' => array_keys($this->required), |
238
|
|
|
'properties' => self::objectsToArray($this->properties), |
239
|
|
|
'minProperties' => $this->minProperties, |
240
|
|
|
'maxProperties' => $this->maxProperties, |
241
|
|
|
'discriminator' => $this->discriminator, |
242
|
|
|
'additionalProperties' => $this->additionalProperties instanceof AbstractType ? $this->additionalProperties->toArray() : $this->additionalProperties, |
243
|
|
|
), parent::toArray())); |
244
|
|
|
} |
245
|
|
|
|
246
|
|
|
public function __toString() |
247
|
|
|
{ |
248
|
|
|
return __CLASS__; |
249
|
|
|
} |
250
|
|
|
|
251
|
|
|
} |
252
|
|
|
|
This check looks for the bodies of
if
statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.These
if
bodies can be removed. If you have an empty if but statements in theelse
branch, consider inverting the condition.could be turned into
This is much more concise to read.