Completed
Push — master ( 032827...a51b13 )
by Martijn
02:15
created

Swagger::getSecurity()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 4
nc 2
nop 1
dl 0
loc 8
rs 9.4285
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
	private $definitions = array();
33
34
	/**
35
	 * @var Tag[] $Tags
36
	 */
37
	private $Tags = array();
38
39
	/**
40
	 * Default tag for new endpoints/operations. Set by the api command.
41
	 * @var Tag
42
	 */
43
	private $defaultTag = null;
44
	private $securityDefinitions = array();
45
	private $security = array();
46
47
	/**
48
	 * @inheritDoc
49
	 * @param string $host
50
	 * @param string $basePath
51
	 */
52
	public function __construct($host = null, $basePath = null)
53
	{
54
		parent::__construct(null);
55
56
		$this->host = $host;
57
		$this->basePath = $basePath;
58
59
		$this->Info = new Info($this);
60
	}
61
62
	/**
63
	 * @inheritDoc
64
	 */
65
	protected function getRoot()
66
	{
67
		return $this;
68
	}
69
70
	/**
71
	 * Return all consumes
72
	 * @todo Deprecate in favour of a getConsume($name);
73
	 * @return string
74
	 */
75
	public function getConsumes()
76
	{
77
		return $this->consumes;
78
	}
79
80
	/**
81
	 * Return the named security if it exists. Otherwise return FALSE
82
	 * @param string $name
83
	 * @return boolean|SecurityScheme
84
	 */
85
	public function getSecurity($name)
86
	{
87
		if (isset($this->securityDefinitions[$name])) {
88
			return $this->securityDefinitions[$name];
89
		}
90
91
		return false;
92
	}
93
94
	/**
95
	 * @param string $command
96
	 * @param string $data
97
	 * @return \SwaggerGen\Swagger\AbstractObject|boolean
98
	 */
99
	public function handleCommand($command, $data = null)
100
	{
101
		switch (strtolower($command)) {
102
			// pass to Info
103
			case 'title':
104
			case 'description':
105
			case 'version':
106
			case 'terms': // alias
107
			case 'tos': // alias
108
			case 'termsofservice':
109
			case 'contact':
110
			case 'license':
111
				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...
112
113
			// string[]
114
			case 'scheme':
115
			case 'schemes':
116
				$this->schemes = array_unique(array_merge($this->schemes, self::wordSplit($data)));
117
				return $this;
118
119
			// MIME[]
120
			case 'consume':
121
			case 'consumes':
122
				$this->consumes = array_merge($this->consumes, self::translateMimeTypes(self::wordSplit($data)));
123
				return $this;
124
				
125
			case 'produce':
126 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...
127
				$this->produces = array_merge($this->produces, self::translateMimeTypes(self::wordSplit($data)));
128
				return $this;
129
130
			case 'model': // alias
131
				$data = 'params ' . $data;
132
			// Fallthrough intentional
133
			case 'define':
134
			case 'definition':
135
				$type = self::wordShift($data);
136
				switch ($type) {
137
					case 'params':
138
					case 'parameters': // alias
139
						$definition = new Schema($this);
140
						break;
141
142
					default:
143
						throw new \SwaggerGen\Exception("Unsupported definition type: '{$type}'");
144
				}
145
146
				$name = self::wordShift($data);
147
				if (empty($name)) {
148
					throw new \SwaggerGen\Exception('Missing definition name');
149
				}
150
				$this->definitions[$name] = $definition;
151
				return $definition;
152
153
			case 'api': // alias
154
			case 'tag':
155
				$tagname = self::wordShift($data);
156
				if (empty($tagname)) {
157
					throw new \SwaggerGen\Exception('Missing tag name');
158
				}
159
160
				$Tag = null;
161
				foreach ($this->Tags as $T) {
162
					if ($T->getName() === $tagname) {
163
						$Tag = $T;
164
						break;
165
					}
166
				}
167
				if (!$Tag) {
168
					$Tag = new Tag($this, $tagname, $data);
169
					$this->Tags[] = $Tag;
170
				}
171
172
				if ($command === 'api') { // backwards compatibility
173
					$this->defaultTag = $Tag;
174
				}
175
				return $Tag;
176
177
			case 'endpoint':
178
				$path = self::wordShift($data);
179
				if ($path{0} !== '/') {
180
					$path = '/' . $path;
181
				}
182
183
				$Tag = null;
184
				if (($tagname = self::wordShift($data)) !== false) {
185
					foreach ($this->Tags as $T) {
186
						if (strtolower($T->getName()) === strtolower($tagname)) {
187
							$Tag = $T;
188
							break;
189
						}
190
					}
191
					if (!$Tag) {
192
						$Tag = new Tag($this, $tagname, $data);
193
						$this->Tags[] = $Tag;
194
					}
195
				}
196
197
				if (!isset($this->Paths[$path])) {
198
					$this->Paths[$path] = new Path($this, $Tag ?: $this->defaultTag);
199
				}
200
				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...
201
202 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...
203
				$name = self::wordShift($data);
204
				if (empty($name)) {
205
					throw new \SwaggerGen\Exception('Missing security name');
206
				}
207
				$type = self::wordShift($data);
208
				if (empty($type)) {
209
					throw new \SwaggerGen\Exception('Missing security type');
210
				}
211
				$SecurityScheme = new SecurityScheme($this, $type, $data);
212
				$this->securityDefinitions[$name] = $SecurityScheme;
213
				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...
214
215 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...
216
				$name = self::wordShift($data);
217
				if (empty($name)) {
218
					throw new \SwaggerGen\Exception('Missing require name');
219
				}
220
				$scopes = self::wordSplit($data);
221
				sort($scopes);
222
				$this->security[] = array(
223
					$name => empty($scopes) ? array() : $scopes,
224
				);
225
				return $this;
226
		}
227
228
		return parent::handleCommand($command, $data);
229
	}
230
231
	/**
232
	 * @inheritDoc
233
	 */
234
	public function toArray()
235
	{
236
		if (empty($this->Paths)) {
237
			throw new \SwaggerGen\Exception('No path defined');
238
		}
239
240
241
		$schemes = array_unique($this->schemes);
242
		sort($schemes);
243
244
		$consumes = array_unique($this->consumes);
245
		sort($consumes);
246
247
		$produces = array_unique($this->produces);
248
		sort($produces);
249
250
		foreach ($this->security as $security) {
251
			foreach ($security as $name => $scopes) {
252
				if (!isset($this->securityDefinitions[$name])) {
253
					throw new \SwaggerGen\Exception("Required security scheme not defined: '{$name}'");
254
				}
255
			}
256
		}
257
258
		return self::arrayFilterNull(array_merge(array(
259
					'swagger' => $this->swagger,
260
					'info' => $this->Info->toArray(),
261
					'host' => empty($this->host) ? null : $this->host,
262
					'basePath' => empty($this->basePath) ? null : $this->basePath,
263
					'consumes' => $consumes,
264
					'produces' => $produces,
265
					'schemes' => $schemes,
266
					'paths' => self::objectsToArray($this->Paths),
267
					'definitions' => self::objectsToArray($this->definitions),
268
					'securityDefinitions' => self::objectsToArray($this->securityDefinitions),
269
					'security' => $this->security,
270
					'tags' => self::objectsToArray($this->Tags),
271
								), parent::toArray()));
272
	}
273
274
	public function __toString()
275
	{
276
		return __CLASS__;
277
	}
278
	
279
	public function resolveReference($name) {
280
		if (isset($this->definitions[$name])) {
281
			return '#/definitions/' . $name;
282
		}
283
	}
284
285
}
286