Passed
Pull Request — master (#40)
by Viacheslav
02:03
created

Schema::processString()   C

Complexity

Conditions 11
Paths 108

Size

Total Lines 23
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 16
CRAP Score 11

Importance

Changes 0
Metric Value
dl 0
loc 23
ccs 16
cts 16
cp 1
rs 5.1372
c 0
b 0
f 0
cc 11
eloc 15
nc 108
nop 2
crap 11

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace Swaggest\JsonSchema;
4
5
6
use PhpLang\ScopeExit;
7
use Swaggest\JsonDiff\JsonDiff;
8
use Swaggest\JsonDiff\JsonPointer;
9
use Swaggest\JsonSchema\Constraint\Content;
10
use Swaggest\JsonSchema\Constraint\Format;
11
use Swaggest\JsonSchema\Constraint\Properties;
12
use Swaggest\JsonSchema\Constraint\Type;
13
use Swaggest\JsonSchema\Constraint\UniqueItems;
14
use Swaggest\JsonSchema\Exception\ArrayException;
15
use Swaggest\JsonSchema\Exception\ConstException;
16
use Swaggest\JsonSchema\Exception\EnumException;
17
use Swaggest\JsonSchema\Exception\LogicException;
18
use Swaggest\JsonSchema\Exception\NumericException;
19
use Swaggest\JsonSchema\Exception\ObjectException;
20
use Swaggest\JsonSchema\Exception\StringException;
21
use Swaggest\JsonSchema\Exception\TypeException;
22
use Swaggest\JsonSchema\Meta\MetaHolder;
23
use Swaggest\JsonSchema\Path\PointerUtil;
24
use Swaggest\JsonSchema\Structure\ClassStructure;
25
use Swaggest\JsonSchema\Structure\Egg;
26
use Swaggest\JsonSchema\Structure\ObjectItem;
27
use Swaggest\JsonSchema\Structure\ObjectItemContract;
28
29
/**
30
 * Class Schema
31
 * @package Swaggest\JsonSchema
32
 */
33
class Schema extends JsonSchema implements MetaHolder, SchemaContract
34
{
35
    const ENUM_NAMES_PROPERTY = 'x-enum-names';
36
    const CONST_PROPERTY = 'const';
37
38
    const DEFAULT_MAPPING = 'default';
39
40
    const VERSION_AUTO = 'a';
41
    const VERSION_DRAFT_04 = 4;
42
    const VERSION_DRAFT_06 = 6;
43
    const VERSION_DRAFT_07 = 7;
44
45
    const PROP_REF = '$ref';
46
    const PROP_ID = '$id';
47
    const PROP_ID_D4 = 'id';
48
49
    // Object
50
    /** @var null|Properties|Schema[]|Schema */
51
    public $properties;
52
    /** @var Schema|bool */
53
    public $additionalProperties;
54
    /** @var Schema[]|Properties */
55
    public $patternProperties;
56
    /** @var string[][]|Schema[]|\stdClass */
57
    public $dependencies;
58
59
    // Array
60
    /** @var null|Schema|Schema[] */
61
    public $items;
62
    /** @var null|Schema|bool */
63
    public $additionalItems;
64
65
    /** @var Schema[] */
66
    public $allOf;
67
    /** @var Schema */
68
    public $not;
69
    /** @var Schema[] */
70
    public $anyOf;
71
    /** @var Schema[] */
72
    public $oneOf;
73
74
    /** @var Schema */
75
    public $if;
76
    /** @var Schema */
77
    public $then;
78
    /** @var Schema */
79
    public $else;
80
81
82
    public $objectItemClass;
83
    private $useObjectAsArray = false;
84
85
    private $__dataToProperty = array();
86
    private $__propertyToData = array();
87
88
    private $__booleanSchema;
89
90 3
    public function addPropertyMapping($dataName, $propertyName, $mapping = self::DEFAULT_MAPPING)
91
    {
92 3
        $this->__dataToProperty[$mapping][$dataName] = $propertyName;
93 3
        $this->__propertyToData[$mapping][$propertyName] = $dataName;
94
95 3
        if ($mapping === self::DEFAULT_MAPPING && $this->properties instanceof Properties) {
96 3
            $this->properties->__defaultMapping[$propertyName] = $dataName;
0 ignored issues
show
Bug Best Practice introduced by
The property __defaultMapping does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
97
        }
98 3
        return $this;
99
    }
100
101
    /**
102
     * @param mixed $data
103
     * @param Context|null $options
104
     * @return SchemaContract
105
     * @throws Exception
106
     * @throws InvalidValue
107
     * @throws \Exception
108
     */
109 3188
    public static function import($data, Context $options = null)
110
    {
111 3188
        if (null === $options) {
112 24
            $options = new Context();
113
        }
114
115 3188
        $options->applyDefaults = false;
116
117 3188
        if (isset($options->schemasCache) && is_object($data)) {
118
            if ($options->schemasCache->contains($data)) {
119
                return $options->schemasCache->offsetGet($data);
120
            } else {
121
                $schema = parent::import($data, $options);
122
                $options->schemasCache->attach($data, $schema);
123
                return $schema;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $schema also could return the type Swaggest\JsonSchema\Structure\ClassStructureTrait which is incompatible with the documented return type Swaggest\JsonSchema\SchemaContract.
Loading history...
124
            }
125
        }
126
127
        // string $data is expected to be $ref uri
128 3188
        if (is_string($data)) {
129 4
            $data = (object)array(self::PROP_REF => $data);
130
        }
131
132 3188
        $data = self::unboolSchema($data);
133 3188
        if ($data instanceof SchemaContract) {
134 72
            return $data;
135
        }
136
137 3116
        return parent::import($data, $options);
0 ignored issues
show
Bug Best Practice introduced by
The expression return parent::import($data, $options) also could return the type Swaggest\JsonSchema\Structure\ClassStructureTrait which is incompatible with the documented return type Swaggest\JsonSchema\SchemaContract.
Loading history...
138
    }
139
140
    /**
141
     * @param mixed $data
142
     * @param Context|null $options
143
     * @return array|mixed|null|object|\stdClass
144
     * @throws Exception
145
     * @throws InvalidValue
146
     * @throws \Exception
147
     */
148 3210
    public function in($data, Context $options = null)
149
    {
150 3210
        if (null !== $this->__booleanSchema) {
151 72
            if ($this->__booleanSchema) {
152 36
                return $data;
153 36
            } elseif (empty($options->skipValidation)) {
154 18
                $this->fail(new InvalidValue('Denied by false schema'), '#');
155
            }
156
        }
157
158 3156
        if ($options === null) {
159 59
            $options = new Context();
160
        }
161
162 3156
        $options->import = true;
163
164 3156
        if ($options->refResolver === null) {
165 2837
            $options->refResolver = new RefResolver($data);
166
        } else {
167 1701
            $options->refResolver->setRootData($data);
168
        }
169
170 3156
        if ($options->remoteRefProvider) {
171 3090
            $options->refResolver->setRemoteRefProvider($options->remoteRefProvider);
172
        }
173
174 3156
        $options->refResolver->preProcessReferences($data, $options);
175
176 3156
        return $this->process($data, $options, '#');
177
    }
178
179
180
    /**
181
     * @param mixed $data
182
     * @param Context|null $options
183
     * @return array|mixed|null|object|\stdClass
184
     * @throws InvalidValue
185
     * @throws \Exception
186
     */
187 2371
    public function out($data, Context $options = null)
188
    {
189 2371
        if ($options === null) {
190 962
            $options = new Context();
191
        }
192
193 2371
        $options->circularReferences = new \SplObjectStorage();
194 2371
        $options->import = false;
195 2371
        return $this->process($data, $options);
196
    }
197
198
    /**
199
     * @param mixed $data
200
     * @param Context $options
201
     * @param string $path
202
     * @throws InvalidValue
203
     * @throws \Exception
204
     */
205 3140
    private function processType($data, Context $options, $path = '#')
206
    {
207 3140
        if ($options->tolerateStrings && is_string($data)) {
208
            $valid = Type::readString($this->type, $data);
209
        } else {
210 3140
            $valid = Type::isValid($this->type, $data, $options->version);
211
        }
212 3140
        if (!$valid) {
213 681
            $this->fail(new TypeException(ucfirst(
214 681
                    implode(', ', is_array($this->type) ? $this->type : array($this->type))
215 681
                    . ' expected, ' . json_encode($data) . ' received')
216 681
            ), $path);
217
        }
218 3138
    }
219
220
    /**
221
     * @param mixed $data
222
     * @param string $path
223
     * @throws InvalidValue
224
     * @throws \Exception
225
     */
226 1478
    private function processEnum($data, $path = '#')
227
    {
228 1478
        $enumOk = false;
229 1478
        foreach ($this->enum as $item) {
230 1478
            if ($item === $data) {
231 1458
                $enumOk = true;
232 1458
                break;
233
            } else {
234 1414
                if (is_array($item) || is_object($item)) {
235 12
                    $diff = new JsonDiff($item, $data, JsonDiff::STOP_ON_DIFF);
236 12
                    if ($diff->getDiffCnt() === 0) {
237 3
                        $enumOk = true;
238 1414
                        break;
239
                    }
240
                }
241
            }
242
        }
243 1478
        if (!$enumOk) {
244 94
            $this->fail(new EnumException('Enum failed, enum: ' . json_encode($this->enum) . ', data: ' . json_encode($data)), $path);
245
        }
246 1461
    }
247
248
    /**
249
     * @param mixed $data
250
     * @param string $path
251
     * @throws InvalidValue
252
     * @throws \Swaggest\JsonDiff\Exception
253
     */
254 43
    private function processConst($data, $path)
255
    {
256 43
        if ($this->const !== $data) {
257 37
            if ((is_object($this->const) && is_object($data))
258 37
                || (is_array($this->const) && is_array($data))) {
259 15
                $diff = new JsonDiff($this->const, $data,
260 15
                    JsonDiff::STOP_ON_DIFF);
261 15
                if ($diff->getDiffCnt() != 0) {
262 15
                    $this->fail(new ConstException('Const failed'), $path);
263
                }
264
            } else {
265 22
                $this->fail(new ConstException('Const failed'), $path);
266
            }
267
        }
268 20
    }
269
270
    /**
271
     * @param mixed $data
272
     * @param Context $options
273
     * @param string $path
274
     * @throws InvalidValue
275
     * @throws \Exception
276
     * @throws \Swaggest\JsonDiff\Exception
277
     */
278 69
    private function processNot($data, Context $options, $path)
279
    {
280 69
        $exception = false;
281
        try {
282 69
            self::unboolSchema($this->not)->process($data, $options, $path . '->not');
283 16
        } catch (InvalidValue $exception) {
284
            // Expected exception
285
        }
286 69
        if ($exception === false) {
287 55
            $this->fail(new LogicException('Not ' . json_encode($this->not) . ' expected, ' . json_encode($data) . ' received'), $path);
288
        }
289 16
    }
290
291
    /**
292
     * @param string $data
293
     * @param string $path
294
     * @throws InvalidValue
295
     */
296 2241
    private function processString($data, $path)
297
    {
298 2241
        if ($this->minLength !== null) {
299 38
            if (mb_strlen($data, 'UTF-8') < $this->minLength) {
300 9
                $this->fail(new StringException('String is too short', StringException::TOO_SHORT), $path);
301
            }
302
        }
303 2235
        if ($this->maxLength !== null) {
304 43
            if (mb_strlen($data, 'UTF-8') > $this->maxLength) {
305 19
                $this->fail(new StringException('String is too long', StringException::TOO_LONG), $path);
306
            }
307
        }
308 2232
        if ($this->pattern !== null) {
309 18
            if (0 === preg_match(Helper::toPregPattern($this->pattern), $data)) {
310 4
                $this->fail(new StringException(json_encode($data) . ' does not match to '
311 4
                    . $this->pattern, StringException::PATTERN_MISMATCH), $path);
312
            }
313
        }
314 2232
        if ($this->format !== null) {
315 409
            $validationError = Format::validationError($this->format, $data);
316 409
            if ($validationError !== null) {
317 137
                if (!($this->format === "uri" && substr($path, -3) === ':id')) {
318 122
                    $this->fail(new StringException($validationError), $path);
319
                }
320
            }
321
        }
322 2232
    }
323
324
    /**
325
     * @param float|int $data
326
     * @param string $path
327
     * @throws InvalidValue
328
     */
329 1048
    private function processNumeric($data, $path)
330
    {
331 1048
        if ($this->multipleOf !== null) {
332 39
            $div = $data / $this->multipleOf;
333 39
            if ($div != (int)$div) {
334 15
                $this->fail(new NumericException($data . ' is not multiple of ' . $this->multipleOf, NumericException::MULTIPLE_OF), $path);
335
            }
336
        }
337
338 1048
        if ($this->exclusiveMaximum !== null && !is_bool($this->exclusiveMaximum)) {
339 32
            if ($data >= $this->exclusiveMaximum) {
340 18
                $this->fail(new NumericException(
341 18
                    'Value less or equal than ' . $this->exclusiveMaximum . ' expected, ' . $data . ' received',
342 18
                    NumericException::MAXIMUM), $path);
343
            }
344
        }
345
346 1048
        if ($this->exclusiveMinimum !== null && !is_bool($this->exclusiveMinimum)) {
347 24
            if ($data <= $this->exclusiveMinimum) {
348 12
                $this->fail(new NumericException(
349 12
                    'Value more or equal than ' . $this->exclusiveMinimum . ' expected, ' . $data . ' received',
350 12
                    NumericException::MINIMUM), $path);
351
            }
352
        }
353
354 1048
        if ($this->maximum !== null) {
355 43
            if ($this->exclusiveMaximum === true) {
356 3
                if ($data >= $this->maximum) {
357 2
                    $this->fail(new NumericException(
358 2
                        'Value less or equal than ' . $this->maximum . ' expected, ' . $data . ' received',
359 3
                        NumericException::MAXIMUM), $path);
360
                }
361
            } else {
362 40
                if ($data > $this->maximum) {
363 13
                    $this->fail(new NumericException(
364 13
                        'Value less than ' . $this->minimum . ' expected, ' . $data . ' received',
365 13
                        NumericException::MAXIMUM), $path);
366
                }
367
            }
368
        }
369
370 1048
        if ($this->minimum !== null) {
371 516
            if ($this->exclusiveMinimum === true) {
372 93
                if ($data <= $this->minimum) {
373 2
                    $this->fail(new NumericException(
374 2
                        'Value more or equal than ' . $this->minimum . ' expected, ' . $data . ' received',
375 93
                        NumericException::MINIMUM), $path);
376
                }
377
            } else {
378 441
                if ($data < $this->minimum) {
379 43
                    $this->fail(new NumericException(
380 43
                        'Value more than ' . $this->minimum . ' expected, ' . $data . ' received',
381 43
                        NumericException::MINIMUM), $path);
382
                }
383
            }
384
        }
385 1047
    }
386
387
    /**
388
     * @param mixed $data
389
     * @param Context $options
390
     * @param string $path
391
     * @return array|mixed|null|object|\stdClass
392
     * @throws InvalidValue
393
     * @throws \Exception
394
     * @throws \Swaggest\JsonDiff\Exception
395
     */
396 105
    private function processOneOf($data, Context $options, $path)
397
    {
398 105
        $successes = 0;
399 105
        $failures = '';
400 105
        $subErrors = [];
401 105
        $skipValidation = false;
402 105
        if ($options->skipValidation) {
403 41
            $skipValidation = true;
404 41
            $options->skipValidation = false;
405
        }
406
407 105
        $result = $data;
408 105
        foreach ($this->oneOf as $index => $item) {
409
            try {
410 105
                $result = self::unboolSchema($item)->process($data, $options, $path . '->oneOf[' . $index . ']');
411 83
                $successes++;
412 83
                if ($successes > 1 || $options->skipValidation) {
413 83
                    break;
414
                }
415 73
            } catch (InvalidValue $exception) {
416 73
                $subErrors[$index] = $exception;
417 105
                $failures .= ' ' . $index . ': ' . Helper::padLines(' ', $exception->getMessage()) . "\n";
418
                // Expected exception
419
            }
420
        }
421 105
        if ($skipValidation) {
422 41
            $options->skipValidation = true;
423 41
            if ($successes === 0) {
424 8
                $result = self::unboolSchema($this->oneOf[0])->process($data, $options, $path . '->oneOf[0]');
425
            }
426
        }
427
428 105
        if (!$options->skipValidation) {
429 64
            if ($successes === 0) {
430 19
                $exception = new LogicException('No valid results for oneOf {' . "\n" . substr($failures, 0, -1) . "\n}");
431 19
                $exception->error = 'No valid results for oneOf';
432 19
                $exception->subErrors = $subErrors;
433 19
                $this->fail($exception, $path);
434 50
            } elseif ($successes > 1) {
435 17
                $exception = new LogicException('More than 1 valid result for oneOf: '
436 17
                    . $successes . '/' . count($this->oneOf) . ' valid results for oneOf {'
437 17
                    . "\n" . substr($failures, 0, -1) . "\n}");
438 17
                $exception->error = 'More than 1 valid result for oneOf';
439 17
                $exception->subErrors = $subErrors;
440 17
                $this->fail($exception, $path);
441
            }
442
        }
443 75
        return $result;
444
    }
445
446
    /**
447
     * @param mixed $data
448
     * @param Context $options
449
     * @param string $path
450
     * @return array|mixed|null|object|\stdClass
451
     * @throws InvalidValue
452
     * @throws \Exception
453
     * @throws \Swaggest\JsonDiff\Exception
454
     */
455 1716
    private function processAnyOf($data, Context $options, $path)
456
    {
457 1716
        $successes = 0;
458 1716
        $failures = '';
459 1716
        $subErrors = [];
460 1716
        $result = $data;
461 1716
        foreach ($this->anyOf as $index => $item) {
462
            try {
463 1716
                $result = self::unboolSchema($item)->process($data, $options, $path . '->anyOf[' . $index . ']');
464 1710
                $successes++;
465 1710
                if ($successes) {
466 1710
                    break;
467
                }
468 430
            } catch (InvalidValue $exception) {
469 430
                $subErrors[$index] = $exception;
470 430
                $failures .= ' ' . $index . ': ' . $exception->getMessage() . "\n";
471
                // Expected exception
472
            }
473
        }
474 1716
        if (!$successes && !$options->skipValidation) {
475 29
            $exception = new LogicException('No valid results for anyOf {' . "\n"
476 29
                . substr(Helper::padLines(' ', $failures, false), 0, -1)
477 29
                . "\n}");
478 29
            $exception->error = 'No valid results for anyOf';
479 29
            $exception->subErrors = $subErrors;
480 29
            $this->fail($exception, $path);
481
        }
482 1710
        return $result;
483
    }
484
485
    /**
486
     * @param mixed $data
487
     * @param Context $options
488
     * @param string $path
489
     * @return array|mixed|null|object|\stdClass
490
     * @throws InvalidValue
491
     * @throws \Exception
492
     * @throws \Swaggest\JsonDiff\Exception
493
     */
494 354
    private function processAllOf($data, Context $options, $path)
495
    {
496 354
        $result = $data;
497 354
        foreach ($this->allOf as $index => $item) {
498 354
            $result = self::unboolSchema($item)->process($data, $options, $path . '->allOf[' . $index . ']');
499
        }
500 316
        return $result;
501
    }
502
503
    /**
504
     * @param mixed $data
505
     * @param Context $options
506
     * @param string $path
507
     * @return array|mixed|null|object|\stdClass
508
     * @throws InvalidValue
509
     * @throws \Exception
510
     * @throws \Swaggest\JsonDiff\Exception
511
     */
512 26
    private function processIf($data, Context $options, $path)
513
    {
514 26
        $valid = true;
515
        try {
516 26
            self::unboolSchema($this->if)->process($data, $options, $path . '->if');
517 13
        } catch (InvalidValue $exception) {
518 13
            $valid = false;
519
        }
520 26
        if ($valid) {
521 18
            if ($this->then !== null) {
522 18
                return self::unboolSchema($this->then)->process($data, $options, $path . '->then');
523
            }
524
        } else {
525 13
            if ($this->else !== null) {
526 6
                return self::unboolSchema($this->else)->process($data, $options, $path . '->else');
527
            }
528
        }
529 10
        return null;
530
    }
531
532
    /**
533
     * @param \stdClass $data
534
     * @param Context $options
535
     * @param string $path
536
     * @throws InvalidValue
537
     */
538 161
    private function processObjectRequired($data, Context $options, $path)
539
    {
540 161
        if (isset($this->__dataToProperty[$options->mapping])) {
541 2
            if ($options->import) {
542 1
                foreach ($this->required as $item) {
543 1
                    if (isset($this->__propertyToData[$options->mapping][$item])) {
544 1
                        $item = $this->__propertyToData[$options->mapping][$item];
545
                    }
546 1
                    if (!property_exists($data, $item)) {
547 1
                        $this->fail(new ObjectException('Required property missing: ' . $item . ', data: ' . json_encode($data, JSON_UNESCAPED_SLASHES), ObjectException::REQUIRED), $path);
548
                    }
549
                }
550
            } else {
551 2
                foreach ($this->required as $item) {
552 2
                    if (isset($this->__dataToProperty[$options->mapping][$item])) {
553
                        $item = $this->__dataToProperty[$options->mapping][$item];
554
                    }
555 2
                    if (!property_exists($data, $item)) {
556 2
                        $this->fail(new ObjectException('Required property missing: ' . $item . ', data: ' . json_encode($data, JSON_UNESCAPED_SLASHES), ObjectException::REQUIRED), $path);
557
                    }
558
                }
559
            }
560
561
        } else {
562 160
            foreach ($this->required as $item) {
563 156
                if (!property_exists($data, $item)) {
564 156
                    $this->fail(new ObjectException('Required property missing: ' . $item . ', data: ' . json_encode($data, JSON_UNESCAPED_SLASHES), ObjectException::REQUIRED), $path);
565
                }
566
            }
567
        }
568 129
    }
569
570
    /**
571
     * @param \stdClass $data
572
     * @param Context $options
573
     * @param string $path
574
     * @param ObjectItemContract|null $result
575
     * @return array|null|ClassStructure|ObjectItemContract
576
     * @throws InvalidValue
577
     * @throws \Exception
578
     * @throws \Swaggest\JsonDiff\Exception
579
     */
580 3153
    private function processObject($data, Context $options, $path, $result = null)
581
    {
582 3153
        $import = $options->import;
583
584 3153
        if (!$options->skipValidation && $this->required !== null) {
585 161
            $this->processObjectRequired($data, $options, $path);
586
        }
587
588 3152
        if ($import) {
589 3138
            if (!$options->validateOnly) {
590
591 3138
                if ($this->useObjectAsArray) {
592 1
                    $result = array();
593 3137
                } elseif (!$result instanceof ObjectItemContract) {
594
                    //* todo check performance impact
595 3137
                    if (null === $this->objectItemClass) {
596 1156
                        $result = new ObjectItem();
597
                    } else {
598 3126
                        $className = $this->objectItemClass;
599 3126
                        if ($options->objectItemClassMapping !== null) {
600
                            if (isset($options->objectItemClassMapping[$className])) {
601
                                $className = $options->objectItemClassMapping[$className];
602
                            }
603
                        }
604 3126
                        $result = new $className;
605
                    }
606
                    //*/
607
608
609 3137
                    if ($result instanceof ClassStructure) {
610 3125
                        if ($result->__validateOnSet) {
0 ignored issues
show
Bug Best Practice introduced by
The property __validateOnSet does not exist on Swaggest\JsonSchema\Structure\ObjectItem. Since you implemented __get, consider adding a @property annotation.
Loading history...
611 3125
                            $result->__validateOnSet = false;
0 ignored issues
show
Bug Best Practice introduced by
The property __validateOnSet does not exist on Swaggest\JsonSchema\Structure\ObjectItem. Since you implemented __set, consider adding a @property annotation.
Loading history...
612
                            /** @noinspection PhpUnusedLocalVariableInspection */
613
                            /* todo check performance impact
0 ignored issues
show
Unused Code Comprehensibility introduced by
43% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
614
                            $validateOnSetHandler = new ScopeExit(function () use ($result) {
615
                                $result->__validateOnSet = true;
616
                            });
617
                            //*/
618
                        }
619
                    }
620
621
                    //* todo check performance impact
622 3137
                    if ($result instanceof ObjectItemContract) {
623 3137
                        $result->setDocumentPath($path);
624
                    }
625
                    //*/
626
                }
627
            }
628
        }
629
630
        // @todo better check for schema id
631
632 3152
        if ($import
633 3152
            && isset($data->{Schema::PROP_ID_D4})
634 3152
            && ($options->version === Schema::VERSION_DRAFT_04 || $options->version === Schema::VERSION_AUTO)
635 3152
            && is_string($data->{Schema::PROP_ID_D4})) {
636 35
            $id = $data->{Schema::PROP_ID_D4};
637 35
            $refResolver = $options->refResolver;
638 35
            $parentScope = $refResolver->updateResolutionScope($id);
639
            /** @noinspection PhpUnusedLocalVariableInspection */
640
            $defer = new ScopeExit(function () use ($parentScope, $refResolver) {
0 ignored issues
show
Unused Code introduced by
The assignment to $defer is dead and can be removed.
Loading history...
641 35
                $refResolver->setResolutionScope($parentScope);
642 35
            });
643
        }
644
645 3152
        if ($import
646 3152
            && isset($data->{self::PROP_ID})
647 3152
            && ($options->version >= Schema::VERSION_DRAFT_06 || $options->version === Schema::VERSION_AUTO)
648 3152
            && is_string($data->{self::PROP_ID})) {
649 114
            $id = $data->{self::PROP_ID};
650 114
            $refResolver = $options->refResolver;
651 114
            $parentScope = $refResolver->updateResolutionScope($id);
652
            /** @noinspection PhpUnusedLocalVariableInspection */
653
            $defer = new ScopeExit(function () use ($parentScope, $refResolver) {
654 114
                $refResolver->setResolutionScope($parentScope);
655 114
            });
656
        }
657
658 3152
        if ($import) {
659
            try {
660
661 3138
                $refProperty = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $refProperty is dead and can be removed.
Loading history...
662 3138
                $dereference = true;
663
664 3138
                if (isset($data->{self::PROP_REF})) {
665 431
                    if (null === $refProperty = $this->properties[self::PROP_REF]) {
666 431
                        if (isset($this->__dataToProperty[$options->mapping][self::PROP_REF])) {
667 419
                            $refProperty = $this->properties[$this->__dataToProperty[$options->mapping][self::PROP_REF]];
668
                        }
669
                    }
670
671 431
                    if (isset($refProperty) && ($refProperty->format !== Format::URI_REFERENCE)) {
672 14
                        $dereference = false;
673
                    }
674
                }
675
676
                if (
677 3138
                    isset($data->{self::PROP_REF})
678 3138
                    && is_string($data->{self::PROP_REF})
679 3138
                    && $dereference
680
                ) {
681 419
                    $refString = $data->{self::PROP_REF};
682
683
                    // todo check performance impact
684 419
                    if ($refString === 'http://json-schema.org/draft-04/schema#'
685 409
                        || $refString === 'http://json-schema.org/draft-06/schema#'
686 419
                        || $refString === 'http://json-schema.org/draft-07/schema#') {
687 26
                        return Schema::schema();
688
                    }
689
690
                    // TODO consider process # by reference here ?
691 393
                    $refResolver = $options->refResolver;
692 393
                    $preRefScope = $refResolver->getResolutionScope();
693
                    /** @noinspection PhpUnusedLocalVariableInspection */
694
                    $deferRefScope = new ScopeExit(function () use ($preRefScope, $refResolver) {
0 ignored issues
show
Unused Code introduced by
The assignment to $deferRefScope is dead and can be removed.
Loading history...
695 393
                        $refResolver->setResolutionScope($preRefScope);
696 393
                    });
697
698 393
                    $ref = $refResolver->resolveReference($refString);
699 393
                    $data = self::unboolSchemaData($ref->getData());
700 393
                    if (!$options->validateOnly) {
701 393
                        if ($ref->isImported()) {
702 174
                            $refResult = $ref->getImported();
703 174
                            return $refResult;
704
                        }
705 393
                        $ref->setImported($result);
706
                        try {
707 393
                            $refResult = $this->process($data, $options, $path . '->$ref:' . $refString, $result);
708 393
                            if ($refResult instanceof ObjectItemContract) {
709 393
                                $refResult->setFromRef($refString);
710
                            }
711 393
                            $ref->setImported($refResult);
712 1
                        } catch (InvalidValue $exception) {
713 1
                            $ref->unsetImported();
714 1
                            throw $exception;
715
                        }
716 393
                        return $refResult;
717
                    } else {
718 3138
                        $this->process($data, $options, $path . '->$ref:' . $refString);
719
                    }
720
                }
721 1
            } catch (InvalidValue $exception) {
722 1
                $this->fail($exception, $path);
723
            }
724
        }
725
726
        /** @var Schema[]|null $properties */
727 3152
        $properties = null;
728
729 3152
        $nestedProperties = null;
730 3152
        if ($this->properties !== null) {
731 3138
            $properties = $this->properties->toArray(); // todo call directly
732 3138
            if ($this->properties instanceof Properties) {
733 3138
                $nestedProperties = $this->properties->nestedProperties;
0 ignored issues
show
Bug Best Practice introduced by
The property nestedProperties does not exist on Swaggest\JsonSchema\Schema. Since you implemented __get, consider adding a @property annotation.
Loading history...
734
            } else {
735 579
                $nestedProperties = array();
736
            }
737
        }
738
739 3152
        $array = array();
740 3152
        if (!empty($this->__dataToProperty[$options->mapping])) { // todo skip on $options->validateOnly
741 3120
            foreach ((array)$data as $key => $value) {
742 3116
                if ($import) {
743 3113
                    if (isset($this->__dataToProperty[$options->mapping][$key])) {
744 3113
                        $key = $this->__dataToProperty[$options->mapping][$key];
745
                    }
746
                } else {
747 23
                    if (isset($this->__propertyToData[$options->mapping][$key])) {
748 1
                        $key = $this->__propertyToData[$options->mapping][$key];
749
                    }
750
                }
751 3120
                $array[$key] = $value;
752
            }
753
        } else {
754 1235
            $array = (array)$data;
755
        }
756
757 3152
        if (!$options->skipValidation) {
758 3131
            if ($this->minProperties !== null && count($array) < $this->minProperties) {
759 4
                $this->fail(new ObjectException("Not enough properties", ObjectException::TOO_FEW), $path);
760
            }
761 3131
            if ($this->maxProperties !== null && count($array) > $this->maxProperties) {
762 3
                $this->fail(new ObjectException("Too many properties", ObjectException::TOO_MANY), $path);
763
            }
764 3131
            if ($this->propertyNames !== null) {
765 17
                $propertyNames = self::unboolSchema($this->propertyNames);
766 17
                foreach ($array as $key => $tmp) {
767 10
                    $propertyNames->process($key, $options, $path . '->propertyNames:' . $key);
768
                }
769
            }
770
        }
771
772 3152
        $defaultApplied = array();
773 3152
        if ($import
774 3152
            && !$options->validateOnly
775 3152
            && $options->applyDefaults
776 3152
            && $properties !== null
777
        ) {
778 38
            foreach ($properties as $key => $property) {
779
                // todo check when property is \stdClass `{}` here (RefTest)
780 36
                if ($property instanceof SchemaContract && null !== $default = $property->getDefault()) {
781 6
                    if (isset($this->__dataToProperty[$options->mapping][$key])) {
782
                        $key = $this->__dataToProperty[$options->mapping][$key];
783
                    }
784 6
                    if (!array_key_exists($key, $array)) {
785 6
                        $defaultApplied[$key] = true;
786 36
                        $array[$key] = $default;
787
                    }
788
                }
789
            }
790
        }
791
792 3152
        foreach ($array as $key => $value) {
793 3142
            if ($key === '' && PHP_VERSION_ID < 71000) {
794 1
                $this->fail(new InvalidValue('Empty property name'), $path);
795
            }
796
797 3141
            $found = false;
798
799 3141
            if (!$options->skipValidation && !empty($this->dependencies)) {
800 73
                $deps = $this->dependencies;
801 73
                if (isset($deps->$key)) {
802 63
                    $dependencies = $deps->$key;
803 63
                    $dependencies = self::unboolSchema($dependencies);
804 63
                    if ($dependencies instanceof SchemaContract) {
805 29
                        $dependencies->process($data, $options, $path . '->dependencies:' . $key);
806
                    } else {
807 34
                        foreach ($dependencies as $item) {
808 31
                            if (!property_exists($data, $item)) {
809 18
                                $this->fail(new ObjectException('Dependency property missing: ' . $item,
810 31
                                    ObjectException::DEPENDENCY_MISSING), $path);
811
                            }
812
                        }
813
                    }
814
                }
815
            }
816
817 3141
            $propertyFound = false;
818 3141
            if (isset($properties[$key])) {
819
                /** @var Schema[] $properties */
820 3131
                $prop = self::unboolSchema($properties[$key]);
821 3131
                $propertyFound = true;
822 3131
                $found = true;
823 3131
                if ($prop instanceof SchemaContract) {
824 3074
                    $value = $prop->process(
825 3074
                        $value,
826 3074
                        isset($defaultApplied[$key]) ? $options->withDefault() : $options,
827 3074
                        $path . '->properties:' . $key
828
                    );
829
                }
830
            }
831
832
            /** @var Egg[] $nestedEggs */
833 3136
            $nestedEggs = null;
834 3136
            if (isset($nestedProperties[$key])) {
835 6
                $found = true;
836 6
                $nestedEggs = $nestedProperties[$key];
837
                // todo iterate all nested props?
838 6
                $value = self::unboolSchema($nestedEggs[0]->propertySchema)->process($value, $options, $path . '->nestedProperties:' . $key);
839
            }
840
841 3136
            if ($this->patternProperties !== null) {
842 166
                foreach ($this->patternProperties as $pattern => $propertySchema) {
843 166
                    if (preg_match(Helper::toPregPattern($pattern), $key)) {
844 120
                        $found = true;
845 120
                        $value = self::unboolSchema($propertySchema)->process($value, $options,
846 120
                            $path . '->patternProperties[' . strtr($pattern, array('~' => '~1', ':' => '~2')) . ']:' . $key);
847 93
                        if (!$options->validateOnly && $import) {
848 144
                            $result->addPatternPropertyName($pattern, $key);
0 ignored issues
show
Bug introduced by
The method addPatternPropertyName() does not exist on Swaggest\JsonSchema\Structure\ObjectItemContract. It seems like you code against a sub-type of said class. However, the method does not exist in Swaggest\JsonSchema\Stru...\ClassStructureContract. Are you sure you never get one of those? ( Ignorable by Annotation )

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

848
                            $result->/** @scrutinizer ignore-call */ 
849
                                     addPatternPropertyName($pattern, $key);
Loading history...
849
                        }
850
                        //break; // todo manage multiple import data properly (pattern accessor)
851
                    }
852
                }
853
            }
854 3136
            if (!$found && $this->additionalProperties !== null) {
855 995
                if (!$options->skipValidation && $this->additionalProperties === false) {
856 12
                    $this->fail(new ObjectException('Additional properties not allowed: ' . $key), $path);
857
                }
858
859 995
                if ($this->additionalProperties instanceof SchemaContract) {
860 995
                    $value = $this->additionalProperties->process($value, $options, $path . '->additionalProperties:' . $key);
861
                }
862
863 988
                if ($import && !$this->useObjectAsArray && !$options->validateOnly) {
864 985
                    $result->addAdditionalPropertyName($key);
865
                }
866
            }
867
868 3132
            if (!$options->validateOnly && $nestedEggs && $import) {
869 5
                foreach ($nestedEggs as $nestedEgg) {
870 5
                    $result->setNestedProperty($key, $value, $nestedEgg);
871
                }
872 5
                if ($propertyFound) {
873 5
                    $result->$key = $value;
874
                }
875
            } else {
876 3131
                if ($this->useObjectAsArray && $import) {
877 1
                    $result[$key] = $value;
878
                } else {
879 3131
                    if ($found || !$import) {
880 3130
                        $result->$key = $value;
881 1141
                    } elseif (!isset($result->$key)) {
882 3132
                        $result->$key = $value;
883
                    }
884
                }
885
            }
886
        }
887
888 3140
        return $result;
889
    }
890
891
    /**
892
     * @param array $data
893
     * @param Context $options
894
     * @param string $path
895
     * @param array $result
896
     * @return mixed
897
     * @throws InvalidValue
898
     * @throws \Exception
899
     * @throws \Swaggest\JsonDiff\Exception
900
     */
901 1217
    private function processArray($data, Context $options, $path, $result)
902
    {
903 1217
        $count = count($data);
904 1217
        if (!$options->skipValidation) {
905 1017
            if ($this->minItems !== null && $count < $this->minItems) {
906 9
                $this->fail(new ArrayException("Not enough items in array"), $path);
907
            }
908
909 1011
            if ($this->maxItems !== null && $count > $this->maxItems) {
910 6
                $this->fail(new ArrayException("Too many items in array"), $path);
911
            }
912
        }
913
914 1205
        $pathItems = 'items';
915 1205
        $this->items = self::unboolSchema($this->items);
916 1205
        if ($this->items instanceof SchemaContract) {
0 ignored issues
show
introduced by
$this->items is never a sub-type of Swaggest\JsonSchema\SchemaContract.
Loading history...
917 852
            $items = array();
918 852
            $additionalItems = $this->items;
919 690
        } elseif ($this->items === null) { // items defaults to empty schema so everything is valid
920 690
            $items = array();
921 690
            $additionalItems = true;
922
        } else { // listed items
923 104
            $items = $this->items;
924 104
            $additionalItems = $this->additionalItems;
925 104
            $pathItems = 'additionalItems';
926
        }
927
928
        /**
929
         * @var Schema|Schema[] $items
930
         * @var null|bool|Schema $additionalItems
931
         */
932 1205
        $itemsLen = is_array($items) ? count($items) : 0;
933 1205
        $index = 0;
934 1205
        foreach ($result as $key => $value) {
935 1080
            if ($index < $itemsLen) {
936 94
                $itemSchema = self::unboolSchema($items[$index]);
937 94
                $result[$key] = $itemSchema->process($value, $options, $path . '->items:' . $index);
938
            } else {
939 1080
                if ($additionalItems instanceof SchemaContract) {
940 826
                    $result[$key] = $additionalItems->process($value, $options, $path . '->' . $pathItems
941 826
                        . '[' . $index . ']:' . $index);
942 574
                } elseif (!$options->skipValidation && $additionalItems === false) {
943 6
                    $this->fail(new ArrayException('Unexpected array item'), $path);
944
                }
945
            }
946 1060
            ++$index;
947
        }
948
949 1181
        if (!$options->skipValidation && $this->uniqueItems) {
950 508
            if (!UniqueItems::isValid($data)) {
951 19
                $this->fail(new ArrayException('Array is not unique'), $path);
952
            }
953
        }
954
955 1162
        if (!$options->skipValidation && $this->contains !== null) {
956
            /** @var Schema|bool $contains */
957 36
            $contains = $this->contains;
958 36
            if ($contains === false) {
959 4
                $this->fail(new ArrayException('Contains is false'), $path);
960
            }
961 32
            if ($count === 0) {
962 7
                $this->fail(new ArrayException('Empty array fails contains constraint'), $path);
963
            }
964 25
            if ($contains === true) {
965 2
                $contains = self::unboolSchema($contains);
966
            }
967 25
            $containsOk = false;
968 25
            foreach ($data as $key => $item) {
969
                try {
970 25
                    $contains->process($item, $options, $path . '->' . $key);
971 17
                    $containsOk = true;
972 17
                    break;
973 21
                } catch (InvalidValue $exception) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
974
                }
975
            }
976 25
            if (!$containsOk) {
977 8
                $this->fail(new ArrayException('Array fails contains constraint'), $path);
978
            }
979
        }
980 1143
        return $result;
981
    }
982
983
    /**
984
     * @param mixed|string $data
985
     * @param Context $options
986
     * @param string $path
987
     * @return bool|mixed|string
988
     * @throws InvalidValue
989
     */
990 14
    private function processContent($data, Context $options, $path)
991
    {
992
        try {
993 14
            if ($options->unpackContentMediaType) {
994 7
                return Content::process($options, $this->contentEncoding, $this->contentMediaType, $data, $options->import);
995
            } else {
996 7
                Content::process($options, $this->contentEncoding, $this->contentMediaType, $data, true);
997
            }
998 4
        } catch (InvalidValue $exception) {
999 4
            $this->fail($exception, $path);
1000
        }
1001 7
        return $data;
1002
    }
1003
1004
    /**
1005
     * @param mixed $data
1006
     * @param Context $options
1007
     * @param string $path
1008
     * @param mixed|null $result
1009
     * @return array|mixed|null|object|\stdClass
1010
     * @throws InvalidValue
1011
     * @throws \Exception
1012
     * @throws \Swaggest\JsonDiff\Exception
1013
     */
1014 3199
    public function process($data, Context $options, $path = '#', $result = null)
1015
    {
1016 3199
        $import = $options->import;
1017
1018 3199
        if (!$import && $data instanceof SchemaExporter) {
1019 1
            $data = $data->exportSchema(); // Used to export ClassStructure::schema()
1020
        }
1021
1022 3199
        if (!$import && $data instanceof ObjectItemContract) {
1023 787
            $result = new \stdClass();
1024
1025 787
            if ('#' === $path) {
1026 774
                $injectDefinitions = new ScopeExit(function () use ($result, $options) {
0 ignored issues
show
Unused Code introduced by
The assignment to $injectDefinitions is dead and can be removed.
Loading history...
1027 774
                    foreach ($options->exportedDefinitions as $ref => $data) {
1028 4
                        JsonPointer::add($result, JsonPointer::splitPath($ref), $data,
1029 4
                            JsonPointer::SKIP_IF_ISSET + JsonPointer::RECURSIVE_KEY_CREATION);
1030
                    }
1031 774
                });
1032
            }
1033
1034 787
            if ('#' !== $path && $ref = $data->getFromRef()) {
1035 4
                if ($ref[0] === '#') {
1036 4
                    if (isset($options->exportedDefinitions[$ref])) {
1037 1
                        $result->{self::PROP_REF} = $ref;
1038 1
                        return $result;
1039 4
                    } elseif (!array_key_exists($ref, $options->exportedDefinitions)) {
1040 4
                        $exported = null;
1041 4
                        $options->exportedDefinitions[$ref] = &$exported;
1042 4
                        $exported = $this->process($data, $options, $ref);
0 ignored issues
show
Unused Code introduced by
The assignment to $exported is dead and can be removed.
Loading history...
1043 4
                        $result->{self::PROP_REF} = $ref;
1044 4
                        return $result;
1045
                    }
1046
                }
1047
            }
1048
1049 787
            if ($options->circularReferences->contains($data)) {
1050
                /** @noinspection PhpIllegalArrayKeyTypeInspection */
1051 1
                $path = $options->circularReferences[$data];
1052 1
                $result->{self::PROP_REF} = PointerUtil::getDataPointer($path, true);
1053 1
                return $result;
1054
            }
1055 787
            $options->circularReferences->attach($data, $path);
1056
1057 787
            $data = $data->jsonSerialize();
1058
        }
1059
1060 3199
        $path .= $this->getFromRefPath();
1061
1062 3199
        if (!$import && is_array($data) && $this->useObjectAsArray) {
1063 1
            $data = (object)$data;
1064
        }
1065
1066 3199
        if (null !== $options->dataPreProcessor) {
1067
            $data = $options->dataPreProcessor->process($data, $this, $import);
1068
        }
1069
1070 3199
        if ($result === null) {
1071 3199
            $result = $data;
1072
        }
1073
1074 3199
        if ($options->skipValidation) {
1075 1416
            goto skipValidation;
1076
        }
1077
1078 3161
        if ($this->type !== null) {
1079 3140
            $this->processType($data, $options, $path);
1080
        }
1081
1082 3160
        if ($this->enum !== null) {
1083 1478
            $this->processEnum($data, $path);
1084
        }
1085
1086 3160
        if (array_key_exists(self::CONST_PROPERTY, $this->__arrayOfData)) {
1087 43
            $this->processConst($data, $path);
1088
        }
1089
1090 3160
        if ($this->not !== null) {
1091 69
            $this->processNot($data, $options, $path);
1092
        }
1093
1094 3160
        if (is_string($data)) {
1095 2241
            $this->processString($data, $path);
1096
        }
1097
1098 3160
        if (is_int($data) || is_float($data)) {
1099 1048
            $this->processNumeric($data, $path);
1100
        }
1101
1102 3160
        if ($this->if !== null) {
1103 26
            $result = $this->processIf($data, $options, $path);
1104
        }
1105
1106
        skipValidation:
1107
1108 3198
        if ($this->oneOf !== null) {
1109 105
            $result = $this->processOneOf($data, $options, $path);
1110
        }
1111
1112 3198
        if ($this->anyOf !== null) {
1113 1716
            $result = $this->processAnyOf($data, $options, $path);
1114
        }
1115
1116 3198
        if ($this->allOf !== null) {
1117 354
            $result = $this->processAllOf($data, $options, $path);
1118
        }
1119
1120 3198
        if ($data instanceof \stdClass) {
1121 3153
            $result = $this->processObject($data, $options, $path, $result);
1122
        }
1123
1124 3195
        if (is_array($data)) {
1125 1217
            $result = $this->processArray($data, $options, $path, $result);
0 ignored issues
show
Bug introduced by
It seems like $result can also be of type object and stdClass and Swaggest\JsonSchema\Schema and Swaggest\JsonSchema\Structure\ObjectItemContract and Swaggest\JsonSchema\Structure\ClassStructure; however, parameter $result of Swaggest\JsonSchema\Schema::processArray() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

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

1125
            $result = $this->processArray($data, $options, $path, /** @scrutinizer ignore-type */ $result);
Loading history...
1126
        }
1127
1128 3195
        if ($this->contentEncoding !== null || $this->contentMediaType !== null) {
1129 14
            $result = $this->processContent($data, $options, $path);
1130
        }
1131
1132 3195
        return $result;
1133
    }
1134
1135
    /**
1136
     * @param boolean $useObjectAsArray
1137
     * @return Schema
1138
     */
1139 1
    public function setUseObjectAsArray($useObjectAsArray)
1140
    {
1141 1
        $this->useObjectAsArray = $useObjectAsArray;
1142 1
        return $this;
1143
    }
1144
1145
    /**
1146
     * @param InvalidValue $exception
1147
     * @param string $path
1148
     * @throws InvalidValue
1149
     */
1150 1223
    private function fail(InvalidValue $exception, $path)
1151
    {
1152 1223
        if ($path !== '#') {
1153 756
            $exception->addPath($path);
1154
        }
1155 1223
        throw $exception;
1156
    }
1157
1158 13
    public static function integer()
1159
    {
1160 13
        $schema = new static();
1161 13
        $schema->type = Type::INTEGER;
1162 13
        return $schema;
1163
    }
1164
1165 4
    public static function number()
1166
    {
1167 4
        $schema = new static();
1168 4
        $schema->type = Type::NUMBER;
1169 4
        return $schema;
1170
    }
1171
1172 10
    public static function string()
1173
    {
1174 10
        $schema = new static();
1175 10
        $schema->type = Type::STRING;
1176 10
        return $schema;
1177
    }
1178
1179 4
    public static function boolean()
1180
    {
1181 4
        $schema = new static();
1182 4
        $schema->type = Type::BOOLEAN;
1183 4
        return $schema;
1184
    }
1185
1186 6
    public static function object()
1187
    {
1188 6
        $schema = new static();
1189 6
        $schema->type = Type::OBJECT;
1190 6
        return $schema;
1191
    }
1192
1193 1
    public static function arr()
1194
    {
1195 1
        $schema = new static();
1196 1
        $schema->type = Type::ARR;
1197 1
        return $schema;
1198
    }
1199
1200
    public static function null()
1201
    {
1202
        $schema = new static();
1203
        $schema->type = Type::NULL;
1204
        return $schema;
1205
    }
1206
1207
1208
    /**
1209
     * @param Properties $properties
1210
     * @return Schema
1211
     */
1212 3
    public function setProperties($properties)
1213
    {
1214 3
        $this->properties = $properties;
1215 3
        return $this;
1216
    }
1217
1218
    /**
1219
     * @param string $name
1220
     * @param Schema $schema
1221
     * @return $this
1222
     */
1223 3
    public function setProperty($name, $schema)
1224
    {
1225 3
        if (null === $this->properties) {
1226 3
            $this->properties = new Properties();
1227
        }
1228 3
        $this->properties->__set($name, $schema);
1229 3
        return $this;
1230
    }
1231
1232
    /**
1233
     * @param string $name
1234
     * @param SchemaContract $schema
1235
     * @return $this
1236
     * @throws Exception
1237
     */
1238
    public function setPatternProperty($name, $schema)
1239
    {
1240
        if (null === $this->patternProperties) {
1241
            $this->patternProperties = new Properties();
1242
        }
1243
        $this->patternProperties->__set($name, $schema);
1244
        return $this;
1245
    }
1246
1247
1248
    /** @var mixed[] */
1249
    private $metaItems = array();
1250
1251 1
    public function addMeta($meta, $name = null)
1252
    {
1253 1
        if ($name === null) {
1254 1
            $name = get_class($meta);
1255
        }
1256 1
        $this->metaItems[$name] = $meta;
1257 1
        return $this;
1258
    }
1259
1260 1
    public function getMeta($name)
1261
    {
1262 1
        if (isset($this->metaItems[$name])) {
1263 1
            return $this->metaItems[$name];
1264
        }
1265
        return null;
1266
    }
1267
1268
    /**
1269
     * @param Context $options
1270
     * @return ObjectItemContract
1271
     */
1272 5
    public function makeObjectItem(Context $options = null)
1273
    {
1274 5
        if (null === $this->objectItemClass) {
1275
            return new ObjectItem();
1276
        } else {
1277 5
            $className = $this->objectItemClass;
1278 5
            if ($options !== null) {
1279
                if (isset($options->objectItemClassMapping[$className])) {
1280
                    $className = $options->objectItemClassMapping[$className];
1281
                }
1282
            }
1283 5
            return new $className;
1284
        }
1285
    }
1286
1287
    /**
1288
     * @param mixed $schema
1289
     * @return mixed|Schema
1290
     */
1291 3211
    private static function unboolSchema($schema)
1292
    {
1293 3211
        static $trueSchema;
1294 3211
        static $falseSchema;
1295
1296 3211
        if (null === $trueSchema) {
1297 1
            $trueSchema = new Schema();
1298 1
            $trueSchema->__booleanSchema = true;
1299 1
            $falseSchema = new Schema();
1300 1
            $falseSchema->not = $trueSchema;
1301 1
            $falseSchema->__booleanSchema = false;
1302
        }
1303
1304 3211
        if ($schema === true) {
1305 108
            return $trueSchema;
1306 3183
        } elseif ($schema === false) {
1307 94
            return $falseSchema;
1308
        } else {
1309 3151
            return $schema;
1310
        }
1311
    }
1312
1313
    /**
1314
     * @param mixed $data
1315
     * @return \stdClass
1316
     */
1317 393
    private static function unboolSchemaData($data)
1318
    {
1319 393
        static $trueSchema;
1320 393
        static $falseSchema;
1321
1322 393
        if (null === $trueSchema) {
1323 1
            $trueSchema = new \stdClass();
1324 1
            $falseSchema = new \stdClass();
1325 1
            $falseSchema->not = $trueSchema;
1326
        }
1327
1328 393
        if ($data === true) {
1329 6
            return $trueSchema;
1330 389
        } elseif ($data === false) {
1331 5
            return $falseSchema;
1332
        } else {
1333 384
            return $data;
1334
        }
1335
    }
1336
1337 36
    public function getDefault()
1338
    {
1339 36
        return $this->default;
1340
    }
1341
1342 92
    public function getProperties()
1343
    {
1344 92
        return $this->properties;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->properties also could return the type Swaggest\JsonSchema\Schema which is incompatible with the return type mandated by Swaggest\JsonSchema\Sche...ntract::getProperties() of null|Swaggest\JsonSchema...a\Constraint\Properties.
Loading history...
1345
    }
1346
1347
    public function getObjectItemClass()
1348
    {
1349
        return $this->objectItemClass;
1350
    }
1351
1352
    /**
1353
     * @return string[]
1354
     */
1355 90
    public function getPropertyNames()
1356
    {
1357 90
        return array_keys($this->getProperties()->toArray());
1358
    }
1359
1360
    /**
1361
     * @return string[]
1362
     */
1363 90
    public function getNestedPropertyNames()
1364
    {
1365 90
        return $this->getProperties()->nestedPropertyNames;
1366
    }
1367
1368
}
1369