Completed
Push — master ( df29f1...d6b8fe )
by Viacheslav
08:12
created

TypeBuilder::typeName()   A

Complexity

Conditions 4
Paths 6

Size

Total Lines 13
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 4
eloc 6
c 1
b 0
f 0
nc 6
nop 2
dl 0
loc 13
rs 10
1
<?php
2
3
namespace Swaggest\PhpCodeBuilder\JSDoc;
4
5
use Swaggest\CodeBuilder\CodeBuilder;
6
use Swaggest\JsonSchema\Schema;
7
use Swaggest\PhpCodeBuilder\PhpCode;
8
9
class TypeBuilder
10
{
11
    /** @var \SplObjectStorage */
12
    private $processed;
13
14
    public $trimNamePrefix = [
15
        '#/definitions'
16
    ];
17
18
    public $file = '';
19
20
    public function __construct()
21
    {
22
        $this->processed = new \SplObjectStorage();
23
    }
24
25
    /**
26
     * @param Schema|boolean $schema
27
     * @param string $path
28
     * @return string
29
     */
30
    public function getTypeString($schema, $path = '')
31
    {
32
        $schema = Schema::unboolSchema($schema);
33
34
        $isOptional = false;
0 ignored issues
show
Unused Code introduced by
The assignment to $isOptional is dead and can be removed.
Loading history...
35
        $isObject = false;
36
        $isArray = false;
37
        $isBoolean = false;
38
        $isString = false;
39
        $isNumber = false;
40
41
        $type = $schema->type;
42
        if (!is_array($type)) {
43
            $type = [$type];
44
        }
45
46
        $or = [];
47
48
        if ($schema->oneOf !== null) {
49
            foreach ($schema->oneOf as $item) {
50
                $or[] = $this->getTypeString($item);
51
            }
52
        }
53
54
        if ($schema->anyOf !== null) {
55
            foreach ($schema->anyOf as $item) {
56
                $or[] = $this->getTypeString($item);
57
            }
58
        }
59
60
        if ($schema->allOf !== null) {
61
            foreach ($schema->allOf as $item) {
62
                $or[] = $this->getTypeString($item);
63
            }
64
        }
65
66
        if ($schema->then !== null) {
67
            $or[] = $this->getTypeString($schema->then);
68
        }
69
70
        if ($schema->else !== null) {
71
            $or[] = $this->getTypeString($schema->else);
72
        }
73
74
        foreach ($type as $i => $t) {
75
            switch ($t) {
76
                case Schema::NULL:
77
                    $isOptional = true;
78
                    break;
79
80
                case Schema::OBJECT:
81
                    $isObject = true;
82
                    break;
83
84
                case Schema::_ARRAY:
85
                    $isArray = true;
86
                    break;
87
88
                case Schema::NUMBER:
89
                case Schema::INTEGER:
90
                    $isNumber = true;
91
                    break;
92
93
                case Schema::STRING:
94
                    $isString = true;
95
                    break;
96
97
                case Schema::BOOLEAN:
98
                    $isBoolean = true;
99
                    break;
100
101
            }
102
        }
103
104
        if ($isObject) {
105
            $typeAdded = false;
106
107
            if (!empty($schema->properties)) {
108
                if ($this->processed->contains($schema)) {
109
                    $or [] = $this->processed->offsetGet($schema);
110
                    $typeAdded = true;
111
                } else {
112
                    if ($schema instanceof Schema) {
113
                        $typeName = $this->typeName($schema, $path);
114
                        $this->makeObjectTypeDef($schema, $path);
115
116
                        $or [] = $typeName;
117
                        $typeAdded = true;
118
                    }
119
                }
120
121
            }
122
123
            if ($schema->additionalProperties instanceof Schema) {
124
                $typeName = $this->getTypeString($schema->additionalProperties, $path . '/additionalProperties');
125
                $or [] = "object<string, $typeName>";
126
                $typeAdded = true;
127
            }
128
129
            if (!empty($schema->patternProperties)) {
130
                foreach ($schema->patternProperties as $pattern => $propertySchema) {
131
                    if ($propertySchema instanceof Schema) {
132
                        $typeName = $this->getTypeString($propertySchema, $path . '/patternProperties/' . $pattern);
133
                        $or [] = $typeName;
134
                        $typeAdded = true;
135
                    }
136
                }
137
            }
138
139
            if (!$typeAdded) {
140
                $or [] = 'object';
141
            }
142
        }
143
144
        if ($isArray) {
145
            $typeAdded = false;
146
147
            if ($schema->items instanceof Schema) {
148
                $typeName = $this->getTypeString($schema->items, $path . '/items');
149
                $or [] = "array<$typeName>";
150
                $typeAdded = true;
151
            }
152
153
            if ($schema->additionalItems instanceof Schema) {
154
                $typeName = $this->getTypeString($schema->additionalItems, $path . '/additionalItems');
155
                $or [] = "array<$typeName>";
156
                $typeAdded = true;
157
            }
158
159
            if (!$typeAdded) {
160
                $or [] = 'array';
161
            }
162
        }
163
164
        if ($isString) {
165
            $or [] = 'string';
166
        }
167
168
        if ($isNumber) {
169
            $or [] = 'number';
170
        }
171
172
        if ($isBoolean) {
173
            $or [] = 'boolean';
174
        }
175
176
        $res = '';
177
        foreach ($or as $item) {
178
            if (!empty($item) && $item !== '*') {
179
                $res .= '|' . $item;
180
            }
181
        }
182
183
        if ($res !== '') {
184
            $res = substr($res, 1);
185
        } else {
186
            $res = '*';
187
        }
188
189
        return $res;
190
    }
191
192
    private function typeName(Schema $schema, $path)
193
    {
194
        if ($fromRefs = $schema->getFromRefs()) {
195
            $path = $fromRefs[count($fromRefs) - 1];
196
        }
197
198
        foreach ($this->trimNamePrefix as $prefix) {
199
            if ($prefix === substr($path, 0, strlen($prefix))) {
200
                $path = substr($path, strlen($prefix));
201
            }
202
        }
203
204
        return PhpCode::makePhpName($path, false);
205
    }
206
207
    private function makeObjectTypeDef(Schema $schema, $path)
208
    {
209
        $typeName = $this->typeName($schema, $path);
210
        $this->processed->attach($schema, $typeName);
211
212
        $head = '';
213
        if (!empty($schema->title)) {
214
            $head .= $schema->title . "\n";
215
        }
216
217
        if (!empty($schema->description)) {
218
            $head .= $schema->description . "\n";
219
        }
220
221
        if ($head !== '') {
222
            $head = "\n" . CodeBuilder::padLines(' * ', trim($head), false);
223
        }
224
225
        $res = <<<JSDOC
226
/**$head
227
 * @typedef {$typeName}
228
 * @type {object}
229
230
JSDOC;
231
        if (!empty($schema->properties)) {
232
            foreach ($schema->properties as $propertyName => $propertySchema) {
233
                $typeString = $this->getTypeString($propertySchema, $path . '/' . $propertyName);
234
                $res .= <<<JSDOC
235
 * @property {{$typeString}} {$propertyName}{$this->description($propertySchema)}.
236
237
JSDOC;
238
239
            }
240
        }
241
242
        $res .= <<<JSDOC
243
 */
244
245
246
JSDOC;
247
248
        $this->file .= $res;
249
250
        return $typeName;
251
    }
252
253
    private function description(Schema $schema)
254
    {
255
        $res = str_replace("\n", " ", $schema->title . $schema->description);
256
        if ($res) {
257
            return ' - ' . rtrim($res, '.');
258
        }
259
260
        return '';
261
    }
262
}