Completed
Push — master ( 8b636b...7c2d20 )
by Martijn
13s
created

ObjectType   C

Complexity

Total Complexity 55

Size/Duplication

Total Lines 202
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 3

Importance

Changes 0
Metric Value
dl 0
loc 202
rs 6.8
c 0
b 0
f 0
wmc 55
lcom 1
cbo 3

8 Methods

Rating   Name   Duplication   Size   Complexity  
A parseDefinition() 0 18 3
A parseFormat() 0 6 2
B parseProperties() 0 17 7
C parseRange() 0 19 15
A setDiscriminator() 0 14 4
C handleCommand() 0 73 22
A toArray() 0 11 1
A __toString() 0 4 1

How to fix   Complexity   

Complex Class

Complex classes like ObjectType often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use ObjectType, and based on these observations, apply Extract Interface, too.

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_END = '$/';
23
24
	private $minProperties = null;
25
	private $maxProperties = null;
26
	private $discriminator = null;
27
	private $required = array();
28
29
	/**
30
	 * @var Property[]
31
	 */
32
	private $properties = array();
33
34
	/**
35
	 * @var Property
36
	 */
37
	private $mostRecentProperty = null;
38
39
	protected function parseDefinition($definition)
40
	{
41
		$definition = self::trim($definition);
42
43
		$match = array();
44
		if (preg_match(self::REGEX_START . self::REGEX_FORMAT . self::REGEX_CONTENT . self::REGEX_RANGE . self::REGEX_END, $definition, $match) === 1) {
0 ignored issues
show
Unused Code introduced by
This if statement is empty and can be removed.

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 the else branch, consider inverting the condition.

if (rand(1, 6) > 3) {
//print "Check failed";
} else {
    print "Check succeeded";
}

could be turned into

if (rand(1, 6) <= 3) {
    print "Check succeeded";
}

This is much more concise to read.

Loading history...
45
			// recognized format
46
		} elseif (preg_match(self::REGEX_START . self::REGEX_OBJECT_CONTENT . self::REGEX_RANGE . self::REGEX_END, $definition, $match) === 1) {
47
			$match[1] = 'object';
48
		} else {
49
			throw new \SwaggerGen\Exception("Unparseable object definition: '{$definition}'");
50
		}
51
52
53
		$this->parseFormat($definition, $match);
54
		$this->parseProperties($definition, $match);
55
		$this->parseRange($definition, $match);
56
	}
57
58
	private function parseFormat($definition, $match)
59
	{
60
		if (strtolower($match[1]) !== 'object') {
61
			throw new \SwaggerGen\Exception("Not an object: '{$definition}'");
62
		}
63
	}
64
65
	private function parseProperties($definition, $match)
1 ignored issue
show
Unused Code introduced by
The parameter $definition is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
66
	{
67
		if (!empty($match[2])) {
68
			do {
69
				if (($property = self::parseListItem($match[2])) !== '') {
70
					$prop_match = array();
71
					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) {
72
						throw new \SwaggerGen\Exception("Unparseable property definition: '{$property}'");
73
					}
74
					$this->properties[$prop_match[1]] = new Property($this, $prop_match[3]);
75
					if ($prop_match[2] !== '!' && $prop_match[2] !== '?') {
76
						$this->required[$prop_match[1]] = true;
77
					}
78
				}
79
			} while ($property !== '');
80
		}
81
	}
82
83
	private function parseRange($definition, $match)
84
	{
85
		if (!empty($match[3])) {
86
			if ($match[4] === '' && $match[5] === '') {
87
				throw new \SwaggerGen\Exception("Empty object range: '{$definition}'");
88
			}
89
90
			$exclusiveMinimum = isset($match[3]) ? ($match[3] == '<') : null;
91
			$this->minProperties = $match[4] === '' ? null : intval($match[4]);
92
			$this->maxProperties = $match[5] === '' ? null : intval($match[5]);
93
			$exclusiveMaximum = isset($match[6]) ? ($match[6] == '>') : null;
94
			if ($this->minProperties && $this->maxProperties && $this->minProperties > $this->maxProperties) {
95
				self::swap($this->minProperties, $this->maxProperties);
96
				self::swap($exclusiveMinimum, $exclusiveMaximum);
97
			}
98
			$this->minProperties = $this->minProperties === null ? null : max(0, $exclusiveMinimum ? $this->minProperties + 1 : $this->minProperties);
99
			$this->maxProperties = $this->maxProperties === null ? null : max(0, $exclusiveMaximum ? $this->maxProperties - 1 : $this->maxProperties);
100
		}
101
	}
102
103
	private function setDiscriminator($discriminator)
104
	{
105
		if (!empty($this->discriminator)) {
106
			throw new \SwaggerGen\Exception("Discriminator may only be set once, "
107
			                                . "trying to change it "
108
			                                . "from '{$this->discriminator}' "
109
			                                . "to '{$discriminator}'");
110
		}
111
		if (isset($this->properties[$discriminator]) && empty($this->required[$discriminator])) {
112
			throw new \SwaggerGen\Exception("Discriminator must be a required property, "
113
			                                . "property '{$discriminator}' is not required");
114
		}
115
		$this->discriminator = $discriminator;
116
	}
117
118
	/**
119
	 * @param string $command The comment command
120
	 * @param string $data Any data added after the command
121
	 * @return \SwaggerGen\Swagger\Type\AbstractType|boolean
122
	 */
123
	public function handleCommand($command, $data = null)
124
	{
125
		switch (strtolower($command)) {
126
			// type name description...
127
			case 'discriminator':
128
				$discriminator = self::wordShift($data);
129
				$this->setDiscriminator($discriminator);
130
				return $this;
131
			case 'property':
132
			case 'property?':
133
			case 'property!':
134
				$definition = self::wordShift($data);
135
				if (empty($definition)) {
136
					throw new \SwaggerGen\Exception("Missing property definition");
137
				}
138
139
				$name = self::wordShift($data);
140
				if (empty($name)) {
141
					throw new \SwaggerGen\Exception("Missing property name: '{$definition}'");
142
				}
143
144
				$readOnly = null;
145
				$required = false;
146
				$propertySuffix = substr($command, -1);
147
				if ($propertySuffix === '!') {
148
					$readOnly = true;
149
				} else if ($propertySuffix !== '?') {
150
					$required = true;
151
				}
152
153
				if (($name === $this->discriminator) && !$required) {
154
					throw new \SwaggerGen\Exception("Discriminator must be a required property, "
155
					                                . "property '{$name}' is not required");
156
				}
157
158
				unset($this->required[$name]);
159
				if ($required) {
160
					$this->required[$name] = true;
161
				}
162
163
				$this->mostRecentProperty = new Property($this, $definition, $data, $readOnly);
164
				$this->properties[$name] = $this->mostRecentProperty;
165
				return $this;
166
167
			case 'min':
168
				$this->minProperties = intval($data);
169
				if ($this->minProperties < 0) {
170
					throw new \SwaggerGen\Exception("Minimum less than zero: '{$data}'");
171
				}
172
				if ($this->maxProperties !== null && $this->minProperties > $this->maxProperties) {
173
					throw new \SwaggerGen\Exception("Minimum greater than maximum: '{$data}'");
174
				}
175
				$this->minProperties = intval($data);
176
				return $this;
177
178
			case 'max':
179
				$this->maxProperties = intval($data);
180
				if ($this->minProperties !== null && $this->minProperties > $this->maxProperties) {
181
					throw new \SwaggerGen\Exception("Maximum less than minimum: '{$data}'");
182
				}
183
				if ($this->maxProperties < 0) {
184
					throw new \SwaggerGen\Exception("Maximum less than zero: '{$data}'");
185
				}
186
				return $this;
187
		}
188
189
		// Pass through to most recent Property
190
		if ($this->mostRecentProperty && $this->mostRecentProperty->handleCommand($command, $data)) {
191
			return $this;
192
		}
193
194
		return parent::handleCommand($command, $data);
195
	}
196
197
	public function toArray()
198
	{
199
		return self::arrayFilterNull(array_merge(array(
200
					'type' => 'object',
201
					'required' => array_keys($this->required),
202
					'properties' => self::objectsToArray($this->properties),
203
					'minProperties' => $this->minProperties,
204
					'maxProperties' => $this->maxProperties,
205
					'discriminator' => $this->discriminator,
206
								), parent::toArray()));
207
	}
208
209
	public function __toString()
210
	{
211
		return __CLASS__;
212
	}
213
214
}
215