Passed
Pull Request — master (#34)
by Anatoly
02:26
created

OpenApi::handleComponentSchemas()   A

Complexity

Conditions 6
Paths 1

Size

Total Lines 22
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 42

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 11
c 1
b 0
f 0
dl 0
loc 22
ccs 0
cts 15
cp 0
rs 9.2222
cc 6
nc 1
nop 0
crap 42
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\Operation;
19
use Sunrise\Http\Router\Annotation\OpenApi\Parameter;
20
use Sunrise\Http\Router\Annotation\OpenApi\Schema;
21
use Sunrise\Http\Router\RouteCollectionInterface;
22
use Sunrise\Http\Router\RouteInterface;
23
use ReflectionClass;
24
25
/**
26
 * Import functions
27
 */
28
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...
29
use function array_walk_recursive;
30
use function class_exists;
31
use function str_replace;
32
use function strtolower;
33
34
/**
35
 * OpenApi
36
 */
37
class OpenApi
38
{
39
40
    /**
41
     * @var RouteCollectionInterface
42
     */
43
    private $routes;
44
45
    /**
46
     * @var SimpleAnnotationReader
47
     */
48
    private $annotationReader;
49
50
    /**
51
     * @var array
52
     */
53
    private $description = [];
54
55
    /**
56
     * Constructor of the class
57
     *
58
     * @param string $title
59
     * @param string $version
60
     * @param RouteCollectionInterface $routes
61
     */
62
    public function __construct(string $title, string $version, RouteCollectionInterface $routes)
63
    {
64
        $this->routes = $routes;
65
66
        $this->annotationReader = new SimpleAnnotationReader();
67
        $this->annotationReader->addNamespace('Sunrise\Http\Router\Annotation');
68
69
        $this->description['openapi'] = '3.0.2';
70
        $this->description['info']['title'] = $title;
71
        $this->description['info']['version'] = $version;
72
73
        // auto build...
74
        $this->build();
75
    }
76
77
    /**
78
     * @return array
79
     */
80
    public function toArray() : array
81
    {
82
        return $this->description;
83
    }
84
85
    /**
86
     * @return void
87
     */
88
    private function build() : void
89
    {
90
        foreach ($this->routes->all() as $route) {
91
            $path = $this->createPatternedPathFromRoute($route);
92
            $operation = $this->createOperationAnnotationFromRoute($route);
93
94
            foreach ($route->getMethods() as $method) {
95
                $method = strtolower($method);
96
97
                $this->description['paths'][$path][$method]['operationId'] = $route->getName();
98
                $this->description['paths'][$path][$method] += $operation->toArray();
99
            }
100
        }
101
102
        $this->handleComponentSchemas();
103
    }
104
105
    /**
106
     * @param RouteInterface $route
107
     *
108
     * @return string
109
     *
110
     * @link https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#patterned-fields
111
     */
112
    private function createPatternedPathFromRoute(RouteInterface $route) : string
113
    {
114
        $path = $route->getPath();
115
        $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

115
        $attributes = /** @scrutinizer ignore-call */ path_parse($path);
Loading history...
116
117
        foreach ($attributes as $attribute) {
118
            $path = str_replace($attribute['raw'], '{' . $attribute['name'] . '}', $path);
119
        }
120
121
        return str_replace(['(', ')'], '', $path);
122
    }
123
124
    /**
125
     * @param RouteInterface $route
126
     *
127
     * @return Operation
128
     */
129
    private function createOperationAnnotationFromRoute(RouteInterface $route) : Operation
130
    {
131
        $source = $route->getRequestHandler();
132
133
        $operation = $this->annotationReader->getClassAnnotation(new ReflectionClass($source), Operation::class);
134
135
        if (!$operation) {
136
            $operation = new Operation();
137
        }
138
139
        $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

139
        $attributes = /** @scrutinizer ignore-call */ path_parse($route->getPath());
Loading history...
140
141
        foreach ($attributes as $attribute) {
142
            $parameter = new Parameter();
143
            $parameter->in = 'path';
144
            $parameter->name = $attribute['name'];
145
            $parameter->required = $attribute['isOptional'];
146
147
            $operation->parameters[] = $parameter;
148
        }
149
150
        return $operation;
151
    }
152
153
    /**
154
     * @return void
155
     */
156
    private function handleComponentSchemas() : void
157
    {
158
        array_walk_recursive($this->description, function (&$value, $key) {
159
            if (!('$ref' === $key && null !== $value && class_exists($value))) {
160
                return;
161
            }
162
163
            // the schema already exists...
164
            if (isset($this->description['components']['schemas'][$value])) {
165
                return;
166
            }
167
168
            $schema = $this->annotationReader->getClassAnnotation(new ReflectionClass($value), Schema::class);
169
170
            if (!$schema) {
171
                return;
172
            }
173
174
            $this->description['components']['schemas'][$value]['type'] = 'object';
175
            $this->description['components']['schemas'][$value] += $schema->toArray();
176
177
            $value = '#/components/schemas/' . $value;
178
        });
179
    }
180
}
181