Passed
Branch release/v2.0.0 (f3d8ba)
by Anatoly
03:59
created

OpenApi::setTitle()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
eloc 1
c 0
b 0
f 0
dl 0
loc 3
ccs 0
cts 3
cp 0
rs 10
cc 1
nc 1
nop 1
crap 2
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;
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\Annotation\OpenApi\Schema;
22
use Sunrise\Http\Router\OpenApi\Info;
23
use Sunrise\Http\Router\OpenApi\SecurityRequirement;
24
use Sunrise\Http\Router\OpenApi\SecurityScheme;
25
use Sunrise\Http\Router\OpenApi\Server;
26
use ReflectionClass;
27
28
/**
29
 * Import functions
30
 */
31
use function array_walk_recursive;
32
use function strtolower;
33
34
/**
35
 * OpenApi
36
 */
37
class OpenApi
38
{
39
40
    /**
41
     * @var string
42
     */
43
    public const VERSION = '3.0.2';
44
45
    /**
46
     * @var array
47
     */
48
    private $documentation;
49
50
    /**
51
     * @var SimpleAnnotationReader
52
     */
53
    private $annotationReader;
54
55
    /**
56
     * @var RouteInterface[]
57
     */
58
    private $routes = [];
59
60
    /**
61
     * Constructor of the class
62
     *
63
     * @param Info $info
64
     */
65
    public function __construct(Info $info)
66
    {
67
        $this->documentation = [
68
            'openapi' => self::VERSION,
69
            'info' => $info->toArray(),
70
        ];
71
72
        $this->annotationReader = new SimpleAnnotationReader();
73
        $this->annotationReader->addNamespace('Sunrise\Http\Router\Annotation');
74
    }
75
76
    /**
77
     * @param RouteInterface ...$routes
78
     *
79
     * @return void
80
     */
81
    public function addRoute(RouteInterface ...$routes) : void
82
    {
83
        foreach ($routes as $route) {
84
            $this->routes[] = $route;
85
        }
86
    }
87
88
    /**
89
     * @param Server $server
90
     *
91
     * @return void
92
     */
93
    public function pushServer(Server $server) : void
94
    {
95
        $this->documentation['servers'][] = $server->toArray();
96
    }
97
98
    /**
99
     * @param SecurityRequirement $securityRequirement
100
     *
101
     * @return void
102
     */
103
    public function pushSecurityRequirement(SecurityRequirement $securityRequirement) : void
104
    {
105
        $this->documentation['security'][] = $securityRequirement->toArray();
106
    }
107
108
    /**
109
     * @param string $name
110
     * @param SecurityScheme $securityScheme
111
     *
112
     * @return void
113
     */
114
    public function pushSecurityScheme(string $name, SecurityScheme $securityScheme) : void
115
    {
116
        $this->documentation['components']['securitySchemes'][$name] = $securityScheme->toArray();
117
    }
118
119
    /**
120
     * @return array
121
     */
122
    public function toArray() : array
123
    {
124
        return $this->documentation;
125
    }
126
127
    /**
128
     * @return string
129
     */
130
    public function toJson() : string
131
    {
132
        return json_encode($this->documentation);
133
    }
134
135
    /**
136
     * @return string
137
     */
138
    public function toYaml() : string
139
    {
140
        return yaml_emit($this->documentation);
141
    }
142
143
    /**
144
     * @return void
145
     */
146
    public function generateDocumentation() : void
147
    {
148
        foreach ($this->routes as $route) {
149
            $path = path_plain($route->getPath());
0 ignored issues
show
Bug introduced by
The function path_plain 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

149
            $path = /** @scrutinizer ignore-call */ path_plain($route->getPath());
Loading history...
150
            $operation = $this->convertRouteToOperation($route);
151
152
            foreach ($route->getMethods() as $method) {
153
                $method = strtolower($method);
154
155
                $this->documentation['paths'][$path][$method]['operationId'] = $route->getName();
156
                $this->documentation['paths'][$path][$method] += $operation->toArray();
157
            }
158
        }
159
160
        $this->handleReferences();
161
    }
162
163
    /**
164
     * @param RouteInterface $route
165
     *
166
     * @return Operation
167
     */
168
    private function convertRouteToOperation(RouteInterface $route) : Operation
169
    {
170
        $target = new ReflectionClass($route->getRequestHandler());
171
        $operation = $this->annotationReader->getClassAnnotation($target, Operation::class) ?? new Operation();
172
        $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

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