Completed
Push — master ( 37902c...ffb897 )
by Martijn
02:31
created

Swagger::getRoot()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 0
dl 0
loc 4
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace SwaggerGen\Swagger;
4
5
/**
6
 * Object representing the root level of a Swagger 2.0 document.
7
 *
8
 * @package    SwaggerGen
9
 * @author     Martijn van der Lee <[email protected]>
10
 * @copyright  2014-2016 Martijn van der Lee
11
 * @license    https://opensource.org/licenses/MIT MIT
12
 */
13
class Swagger extends AbstractDocumentableObject
14
{
15
16
	private $swagger = '2.0';
17
	private $host;
18
	private $basePath;
19
20
	/**
21
	 * @var Info $Info
22
	 */
23
	private $info;
24
	private $schemes = array();
25
	private $consumes = array();
26
	private $produces = array();
27
28
	/**
29
	 * @var \SwaggerGen\Swagger\Path[] $Paths
30
	 */
31
	private $paths = array();
32
33
	/**
34
	 * @var \SwaggerGen\Swagger\Schema[] $definitions
35
	 */
36
	private $definitions = array();
37
38
	/**
39
	 * @var \SwaggerGen\Swagger\IParameter[] $parameters
40
	 */
41
	private $parameters = array();
42
43
	/**
44
	 * @var \SwaggerGen\Swagger\Response[] $responses
45
	 */
46
	private $responses = array();
47
48
	/**
49
	 * @var Tag[] $Tags
50
	 */
51
	private $tags = array();
52
53
	/**
54
	 * Default tag for new endpoints/operations. Set by the api command.
55
	 * @var Tag
56
	 */
57
	private $defaultTag = null;
58
	private $securityDefinitions = array();
59
	private $security = array();
60
61
	/**
62
	 * @inheritDoc
63
	 * @param string $host
64
	 * @param string $basePath
65
	 */
66
	public function __construct($host = null, $basePath = null)
67
	{
68
		parent::__construct(null);
69
70
		$this->host = $host;
71
		$this->basePath = $basePath;
72
73
		$this->info = new Info($this);
74
	}
75
76
	/**
77
	 * @inheritDoc
78
	 */
79
	protected function getSwagger()
80
	{
81
		return $this;
82
	}
83
84
	/**
85
	 * Return all consumes
86
	 * @todo Deprecate in favour of a getConsume($name);
87
	 * @return string
88
	 */
89
	public function getConsumes()
90
	{
91
		return $this->consumes;
92
	}
93
94
	/**
95
	 * Return the named security if it exists. Otherwise return FALSE
96
	 * @param string $name
97
	 * @return boolean|SecurityScheme
98
	 */
99
	public function getSecurity($name)
100
	{
101
		if (isset($this->securityDefinitions[$name])) {
102
			return $this->securityDefinitions[$name];
103
		}
104
105
		return false;
106
	}
107
108
	/**
109
	 * @param string $command
110
	 * @param string $data
111
	 * @return \SwaggerGen\Swagger\AbstractObject|boolean
112
	 */
113
	public function handleCommand($command, $data = null)
114
	{
115
		switch (strtolower($command)) {
116
			// pass to Info
117
			case 'title':
118
			case 'description':
119
			case 'version':
120
			case 'terms': // alias
121
			case 'tos': // alias
122
			case 'termsofservice':
123
			case 'contact':
124
			case 'license':
125
				return $this->info->handleCommand($command, $data);
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->info->hand...mmand($command, $data); (SwaggerGen\Swagger\Info|...n\Swagger\License|false) is incompatible with the return type of the parent method SwaggerGen\Swagger\Abstr...leObject::handleCommand of type SwaggerGen\Swagger\Exter...ocumentableObject|false.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
126
127
			// string[]
128
			case 'scheme':
129
			case 'schemes':
130
				$this->schemes = array_unique(array_merge($this->schemes, self::wordSplit($data)));
131
				return $this;
132
133
			// MIME[]
134
			case 'consume':
135
			case 'consumes':
136
				$this->consumes = array_merge($this->consumes, self::translateMimeTypes(self::wordSplit($data)));
137
				return $this;
138
139
			case 'produce':
140 View Code Duplication
			case 'produces':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
141
				$this->produces = array_merge($this->produces, self::translateMimeTypes(self::wordSplit($data)));
142
				return $this;
143
144
			case 'model': // alias
145
				$data = 'params ' . $data;
146
			// Fallthrough intentional
147
			case 'define':
148
			case 'definition':
149
				$type = self::wordShift($data);
150
				switch ($type) {
151
					case 'params':
152
					case 'parameters': // alias
153
						$definition = new Schema($this);
154
						break;
155
156
					default:
157
						throw new \SwaggerGen\Exception("Unsupported definition type: '{$type}'");
158
				}
159
160
				$name = self::wordShift($data);
161
				if (empty($name)) {
162
					throw new \SwaggerGen\Exception('Missing definition name');
163
				}
164
				$this->definitions[$name] = $definition;
165
				return $definition;
166
167 View Code Duplication
			case 'response':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
168
				$name = self::wordShift($data);
169
				$definition = self::wordShift($data);
170
				$description = $data;
171
				if (empty($description)) {
172
					throw new \SwaggerGen\Exception('Response definition missing description');
173
				}
174
				$Response = new Response($this, $name, $definition === 'null' ? null : $definition, $description);
175
				$this->responses[$name] = $Response;
176
				return $Response;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $Response; (SwaggerGen\Swagger\Response) is incompatible with the return type of the parent method SwaggerGen\Swagger\Abstr...leObject::handleCommand of type SwaggerGen\Swagger\Exter...ocumentableObject|false.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
177
178
			case 'api': // alias
179
			case 'tag':
180
				$tagname = self::wordShift($data);
181
				if (empty($tagname)) {
182
					throw new \SwaggerGen\Exception('Missing tag name');
183
				}
184
185
				$Tag = null;
186
				foreach ($this->tags as $T) {
187
					if ($T->getName() === $tagname) {
188
						$Tag = $T;
189
						break;
190
					}
191
				}
192
				if (!$Tag) {
193
					$Tag = new Tag($this, $tagname, $data);
194
					$this->tags[] = $Tag;
195
				}
196
197
				if ($command === 'api') { // backwards compatibility
198
					$this->defaultTag = $Tag;
199
				}
200
				return $Tag;
201
202
			case 'endpoint':
203
				$path = self::wordShift($data);
204
				if ($path{0} !== '/') {
205
					$path = '/' . $path;
206
				}
207
208
				$Tag = null;
209
				if (($tagname = self::wordShift($data)) !== false) {
210
					foreach ($this->tags as $T) {
211
						if (strtolower($T->getName()) === strtolower($tagname)) {
212
							$Tag = $T;
213
							break;
214
						}
215
					}
216
					if (!$Tag) {
217
						$Tag = new Tag($this, $tagname, $data);
218
						$this->tags[] = $Tag;
219
					}
220
				}
221
222
				if (!isset($this->paths[$path])) {
223
					$this->paths[$path] = new Path($this, $Tag ?: $this->defaultTag);
224
				}
225
				return $this->paths[$path];
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->paths[$path]; (SwaggerGen\Swagger\Path) is incompatible with the return type of the parent method SwaggerGen\Swagger\Abstr...leObject::handleCommand of type SwaggerGen\Swagger\Exter...ocumentableObject|false.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
226
227 View Code Duplication
			case 'security':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
228
				$name = self::wordShift($data);
229
				if (empty($name)) {
230
					throw new \SwaggerGen\Exception('Missing security name');
231
				}
232
				$type = self::wordShift($data);
233
				if (empty($type)) {
234
					throw new \SwaggerGen\Exception('Missing security type');
235
				}
236
				$SecurityScheme = new SecurityScheme($this, $type, $data);
237
				$this->securityDefinitions[$name] = $SecurityScheme;
238
				return $SecurityScheme;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $SecurityScheme; (SwaggerGen\Swagger\SecurityScheme) is incompatible with the return type of the parent method SwaggerGen\Swagger\Abstr...leObject::handleCommand of type SwaggerGen\Swagger\Exter...ocumentableObject|false.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
239
240 View Code Duplication
			case 'require':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
241
				$name = self::wordShift($data);
242
				if (empty($name)) {
243
					throw new \SwaggerGen\Exception('Missing require name');
244
				}
245
				$scopes = self::wordSplit($data);
246
				sort($scopes);
247
				$this->security[] = array(
248
					$name => empty($scopes) ? array() : $scopes,
249
				);
250
				return $this;
251
		}
252
253
		return parent::handleCommand($command, $data);
254
	}
255
256
	/**
257
	 * @inheritDoc
258
	 */
259
	public function toArray()
260
	{
261
		if (empty($this->paths)) {
262
			throw new \SwaggerGen\Exception('No path defined');
263
		}
264
265
266
		$schemes = array_unique($this->schemes);
267
		sort($schemes);
268
269
		$consumes = array_unique($this->consumes);
270
		sort($consumes);
271
272
		$produces = array_unique($this->produces);
273
		sort($produces);
274
275
		foreach ($this->security as $security) {
276
			foreach ($security as $name => $scopes) {
277
				if (!isset($this->securityDefinitions[$name])) {
278
					throw new \SwaggerGen\Exception("Required security scheme not defined: '{$name}'");
279
				}
280
			}
281
		}
282
283
		return self::arrayFilterNull(array_merge(array(
284
					'swagger' => $this->swagger,
285
					'info' => $this->info->toArray(),
286
					'host' => empty($this->host) ? null : $this->host,
287
					'basePath' => empty($this->basePath) ? null : $this->basePath,
288
					'consumes' => $consumes,
289
					'produces' => $produces,
290
					'schemes' => $schemes,
291
					'paths' => self::objectsToArray($this->paths),
292
					'definitions' => self::objectsToArray($this->definitions),
293
					'parameters' => self::objectsToArray($this->parameters),
294
					'responses' => self::objectsToArray($this->responses),
295
					'securityDefinitions' => self::objectsToArray($this->securityDefinitions),
296
					'security' => $this->security,
297
					'tags' => self::objectsToArray($this->tags),
298
								), parent::toArray()));
299
	}
300
301
	public function __toString()
302
	{
303
		return __CLASS__;
304
	}
305
306
	/**
307
	 * Return a reference string for the named reference by looking it up in the
308
	 * various definitions
309
	 * 
310
	 * @param string $name
311
	 * @returnstring
312
	 */
313
	public function resolveReference($name)
314
	{
315
		if (isset($this->responses[$name])) {
316
			return '#/responses/' . $name;
317
		} elseif (isset($this->parameters[$name])) {
318
			return '#/parameters/' . $name;
319
		} elseif (isset($this->definitions[$name])) {
320
			return '#/definitions/' . $name;
321
		} else {
322
			throw new \SwaggerGen\Exception("No reference definition found for '{$name}'");
323
		}
324
	}
325
326
	/**
327
	 * Check if an operation with the given id exists.
328
	 * 
329
	 * @param string $operationId
330
	 * @return boolean
331
	 */
332
	public function hasOperationId($operationId)
333
	{
334
		foreach ($this->paths as $path) {
335
			if ($path->hasOperationId($operationId)) {
336
				return true;
337
			}
338
		}
339
340
		return false;
341
	}
342
343
}
344