Completed
Pull Request — master (#32)
by Joao
10:20
created

SwaggerSchema::getServerUrl()   B

Complexity

Conditions 6
Paths 6

Size

Total Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 21
rs 8.9617
c 0
b 0
f 0
cc 6
nc 6
nop 0
1
<?php
2
3
namespace ByJG\Swagger;
4
5
use ByJG\Swagger\Exception\DefinitionNotFoundException;
6
use ByJG\Swagger\Exception\HttpMethodNotFoundException;
7
use ByJG\Swagger\Exception\InvalidDefinitionException;
8
use ByJG\Swagger\Exception\NotMatchedException;
9
use ByJG\Swagger\Exception\PathNotFoundException;
10
use ByJG\Util\Uri;
11
12
class SwaggerSchema
13
{
14
    protected $jsonFile;
15
    protected $allowNullValues;
16
    protected $specificationVersion;
17
18
    protected $serverVariables = [];
19
20
    const SWAGGER_PATHS = "paths";
21
    const SWAGGER_PARAMETERS = "parameters";
22
    const SWAGGER_COMPONENTS = "components";
23
24
    public function __construct($jsonFile, $allowNullValues = false)
25
    {
26
        $this->jsonFile = json_decode($jsonFile, true);
27
        $this->allowNullValues = (bool) $allowNullValues;
28
        $this->specificationVersion = isset($this->jsonFile['swagger']) ? '2' : '3';
29
    }
30
31
    /**
32
     * Returns the major specification version
33
     * @return string
34
     */
35
    public function getSpecificationVersion()
36
    {
37
        return $this->specificationVersion;
38
    }
39
40
    public function getServerUrl()
41
    {
42
        if (!isset($this->jsonFile['servers'])) {
43
            return '';
44
        }
45
        $serverUrl = $this->jsonFile['servers'][0]['url'];
46
47
        if (isset($this->jsonFile['servers'][0]['variables'])) {
48
            foreach ($this->jsonFile['servers'][0]['variables'] as $var => $value) {
49
                if (!isset($this->serverVariables[$var])) {
50
                    $this->serverVariables[$var] = $value['default'];
51
                }
52
            }
53
        }
54
55
        foreach ($this->serverVariables as $var => $value) {
56
            $serverUrl = preg_replace("/\{$var\}/", $value, $serverUrl);
57
        }
58
59
        return $serverUrl;
60
    }
61
62
    public function getHttpSchema()
63
    {
64
        return isset($this->jsonFile['schemes']) ? $this->jsonFile['schemes'][0] : '';
65
    }
66
67
    public function getHost()
68
    {
69
        return isset($this->jsonFile['host']) ? $this->jsonFile['host'] : '';
70
    }
71
72
    public function getBasePath()
73
    {
74
        if ($this->getSpecificationVersion() === '3') {
75
            $uriServer = new Uri($this->getServerUrl());
76
            return $uriServer->getPath();
77
        }
78
79
        return isset($this->jsonFile['basePath']) ? $this->jsonFile['basePath'] : '';
80
    }
81
82
    /**
83
     * @param $path
84
     * @param $method
85
     * @return mixed
86
     * @throws DefinitionNotFoundException
87
     * @throws HttpMethodNotFoundException
88
     * @throws InvalidDefinitionException
89
     * @throws NotMatchedException
90
     * @throws PathNotFoundException
91
     */
92
    public function getPathDefinition($path, $method)
93
    {
94
        $method = strtolower($method);
95
96
        $path = preg_replace('~^' . $this->getBasePath() . '~', '', $path);
97
98
        $uri = new Uri($path);
99
100
        // Try direct match
101
        if (isset($this->jsonFile[self::SWAGGER_PATHS][$uri->getPath()])) {
102
            if (isset($this->jsonFile[self::SWAGGER_PATHS][$uri->getPath()][$method])) {
103
                return $this->jsonFile[self::SWAGGER_PATHS][$uri->getPath()][$method];
104
            }
105
            throw new HttpMethodNotFoundException("The http method '$method' not found in '$path'");
106
        }
107
108
        // Try inline parameter
109
        foreach (array_keys($this->jsonFile[self::SWAGGER_PATHS]) as $pathItem) {
110
            if (strpos($pathItem, '{') === false) {
111
                continue;
112
            }
113
114
            $pathItemPattern = '~^' . preg_replace('~{(.*?)}~', '(?<\1>[^/]+)', $pathItem) . '$~';
115
116
            $matches = [];
117
            if (preg_match($pathItemPattern, $uri->getPath(), $matches)) {
118
                $pathDef = $this->jsonFile[self::SWAGGER_PATHS][$pathItem];
119
                if (!isset($pathDef[$method])) {
120
                    throw new HttpMethodNotFoundException("The http method '$method' not found in '$path'");
121
                }
122
123
                $parametersPathMethod = [];
124
                $parametersPath = [];
125
126
                if (isset($pathDef[$method][self::SWAGGER_PARAMETERS])) {
127
                    $parametersPathMethod = $pathDef[$method][self::SWAGGER_PARAMETERS];
128
                }
129
130
                if (isset($pathDef[self::SWAGGER_PARAMETERS])) {
131
                    $parametersPath = $pathDef[self::SWAGGER_PARAMETERS];
132
                }
133
134
                $this->validateArguments('path', array_merge($parametersPathMethod, $parametersPath), $matches);
135
136
                return $pathDef[$method];
137
            }
138
        }
139
140
        throw new PathNotFoundException('Path "' . $path . '" not found');
141
    }
142
143
    /**
144
     * @param $parameterIn
145
     * @param $parameters
146
     * @param $arguments
147
     * @throws DefinitionNotFoundException
148
     * @throws InvalidDefinitionException
149
     * @throws NotMatchedException
150
     */
151
    private function validateArguments($parameterIn, $parameters, $arguments)
152
    {
153
        if ($this->getSpecificationVersion() === '3') {
154
            foreach ($parameters as $parameter) {
155
                if (isset($parameter['$ref'])) {
156
                    $paramParts = explode("/", $parameter['$ref']);
157
                    if (count($paramParts) != 4 || $paramParts[0] != "#" || $paramParts[1] != self::SWAGGER_COMPONENTS || $paramParts[2] != self::SWAGGER_PARAMETERS) {
158
                        throw new InvalidDefinitionException(
159
                            "Not get the reference in the expected format #/components/parameters/<NAME>"
160
                        );
161
                    }
162
                    if (!isset($this->jsonFile[self::SWAGGER_COMPONENTS][self::SWAGGER_PARAMETERS][$paramParts[3]])) {
163
                        throw new DefinitionNotFoundException(
164
                            "Not find reference #/components/parameters/${paramParts[3]}"
165
                        );
166
                    }
167
                    $parameter = $this->jsonFile[self::SWAGGER_COMPONENTS][self::SWAGGER_PARAMETERS][$paramParts[3]];
168
                }
169 View Code Duplication
                if ($parameter['in'] === $parameterIn &&
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...
170
                    $parameter['schema']['type'] === "integer"
171
                    && filter_var($arguments[$parameter['name']], FILTER_VALIDATE_INT) === false) {
172
                    throw new NotMatchedException('Path expected an integer value');
173
                }
174
            }
175
            return;
176
        }
177
178
        foreach ($parameters as $parameter) {
179 View Code Duplication
            if ($parameter['in'] === $parameterIn
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...
180
                && $parameter['type'] === "integer"
181
                && filter_var($arguments[$parameter['name']], FILTER_VALIDATE_INT) === false) {
182
                throw new NotMatchedException('Path expected an integer value');
183
            }
184
        }
185
    }
186
187
    /**
188
     * @param $name
189
     * @return mixed
190
     * @throws DefinitionNotFoundException
191
     * @throws InvalidDefinitionException
192
     */
193
    public function getDefintion($name)
194
    {
195
        $nameParts = explode('/', $name);
196
197
        if ($this->getSpecificationVersion() === '3') {
198 View Code Duplication
            if (count($nameParts) < 4 || $nameParts[0] !== '#') {
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...
199
                throw new InvalidDefinitionException('Invalid Component');
200
            }
201
202 View Code Duplication
            if (!isset($this->jsonFile[$nameParts[1]][$nameParts[2]][$nameParts[3]])) {
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
                throw new DefinitionNotFoundException("Component'$name' not found");
204
            }
205
206
            return $this->jsonFile[$nameParts[1]][$nameParts[2]][$nameParts[3]];
207
        }
208
209 View Code Duplication
        if (count($nameParts) < 3 || $nameParts[0] !== '#') {
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...
210
            throw new InvalidDefinitionException('Invalid Definition');
211
        }
212
213 View Code Duplication
        if (!isset($this->jsonFile[$nameParts[1]][$nameParts[2]])) {
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...
214
            throw new DefinitionNotFoundException("Definition '$name' not found");
215
        }
216
217
        return $this->jsonFile[$nameParts[1]][$nameParts[2]];
218
    }
219
220
    /**
221
     * @param $path
222
     * @param $method
223
     * @return SwaggerRequestBody
224
     * @throws DefinitionNotFoundException
225
     * @throws HttpMethodNotFoundException
226
     * @throws InvalidDefinitionException
227
     * @throws NotMatchedException
228
     * @throws PathNotFoundException
229
     */
230
    public function getRequestParameters($path, $method)
231
    {
232
        $structure = $this->getPathDefinition($path, $method);
233
234
        if($this->getSpecificationVersion() === '3') {
235
            if (!isset($structure['requestBody'])) {
236
                return new SwaggerRequestBody($this, "$method $path", []);
237
            }
238
            return new SwaggerRequestBody($this, "$method $path", $structure['requestBody']);
239
        }
240
241
        if (!isset($structure[self::SWAGGER_PARAMETERS])) {
242
            return new SwaggerRequestBody($this, "$method $path", []);
243
        }
244
        return new SwaggerRequestBody($this, "$method $path", $structure[self::SWAGGER_PARAMETERS]);
245
    }
246
247
    /**
248
     * @param $path
249
     * @param $method
250
     * @param $status
251
     * @return SwaggerResponseBody
252
     * @throws HttpMethodNotFoundException
253
     * @throws InvalidDefinitionException
254
     * @throws NotMatchedException
255
     * @throws PathNotFoundException
256
     * @throws DefinitionNotFoundException
257
     */
258
    public function getResponseParameters($path, $method, $status)
259
    {
260
        $structure = $this->getPathDefinition($path, $method);
261
262
        if (!isset($structure['responses'][$status])) {
263
            throw new InvalidDefinitionException("Could not found status code '$status' in '$path' and '$method'");
264
        }
265
266
        return new SwaggerResponseBody($this, "$method $status $path", $structure['responses'][$status]);
267
    }
268
269
    /**
270
     * OpenApi 2.0 doesn't describe null values, so this flag defines,
271
     * if match is ok when one of property
272
     *
273
     * @return bool
274
     */
275
    public function isAllowNullValues()
276
    {
277
        return $this->allowNullValues;
278
    }
279
280
    /**
281
     * OpenApi 2.0 doesn't describe null values, so this flag defines,
282
     * if match is ok when one of property
283
     *
284
     * @param $value
285
     */
286
    public function setAllowNullValues($value)
287
    {
288
        $this->allowNullValues = (bool) $value;
289
    }
290
291
    public function setServerVariable($var, $value)
292
    {
293
        $this->serverVariables[$var] = $value;
294
    }
295
}
296