Passed
Pull Request — master (#64)
by Viacheslav
03:06
created

Schema::processAllOf()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

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

539
    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...
540
    {
541 165
        foreach ($this->required as $item) {
542 161
            if (!array_key_exists($item, $array)) {
543 161
                $this->fail(new ObjectException('Required property missing: ' . $item . ', data: ' . json_encode($array, JSON_UNESCAPED_SLASHES), ObjectException::REQUIRED), $path);
544
            }
545
        }
546 133
    }
547
548
    /**
549
     * @param object $data
550
     * @param Context $options
551
     * @param string $path
552
     * @param ObjectItemContract|null $result
553
     * @return array|null|ClassStructure|ObjectItemContract
554
     * @throws InvalidValue
555
     * @throws \Exception
556
     * @throws \Swaggest\JsonDiff\Exception
557
     */
558 3266
    private function processObject($data, Context $options, $path, $result = null)
559
    {
560 3266
        $import = $options->import;
561
562 3266
        $hasMapping = isset($this->properties->__dataToProperty[$options->mapping]);
0 ignored issues
show
Bug Best Practice introduced by
The property __dataToProperty does not exist on Swaggest\JsonSchema\Schema. Since you implemented __get, consider adding a @property annotation.
Loading history...
563
564 3266
        $array = !$data instanceof \stdClass ? get_object_vars($data) : (array)$data;
565
566
        // convert imported data to default mapping before validation
567 3266
        if ($import && $options->mapping !== self::DEFAULT_MAPPING) {
568 2
            if (isset($this->properties->__dataToProperty[$options->mapping])) {
569 2
                foreach ($this->properties->__dataToProperty[$options->mapping] as $dataName => $propertyName) {
570 2
                    if (!isset($array[$dataName])) {
571
                        continue;
572
                    }
573
574 2
                    $propertyName = isset($this->properties->__propertyToData[self::DEFAULT_MAPPING][$propertyName])
0 ignored issues
show
Bug Best Practice introduced by
The property __propertyToData does not exist on Swaggest\JsonSchema\Schema. Since you implemented __get, consider adding a @property annotation.
Loading history...
575 2
                        ? $this->properties->__propertyToData[self::DEFAULT_MAPPING][$propertyName]
576 2
                        : $propertyName;
577 2
                    if ($propertyName !== $dataName) {
578 2
                        $array[$propertyName] = $array[$dataName];
579 2
                        unset($array[$dataName]);
580
                    }
581
                }
582
            }
583
        }
584
585 3266
        if (!$options->skipValidation && $this->required !== null) {
586 165
            $this->processObjectRequired($array, $options, $path);
587
        }
588
589
        // build result entity
590 3265
        if ($import) {
591 3248
            if (!$options->validateOnly) {
592
593 3248
                if ($this->useObjectAsArray) {
594 1
                    $result = array();
595 3247
                } elseif (!$result instanceof ObjectItemContract) {
596
                    //* todo check performance impact
597 3247
                    if (null === $this->objectItemClass) {
598 1183
                        $result = new ObjectItem();
599
                    } else {
600 3236
                        $className = $this->objectItemClass;
601 3236
                        if ($options->objectItemClassMapping !== null) {
602
                            if (isset($options->objectItemClassMapping[$className])) {
603
                                $className = $options->objectItemClassMapping[$className];
604
                            }
605
                        }
606 3236
                        $result = new $className;
607
                    }
608
                    //*/
609
610
611 3247
                    if ($result instanceof ClassStructure) {
612 3232
                        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...
613 3232
                            $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...
614
                            /** @noinspection PhpUnusedLocalVariableInspection */
615
                            /* todo check performance impact
0 ignored issues
show
Unused Code Comprehensibility introduced by
43% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

Loading history...
616
                            $validateOnSetHandler = new ScopeExit(function () use ($result) {
617
                                $result->__validateOnSet = true;
618
                            });
619
                            //*/
620
                        }
621
                    }
622
623
                    //* todo check performance impact
624 3247
                    if ($result instanceof ObjectItemContract) {
625 3247
                        $result->setDocumentPath($path);
626
                    }
627
                    //*/
628
                }
629
            }
630
        }
631
632
        // @todo better check for schema id
633
634 3265
        if ($import
635 3265
            && isset($array[Schema::PROP_ID_D4])
636 3265
            && ($options->version === Schema::VERSION_DRAFT_04 || $options->version === Schema::VERSION_AUTO)
637 3265
            && is_string($array[Schema::PROP_ID_D4])) {
638 42
            $id = $array[Schema::PROP_ID_D4];
639 42
            $refResolver = $options->refResolver;
640 42
            $parentScope = $refResolver->updateResolutionScope($id);
641
            /** @noinspection PhpUnusedLocalVariableInspection */
642
            $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...
643 42
                $refResolver->setResolutionScope($parentScope);
644 42
            });
645
        }
646
647 3265
        if ($import
648 3265
            && isset($array[self::PROP_ID])
649 3265
            && ($options->version >= Schema::VERSION_DRAFT_06 || $options->version === Schema::VERSION_AUTO)
650 3265
            && is_string($array[self::PROP_ID])) {
651 114
            $id = $array[self::PROP_ID];
652 114
            $refResolver = $options->refResolver;
653 114
            $parentScope = $refResolver->updateResolutionScope($id);
654
            /** @noinspection PhpUnusedLocalVariableInspection */
655
            $defer = new ScopeExit(function () use ($parentScope, $refResolver) {
656 114
                $refResolver->setResolutionScope($parentScope);
657 114
            });
658
        }
659
660
        // check $ref
661 3265
        if ($import) {
662
            try {
663
664 3248
                $refProperty = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $refProperty is dead and can be removed.
Loading history...
665 3248
                $dereference = true;
666
667 3248
                if (isset($array[self::PROP_REF])) {
668 447
                    $refProperty = $this->properties[self::PROP_REF];
669
670 447
                    if (isset($refProperty) && ($refProperty->format !== Format::URI_REFERENCE)) {
671 14
                        $dereference = false;
672
                    }
673
                }
674
675
                if (
676 3248
                    isset($array[self::PROP_REF])
677 3248
                    && is_string($array[self::PROP_REF])
678 3248
                    && $dereference
679
                ) {
680 435
                    $refString = $array[self::PROP_REF];
681
682
                    // todo check performance impact
683 435
                    if ($refString === 'http://json-schema.org/draft-04/schema#'
684 425
                        || $refString === 'http://json-schema.org/draft-06/schema#'
685 435
                        || $refString === 'http://json-schema.org/draft-07/schema#') {
686 26
                        return Schema::schema();
687
                    }
688
689
                    // TODO consider process # by reference here ?
690 409
                    $refResolver = $options->refResolver;
691 409
                    $preRefScope = $refResolver->getResolutionScope();
692
                    /** @noinspection PhpUnusedLocalVariableInspection */
693
                    $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...
694 409
                        $refResolver->setResolutionScope($preRefScope);
695 409
                    });
696
697 409
                    $ref = $refResolver->resolveReference($refString);
698 409
                    $data = self::unboolSchemaData($ref->getData());
699 409
                    if (!$options->validateOnly) {
700 409
                        if ($ref->isImported()) {
701 185
                            $refResult = $ref->getImported();
702 185
                            return $refResult;
703
                        }
704 409
                        $ref->setImported($result);
705
                        try {
706 409
                            $refResult = $this->process($data, $options, $path . '->$ref:' . $refString, $result);
707 409
                            if ($refResult instanceof ObjectItemContract) {
708 409
                                if ($refResult->getFromRefs()) {
709 47
                                    $refResult = clone $refResult; // @todo check performance, consider option
710
                                }
711 409
                                $refResult->setFromRef($refString);
712
                            }
713 409
                            $ref->setImported($refResult);
714 1
                        } catch (InvalidValue $exception) {
715 1
                            $ref->unsetImported();
716 1
                            throw $exception;
717
                        }
718 409
                        return $refResult;
719
                    } else {
720 3248
                        $this->process($data, $options, $path . '->$ref:' . $refString);
721
                    }
722
                }
723 1
            } catch (InvalidValue $exception) {
724 1
                $this->fail($exception, $path);
725
            }
726
        }
727
728
        /** @var Schema[]|null $properties */
729 3265
        $properties = null;
730
731 3265
        $nestedProperties = null;
732 3265
        if ($this->properties !== null) {
733 3251
            $properties = $this->properties->toArray(); // todo call directly
734
735 3251
            if ($this->properties instanceof Properties) {
736 3251
                $nestedProperties = $this->properties->nestedProperties;
737
            } else {
738 588
                $nestedProperties = array();
739
            }
740
        }
741
742
743 3265
        if (!$options->skipValidation) {
744 3244
            if ($this->minProperties !== null && count($array) < $this->minProperties) {
745 4
                $this->fail(new ObjectException("Not enough properties", ObjectException::TOO_FEW), $path);
746
            }
747 3244
            if ($this->maxProperties !== null && count($array) > $this->maxProperties) {
748 3
                $this->fail(new ObjectException("Too many properties", ObjectException::TOO_MANY), $path);
749
            }
750 3244
            if ($this->propertyNames !== null) {
751 19
                $propertyNames = self::unboolSchema($this->propertyNames);
752 19
                foreach ($array as $key => $tmp) {
753 12
                    $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

753
                    $propertyNames->/** @scrutinizer ignore-call */ 
754
                                    process($key, $options, $path . '->propertyNames:' . $key);
Loading history...
754
                }
755
            }
756
        }
757
758 3265
        $defaultApplied = array();
759 3265
        if ($import
760 3265
            && !$options->validateOnly
761 3265
            && $options->applyDefaults
762 3265
            && $properties !== null
763
        ) {
764 41
            foreach ($properties as $key => $property) {
765
                // todo check when property is \stdClass `{}` here (RefTest)
766 39
                if ($property instanceof SchemaContract && null !== $default = $property->getDefault()) {
767 6
                    if (!array_key_exists($key, $array)) {
768 6
                        $defaultApplied[$key] = true;
769 39
                        $array[$key] = $default;
770
                    }
771
                }
772
            }
773
        }
774
775 3265
        foreach ($array as $key => $value) {
776 3255
            if ($key === '' && PHP_VERSION_ID < 71000) {
777 1
                $this->fail(new InvalidValue('Empty property name'), $path);
778
            }
779
780 3254
            $found = false;
781
782 3254
            if (!$options->skipValidation && !empty($this->dependencies)) {
783 73
                $deps = $this->dependencies;
784 73
                if (isset($deps->$key)) {
785 63
                    $dependencies = $deps->$key;
786 63
                    $dependencies = self::unboolSchema($dependencies);
787 63
                    if ($dependencies instanceof SchemaContract) {
788 29
                        $dependencies->process($data, $options, $path . '->dependencies:' . $key);
789
                    } else {
790 34
                        foreach ($dependencies as $item) {
791 31
                            if (!array_key_exists($item, $array)) {
792 18
                                $this->fail(new ObjectException('Dependency property missing: ' . $item,
793 31
                                    ObjectException::DEPENDENCY_MISSING), $path);
794
                            }
795
                        }
796
                    }
797
                }
798
            }
799
800 3254
            $propertyFound = false;
801 3254
            if (isset($properties[$key])) {
802
                /** @var Schema[] $properties */
803 3244
                $prop = self::unboolSchema($properties[$key]);
804 3244
                $propertyFound = true;
805 3244
                $found = true;
806 3244
                if ($prop instanceof SchemaContract) {
807 3187
                    $value = $prop->process(
808 3187
                        $value,
809 3187
                        isset($defaultApplied[$key]) ? $options->withDefault() : $options,
810 3187
                        $path . '->properties:' . $key
811
                    );
812
                }
813
            }
814
815
            /** @var Egg[] $nestedEggs */
816 3249
            $nestedEggs = null;
817 3249
            if (isset($nestedProperties[$key])) {
818 6
                $found = true;
819 6
                $nestedEggs = $nestedProperties[$key];
820
                // todo iterate all nested props?
821 6
                $value = self::unboolSchema($nestedEggs[0]->propertySchema)->process($value, $options, $path . '->nestedProperties:' . $key);
822
            }
823
824 3249
            if ($this->patternProperties !== null) {
825 178
                foreach ($this->patternProperties as $pattern => $propertySchema) {
826 178
                    if (preg_match(Helper::toPregPattern($pattern), $key)) {
827 126
                        $found = true;
828 126
                        $value = self::unboolSchema($propertySchema)->process($value, $options,
829 126
                            $path . '->patternProperties[' . strtr($pattern, array('~' => '~1', ':' => '~2')) . ']:' . $key);
830 99
                        if (!$options->validateOnly && $import) {
831 156
                            $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

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

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
1132
        }
1133
1134
        skipValidation:
1135
1136 3311
        if ($this->oneOf !== null) {
1137 107
            $result = $this->processOneOf($data, $options, $path);
1138
        }
1139
1140 3311
        if ($this->anyOf !== null) {
1141 1760
            $result = $this->processAnyOf($data, $options, $path);
1142
        }
1143
1144 3311
        if ($this->allOf !== null) {
1145 354
            $result = $this->processAllOf($data, $options, $path);
1146
        }
1147
1148 3311
        if (is_object($data)) {
1149 3266
            $result = $this->processObject($data, $options, $path, $result);
1150
        }
1151
1152 3308
        if (is_array($data)) {
1153 1238
            $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\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

1153
            $result = $this->processArray($data, $options, $path, /** @scrutinizer ignore-type */ $result);
Loading history...
1154
        }
1155
1156 3308
        if ($this->contentEncoding !== null || $this->contentMediaType !== null) {
1157 20
            if ($import && !is_string($data)) {
1158 6
                return $result;
1159
            }
1160 20
            $result = $this->processContent($data, $options, $path);
1161
        }
1162
1163 3308
        return $result;
1164
    }
1165
1166
    /**
1167
     * @param boolean $useObjectAsArray
1168
     * @return Schema
1169
     */
1170 1
    public function setUseObjectAsArray($useObjectAsArray)
1171
    {
1172 1
        $this->useObjectAsArray = $useObjectAsArray;
1173 1
        return $this;
1174
    }
1175
1176
    /**
1177
     * @param InvalidValue $exception
1178
     * @param string $path
1179
     * @throws InvalidValue
1180
     */
1181 1263
    private function fail(InvalidValue $exception, $path)
1182
    {
1183 1263
        $exception->addPath($path);
1184 1263
        throw $exception;
1185
    }
1186
1187 16
    public static function integer()
1188
    {
1189 16
        $schema = new static();
1190 16
        $schema->type = Type::INTEGER;
1191 16
        return $schema;
1192
    }
1193
1194 4
    public static function number()
1195
    {
1196 4
        $schema = new static();
1197 4
        $schema->type = Type::NUMBER;
1198 4
        return $schema;
1199
    }
1200
1201 13
    public static function string()
1202
    {
1203 13
        $schema = new static();
1204 13
        $schema->type = Type::STRING;
1205 13
        return $schema;
1206
    }
1207
1208 4
    public static function boolean()
1209
    {
1210 4
        $schema = new static();
1211 4
        $schema->type = Type::BOOLEAN;
1212 4
        return $schema;
1213
    }
1214
1215 8
    public static function object()
1216
    {
1217 8
        $schema = new static();
1218 8
        $schema->type = Type::OBJECT;
1219 8
        return $schema;
1220
    }
1221
1222 1
    public static function arr()
1223
    {
1224 1
        $schema = new static();
1225 1
        $schema->type = Type::ARR;
1226 1
        return $schema;
1227
    }
1228
1229
    public static function null()
1230
    {
1231
        $schema = new static();
1232
        $schema->type = Type::NULL;
1233
        return $schema;
1234
    }
1235
1236
1237
    /**
1238
     * @param Properties $properties
1239
     * @return Schema
1240
     */
1241 3
    public function setProperties($properties)
1242
    {
1243 3
        $this->properties = $properties;
1244 3
        return $this;
1245
    }
1246
1247
    /**
1248
     * @param string $name
1249
     * @param Schema $schema
1250
     * @return $this
1251
     */
1252 5
    public function setProperty($name, $schema)
1253
    {
1254 5
        if (null === $this->properties) {
1255 5
            $this->properties = new Properties();
1256
        }
1257 5
        $this->properties->__set($name, $schema);
1258 5
        return $this;
1259
    }
1260
1261
    /**
1262
     * @param string $name
1263
     * @param SchemaContract $schema
1264
     * @return $this
1265
     * @throws Exception
1266
     */
1267
    public function setPatternProperty($name, $schema)
1268
    {
1269
        if (null === $this->patternProperties) {
1270
            $this->patternProperties = new Properties();
1271
        }
1272
        $this->patternProperties->__set($name, $schema);
1273
        return $this;
1274
    }
1275
1276
1277
    /** @var mixed[] */
1278
    private $metaItems = array();
1279
1280 1
    public function addMeta($meta, $name = null)
1281
    {
1282 1
        if ($name === null) {
1283 1
            $name = get_class($meta);
1284
        }
1285 1
        $this->metaItems[$name] = $meta;
1286 1
        return $this;
1287
    }
1288
1289 1
    public function getMeta($name)
1290
    {
1291 1
        if (isset($this->metaItems[$name])) {
1292 1
            return $this->metaItems[$name];
1293
        }
1294
        return null;
1295
    }
1296
1297
    /**
1298
     * @param Context $options
1299
     * @return ObjectItemContract
1300
     */
1301 5
    public function makeObjectItem(Context $options = null)
1302
    {
1303 5
        if (null === $this->objectItemClass) {
1304
            return new ObjectItem();
1305
        } else {
1306 5
            $className = $this->objectItemClass;
1307 5
            if ($options !== null) {
1308
                if (isset($options->objectItemClassMapping[$className])) {
1309
                    $className = $options->objectItemClassMapping[$className];
1310
                }
1311
            }
1312 5
            return new $className;
1313
        }
1314
    }
1315
1316
    /**
1317
     * @param mixed $schema
1318
     * @return mixed|Schema
1319
     */
1320 3324
    private static function unboolSchema($schema)
1321
    {
1322 3324
        static $trueSchema;
1323 3324
        static $falseSchema;
1324
1325 3324
        if (null === $trueSchema) {
1326 1
            $trueSchema = new Schema();
1327 1
            $trueSchema->__booleanSchema = true;
1328 1
            $falseSchema = new Schema();
1329 1
            $falseSchema->not = $trueSchema;
1330 1
            $falseSchema->__booleanSchema = false;
1331
        }
1332
1333 3324
        if ($schema === true) {
1334 108
            return $trueSchema;
1335 3296
        } elseif ($schema === false) {
1336 94
            return $falseSchema;
1337
        } else {
1338 3264
            return $schema;
1339
        }
1340
    }
1341
1342
    /**
1343
     * @param mixed $data
1344
     * @return \stdClass
1345
     */
1346 409
    private static function unboolSchemaData($data)
1347
    {
1348 409
        static $trueSchema;
1349 409
        static $falseSchema;
1350
1351 409
        if (null === $trueSchema) {
1352 1
            $trueSchema = new \stdClass();
1353 1
            $falseSchema = new \stdClass();
1354 1
            $falseSchema->not = $trueSchema;
1355
        }
1356
1357 409
        if ($data === true) {
1358 6
            return $trueSchema;
1359 405
        } elseif ($data === false) {
1360 5
            return $falseSchema;
1361
        } else {
1362 400
            return $data;
1363
        }
1364
    }
1365
1366 39
    public function getDefault()
1367
    {
1368 39
        return $this->default;
1369
    }
1370
1371 99
    public function getProperties()
1372
    {
1373 99
        return $this->properties;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->properties also could return the type Swaggest\JsonSchema\Schema which is incompatible with the return type mandated by Swaggest\JsonSchema\Sche...ntract::getProperties() of Swaggest\JsonSchema\Cons...sonSchema\Schema[]|null.
Loading history...
1374
    }
1375
1376
    public function getObjectItemClass()
1377
    {
1378
        return $this->objectItemClass;
1379
    }
1380
1381
    /**
1382
     * @return string[]
1383
     */
1384
    public function getPropertyNames()
1385
    {
1386
        return array_keys($this->getProperties()->toArray());
1387
    }
1388
1389
    /**
1390
     * @return string[]
1391
     */
1392 97
    public function getNestedPropertyNames()
1393
    {
1394 97
        return $this->getProperties()->nestedPropertyNames;
1395
    }
1396
1397
}
1398