Completed
Push — master ( aa5f27...b44d8e )
by Viacheslav
12:08 queued 02:08
created

TypeBuilder::processAdditionalPatternProperties()   B

Complexity

Conditions 7
Paths 15

Size

Total Lines 17
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 7.7656

Importance

Changes 0
Metric Value
cc 7
eloc 11
c 0
b 0
f 0
nc 15
nop 0
dl 0
loc 17
ccs 9
cts 12
cp 0.75
crap 7.7656
rs 8.8333
1
<?php
2
3
namespace Swaggest\PhpCodeBuilder\JsonSchema;
4
5
6
use Swaggest\JsonSchema\Constraint\Type;
7
use Swaggest\JsonSchema\Schema;
8
use Swaggest\PhpCodeBuilder\PhpAnyType;
9
use Swaggest\PhpCodeBuilder\PhpStdType;
10
use Swaggest\PhpCodeBuilder\Types\ArrayOf;
11
use Swaggest\PhpCodeBuilder\Types\OrType;
12
13
class TypeBuilder
14
{
15
    /** @var Schema */
16
    private $schema;
17
    /** @var string */
18
    private $path;
19
    /** @var PhpBuilder */
20
    private $phpBuilder;
21
    /** @var OrType */
22
    private $result;
23
24
    /**
25
     * TypeBuilder constructor.
26
     * @param Schema $schema
27
     * @param string $path
28
     * @param PhpBuilder $phpBuilder
29
     */
30 12
    public function __construct($schema, $path, PhpBuilder $phpBuilder)
31
    {
32 12
        $this->schema = $schema;
33 12
        $this->path = $path;
34 12
        $this->phpBuilder = $phpBuilder;
35 12
    }
36
37 12
    /**
38
     * @throws Exception
39 12
     * @throws \Swaggest\PhpCodeBuilder\Exception
40 12
     */
41 3
    private function processLogicType()
42 12
    {
43 4
        $orSchemas = null;
44 12
        if ($this->schema->allOf !== null) {
45 4
            $orSchemas = $this->schema->allOf;
46
        } elseif ($this->schema->anyOf !== null) {
47
            $orSchemas = $this->schema->anyOf;
48 12
        } elseif ($this->schema->oneOf !== null) {
49 7
            $orSchemas = $this->schema->oneOf;
50 7
        }
51
52
        if ($orSchemas !== null) {
53 12
            foreach ($orSchemas as $item) {
54
                $this->result->add($this->phpBuilder->getType($item, $this->path));
55 12
            }
56
        }
57 12
    }
58
59 12
    /**
60 12
     * @throws Exception
61 2
     * @throws \Swaggest\PhpCodeBuilder\Exception
62 2
     */
63 12
    private function processAdditionalPatternProperties()
64 12
    {
65 12
        $schema = $this->schema;
66
67
        if ($schema->additionalProperties instanceof Schema) {
68
            $type = $this->phpBuilder->getType($schema->additionalProperties, $this->path . '->additionalProperties');
69
            if ($type !== PhpStdType::mixed()) {
70
                $this->result->add(new ArrayOf($type));
71
            }
72 12
        }
73 12
74 12
        if ($schema->patternProperties !== null) {
75 12
            foreach ($schema->patternProperties as $pattern => $property) {
76
                if ($property instanceof Schema) {
77 12
                    $type = $this->phpBuilder->getType($property, $this->path . "->patternProperties->{{$pattern}}");
78 2
                    if ($type !== PhpStdType::mixed()) {
79
                        $this->result->add(new ArrayOf($type));
80
                    }
81
                }
82 12
            }
83
        }
84 12
85
    }
86 12
87
88
    /**
89 12
     * @throws Exception
90
     * @throws \Swaggest\PhpCodeBuilder\Exception
91 12
     */
92 4
    private function processArrayType()
93
    {
94
        $schema = $this->schema;
95
96
        /** @var string $pathItems */
97
        $pathItems = Schema::names()->items;
0 ignored issues
show
Documentation Bug introduced by
It seems like Swaggest\JsonSchema\Schema::names()->items can also be of type string. However, the property $items is declared as type Swaggest\JsonSchema\Sche...a\SchemaContract[]|null. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
98 12
        if ($this->isSchema($schema->items)) {
99 3
            $items = array();
100 3
            $additionalItems = $schema->items;
101 3
        } elseif ($schema->items === null) { // items defaults to empty schema so everything is valid
102
            $items = array();
103
            $additionalItems = true;
104
        } else { // listed items
105 12
            $items = $schema->items;
106
            $additionalItems = $schema->additionalItems;
107 8
            $pathItems = Schema::names()->additionalItems;
0 ignored issues
show
Documentation Bug introduced by
It seems like Swaggest\JsonSchema\Sche...ames()->additionalItems can also be of type string. However, the property $additionalItems is declared as type Swaggest\JsonSchema\SchemaContract|boolean|null. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
108
        }
109 8
110
        $itemsLen = is_array($items) ? count($items) : 0;
111 5
        $index = 0;
112
        if ($index < $itemsLen) {
113
        } else {
114 6
            if ($additionalItems instanceof Schema) {
115
                $this->result->add(new ArrayOf($this->phpBuilder->getType($additionalItems, $this->path . '->' . $pathItems)));
116
            }
117 6
        }
118
    }
119
120 7
    private function isSchema($var)
121
    {
122
        return $var instanceof Schema;
123
    }
124
125
    /**
126
     * @throws Exception
127
     * @throws \Swaggest\PhpCodeBuilder\Exception
128 4
     */
129
    private function processObjectType()
130
    {
131 2
        if ($this->schema->patternProperties !== null) {
132
            foreach ($this->schema->patternProperties as $pattern => $schema) {
133
                //$this->result->add(new ArrayOf($this->phpBuilder->getType($schema, $this->path . '->' . $pattern)));
134 7
            }
135
        }
136
137
138
        if ($this->schema->additionalProperties instanceof Schema) {
139
            $this->result->add(new ArrayOf($this->phpBuilder->getType(
140
                $this->schema->additionalProperties,
141
                $this->path . '->' . Schema::names()->additionalProperties)
0 ignored issues
show
Bug introduced by
Are you sure Swaggest\JsonSchema\Sche...)->additionalProperties of type Swaggest\JsonSchema\SchemaContract|boolean|string can be used in concatenation? ( Ignorable by Annotation )

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

141
                $this->path . '->' . /** @scrutinizer ignore-type */ Schema::names()->additionalProperties)
Loading history...
142
            ));
143
        }
144 12
145
    }
146 12
147 10
    private function typeSwitch($type)
148 10
    {
149
        switch ($type) {
150 12
            case Type::INTEGER:
151
                return PhpStdType::int();
152
153
            case Type::NUMBER:
154
                return PhpStdType::float();
155
156
            case TYPE::BOOLEAN:
157 12
                return PhpStdType::bool();
158
159 12
            case Type::STRING:
160 12
                return PhpStdType::string();
161
162
            /*
163
            case Type::OBJECT:
164 12
                return PhpStdType::object();
165 4
            */
166
167
            case Type::ARR:
168
                return PhpStdType::arr();
169
170 12
            case Type::NULL:
171 12
                return PhpStdType::null();
172 12
173 12
            default:
174
                return null;
175 12
        }
176
    }
177
178
    /**
179 12
     * @throws Exception
180 8
     * @throws \Swaggest\PhpCodeBuilder\Exception
181
     */
182
    private function processNamedClass()
183 12
    {
184
        if ($this->schema->properties !== null) {
185
            $class = $this->phpBuilder->getClass($this->schema, $this->path);
186
            $this->result->add($class);
187
        }
188
    }
189
190
    /**
191
     * @return PhpAnyType
192
     * @throws Exception
193
     * @throws \Swaggest\PhpCodeBuilder\Exception
194
     */
195
    public function build()
196
    {
197
        $this->result = new OrType();
198
        if ($this->schema === null) {
199
            throw new Exception('Null schema');
200
        }
201
202
        if ($fromRefs = $this->schema->getFromRefs()) {
203
            $this->path = $fromRefs[count($fromRefs) - 1];
204
            //$this->result->add($this->phpBuilder->getType($this->schema->ref->getData(), $this->schema->ref->ref));
205
        }
206
207
208
        $this->processNamedClass();
209
        $this->processLogicType();
210
        $this->processArrayType();
211
        $this->processObjectType();
212
        $this->processAdditionalPatternProperties();
213
214
        if (is_array($this->schema->type)) {
215
            foreach ($this->schema->type as $type) {
216
                $this->result->add($this->typeSwitch($type));
217
            }
218
        } elseif ($this->schema->type) {
219
            $this->result->add($this->typeSwitch($this->schema->type));
220
        }
221
222
        return $this->result->simplify();
223
224
    }
225
}