Completed
Push — master ( 914d07...23b670 )
by Viacheslav
13:08 queued 02:51
created

Schema::process()   D

Complexity

Conditions 42

Size

Total Lines 139
Code Lines 77

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 68
CRAP Score 42.0053

Importance

Changes 4
Bugs 0 Features 0
Metric Value
eloc 77
c 4
b 0
f 0
dl 0
loc 139
ccs 68
cts 69
cp 0.9855
rs 4.1666
cc 42
nop 4
crap 42.0053

How to fix   Long Method    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
                $enumOk = true;
234
                break;
235 1449
            } else {
236 12
                if (is_array($item) || is_object($item)) {
237 12
                    $diff = new JsonDiff($item, $data, JsonDiff::STOP_ON_DIFF);
238 3
                    if ($diff->getDiffCnt() === 0) {
239 3
                        $enumOk = true;
240
                        break;
241
                    }
242
                }
243
            }
244 1513
        }
245 107
        if (!$enumOk) {
246
            $this->fail(new EnumException('Enum failed, enum: ' . json_encode($this->enum) . ', data: ' . json_encode($data)), $path);
247 1496
        }
248
    }
249
250
    /**
251
     * @param mixed $data
252
     * @param string $path
253
     * @throws InvalidValue
254
     * @throws \Swaggest\JsonDiff\Exception
255 43
     */
256
    private function processConst($data, $path)
257 43
    {
258 37
        if ($this->const !== $data) {
259 37
            if ((is_object($this->const) && is_object($data))
260 15
                || (is_array($this->const) && is_array($data))) {
261 15
                $diff = new JsonDiff($this->const, $data,
262 15
                    JsonDiff::STOP_ON_DIFF);
263 15
                if ($diff->getDiffCnt() != 0) {
264
                    $this->fail(new ConstException('Const failed'), $path);
265
                }
266 22
            } else {
267
                $this->fail(new ConstException('Const failed'), $path);
268
            }
269 20
        }
270
    }
271
272
    /**
273
     * @param mixed $data
274
     * @param Context $options
275
     * @param string $path
276
     * @throws InvalidValue
277
     * @throws \Exception
278
     * @throws \Swaggest\JsonDiff\Exception
279 70
     */
280
    private function processNot($data, Context $options, $path)
281 70
    {
282
        $exception = false;
283 70
        try {
284 16
            self::unboolSchema($this->not)->process($data, $options, $path . '->not');
285
        } catch (InvalidValue $exception) {
286
            // Expected exception
287 70
        }
288 56
        if ($exception === false) {
289
            $this->fail(new LogicException('Not ' . json_encode($this->not) . ' expected, ' . json_encode($data) . ' received'), $path . '->not');
290 16
        }
291
    }
292
293
    /**
294
     * @param string $data
295
     * @param string $path
296
     * @throws InvalidValue
297 2331
     */
298
    private function processString($data, $path)
299 2331
    {
300 38
        if ($this->minLength !== null) {
301 9
            if (mb_strlen($data, 'UTF-8') < $this->minLength) {
302
                $this->fail(new StringException('String is too short', StringException::TOO_SHORT), $path);
303
            }
304 2325
        }
305 43
        if ($this->maxLength !== null) {
306 19
            if (mb_strlen($data, 'UTF-8') > $this->maxLength) {
307
                $this->fail(new StringException('String is too long', StringException::TOO_LONG), $path);
308
            }
309 2322
        }
310 18
        if ($this->pattern !== null) {
311 4
            if (0 === preg_match(Helper::toPregPattern($this->pattern), $data)) {
312 4
                $this->fail(new StringException(json_encode($data) . ' does not match to '
313
                    . $this->pattern, StringException::PATTERN_MISMATCH), $path);
314
            }
315 2322
        }
316 445
        if ($this->format !== null) {
317 445
            $validationError = Format::validationError($this->format, $data);
318 150
            if ($validationError !== null) {
319 131
                if (!($this->format === "uri" && substr($path, -3) === ':id')) {
320
                    $this->fail(new StringException($validationError), $path);
321
                }
322
            }
323 2322
        }
324
    }
325
326
    /**
327
     * @param float|int $data
328
     * @param string $path
329
     * @throws InvalidValue
330 1090
     */
331
    private function processNumeric($data, $path)
332 1090
    {
333 39
        if ($this->multipleOf !== null) {
334 39
            $div = $data / $this->multipleOf;
335 15
            if ($div != (int)$div) {
336
                $this->fail(new NumericException($data . ' is not multiple of ' . $this->multipleOf, NumericException::MULTIPLE_OF), $path);
337
            }
338
        }
339 1090
340 32
        if ($this->exclusiveMaximum !== null && !is_bool($this->exclusiveMaximum)) {
341 18
            if ($data >= $this->exclusiveMaximum) {
342 18
                $this->fail(new NumericException(
343 18
                    'Value less or equal than ' . $this->exclusiveMaximum . ' expected, ' . $data . ' received',
344
                    NumericException::MAXIMUM), $path);
345
            }
346
        }
347 1090
348 24
        if ($this->exclusiveMinimum !== null && !is_bool($this->exclusiveMinimum)) {
349 12
            if ($data <= $this->exclusiveMinimum) {
350 12
                $this->fail(new NumericException(
351 12
                    'Value more or equal than ' . $this->exclusiveMinimum . ' expected, ' . $data . ' received',
352
                    NumericException::MINIMUM), $path);
353
            }
354
        }
355 1090
356 46
        if ($this->maximum !== null) {
357 3
            if ($this->exclusiveMaximum === true) {
358 2
                if ($data >= $this->maximum) {
359 2
                    $this->fail(new NumericException(
360 3
                        'Value less or equal than ' . $this->maximum . ' expected, ' . $data . ' received',
361
                        NumericException::MAXIMUM), $path);
362
                }
363 43
            } else {
364 14
                if ($data > $this->maximum) {
365 14
                    $this->fail(new NumericException(
366 14
                        'Value less than ' . $this->maximum . ' expected, ' . $data . ' received',
367
                        NumericException::MAXIMUM), $path);
368
                }
369
            }
370
        }
371 1090
372 524
        if ($this->minimum !== null) {
373 93
            if ($this->exclusiveMinimum === true) {
374 2
                if ($data <= $this->minimum) {
375 2
                    $this->fail(new NumericException(
376 93
                        'Value more or equal than ' . $this->minimum . ' expected, ' . $data . ' received',
377
                        NumericException::MINIMUM), $path);
378
                }
379 449
            } else {
380 46
                if ($data < $this->minimum) {
381 46
                    $this->fail(new NumericException(
382 46
                        'Value more than ' . $this->minimum . ' expected, ' . $data . ' received',
383
                        NumericException::MINIMUM), $path);
384
                }
385
            }
386 1089
        }
387
    }
388
389
    /**
390
     * @param mixed $data
391
     * @param Context $options
392
     * @param string $path
393
     * @return array|mixed|null|object|\stdClass
394
     * @throws InvalidValue
395
     * @throws \Exception
396
     * @throws \Swaggest\JsonDiff\Exception
397 107
     */
398
    private function processOneOf($data, Context $options, $path)
399 107
    {
400 107
        $successes = 0;
401 107
        $failures = '';
402 107
        $subErrors = [];
403 107
        $skipValidation = false;
404 41
        if ($options->skipValidation) {
405 41
            $skipValidation = true;
406
            $options->skipValidation = false;
407
        }
408 107
409 107
        $result = $data;
410
        foreach ($this->oneOf as $index => $item) {
411 107
            try {
412 85
                $result = self::unboolSchema($item)->process($data, $options, $path . '->oneOf[' . $index . ']');
413 85
                $successes++;
414 85
                if ($successes > 1 || $options->skipValidation) {
415
                    break;
416 75
                }
417 75
            } catch (InvalidValue $exception) {
418 75
                $subErrors[$index] = $exception;
419
                $failures .= ' ' . $index . ': ' . Helper::padLines(' ', $exception->getMessage()) . "\n";
420
                // Expected exception
421
            }
422 107
        }
423 41
        if ($skipValidation) {
424 41
            $options->skipValidation = true;
425 8
            if ($successes === 0) {
426
                $result = self::unboolSchema($this->oneOf[0])->process($data, $options, $path . '->oneOf[0]');
427
            }
428
        }
429 107
430 66
        if (!$options->skipValidation) {
431 20
            if ($successes === 0) {
432 20
                $exception = new LogicException('No valid results for oneOf {' . "\n" . substr($failures, 0, -1) . "\n}");
433 20
                $exception->error = 'No valid results for oneOf';
434 20
                $exception->subErrors = $subErrors;
435 52
                $this->fail($exception, $path);
436 17
            } elseif ($successes > 1) {
437 17
                $exception = new LogicException('More than 1 valid result for oneOf: '
438 17
                    . $successes . '/' . count($this->oneOf) . ' valid results for oneOf {'
439 17
                    . "\n" . substr($failures, 0, -1) . "\n}");
440 17
                $exception->error = 'More than 1 valid result for oneOf';
441 17
                $exception->subErrors = $subErrors;
442
                $this->fail($exception, $path);
443
            }
444 77
        }
445
        return $result;
446
    }
447
448
    /**
449
     * @param mixed $data
450
     * @param Context $options
451
     * @param string $path
452
     * @return array|mixed|null|object|\stdClass
453
     * @throws InvalidValue
454
     * @throws \Exception
455
     * @throws \Swaggest\JsonDiff\Exception
456 1763
     */
457
    private function processAnyOf($data, Context $options, $path)
458 1763
    {
459 1763
        $successes = 0;
460 1763
        $failures = '';
461 1763
        $subErrors = [];
462 1763
        $result = $data;
463
        foreach ($this->anyOf as $index => $item) {
464 1763
            try {
465 1757
                $result = self::unboolSchema($item)->process($data, $options, $path . '->anyOf[' . $index . ']');
466 1757
                $successes++;
467 1757
                if ($successes) {
468
                    break;
469 445
                }
470 445
            } catch (InvalidValue $exception) {
471 445
                $subErrors[$index] = $exception;
472
                $failures .= ' ' . $index . ': ' . $exception->getMessage() . "\n";
473
                // Expected exception
474
            }
475 1763
        }
476 31
        if (!$successes && !$options->skipValidation) {
477 31
            $exception = new LogicException('No valid results for anyOf {' . "\n"
478 31
                . substr(Helper::padLines(' ', $failures, false), 0, -1)
479 31
                . "\n}");
480 31
            $exception->error = 'No valid results for anyOf';
481 31
            $exception->subErrors = $subErrors;
482
            $this->fail($exception, $path);
483 1757
        }
484
        return $result;
485
    }
486
487
    /**
488
     * @param mixed $data
489
     * @param Context $options
490
     * @param string $path
491
     * @return array|mixed|null|object|\stdClass
492
     * @throws InvalidValue
493
     * @throws \Exception
494
     * @throws \Swaggest\JsonDiff\Exception
495 354
     */
496
    private function processAllOf($data, Context $options, $path)
497 354
    {
498 354
        $result = $data;
499 354
        foreach ($this->allOf as $index => $item) {
500
            $result = self::unboolSchema($item)->process($data, $options, $path . '->allOf[' . $index . ']');
501 314
        }
502
        return $result;
503
    }
504
505
    /**
506
     * @param mixed $data
507
     * @param Context $options
508
     * @param string $path
509
     * @return array|mixed|null|object|\stdClass
510
     * @throws InvalidValue
511
     * @throws \Exception
512
     * @throws \Swaggest\JsonDiff\Exception
513 26
     */
514
    private function processIf($data, Context $options, $path)
515 26
    {
516
        $valid = true;
517 26
        try {
518 13
            self::unboolSchema($this->if)->process($data, $options, $path . '->if');
519 13
        } catch (InvalidValue $exception) {
520
            $valid = false;
521 26
        }
522 18
        if ($valid) {
523 18
            if ($this->then !== null) {
524
                return self::unboolSchema($this->then)->process($data, $options, $path . '->then');
525
            }
526 13
        } else {
527 6
            if ($this->else !== null) {
528
                return self::unboolSchema($this->else)->process($data, $options, $path . '->else');
529
            }
530 10
        }
531
        return null;
532
    }
533
534
    /**
535
     * @param array $array
536
     * @param Context $options
537
     * @param string $path
538
     * @throws InvalidValue
539 168
     */
540
    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

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

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

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

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