Completed
Pull Request — master (#25)
by
unknown
05:01
created

SwaggerSchema   B

Complexity

Total Complexity 46

Size/Duplication

Total Lines 224
Duplicated Lines 5.36 %

Coupling/Cohesion

Components 1
Dependencies 7

Importance

Changes 0
Metric Value
wmc 46
lcom 1
cbo 7
dl 12
loc 224
rs 8.72
c 0
b 0
f 0

13 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 6 2
A getSpecificationVersion() 0 4 1
A getServerUrl() 0 4 2
A getHttpSchema() 0 4 2
A getHost() 0 4 2
A getBasePath() 0 9 5
B getPathDefinition() 0 37 7
B validateArguments() 0 20 9
B getDefintion() 12 26 8
A getRequestParameters() 0 16 4
A getResponseParameters() 0 10 2
A isAllowNullValues() 0 4 1
A setAllowNullValues() 0 4 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like SwaggerSchema 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 SwaggerSchema, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * User: jg
4
 * Date: 22/05/17
5
 * Time: 09:29
6
 */
7
8
namespace ByJG\Swagger;
9
10
use ByJG\Swagger\Exception\DefinitionNotFoundException;
11
use ByJG\Swagger\Exception\HttpMethodNotFoundException;
12
use ByJG\Swagger\Exception\InvalidDefinitionException;
13
use ByJG\Swagger\Exception\NotMatchedException;
14
use ByJG\Swagger\Exception\PathNotFoundException;
15
16
class SwaggerSchema
17
{
18
    protected $jsonFile;
19
    protected $allowNullValues;
20
    protected $specificationVersion;
21
22
    const SWAGGER_PATHS="paths";
23
    const SWAGGER_PARAMETERS="parameters";
24
25
    public function __construct($jsonFile, $allowNullValues = false)
26
    {
27
        $this->jsonFile = json_decode($jsonFile, true);
28
        $this->allowNullValues = (bool) $allowNullValues;
29
        $this->specificationVersion = isset($this->jsonFile['swagger']) ? '2' : '3';
30
    }
31
32
    /**
33
     * Returns the major specification version
34
     * @return string
35
     */
36
    public function getSpecificationVersion()
37
    {
38
        return $this->specificationVersion;
39
    }
40
41
    public function getServerUrl()
42
    {
43
        return isset($this->jsonFile['servers']) ? $this->jsonFile['servers'][0]['url'] : '';
44
    }
45
46
    public function getHttpSchema()
47
    {
48
        return isset($this->jsonFile['schemes']) ? $this->jsonFile['schemes'][0] : '';
49
    }
50
51
    public function getHost()
52
    {
53
        return isset($this->jsonFile['host']) ? $this->jsonFile['host'] : '';
54
    }
55
56
    public function getBasePath()
57
    {
58
        if ($this->getSpecificationVersion() === '3') {
59
            $basePath =isset($this->jsonFile['servers']) ? explode('/', $this->jsonFile['servers'][0]['url']) : '';
60
            return is_array($basePath) ? '/' . end($basePath) : $basePath;
61
        }
62
63
        return isset($this->jsonFile['basePath']) ? $this->jsonFile['basePath'] : '';
64
    }
65
66
    /**
67
     * @param $path
68
     * @param $method
69
     * @return mixed
70
     * @throws \ByJG\Swagger\Exception\HttpMethodNotFoundException
71
     * @throws \ByJG\Swagger\Exception\NotMatchedException
72
     * @throws \ByJG\Swagger\Exception\PathNotFoundException
73
     */
74
    public function getPathDefinition($path, $method)
75
    {
76
        $method = strtolower($method);
77
78
        $path = preg_replace('~^' . $this->getBasePath() . '~', '', $path);
79
80
        // Try direct match
81
        if (isset($this->jsonFile[self::SWAGGER_PATHS][$path])) {
82
            if (isset($this->jsonFile[self::SWAGGER_PATHS][$path][$method])) {
83
                return $this->jsonFile[self::SWAGGER_PATHS][$path][$method];
84
            }
85
            throw new HttpMethodNotFoundException("The http method '$method' not found in '$path'");
86
        }
87
88
        // Try inline parameter
89
        foreach (array_keys($this->jsonFile[self::SWAGGER_PATHS]) as $pathItem) {
90
            if (strpos($pathItem, '{') === false) {
91
                continue;
92
            }
93
94
            $pathItemPattern = '~^' . preg_replace('~\{(.*?)\}~', '(?<\1>[^/]+)', $pathItem) . '$~';
95
96
            $matches = [];
97
            if (preg_match($pathItemPattern, $path, $matches)) {
98
                $pathDef = $this->jsonFile[self::SWAGGER_PATHS][$pathItem];
99
                if (!isset($pathDef[$method])) {
100
                    throw new HttpMethodNotFoundException("The http method '$method' not found in '$path'");
101
                }
102
103
                $this->validateArguments('path', $pathDef[$method][self::SWAGGER_PARAMETERS], $matches);
104
105
                return $pathDef[$method];
106
            }
107
        }
108
109
        throw new PathNotFoundException('Path "' . $path . '" not found');
110
    }
111
112
    /**
113
     * @param $parameterIn
114
     * @param $parameters
115
     * @param $arguments
116
     * @throws \ByJG\Swagger\Exception\NotMatchedException
117
     */
118
    private function validateArguments($parameterIn, $parameters, $arguments)
119
    {
120
        if ($this->getSpecificationVersion() === '3') {
121
            foreach ($parameters as $parameter) {
122
                if ($parameter['schema']['type'] === "integer"
123
                    && filter_var($arguments[$parameter['name']], FILTER_VALIDATE_INT) === false) {
124
                    throw new NotMatchedException('Path expected an integer value');
125
                }
126
            }
127
            return;
128
        }
129
130
        foreach ($parameters as $parameter) {
131
            if ($parameter['in'] === $parameterIn
132
                && $parameter['type'] === "integer"
133
                && filter_var($arguments[$parameter['name']], FILTER_VALIDATE_INT) === false) {
134
                throw new NotMatchedException('Path expected an integer value');
135
            }
136
        }
137
    }
138
139
    /**
140
     * @param $name
141
     * @return mixed
142
     * @throws DefinitionNotFoundException
143
     * @throws InvalidDefinitionException
144
     */
145
    public function getDefintion($name)
146
    {
147
        $nameParts = explode('/', $name);
148
149
        if ($this->getSpecificationVersion() === '3') {
150 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...
151
                throw new InvalidDefinitionException('Invalid Component');
152
            }
153
154 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...
155
                throw new DefinitionNotFoundException("Component'$name' not found");
156
            }
157
158
            return $this->jsonFile[$nameParts[1]][$nameParts[2]][$nameParts[3]];
159
        }
160
161 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...
162
            throw new InvalidDefinitionException('Invalid Definition');
163
        }
164
165 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...
166
            throw new DefinitionNotFoundException("Definition '$name' not found");
167
        }
168
169
        return $this->jsonFile[$nameParts[1]][$nameParts[2]];
170
    }
171
172
    /**
173
     * @param $path
174
     * @param $method
175
     * @return \ByJG\Swagger\SwaggerRequestBody
176
     * @throws \ByJG\Swagger\Exception\HttpMethodNotFoundException
177
     * @throws \ByJG\Swagger\Exception\NotMatchedException
178
     * @throws \ByJG\Swagger\Exception\PathNotFoundException
179
     */
180
    public function getRequestParameters($path, $method)
181
    {
182
        $structure = $this->getPathDefinition($path, $method);
183
184
        if($this->getSpecificationVersion() === '3') {
185
            if (!isset($structure['requestBody'])) {
186
                return new SwaggerRequestBody($this, "$method $path", []);
187
            }
188
            return new SwaggerRequestBody($this, "$method $path", $structure['requestBody']);
189
        }
190
191
        if (!isset($structure[self::SWAGGER_PARAMETERS])) {
192
            return new SwaggerRequestBody($this, "$method $path", []);
193
        }
194
        return new SwaggerRequestBody($this, "$method $path", $structure[self::SWAGGER_PARAMETERS]);
195
    }
196
197
    /**
198
     * @param $path
199
     * @param $method
200
     * @param $status
201
     * @return \ByJG\Swagger\SwaggerResponseBody
202
     * @throws \ByJG\Swagger\Exception\HttpMethodNotFoundException
203
     * @throws \ByJG\Swagger\Exception\InvalidDefinitionException
204
     * @throws \ByJG\Swagger\Exception\NotMatchedException
205
     * @throws \ByJG\Swagger\Exception\PathNotFoundException
206
     */
207
    public function getResponseParameters($path, $method, $status)
208
    {
209
        $structure = $this->getPathDefinition($path, $method);
210
211
        if (!isset($structure['responses'][$status])) {
212
            throw new InvalidDefinitionException("Could not found status code '$status' in '$path' and '$method'");
213
        }
214
215
        return new SwaggerResponseBody($this, "$method $status $path", $structure['responses'][$status]);
216
    }
217
218
    /**
219
     * OpenApi 2.0 doesn't describe null values, so this flag defines,
220
     * if match is ok when one of property
221
     *
222
     * @return bool
223
     */
224
    public function isAllowNullValues()
225
    {
226
        return $this->allowNullValues;
227
    }
228
229
    /**
230
     * OpenApi 2.0 doesn't describe null values, so this flag defines,
231
     * if match is ok when one of property
232
     *
233
     * @param $value
234
     */
235
    public function setAllowNullValues($value)
236
    {
237
        $this->allowNullValues = (bool) $value;
238
    }
239
}
240