Swagger::extractParametersFromRawParameters()   C
last analyzed

Complexity

Conditions 8
Paths 13

Size

Total Lines 36
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 72

Importance

Changes 0
Metric Value
dl 0
loc 36
ccs 0
cts 31
cp 0
rs 5.3846
c 0
b 0
f 0
cc 8
eloc 26
nc 13
nop 1
crap 72
1
<?php
2
3
namespace Phrest;
4
5
class Swagger
6
{
7
    const SCHEMA_ID = 'file://swagger';
8
9
    private const CACHE_SWAGGER = 'Phrest_Swagger';
10
11
    private const CACHE_SWAGGER_OPERATION_PARAMETERS = 'Phrest_Swagger_Operation_Parameters';
12
13
    /**
14
     * @var \Zend\Cache\Storage\StorageInterface
15
     */
16
    private $cache;
17
18
    /**
19
     * @var \JsonSchema\SchemaStorage
20
     */
21
    private $schemaStorage;
22
23
    /**
24
     * @var string
25
     */
26
    private $swagger;
27
28
    /**
29
     * @var array
30
     */
31
    private $parametersByOperationId;
32
33
    public function __construct(\Zend\Cache\Storage\StorageInterface $cache, string $swaggerScanDirectory)
34
    {
35
        $this->cache = $cache;
36
37
        if (!$this->cache->hasItem(self::CACHE_SWAGGER)) {
38
            $this->swagger = \Swagger\scan($swaggerScanDirectory);
39
            $this->cache->setItem(self::CACHE_SWAGGER, $this->swagger);
40
        } else {
41
            $this->swagger = $this->cache->getItem(self::CACHE_SWAGGER);
42
        }
43
44
        $this->schemaStorage = new \JsonSchema\SchemaStorage();
45
        $this->schemaStorage->addSchema(Swagger::SCHEMA_ID, json_decode($this->swagger));
46
47
        if ($this->cache->hasItem(self::CACHE_SWAGGER_OPERATION_PARAMETERS)) {
48
            $this->parametersByOperationId = unserialize($this->cache->getItem(self::CACHE_SWAGGER_OPERATION_PARAMETERS));
49
        } else {
50
            $this->parametersByOperationId = $this->extractParameters();
51
            $this->cache->setItem(self::CACHE_SWAGGER_OPERATION_PARAMETERS, serialize($this->parametersByOperationId));
52
        }
53
    }
54
55
    public function getSchemaStorage(): \JsonSchema\SchemaStorage
56
    {
57
        return $this->schemaStorage;
58
    }
59
60
    private function extractParameters(): array
61
    {
62
        // Swagger Paths[]
63
        $paths = (array)$this->schemaStorage->resolveRef(self::SCHEMA_ID . '#/paths');
64
65
        $rawParametersByOperationId = [];
66
        foreach ($paths as $path => $pathItem) {
67
            // $path => '/some-path'
68
            // $pathItem => Swagger Path Item Object
69
70
            // Resolve Swagger Path Item Object $ref
71
            // "If there are conflicts between the referenced definition and this Path Item's definition, the behavior is undefined."
72
            // So we just allow either direct definition or using of $ref. But never both. $ref will overwrite all direct definitions.
73
            if (property_exists($pathItem, '$ref')) {
74
                $pathItemArray = (array)$pathItem;
75
                $pathItem = $this->schemaStorage->resolveRef($pathItemArray['$ref']);
76
            }
77
78
            // Handle @SWG\Parameter on @SWG\Path
79
            $pathItemParameters = [];
80
            if (property_exists($pathItem, 'parameters')) {
81
                $pathItemParameters = (array)($pathItem->parameters ?? []);
82
83
                // Resolve Swagger Path Item Parameters $ref
84
                if (array_key_exists('$ref', $pathItemParameters)) {
85
                    $pathItemParameters = (array)$this->schemaStorage->resolveRef($pathItemParameters['$ref']);
86
                }
87
            }
88
89
            $operations = ['get', 'put', 'post', 'delete', 'options', 'head', 'patch'];
90
            foreach ($operations as $operationName) {
91
                if (!property_exists($pathItem, $operationName)) {
92
                    continue;
93
                }
94
                $operation = $pathItem->$operationName;
95
96
                if (!property_exists($operation, 'operationId')) {
97
                    continue;
98
                }
99
100
                $operationId = $operation->operationId;
101
102
                if (array_key_exists($operationId, $rawParametersByOperationId)) {
103
                    throw new \Phrest\Exception('Swagger operationId duplicate found (' . implode('::', [$path, $operationName, $operationId]) . ').');
104
                }
105
106
                $parameters = [];
107
                if (property_exists($operation, 'parameters')) {
108
                    $parameters = (array)$operation->parameters;
109
                }
110
111
                // parameters can be defined for all operations - parameter in operations will override them
112
                $rawParametersByOperationId[$operationId] = array_merge($pathItemParameters, $parameters);
113
            }
114
        }
115
116
        $parametersByOperationId = [];
117
        foreach ($rawParametersByOperationId as $operationId => $parameters) {
118
            $parametersByOperationId[$operationId] = $this->extractParametersFromRawParameters($parameters);
119
        }
120
121
        return $parametersByOperationId;
122
    }
123
124
    private function extractParametersFromRawParameters(array $parameters): Swagger\Parameters
125
    {
126
        $bodyParameter = [];
127
        $queryParameters = [];
128
        $headerParameters = [];
129
        $pathParameters = [];
130
        foreach ($parameters as $parameter) {
131
            $parameter = (array)$parameter;
132
133
            // resolve refs
134
            if (array_key_exists('$ref', $parameter)) {
135
                $parameter = (array)$this->schemaStorage->resolveRef($parameter['$ref']);
136
            }
137
138
            switch ($parameter['in']) {
139
                case 'query':
140
                    $queryParameters[$parameter['name']] = $parameter;
141
                    break;
142
                case 'body':
143
                    $bodyParameter = $parameter;
144
                    $ref = (array)$bodyParameter['schema'];
145
                    $bodyParameter['schema'] = $this->schemaStorage->resolveRef($ref['$ref']);
146
                    break;
147
                case 'header':
148
                    $headerParameters[$parameter['name']] = $parameter;
149
                    break;
150
                case 'path':
151
                    $pathParameters[$parameter['name']] = $parameter;
152
                    break;
153
                case 'formData':
154
                    // @todo implement formData validation
155
                    // throw new \Phrest\Exception('swagger parameter location "formData" not yet implemented');
156
                    break;
157
            }
158
        }
159
        return new Swagger\Parameters($bodyParameter, $queryParameters, $headerParameters, $pathParameters);
160
    }
161
162
    public function getParameters(string $operationId): Swagger\Parameters
163
    {
164
        if (!array_key_exists($operationId, $this->parametersByOperationId)) {
165
            throw new \Phrest\Exception('OperationId "' . $operationId . '" not found in swagger.');
166
        }
167
        return $this->parametersByOperationId[$operationId];
168
    }
169
170
    public function __toString(): string
171
    {
172
        return $this->swagger;
173
    }
174
}
175