Completed
Push — master ( 6f57c0...93a727 )
by Viacheslav
14:19 queued 04:20
created

Schema::processConst()   C

Complexity

Conditions 12
Paths 4

Size

Total Lines 18
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 12

Importance

Changes 0
Metric Value
eloc 12
c 0
b 0
f 0
dl 0
loc 18
ccs 5
cts 5
cp 1
rs 6.9666
cc 12
nop 2
crap 12
nc 4

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
use Swaggest\JsonSchema\Structure\WithResolvedValue;
29
30
/**
31
 * Class Schema
32
 * @package Swaggest\JsonSchema
33
 */
34
class Schema extends JsonSchema implements MetaHolder, SchemaContract
35
{
36
    const ENUM_NAMES_PROPERTY = 'x-enum-names';
37
    const CONST_PROPERTY = 'const';
38
39
    const DEFAULT_MAPPING = 'default';
40
41
    const VERSION_AUTO = 'a';
42
    const VERSION_DRAFT_04 = 4;
43
    const VERSION_DRAFT_06 = 6;
44
    const VERSION_DRAFT_07 = 7;
45
46
    const PROP_REF = '$ref';
47
    const PROP_ID = '$id';
48
    const PROP_ID_D4 = 'id';
49
50
    // Object
51
    /** @var null|Properties */
52
    public $properties;
53
    /** @var SchemaContract|bool */
54
    public $additionalProperties;
55
    /** @var SchemaContract[]|Properties */
56
    public $patternProperties;
57
    /** @var string[][]|Schema[]|\stdClass */
58
    public $dependencies;
59
60
    // Array
61
    /** @var null|SchemaContract|SchemaContract[] */
62
    public $items;
63
    /** @var null|SchemaContract|bool */
64
    public $additionalItems;
65
66
    /** @var SchemaContract[] */
67
    public $allOf;
68
    /** @var SchemaContract */
69
    public $not;
70
    /** @var SchemaContract[] */
71
    public $anyOf;
72
    /** @var SchemaContract[] */
73
    public $oneOf;
74
75
    /** @var SchemaContract */
76
    public $if;
77
    /** @var SchemaContract */
78
    public $then;
79
    /** @var SchemaContract */
80
    public $else;
81
82
83
    public $objectItemClass;
84
85
    /**
86
     * @todo check usages/deprecate
87
     * @var bool
88
     */
89
    private $useObjectAsArray = false;
90
91
    private $__booleanSchema;
92 4
93
    public function addPropertyMapping($dataName, $propertyName, $mapping = self::DEFAULT_MAPPING)
94 4
    {
95
        if (null === $this->properties) {
96
            $this->properties = new Properties();
97
        }
98 4
99 4
        $this->properties->addPropertyMapping($dataName, $propertyName, $mapping);
100
        return $this;
101
    }
102
103
    /**
104
     * @param mixed $data
105
     * @param Context|null $options
106
     * @return SchemaContract
107
     * @throws Exception
108
     * @throws InvalidValue
109
     * @throws \Exception
110 3298
     */
111
    public static function import($data, Context $options = null)
112 3298
    {
113 33
        if (null === $options) {
114
            $options = new Context();
115
        }
116 3298
117
        $options->applyDefaults = false;
118 3298
119
        if (isset($options->schemasCache) && is_object($data)) {
120
            if ($options->schemasCache->contains($data)) {
121
                return $options->schemasCache->offsetGet($data);
122
            } else {
123
                $schema = parent::import($data, $options);
124
                $options->schemasCache->attach($data, $schema);
125
                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...
126
            }
127
        }
128
129 3298
        // string $data is expected to be $ref uri
130 6
        if (is_string($data)) {
131
            $data = (object)array(self::PROP_REF => $data);
132
        }
133 3298
134 3298
        $data = self::unboolSchema($data);
135 72
        if ($data instanceof SchemaContract) {
136
            return $data;
137
        }
138 3226
139
        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...
140
    }
141
142
    /**
143
     * @param mixed $data
144
     * @param Context|null $options
145
     * @return array|mixed|null|object|\stdClass
146
     * @throws Exception
147
     * @throws InvalidValue
148
     * @throws \Exception
149 3323
     */
150
    public function in($data, Context $options = null)
151 3323
    {
152 72
        if (null !== $this->__booleanSchema) {
153 36
            if ($this->__booleanSchema) {
154 36
                return $data;
155 18
            } elseif (empty($options->skipValidation)) {
156
                $this->fail(new InvalidValue('Denied by false schema'), '#');
157
            }
158
        }
159 3269
160 69
        if ($options === null) {
161
            $options = new Context();
162
        }
163 3269
164
        $options->import = true;
165 3269
166 2936
        if ($options->refResolver === null) {
167
            $options->refResolver = new RefResolver($data);
168 1766
        } else {
169
            $options->refResolver->setRootData($data);
170
        }
171 3269
172 3191
        if ($options->remoteRefProvider) {
173
            $options->refResolver->setRemoteRefProvider($options->remoteRefProvider);
174
        }
175 3269
176
        $options->refResolver->preProcessReferences($data, $options);
177 3269
178
        return $this->process($data, $options, '#');
179
    }
180
181
182
    /**
183
     * @param mixed $data
184
     * @param Context|null $options
185
     * @return array|mixed|null|object|\stdClass
186
     * @throws InvalidValue
187
     * @throws \Exception
188 2452
     */
189
    public function out($data, Context $options = null)
190 2452
    {
191 1007
        if ($options === null) {
192
            $options = new Context();
193
        }
194 2452
195 2452
        $options->circularReferences = new \SplObjectStorage();
196 2452
        $options->import = false;
197
        return $this->process($data, $options);
198
    }
199
200
    /**
201
     * @param mixed $data
202
     * @param Context $options
203
     * @param string $path
204
     * @throws InvalidValue
205
     * @throws \Exception
206 3257
     */
207
    private function processType(&$data, Context $options, $path = '#')
208 3257
    {
209
        if ($options->tolerateStrings && is_string($data)) {
210
            $valid = Type::readString($this->type, $data);
211 3257
        } else {
212
            $valid = Type::isValid($this->type, $data, $options->version);
213 3257
        }
214 696
        if (!$valid) {
215 696
            $this->fail(new TypeException(ucfirst(
216 696
                    implode(', ', is_array($this->type) ? $this->type : array($this->type))
217 696
                    . ' expected, ' . json_encode($data) . ' received')
218
            ), $path);
219 3255
        }
220
    }
221
222
    /**
223
     * @param mixed $data
224
     * @param string $path
225
     * @throws InvalidValue
226
     * @throws \Exception
227 1513
     */
228
    private function processEnum($data, $path = '#')
229 1513
    {
230 1513
        $enumOk = false;
231 1513
        foreach ($this->enum as $item) {
232 1493
            if ($item === $data ||
233 1493
                ( // Int and float equality check.
234
                    (is_int($item) || is_float($item)) &&
235 1449
                    (is_int($data) || is_float($data)) &&
236 12
                    $item == $data
237 12
                )
238 3
            ) {
239 3
                $enumOk = true;
240
                break;
241
            } else {
242
                if (is_array($item) || is_object($item)) {
243
                    $diff = new JsonDiff($item, $data, JsonDiff::STOP_ON_DIFF);
244 1513
                    if ($diff->getDiffCnt() === 0) {
245 107
                        $enumOk = true;
246
                        break;
247 1496
                    }
248
                }
249
            }
250
        }
251
        if (!$enumOk) {
252
            $this->fail(new EnumException('Enum failed, enum: ' . json_encode($this->enum) . ', data: ' . json_encode($data)), $path);
253
        }
254
    }
255 43
256
    /**
257 43
     * @param mixed $data
258 37
     * @param string $path
259 37
     * @throws InvalidValue
260 15
     * @throws \Swaggest\JsonDiff\Exception
261 15
     */
262 15
    private function processConst($data, $path)
263 15
    {
264
        if ($this->const !== $data &&
265
            !( // Int and float equality.
266 22
                (is_int($this->const) || is_float($this->const)) &&
267
                (is_int($data) || is_float($data)) &&
268
                $this->const == $data
269 20
            )
270
        ) {
271
            if ((is_object($this->const) && is_object($data))
272
                || (is_array($this->const) && is_array($data))) {
273
                $diff = new JsonDiff($this->const, $data,
274
                    JsonDiff::STOP_ON_DIFF);
275
                if ($diff->getDiffCnt() != 0) {
276
                    $this->fail(new ConstException('Const failed'), $path);
277
                }
278
            } else {
279 70
                $this->fail(new ConstException('Const failed'), $path);
280
            }
281 70
        }
282
    }
283 70
284 16
    /**
285
     * @param mixed $data
286
     * @param Context $options
287 70
     * @param string $path
288 56
     * @throws InvalidValue
289
     * @throws \Exception
290 16
     * @throws \Swaggest\JsonDiff\Exception
291
     */
292
    private function processNot($data, Context $options, $path)
293
    {
294
        $exception = false;
295
        try {
296
            self::unboolSchema($this->not)->process($data, $options, $path . '->not');
297 2331
        } catch (InvalidValue $exception) {
298
            // Expected exception
299 2331
        }
300 38
        if ($exception === false) {
301 9
            $this->fail(new LogicException('Not ' . json_encode($this->not) . ' expected, ' . json_encode($data) . ' received'), $path . '->not');
302
        }
303
    }
304 2325
305 43
    /**
306 19
     * @param string $data
307
     * @param string $path
308
     * @throws InvalidValue
309 2322
     */
310 18
    private function processString($data, $path)
311 4
    {
312 4
        if ($this->minLength !== null) {
313
            if (mb_strlen($data, 'UTF-8') < $this->minLength) {
314
                $this->fail(new StringException('String is too short', StringException::TOO_SHORT), $path);
315 2322
            }
316 445
        }
317 445
        if ($this->maxLength !== null) {
318 150
            if (mb_strlen($data, 'UTF-8') > $this->maxLength) {
319 131
                $this->fail(new StringException('String is too long', StringException::TOO_LONG), $path);
320
            }
321
        }
322
        if ($this->pattern !== null) {
323 2322
            if (0 === preg_match(Helper::toPregPattern($this->pattern), $data)) {
324
                $this->fail(new StringException(json_encode($data) . ' does not match to '
325
                    . $this->pattern, StringException::PATTERN_MISMATCH), $path);
326
            }
327
        }
328
        if ($this->format !== null) {
329
            $validationError = Format::validationError($this->format, $data);
330 1090
            if ($validationError !== null) {
331
                if (!($this->format === "uri" && substr($path, -3) === ':id')) {
332 1090
                    $this->fail(new StringException($validationError), $path);
333 39
                }
334 39
            }
335 15
        }
336
    }
337
338
    /**
339 1090
     * @param float|int $data
340 32
     * @param string $path
341 18
     * @throws InvalidValue
342 18
     */
343 18
    private function processNumeric($data, $path)
344
    {
345
        if ($this->multipleOf !== null) {
346
            $div = $data / $this->multipleOf;
347 1090
            if ($div != (int)$div) {
348 24
                $this->fail(new NumericException($data . ' is not multiple of ' . $this->multipleOf, NumericException::MULTIPLE_OF), $path);
349 12
            }
350 12
        }
351 12
352
        if ($this->exclusiveMaximum !== null && !is_bool($this->exclusiveMaximum)) {
353
            if ($data >= $this->exclusiveMaximum) {
354
                $this->fail(new NumericException(
355 1090
                    'Value less or equal than ' . $this->exclusiveMaximum . ' expected, ' . $data . ' received',
356 46
                    NumericException::MAXIMUM), $path);
357 3
            }
358 2
        }
359 2
360 3
        if ($this->exclusiveMinimum !== null && !is_bool($this->exclusiveMinimum)) {
361
            if ($data <= $this->exclusiveMinimum) {
362
                $this->fail(new NumericException(
363 43
                    'Value more or equal than ' . $this->exclusiveMinimum . ' expected, ' . $data . ' received',
364 14
                    NumericException::MINIMUM), $path);
365 14
            }
366 14
        }
367
368
        if ($this->maximum !== null) {
369
            if ($this->exclusiveMaximum === true) {
370
                if ($data >= $this->maximum) {
371 1090
                    $this->fail(new NumericException(
372 524
                        'Value less or equal than ' . $this->maximum . ' expected, ' . $data . ' received',
373 93
                        NumericException::MAXIMUM), $path);
374 2
                }
375 2
            } else {
376 93
                if ($data > $this->maximum) {
377
                    $this->fail(new NumericException(
378
                        'Value less than ' . $this->maximum . ' expected, ' . $data . ' received',
379 449
                        NumericException::MAXIMUM), $path);
380 46
                }
381 46
            }
382 46
        }
383
384
        if ($this->minimum !== null) {
385
            if ($this->exclusiveMinimum === true) {
386 1089
                if ($data <= $this->minimum) {
387
                    $this->fail(new NumericException(
388
                        'Value more or equal than ' . $this->minimum . ' expected, ' . $data . ' received',
389
                        NumericException::MINIMUM), $path);
390
                }
391
            } else {
392
                if ($data < $this->minimum) {
393
                    $this->fail(new NumericException(
394
                        'Value more than ' . $this->minimum . ' expected, ' . $data . ' received',
395
                        NumericException::MINIMUM), $path);
396
                }
397 107
            }
398
        }
399 107
    }
400 107
401 107
    /**
402 107
     * @param mixed $data
403 107
     * @param Context $options
404 41
     * @param string $path
405 41
     * @return array|mixed|null|object|\stdClass
406
     * @throws InvalidValue
407
     * @throws \Exception
408 107
     * @throws \Swaggest\JsonDiff\Exception
409 107
     */
410
    private function processOneOf($data, Context $options, $path)
411 107
    {
412 85
        $successes = 0;
413 85
        $failures = '';
414 85
        $subErrors = [];
415
        $skipValidation = false;
416 75
        if ($options->skipValidation) {
417 75
            $skipValidation = true;
418 75
            $options->skipValidation = false;
419
        }
420
421
        $result = $data;
422 107
        foreach ($this->oneOf as $index => $item) {
423 41
            try {
424 41
                $result = self::unboolSchema($item)->process($data, $options, $path . '->oneOf[' . $index . ']');
425 8
                $successes++;
426
                if ($successes > 1 || $options->skipValidation) {
427
                    break;
428
                }
429 107
            } catch (InvalidValue $exception) {
430 66
                $subErrors[$index] = $exception;
431 20
                $failures .= ' ' . $index . ': ' . Helper::padLines(' ', $exception->getMessage()) . "\n";
432 20
                // Expected exception
433 20
            }
434 20
        }
435 52
        if ($skipValidation) {
436 17
            $options->skipValidation = true;
437 17
            if ($successes === 0) {
438 17
                $result = self::unboolSchema($this->oneOf[0])->process($data, $options, $path . '->oneOf[0]');
439 17
            }
440 17
        }
441 17
442
        if (!$options->skipValidation) {
443
            if ($successes === 0) {
444 77
                $exception = new LogicException('No valid results for oneOf {' . "\n" . substr($failures, 0, -1) . "\n}");
445
                $exception->error = 'No valid results for oneOf';
446
                $exception->subErrors = $subErrors;
447
                $this->fail($exception, $path);
448
            } elseif ($successes > 1) {
449
                $exception = new LogicException('More than 1 valid result for oneOf: '
450
                    . $successes . '/' . count($this->oneOf) . ' valid results for oneOf {'
451
                    . "\n" . substr($failures, 0, -1) . "\n}");
452
                $exception->error = 'More than 1 valid result for oneOf';
453
                $exception->subErrors = $subErrors;
454
                $this->fail($exception, $path);
455
            }
456 1763
        }
457
        return $result;
458 1763
    }
459 1763
460 1763
    /**
461 1763
     * @param mixed $data
462 1763
     * @param Context $options
463
     * @param string $path
464 1763
     * @return array|mixed|null|object|\stdClass
465 1757
     * @throws InvalidValue
466 1757
     * @throws \Exception
467 1757
     * @throws \Swaggest\JsonDiff\Exception
468
     */
469 445
    private function processAnyOf($data, Context $options, $path)
470 445
    {
471 445
        $successes = 0;
472
        $failures = '';
473
        $subErrors = [];
474
        $result = $data;
475 1763
        foreach ($this->anyOf as $index => $item) {
476 31
            try {
477 31
                $result = self::unboolSchema($item)->process($data, $options, $path . '->anyOf[' . $index . ']');
478 31
                $successes++;
479 31
                if ($successes) {
480 31
                    break;
481 31
                }
482
            } catch (InvalidValue $exception) {
483 1757
                $subErrors[$index] = $exception;
484
                $failures .= ' ' . $index . ': ' . $exception->getMessage() . "\n";
485
                // Expected exception
486
            }
487
        }
488
        if (!$successes && !$options->skipValidation) {
489
            $exception = new LogicException('No valid results for anyOf {' . "\n"
490
                . substr(Helper::padLines(' ', $failures, false), 0, -1)
491
                . "\n}");
492
            $exception->error = 'No valid results for anyOf';
493
            $exception->subErrors = $subErrors;
494
            $this->fail($exception, $path);
495 354
        }
496
        return $result;
497 354
    }
498 354
499 354
    /**
500
     * @param mixed $data
501 314
     * @param Context $options
502
     * @param string $path
503
     * @return array|mixed|null|object|\stdClass
504
     * @throws InvalidValue
505
     * @throws \Exception
506
     * @throws \Swaggest\JsonDiff\Exception
507
     */
508
    private function processAllOf($data, Context $options, $path)
509
    {
510
        $result = $data;
511
        foreach ($this->allOf as $index => $item) {
512
            $result = self::unboolSchema($item)->process($data, $options, $path . '->allOf[' . $index . ']');
513 26
        }
514
        return $result;
515 26
    }
516
517 26
    /**
518 13
     * @param mixed $data
519 13
     * @param Context $options
520
     * @param string $path
521 26
     * @return array|mixed|null|object|\stdClass
522 18
     * @throws InvalidValue
523 18
     * @throws \Exception
524
     * @throws \Swaggest\JsonDiff\Exception
525
     */
526 13
    private function processIf($data, Context $options, $path)
527 6
    {
528
        $valid = true;
529
        try {
530 10
            self::unboolSchema($this->if)->process($data, $options, $path . '->if');
531
        } catch (InvalidValue $exception) {
532
            $valid = false;
533
        }
534
        if ($valid) {
535
            if ($this->then !== null) {
536
                return self::unboolSchema($this->then)->process($data, $options, $path . '->then');
537
            }
538
        } else {
539 168
            if ($this->else !== null) {
540
                return self::unboolSchema($this->else)->process($data, $options, $path . '->else');
541 168
            }
542 164
        }
543 74
        return null;
544
    }
545
546 136
    /**
547
     * @param array $array
548
     * @param Context $options
549
     * @param string $path
550
     * @throws InvalidValue
551
     */
552
    private function processObjectRequired($array, Context $options, $path)
0 ignored issues
show
Unused Code introduced by
The parameter $options is not used and could be removed. ( Ignorable by Annotation )

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

552
    private function processObjectRequired($array, /** @scrutinizer ignore-unused */ Context $options, $path)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
553
    {
554
        foreach ($this->required as $item) {
555
            if (!array_key_exists($item, $array)) {
556
                $this->fail(new ObjectException('Required property missing: ' . $item . ', data: ' . json_encode($array, JSON_UNESCAPED_SLASHES), ObjectException::REQUIRED), $path);
557
            }
558 3269
        }
559
    }
560 3269
561
    /**
562 3269
     * @param object $data
563
     * @param Context $options
564 3269
     * @param string $path
565
     * @param ObjectItemContract|null $result
566
     * @return array|null|ClassStructure|ObjectItemContract|SchemaContract
567 3269
     * @throws InvalidValue
568 2
     * @throws \Exception
569 2
     * @throws \Swaggest\JsonDiff\Exception
570 2
     */
571
    private function processObject($data, Context $options, $path, $result = null)
572
    {
573
        $import = $options->import;
574 2
575 2
        $hasMapping = $this->properties !== null && isset($this->properties->__dataToProperty[$options->mapping]);
576 2
577 2
        $array = !$data instanceof \stdClass ? get_object_vars($data) : (array)$data;
578 2
579 2
        // convert imported data to default mapping before validation
580
        if ($import && $options->mapping !== self::DEFAULT_MAPPING) {
581
            if ($this->properties !== null && isset($this->properties->__dataToProperty[$options->mapping])) {
582
                foreach ($this->properties->__dataToProperty[$options->mapping] as $dataName => $propertyName) {
583
                    if (!isset($array[$dataName])) {
584
                        continue;
585 3269
                    }
586 168
587
                    $propertyName = isset($this->properties->__propertyToData[self::DEFAULT_MAPPING][$propertyName])
588
                        ? $this->properties->__propertyToData[self::DEFAULT_MAPPING][$propertyName]
589
                        : $propertyName;
590 3268
                    if ($propertyName !== $dataName) {
591 3251
                        $array[$propertyName] = $array[$dataName];
592
                        unset($array[$dataName]);
593 3251
                    }
594 1
                }
595 3250
            }
596
        }
597 3250
598 1186
        if (!$options->skipValidation && $this->required !== null) {
599
            $this->processObjectRequired($array, $options, $path);
600 3239
        }
601 3239
602
        // build result entity
603
        if ($import) {
604
            if (!$options->validateOnly) {
605
606 3239
                if ($this->useObjectAsArray) {
607
                    $result = array();
608
                } else {
609
                    //* todo check performance impact
610
                    if (null === $this->objectItemClass) {
611 3250
                        if (!$result instanceof ObjectItemContract) {
612 3235
                            $result = new ObjectItem();
613 3235
                            $result->setDocumentPath($path);
614
                        }
615
                    } else {
616
                        $className = $this->objectItemClass;
617
                        if ($options->objectItemClassMapping !== null) {
618
                            if (isset($options->objectItemClassMapping[$className])) {
619
                                $className = $options->objectItemClassMapping[$className];
620
                            }
621
                        }
622
                        if (null === $result || get_class($result) !== $className) {
623
                            $result = new $className;
624 3250
                            //* todo check performance impact
625 3250
                            if ($result instanceof ClassStructure) {
626
                                $result->setDocumentPath($path);
627
                                if ($result->__validateOnSet) {
628
                                    $result->__validateOnSet = false;
629
                                    /** @noinspection PhpUnusedLocalVariableInspection */
630
                                    /* todo check performance impact
631
                                    $validateOnSetHandler = new ScopeExit(function () use ($result) {
632
                                        $result->__validateOnSet = true;
633
                                    });
634 3268
                                    //*/
635 3268
                                }
636 3268
                            }
637 3268
                            //*/
638 42
                        }
639 42
                    }
640 42
                    //*/
641
                }
642
            }
643 42
        }
644 42
645
        // @todo better check for schema id
646
647 3268
        if ($import
648 3268
            && isset($array[Schema::PROP_ID_D4])
649 3268
            && ($options->version === Schema::VERSION_DRAFT_04 || $options->version === Schema::VERSION_AUTO)
650 3268
            && is_string($array[Schema::PROP_ID_D4])) {
651 116
            $id = $array[Schema::PROP_ID_D4];
652 116
            $refResolver = $options->refResolver;
653 116
            $parentScope = $refResolver->updateResolutionScope($id);
654
            /** @noinspection PhpUnusedLocalVariableInspection */
655
            $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...
656 116
                $refResolver->setResolutionScope($parentScope);
657 116
            });
658
        }
659
660
        if ($import
661 3268
            && isset($array[self::PROP_ID])
662
            && ($options->version >= Schema::VERSION_DRAFT_06 || $options->version === Schema::VERSION_AUTO)
663
            && is_string($array[self::PROP_ID])) {
664 3251
            $id = $array[self::PROP_ID];
665 3251
            $refResolver = $options->refResolver;
666
            $parentScope = $refResolver->updateResolutionScope($id);
667 3251
            /** @noinspection PhpUnusedLocalVariableInspection */
668 450
            $defer = new ScopeExit(function () use ($parentScope, $refResolver) {
669
                $refResolver->setResolutionScope($parentScope);
670 450
            });
671 14
        }
672
673
        // check $ref
674
        if ($import) {
675
            try {
676 3251
677 3251
                $refProperty = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $refProperty is dead and can be removed.
Loading history...
678 3251
                $dereference = $options->dereference;
679
680 438
                if ($this->properties !== null && isset($array[self::PROP_REF])) {
681
                    $refPropName = self::PROP_REF;
682
                    if ($hasMapping) {
683 438
                        if (isset($this->properties->__dataToProperty[$options->mapping][self::PROP_REF])) {
684 428
                            $refPropName = $this->properties->__dataToProperty[$options->mapping][self::PROP_REF];
685 438
                        }
686 26
                    }
687
688
                    $refProperty = $this->properties[$refPropName];
689
690 412
                    if (isset($refProperty)) {
691 412
                        $dereference = $refProperty->format === Format::URI_REFERENCE;
692
                    }
693
                }
694 412
695 412
                if (
696
                    isset($array[self::PROP_REF])
697 412
                    && is_string($array[self::PROP_REF])
698 412
                    && $dereference
699 412
                ) {
700 412
                    $refString = $array[self::PROP_REF];
701 187
702 187
                    // todo check performance impact
703
                    if ($refString === 'http://json-schema.org/draft-04/schema#'
704 412
                        || $refString === 'http://json-schema.org/draft-06/schema#'
705
                        || $refString === 'http://json-schema.org/draft-07/schema#') {
706 412
                        return Schema::schema();
707 412
                    }
708 412
709 47
                    // TODO consider process # by reference here ?
710
                    $refResolver = $options->refResolver;
711 412
                    $preRefScope = $refResolver->getResolutionScope();
712
                    /** @noinspection PhpUnusedLocalVariableInspection */
713 412
                    $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...
714 1
                        $refResolver->setResolutionScope($preRefScope);
715 1
                    });
716 1
717
                    $ref = $refResolver->resolveReference($refString);
718 412
                    $unresolvedData = $data;
719
                    $data = self::unboolSchemaData($ref->getData());
720 3251
                    if (!$options->validateOnly) {
721
                        if ($ref->isImported()) {
722
                            $refResult = $ref->getImported();
723 1
                            return $refResult;
724 1
                        }
725
                        $ref->setImported($result);
726
                        try {
727
                            // Best effort dereference delivery.
728
                            $refResult = $this->process($data, $options, $path . '->$ref:' . $refString, $result);
729 3268
                            if ($refResult instanceof ObjectItemContract) {
730
                                if ($refResult->getFromRefs()) {
731 3268
                                    $refResult = clone $refResult; // @todo check performance, consider option
732 3268
                                }
733 3254
                                $refResult->setFromRef($refString);
734
                            }
735 3254
                            $ref->setImported($refResult);
736 3254
                            return $refResult;
737
                        } catch (InvalidValue $exception) {
738 590
                            $ref->unsetImported();
739
                            $skipValidation = $options->skipValidation;
740
                            $options->skipValidation = true;
741
                            $refResult = $this->process($data, $options, $path . '->$ref:' . $refString);
742
                            if ($refResult instanceof ObjectItemContract) {
743 3268
                                if ($refResult->getFromRefs()) {
744 3247
                                    $refResult = clone $refResult; // @todo check performance, consider option
745 4
                                }
746
                                $refResult->setFromRef($refString);
747 3247
                            }
748 3
                            $options->skipValidation = $skipValidation;
749
750 3247
                            if ($result instanceof WithResolvedValue) {
751 19
                                $result->setResolvedValue($refResult);
752 19
                            }
753 12
754
                            // Proceeding with unresolved data.
755
                            $data = $unresolvedData;
756
                        }
757
                    } else {
758 3268
                        $this->process($data, $options, $path . '->$ref:' . $refString);
759 3268
                    }
760 3268
                }
761 3268
            } catch (InvalidValue $exception) {
762 3268
                $this->fail($exception, $path);
763
            }
764 43
        }
765
766 41
        /** @var Schema[]|null $properties */
767 6
        $properties = null;
768 6
769 6
        $nestedProperties = null;
770
        if ($this->properties !== null) {
771
            $properties = $this->properties->toArray(); // todo call directly
772
            $nestedProperties = $this->properties->nestedProperties;
773
        }
774
775 3268
776 3258
        if (!$options->skipValidation) {
777 1
            if ($this->minProperties !== null && count($array) < $this->minProperties) {
778
                $this->fail(new ObjectException("Not enough properties", ObjectException::TOO_FEW), $path);
779
            }
780 3257
            if ($this->maxProperties !== null && count($array) > $this->maxProperties) {
781
                $this->fail(new ObjectException("Too many properties", ObjectException::TOO_MANY), $path);
782 3257
            }
783 73
            if ($this->propertyNames !== null) {
784 73
                $propertyNames = self::unboolSchema($this->propertyNames);
785 63
                foreach ($array as $key => $tmp) {
786 63
                    $propertyNames->process($key, $options, $path . '->propertyNames:' . $key);
0 ignored issues
show
introduced by
The method process() does not exist on Swaggest\JsonSchema\JsonSchema. Maybe you want to declare this class abstract? ( Ignorable by Annotation )

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

786
                    $propertyNames->/** @scrutinizer ignore-call */ 
787
                                    process($key, $options, $path . '->propertyNames:' . $key);
Loading history...
787 63
                }
788 29
            }
789
        }
790 34
791 31
        $defaultApplied = array();
792 18
        if ($import
793 18
            && !$options->validateOnly
794
            && $options->applyDefaults
795
            && $properties !== null
796
        ) {
797
            foreach ($properties as $key => $property) {
798
                // todo check when property is \stdClass `{}` here (RefTest)
799
                if ($property instanceof SchemaContract && null !== $default = $property->getDefault()) {
800 3257
                    if (!array_key_exists($key, $array)) {
801 3257
                        $defaultApplied[$key] = true;
802
                        $array[$key] = $default;
803 3247
                    }
804 3247
                }
805 3247
            }
806 3247
        }
807 3190
808 3190
        /**
809 3190
         * @var string $key
810 3190
         * @var mixed $value
811
         */
812
        foreach ($array as $key => $value) {
813
            if ($key === '' && PHP_VERSION_ID < 70100) {
814
                $this->fail(new InvalidValue('Empty property name'), $path);
815
            }
816 3252
817 3252
            $found = false;
818 6
819 6
            if (!$options->skipValidation && !empty($this->dependencies)) {
820
                $deps = $this->dependencies;
821 6
                if (isset($deps->$key)) {
822
                    $dependencies = $deps->$key;
823
                    $dependencies = self::unboolSchema($dependencies);
824 3252
                    if ($dependencies instanceof SchemaContract) {
825 178
                        $dependencies->process($data, $options, $path . '->dependencies:' . $key);
826 178
                    } else {
827 126
                        foreach ($dependencies as $item) {
828 126
                            if (!array_key_exists($item, $array)) {
829 126
                                $this->fail(new ObjectException('Dependency property missing: ' . $item,
830 99
                                    ObjectException::DEPENDENCY_MISSING), $path);
831 99
                            }
832
                        }
833
                    }
834
                }
835
            }
836
837 3252
            $propertyFound = false;
838 1023
            if (isset($properties[$key])) {
839 15
                /** @var Schema[] $properties */
840
                $prop = self::unboolSchema($properties[$key]);
841
                $propertyFound = true;
842 1023
                $found = true;
843 1023
                if ($prop instanceof SchemaContract) {
844
                    $value = $prop->process(
845
                        $value,
846 1016
                        isset($defaultApplied[$key]) ? $options->withDefault() : $options,
847 1012
                        $path . '->properties:' . $key
848
                    );
849
                }
850
            }
851 3248
852
            /** @var Egg[] $nestedEggs */
853 3248
            $nestedEggs = null;
854 3224
            if (isset($nestedProperties[$key])) {
855
                $found = true;
856 75
                $nestedEggs = $nestedProperties[$key];
857
                // todo iterate all nested props?
858
                $value = self::unboolSchema($nestedEggs[0]->propertySchema)->process($value, $options, $path . '->nestedProperties:' . $key);
859
            }
860 3248
861 2
            if ($this->patternProperties !== null) {
862 2
                foreach ($this->patternProperties as $pattern => $propertySchema) {
863
                    if (preg_match(Helper::toPregPattern($pattern), $key)) {
864 2
                        $found = true;
865
                        $value = self::unboolSchema($propertySchema)->process($value, $options,
866
                            $path . '->patternProperties[' . strtr($pattern, array('~' => '~1', ':' => '~2')) . ']:' . $key);
867
                        if (!$options->validateOnly && $import) {
868
                            $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

868
                            $result->/** @scrutinizer ignore-call */ 
869
                                     addPatternPropertyName($pattern, $key);
Loading history...
869 3248
                        }
870 5
                        //break; // todo manage multiple import data properly (pattern accessor)
871 5
                    }
872
                }
873 5
            }
874 5
            if (!$found && $this->additionalProperties !== null) {
875
                if (!$options->skipValidation && $this->additionalProperties === false) {
876
                    $this->fail(new ObjectException('Additional properties not allowed: ' . $key), $path);
877 3247
                }
878 29
879 4
                if ($this->additionalProperties instanceof SchemaContract) {
880
                    $value = $this->additionalProperties->process($value, $options, $path . '->additionalProperties:' . $key);
881
                }
882
883 3247
                if ($import && !$this->useObjectAsArray && !$options->validateOnly) {
884 1
                    $result->addAdditionalPropertyName($key);
885
                }
886 3247
            }
887 3246
888 1181
            $propertyName = $key;
889 1130
890
            if ($hasMapping) {
891
                if ($this->properties !== null && isset($this->properties->__dataToProperty[self::DEFAULT_MAPPING][$key])) {
892
                    // todo check performance of local map access
893
                    $propertyName = $this->properties->__dataToProperty[self::DEFAULT_MAPPING][$key];
894
                }
895 3255
            }
896
897
            if ($options->mapping !== self::DEFAULT_MAPPING) {
898
                if (!$import) {
899
                    if ($this->properties !== null && isset($this->properties->__propertyToData[$options->mapping][$propertyName])) {
900
                        // todo check performance of local map access
901
                        $propertyName = $this->properties->__propertyToData[$options->mapping][$propertyName];
902
                    }
903
                }
904
            }
905
906
            if (!$options->validateOnly && $nestedEggs && $import) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $nestedEggs of type Swaggest\JsonSchema\Structure\Egg[] is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
907
                foreach ($nestedEggs as $nestedEgg) {
908 1241
                    $result->setNestedProperty($key, $value, $nestedEgg);
909
                }
910 1241
                if ($propertyFound) {
911 1241
                    $result->$propertyName = $value;
912 1041
                }
913 9
            } else {
914
                if (!$import && $hasMapping) {
915
                    if ($this->properties !== null && isset($this->properties->__propertyToData[$options->mapping][$propertyName])) {
916 1035
                        $propertyName = $this->properties->__propertyToData[$options->mapping][$propertyName];
917 6
                    }
918
                }
919
920
                if ($this->useObjectAsArray && $import) {
921 1229
                    $result[$propertyName] = $value;
922 1229
                } else {
923 1229
                    if ($found || !$import) {
924 876
                        $result->$propertyName = $value;
925 876
                    } elseif (!isset($result->$propertyName)) {
926 705
                        if (self::PROP_REF !== $propertyName || empty($result->__fromRef)) {
927 705
                            $result->$propertyName = $value;
928 705
                        }
929
                    }
930 104
                }
931 104
            }
932 104
        }
933
934
        return $result;
935
    }
936
937
    /**
938
     * @param array $data
939 1229
     * @param Context $options
940 1229
     * @param string $path
941 1229
     * @param array $result
942 1104
     * @return mixed
943 94
     * @throws InvalidValue
944 94
     * @throws \Exception
945
     * @throws \Swaggest\JsonDiff\Exception
946 1104
     */
947 850
    private function processArray($data, Context $options, $path, $result)
948 850
    {
949 589
        $count = count($data);
950 6
        if (!$options->skipValidation) {
951
            if ($this->minItems !== null && $count < $this->minItems) {
952
                $this->fail(new ArrayException("Not enough items in array"), $path);
953 1082
            }
954
955
            if ($this->maxItems !== null && $count > $this->maxItems) {
956 1203
                $this->fail(new ArrayException("Too many items in array"), $path);
957 525
            }
958 22
        }
959
960
        $pathItems = 'items';
961
        $this->items = self::unboolSchema($this->items);
962 1184
        if ($this->items instanceof SchemaContract) {
0 ignored issues
show
introduced by
$this->items is never a sub-type of Swaggest\JsonSchema\SchemaContract.
Loading history...
963
            $items = array();
964 36
            $additionalItems = $this->items;
965 36
        } elseif ($this->items === null) { // items defaults to empty schema so everything is valid
966 4
            $items = array();
967
            $additionalItems = true;
968 32
        } else { // listed items
969 7
            $items = $this->items;
970
            $additionalItems = $this->additionalItems;
971 25
            $pathItems = 'additionalItems';
972 2
        }
973
974 25
        /**
975 25
         * @var Schema|Schema[] $items
976
         * @var null|bool|Schema $additionalItems
977 25
         */
978 17
        $itemsLen = is_array($items) ? count($items) : 0;
979 17
        $index = 0;
980 21
        foreach ($result as $key => $value) {
981
            if ($index < $itemsLen) {
982
                $itemSchema = self::unboolSchema($items[$index]);
983 25
                $result[$key] = $itemSchema->process($value, $options, $path . '->items:' . $index);
984 8
            } else {
985
                if ($additionalItems instanceof SchemaContract) {
986
                    $result[$key] = $additionalItems->process($value, $options, $path . '->' . $pathItems
987 1165
                        . '[' . $index . ']:' . $index);
988
                } elseif (!$options->skipValidation && $additionalItems === false) {
989
                    $this->fail(new ArrayException('Unexpected array item'), $path);
990
                }
991
            }
992
            ++$index;
993
        }
994
995
        if (!$options->skipValidation && $this->uniqueItems) {
996
            if (!UniqueItems::isValid($data)) {
997 20
                $this->fail(new ArrayException('Array is not unique'), $path);
998
            }
999
        }
1000 20
1001 10
        if (!$options->skipValidation && $this->contains !== null) {
1002
            /** @var Schema|bool $contains */
1003 10
            $contains = $this->contains;
1004
            if ($contains === false) {
0 ignored issues
show
introduced by
The condition $contains === false is always false.
Loading history...
1005 4
                $this->fail(new ArrayException('Contains is false'), $path);
1006 4
            }
1007
            if ($count === 0) {
1008 10
                $this->fail(new ArrayException('Empty array fails contains constraint'), $path);
1009
            }
1010
            if ($contains === true) {
0 ignored issues
show
introduced by
The condition $contains === true is always false.
Loading history...
1011
                $contains = self::unboolSchema($contains);
1012
            }
1013
            $containsOk = false;
1014
            foreach ($data as $key => $item) {
1015
                try {
1016
                    $contains->process($item, $options, $path . '->' . $key);
1017
                    $containsOk = true;
1018
                    break;
1019
                } catch (InvalidValue $exception) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
1020
                }
1021 3316
            }
1022
            if (!$containsOk) {
1023 3316
                $this->fail(new ArrayException('Array fails contains constraint'), $path);
1024 3316
            }
1025
        }
1026 3316
        return $result;
1027 2
    }
1028
1029
    /**
1030 3316
     * @param mixed|string $data
1031 808
     * @param Context $options
1032
     * @param string $path
1033 808
     * @return bool|mixed|string
1034 794
     * @throws InvalidValue
1035 794
     */
1036 6
    private function processContent($data, Context $options, $path)
1037 6
    {
1038
        try {
1039 6
            if ($options->unpackContentMediaType) {
1040
                return Content::process($options, $this->contentEncoding, $this->contentMediaType, $data, $options->import);
1041
            } else {
1042 794
                Content::process($options, $this->contentEncoding, $this->contentMediaType, $data, true);
1043
            }
1044
        } catch (InvalidValue $exception) {
1045 808
            $this->fail($exception, $path);
1046 6
        }
1047
        return $data;
1048 808
    }
1049 6
1050 6
    /**
1051 6
     * @param mixed $data
1052 6
     * @param Context $options
1053 6
     * @param string $path
1054 6
     * @param mixed|null $result
1055 6
     * @return array|mixed|null|object|\stdClass
1056
     * @throws InvalidValue
1057
     * @throws \Exception
1058 6
     * @throws \Swaggest\JsonDiff\Exception
1059 6
     */
1060 2
    public function process($data, Context $options, $path = '#', $result = null)
1061 2
    {
1062 2
        $origData = $data;
0 ignored issues
show
Unused Code introduced by
The assignment to $origData is dead and can be removed.
Loading history...
1063 2
        $import = $options->import;
1064 2
1065
        if (!$import && $data instanceof SchemaExporter) {
1066
            $data = $data->exportSchema(); // Used to export ClassStructure::schema()
1067
        }
1068 6
1069 6
        if (!$import && $data instanceof ObjectItemContract) {
1070
            $result = new \stdClass();
1071
1072
            if ('#' === $path) {
1073 808
                $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...
1074
                    foreach ($options->exportedDefinitions as $ref => $data) {
1075 1
                        if ($data !== null && ($ref[0] === '#' || $ref[1] === '/')) {
1076 1
                            JsonPointer::add($result, JsonPointer::splitPath($ref), $data,
1077 1
                                /*JsonPointer::SKIP_IF_ISSET + */
1078
                                JsonPointer::RECURSIVE_KEY_CREATION);
1079 808
                        }
1080
                    }
1081 808
                });
1082
            }
1083
1084 3316
            if ($options->isRef) {
1085
                $options->isRef = false;
1086 3316
            } else {
1087 1
                if ('#' !== $path && $refs = $data->getFromRefs()) {
1088
                    $ref = $refs[0];
1089
                    if (!array_key_exists($ref, $options->exportedDefinitions) && strpos($ref, '://') === false) {
1090 3316
                        $exported = null;
1091
                        $options->exportedDefinitions[$ref] = &$exported;
1092
                        $options->isRef = true;
1093
                        $exported = $this->process($data, $options, $ref);
1094 3316
                        unset($exported);
1095 3316
                    }
1096
1097
                    $countRefs = count($refs);
1098 3316
                    for ($i = 1; $i < $countRefs; $i++) {
1099 1450
                        $ref = $refs[$i];
1100
                        if (!array_key_exists($ref, $options->exportedDefinitions) && strpos($ref, '://') === false) {
1101
                            $exported = new \stdClass();
1102 3278
                            $exported->{self::PROP_REF} = $refs[$i - 1];
1103 3257
                            $options->exportedDefinitions[$ref] = $exported;
1104
                        }
1105
                    }
1106 3277
1107 1513
                    $result->{self::PROP_REF} = $refs[$countRefs - 1];
1108
                    return $result;
1109
                }
1110 3277
            }
1111 43
1112
            if ($options->circularReferences->contains($data)) {
1113
                /** @noinspection PhpIllegalArrayKeyTypeInspection */
1114 3277
                $path = $options->circularReferences[$data];
1115 70
                $result->{self::PROP_REF} = PointerUtil::getDataPointer($path, true);
1116
                return $result;
1117
            }
1118 3277
            $options->circularReferences->attach($data, $path);
1119 2331
1120
            $data = $data->jsonSerialize();
1121
        }
1122 3277
1123 1090
        $path .= $this->getFromRefPath();
1124
1125
        if (!$import && is_array($data) && $this->useObjectAsArray) {
1126 3276
            $data = (object)$data;
1127 26
        }
1128
1129
        if (null !== $options->dataPreProcessor) {
1130
            $data = $options->dataPreProcessor->process($data, $this, $import);
1131
        }
1132 3314
1133 107
        if ($options->skipValidation) {
1134
            goto skipValidation;
1135
        }
1136 3314
1137 1763
        if ($this->type !== null) {
1138
            $this->processType($data, $options, $path);
1139
        }
1140 3314
1141 354
        if ($this->enum !== null) {
1142
            $this->processEnum($data, $path);
1143
        }
1144 3314
1145 3269
        if (array_key_exists(self::CONST_PROPERTY, $this->__arrayOfData)) {
1146
            $this->processConst($data, $path);
1147
        }
1148 3311
1149 1241
        if ($this->not !== null) {
1150
            $this->processNot($data, $options, $path);
1151
        }
1152 3311
1153 20
        if (is_string($data)) {
1154 6
            $this->processString($data, $path);
1155
        }
1156 20
1157
        if (is_int($data) || is_float($data)) {
1158
            $this->processNumeric($data, $path);
1159 3311
        }
1160
1161
        if ($this->if !== null) {
1162
            $result = $this->processIf($data, $options, $path);
1163
        }
1164
1165
        skipValidation:
1166 1
1167
        if ($result === null) {
1168 1
            $result = $data;
1169 1
        }
1170
1171
        if ($this->oneOf !== null) {
1172
            $result = $this->processOneOf($data, $options, $path);
1173
        }
1174
1175
        if ($this->anyOf !== null) {
1176
            $result = $this->processAnyOf($data, $options, $path);
1177 1264
        }
1178
1179 1264
        if ($this->allOf !== null) {
1180 1264
            $result = $this->processAllOf($data, $options, $path);
1181
        }
1182
1183 16
        if (is_object($data)) {
1184
            $result = $this->processObject($data, $options, $path, $result);
1185 16
        }
1186 16
1187 16
        if (is_array($data)) {
1188
            $result = $this->processArray($data, $options, $path, $result);
0 ignored issues
show
Bug introduced by
It seems like $result can also be of type Swaggest\JsonSchema\SchemaContract and Swaggest\JsonSchema\Structure\ClassStructure and Swaggest\JsonSchema\Structure\ObjectItemContract and object and stdClass; 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

1188
            $result = $this->processArray($data, $options, $path, /** @scrutinizer ignore-type */ $result);
Loading history...
1189
        }
1190 4
1191
        if ($this->contentEncoding !== null || $this->contentMediaType !== null) {
1192 4
            if ($import && !is_string($data)) {
1193 4
                return $result;
1194 4
            }
1195
            $result = $this->processContent($data, $options, $path);
1196
        }
1197 13
1198
        return $result;
1199 13
    }
1200 13
1201 13
    /**
1202
     * @param boolean $useObjectAsArray
1203
     * @return Schema
1204 4
     */
1205
    public function setUseObjectAsArray($useObjectAsArray)
1206 4
    {
1207 4
        $this->useObjectAsArray = $useObjectAsArray;
1208 4
        return $this;
1209
    }
1210
1211 8
    /**
1212
     * @param InvalidValue $exception
1213 8
     * @param string $path
1214 8
     * @throws InvalidValue
1215 8
     */
1216
    private function fail(InvalidValue $exception, $path)
1217
    {
1218 1
        $exception->addPath($path);
1219
        throw $exception;
1220 1
    }
1221 1
1222 1
    public static function integer()
1223
    {
1224
        $schema = new static();
1225
        $schema->type = Type::INTEGER;
1226
        return $schema;
1227
    }
1228
1229
    public static function number()
1230
    {
1231
        $schema = new static();
1232
        $schema->type = Type::NUMBER;
1233
        return $schema;
1234
    }
1235
1236
    public static function string()
1237 3
    {
1238
        $schema = new static();
1239 3
        $schema->type = Type::STRING;
1240 3
        return $schema;
1241
    }
1242
1243
    public static function boolean()
1244
    {
1245
        $schema = new static();
1246
        $schema->type = Type::BOOLEAN;
1247
        return $schema;
1248 5
    }
1249
1250 5
    public static function object()
1251 5
    {
1252
        $schema = new static();
1253 5
        $schema->type = Type::OBJECT;
1254 5
        return $schema;
1255
    }
1256
1257
    public static function arr()
1258
    {
1259
        $schema = new static();
1260
        $schema->type = Type::ARR;
1261
        return $schema;
1262
    }
1263
1264
    public static function null()
1265
    {
1266
        $schema = new static();
1267
        $schema->type = Type::NULL;
1268
        return $schema;
1269
    }
1270
1271
1272
    /**
1273
     * @param Properties $properties
1274
     * @return SchemaContract
1275
     */
1276 1
    public function setProperties($properties)
1277
    {
1278 1
        $this->properties = $properties;
1279 1
        return $this;
1280
    }
1281 1
1282 1
    /**
1283
     * @param string $name
1284
     * @param Schema $schema
1285 1
     * @return $this
1286
     */
1287 1
    public function setProperty($name, $schema)
1288 1
    {
1289
        if (null === $this->properties) {
1290
            $this->properties = new Properties();
1291
        }
1292
        $this->properties->__set($name, $schema);
1293
        return $this;
1294
    }
1295
1296
    /**
1297 5
     * @param string $name
1298
     * @param SchemaContract $schema
1299 5
     * @return $this
1300
     * @throws Exception
1301
     */
1302 5
    public function setPatternProperty($name, $schema)
1303 5
    {
1304
        if (null === $this->patternProperties) {
1305
            $this->patternProperties = new Properties();
1306
        }
1307
        $this->patternProperties->__set($name, $schema);
1308 5
        return $this;
1309
    }
1310
1311
1312
    /** @var mixed[] */
1313
    private $metaItems = array();
1314
1315
    public function addMeta($meta, $name = null)
1316
    {
1317
        if ($name === null) {
1318 3327
            $name = get_class($meta);
1319
        }
1320 3327
        $this->metaItems[$name] = $meta;
1321 3327
        return $this;
1322
    }
1323 3327
1324 1
    public function getMeta($name)
1325 1
    {
1326 1
        if (isset($this->metaItems[$name])) {
1327 1
            return $this->metaItems[$name];
1328 1
        }
1329
        return null;
1330
    }
1331 3327
1332 108
    /**
1333 3299
     * @param Context $options
1334 94
     * @return ObjectItemContract
1335
     */
1336 3267
    public function makeObjectItem(Context $options = null)
1337
    {
1338
        if (null === $this->objectItemClass) {
1339
            return new ObjectItem();
1340
        } else {
1341
            $className = $this->objectItemClass;
1342
            if ($options !== null) {
1343
                if (isset($options->objectItemClassMapping[$className])) {
1344 412
                    $className = $options->objectItemClassMapping[$className];
1345
                }
1346 412
            }
1347 412
            return new $className;
1348
        }
1349 412
    }
1350 1
1351 1
    /**
1352 1
     * Resolves boolean schema into Schema instance.
1353
     *
1354
     * @param mixed $schema
1355 412
     * @return mixed|Schema
1356 6
     */
1357 408
    public static function unboolSchema($schema)
1358 5
    {
1359
        static $trueSchema;
1360 403
        static $falseSchema;
1361
1362
        if (null === $trueSchema) {
1363
            $trueSchema = new Schema();
1364 41
            $trueSchema->__booleanSchema = true;
1365
            $falseSchema = new Schema();
1366 41
            $falseSchema->not = $trueSchema;
1367
            $falseSchema->__booleanSchema = false;
1368
        }
1369 99
1370
        if ($schema === true) {
1371 99
            return $trueSchema;
1372
        } elseif ($schema === false) {
1373
            return $falseSchema;
1374
        } else {
1375
            return $schema;
1376
        }
1377
    }
1378
1379
    /**
1380
     * Converts bool value into an object schema.
1381
     *
1382
     * @param mixed $data
1383
     * @return \stdClass
1384
     */
1385
    public static function unboolSchemaData($data)
1386
    {
1387
        static $trueSchema;
1388
        static $falseSchema;
1389
1390 97
        if (null === $trueSchema) {
1391
            $trueSchema = new \stdClass();
1392 97
            $falseSchema = new \stdClass();
1393
            $falseSchema->not = $trueSchema;
1394
        }
1395
1396
        if ($data === true) {
1397
            return $trueSchema;
1398
        } elseif ($data === false) {
1399
            return $falseSchema;
1400
        } else {
1401
            return $data;
1402
        }
1403
    }
1404
1405
    public function getDefault()
1406
    {
1407
        return $this->default;
1408
    }
1409
1410
    /**
1411
     * @nolint
1412
     */
1413
    public function getProperties()
1414
    {
1415
        return $this->properties;
1416
    }
1417
1418
    public function getObjectItemClass()
1419
    {
1420
        return $this->objectItemClass;
1421
    }
1422
1423
    /**
1424
     * @return string[]
1425
     */
1426
    public function getPropertyNames()
1427
    {
1428
        if (null === $this->properties) {
1429
            return array();
1430
        }
1431
        return array_keys($this->properties->toArray());
1432
    }
1433
1434
    /**
1435
     * @return string[]
1436
     */
1437
    public function getNestedPropertyNames()
1438
    {
1439
        if (null === $this->properties) {
1440
            return array();
1441
        }
1442
        return $this->properties->nestedPropertyNames;
1443
    }
1444
1445
}
1446