Passed
Branch release/v2.0.0 (bc156d)
by Anatoly
03:48
created

OpenApi::handleReferences()   A

Complexity

Conditions 4
Paths 1

Size

Total Lines 22
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 12
c 1
b 0
f 0
dl 0
loc 22
ccs 0
cts 16
cp 0
rs 9.8666
cc 4
nc 1
nop 0
crap 20
1
<?php declare(strict_types=1);
2
3
/**
4
 * It's free open-source software released under the MIT License.
5
 *
6
 * @author Anatoly Fenric <[email protected]>
7
 * @copyright Copyright (c) 2018, Anatoly Fenric
8
 * @license https://github.com/sunrise-php/http-router/blob/master/LICENSE
9
 * @link https://github.com/sunrise-php/http-router
10
 */
11
12
namespace Sunrise\Http\Router\OpenApi;
13
14
/**
15
 * Import classes
16
 */
17
use Doctrine\Common\Annotations\SimpleAnnotationReader;
18
use Sunrise\Http\Router\Annotation\OpenApi\AbstractReference;
19
use Sunrise\Http\Router\Annotation\OpenApi\Operation;
20
use Sunrise\Http\Router\Annotation\OpenApi\Parameter;
21
use Sunrise\Http\Router\RouteInterface;
22
use ReflectionClass;
23
24
/**
25
 * Import functions
26
 */
27
use function Sunrise\Http\Router\path_parse;
0 ignored issues
show
introduced by
The function Sunrise\Http\Router\path_parse was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
28
use function array_walk_recursive;
29
use function str_replace;
30
use function strtolower;
31
32
/**
33
 * OpenApi
34
 */
35
class OpenApi
36
{
37
38
    /**
39
     * Version of OpenAPI specification
40
     *
41
     * @var string
42
     */
43
    public const VERSION = '3.0.2';
44
45
    /**
46
     * @var SimpleAnnotationReader
47
     */
48
    private $annotationReader;
49
50
    /**
51
     * @var RouteInterface[]
52
     */
53
    private $routes = [];
54
55
    /**
56
     * @var string
57
     */
58
    private $title = 'REST API';
59
60
    /**
61
     * @var string
62
     */
63
    private $version = '0.0.1';
64
65
    /**
66
     * @var array
67
     */
68
    private $documentation = [];
69
70
    /**
71
     * Constructor of the class
72
     */
73
    public function __construct()
74
    {
75
        $this->annotationReader = new SimpleAnnotationReader();
76
        $this->annotationReader->addNamespace('Sunrise\Http\Router\Annotation');
77
    }
78
79
    /**
80
     * @param RouteInterface ...$routes
81
     *
82
     * @return void
83
     */
84
    public function addRoute(RouteInterface ...$routes) : void
85
    {
86
        foreach ($routes as $route) {
87
            $this->routes[] = $route;
88
        }
89
    }
90
91
    /**
92
     * @param string $title
93
     *
94
     * @return void
95
     */
96
    public function setTitle(string $title) : void
97
    {
98
        $this->title = $title;
99
    }
100
101
    /**
102
     * @param string $version
103
     *
104
     * @return void
105
     */
106
    public function setVersion(string $version) : void
107
    {
108
        $this->version = $version;
109
    }
110
111
    /**
112
     * @return void
113
     */
114
    public function describe() : void
115
    {
116
        $this->documentation['openapi'] = self::VERSION;
117
        $this->documentation['info']['title'] = $this->title;
118
        $this->documentation['info']['version'] = $this->version;
119
120
        foreach ($this->routes as $route) {
121
            $path = $this->createPatternedPathFromRoute($route);
122
            $operation = $this->createOperationAnnotationFromRoute($route);
123
124
            foreach ($route->getMethods() as $method) {
125
                $method = strtolower($method);
126
127
                $this->documentation['paths'][$path][$method]['operationId'] = $route->getName();
128
                $this->documentation['paths'][$path][$method] += $operation->toArray();
129
            }
130
        }
131
132
        $this->handleReferences();
133
    }
134
135
    /**
136
     * @return array
137
     */
138
    public function toArray() : array
139
    {
140
        return $this->documentation;
141
    }
142
143
    /**
144
     * @param RouteInterface $route
145
     *
146
     * @return string
147
     *
148
     * @link https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#patterned-fields
149
     */
150
    private function createPatternedPathFromRoute(RouteInterface $route) : string
151
    {
152
        $path = $route->getPath();
153
        $attributes = path_parse($path);
0 ignored issues
show
Bug introduced by
The function path_parse was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

153
        $attributes = /** @scrutinizer ignore-call */ path_parse($path);
Loading history...
154
155
        foreach ($attributes as $attribute) {
156
            $path = str_replace($attribute['raw'], '{' . $attribute['name'] . '}', $path);
157
        }
158
159
        return str_replace(['(', ')'], '', $path);
160
    }
161
162
    /**
163
     * @param RouteInterface $route
164
     *
165
     * @return Operation
166
     */
167
    private function createOperationAnnotationFromRoute(RouteInterface $route) : Operation
168
    {
169
        $target = new ReflectionClass($route->getRequestHandler());
170
        $operation = $this->annotationReader->getClassAnnotation($target, Operation::class) ?? new Operation();
171
        $attributes = path_parse($route->getPath());
0 ignored issues
show
Bug introduced by
The function path_parse was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

171
        $attributes = /** @scrutinizer ignore-call */ path_parse($route->getPath());
Loading history...
172
173
        foreach ($attributes as $attribute) {
174
            $parameter = new Parameter();
175
            $parameter->in = 'path';
176
            $parameter->name = $attribute['name'];
177
            $parameter->required = !$attribute['isOptional'];
178
            $operation->parameters[] = $parameter;
179
        }
180
181
        return $operation;
182
    }
183
184
    /**
185
     * @return void
186
     */
187
    private function handleReferences() : void
188
    {
189
        array_walk_recursive($this->documentation, function (&$value) {
190
            if (!($value instanceof AbstractReference)) {
191
                return;
192
            }
193
194
            $ref = $value;
195
            $value = $ref->getComponentPath();
196
            $component = &$this->documentation['components'][$ref->getComponentName()];
197
198
            if (isset($component[$ref->name])) {
199
                return;
200
            }
201
202
            $annotation = $ref->getAnnotation($this->annotationReader);
203
204
            if (empty($annotation)) {
205
                return;
206
            }
207
208
            $component[$ref->name] = $annotation->toArray();
209
        });
210
    }
211
}
212