Skip to content

Commit 5a38f70

Browse files
committed
optional validation
1 parent 1cb70fc commit 5a38f70

File tree

5 files changed

+122
-18
lines changed

5 files changed

+122
-18
lines changed

README.md

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -270,11 +270,13 @@ $properties->dateTime = Schema::string()->meta(new FieldName('date_time'));
270270

271271
```php
272272
$mapper = new NameMapper();
273+
$options = new Context();
274+
$options->dataPreProcessor = $mapper;
273275

274276
$order = new Order();
275277
$order->id = 1;
276278
$order->dateTime = '2015-10-28T07:28:00Z';
277-
$exported = Order::export($order, $mapper);
279+
$exported = Order::export($order, $options);
278280
$json = <<<JSON
279281
{
280282
"id": 1,
@@ -283,7 +285,7 @@ $json = <<<JSON
283285
JSON;
284286
$this->assertSame($json, json_encode($exported, JSON_PRETTY_PRINT));
285287

286-
$imported = Order::import(json_decode($json), $mapper);
288+
$imported = Order::import(json_decode($json), $options);
287289
$this->assertSame('2015-10-28T07:28:00Z', $imported->dateTime);
288290
```
289291

@@ -305,4 +307,21 @@ And get back.
305307
// Retrieving meta
306308
$myMeta = FieldName::get($schema);
307309
$this->assertSame('my-value', $myMeta->name);
310+
```
311+
312+
313+
#### Mapping without validation
314+
315+
If you want to tolerate invalid data or improve mapping performance you can specify `skipValidation` flag in processing `Context`
316+
317+
```
318+
$schema = Schema::object();
319+
$schema->setProperty('one', Schema::integer());
320+
$schema->properties->one->minimum = 5;
321+
322+
$options = new Context();
323+
$options->skipValidation = true;
324+
325+
$res = $schema->in(json_decode('{"one":4}'), $options);
326+
$this->assertSame(4, $res->one);
308327
```

src/Context.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ class Context extends MagicMap
1919
/** @var \SplObjectStorage */
2020
public $circularReferences;
2121

22+
public $skipValidation = false;
23+
2224
/**
2325
* ProcessingOptions constructor.
2426
* @param RemoteRefProvider $remoteRefProvider

src/Schema.php

Lines changed: 29 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,10 @@ public function process($data, Context $options, $path = '#', $result = null)
167167
$result = $data;
168168
}
169169

170+
if ($options->skipValidation) {
171+
goto skipValidation;
172+
}
173+
170174
if ($this->type !== null) {
171175
if (!Type::isValid($this->type, $data)) {
172176
$this->fail(new TypeException(ucfirst(
@@ -311,9 +315,10 @@ public function process($data, Context $options, $path = '#', $result = null)
311315
}
312316
}
313317

318+
skipValidation:
314319

315320
if ($data instanceof \stdClass) {
316-
if ($this->required !== null) {
321+
if (!$options->skipValidation && $this->required !== null) {
317322
foreach ($this->required as $item) {
318323
if (!property_exists($data, $item)) {
319324
$this->fail(new ObjectException('Required property missing: ' . $item, ObjectException::REQUIRED), $path);
@@ -416,20 +421,23 @@ public function process($data, Context $options, $path = '#', $result = null)
416421
$array = (array)$data;
417422
}
418423

419-
if ($this->minProperties !== null && count($array) < $this->minProperties) {
420-
$this->fail(new ObjectException("Not enough properties", ObjectException::TOO_FEW), $path);
421-
}
422-
if ($this->maxProperties !== null && count($array) > $this->maxProperties) {
423-
$this->fail(new ObjectException("Too many properties", ObjectException::TOO_MANY), $path);
424+
if (!$options->skipValidation) {
425+
if ($this->minProperties !== null && count($array) < $this->minProperties) {
426+
$this->fail(new ObjectException("Not enough properties", ObjectException::TOO_FEW), $path);
427+
}
428+
if ($this->maxProperties !== null && count($array) > $this->maxProperties) {
429+
$this->fail(new ObjectException("Too many properties", ObjectException::TOO_MANY), $path);
430+
}
424431
}
432+
425433
foreach ($array as $key => $value) {
426434
if ($key === '' && PHP_VERSION_ID < 71000) {
427435
$this->fail(new InvalidValue('Empty property name'), $path);
428436
}
429437

430438
$found = false;
431439

432-
if (!empty($this->dependencies)) {
440+
if (!$options->skipValidation && !empty($this->dependencies)) {
433441
$deps = $this->dependencies;
434442
if (isset($deps->$key)) {
435443
$dependencies = $deps->$key;
@@ -477,11 +485,14 @@ public function process($data, Context $options, $path = '#', $result = null)
477485
}
478486
}
479487
if (!$found && $this->additionalProperties !== null) {
480-
if ($this->additionalProperties === false) {
488+
if (!$options->skipValidation && $this->additionalProperties === false) {
481489
$this->fail(new ObjectException('Additional properties not allowed'), $path . ':' . $key);
482490
}
483491

484-
$value = $this->additionalProperties->process($value, $options, $path . '->additionalProperties:' . $key);
492+
if ($this->additionalProperties !== false) {
493+
$value = $this->additionalProperties->process($value, $options, $path . '->additionalProperties:' . $key);
494+
}
495+
485496
if ($import && !$this->useObjectAsArray) {
486497
$result->addAdditionalPropertyName($key);
487498
}
@@ -511,12 +522,14 @@ public function process($data, Context $options, $path = '#', $result = null)
511522

512523
if (is_array($data)) {
513524

514-
if ($this->minItems !== null && count($data) < $this->minItems) {
515-
$this->fail(new ArrayException("Not enough items in array"), $path);
516-
}
525+
if (!$options->skipValidation) {
526+
if ($this->minItems !== null && count($data) < $this->minItems) {
527+
$this->fail(new ArrayException("Not enough items in array"), $path);
528+
}
517529

518-
if ($this->maxItems !== null && count($data) > $this->maxItems) {
519-
$this->fail(new ArrayException("Too many items in array"), $path);
530+
if ($this->maxItems !== null && count($data) > $this->maxItems) {
531+
$this->fail(new ArrayException("Too many items in array"), $path);
532+
}
520533
}
521534

522535
$pathItems = 'items';
@@ -542,15 +555,15 @@ public function process($data, Context $options, $path = '#', $result = null)
542555
if ($additionalItems instanceof Schema) {
543556
$result[$key] = $additionalItems->process($value, $options, $path . '->' . $pathItems
544557
. '[' . $index . ']');
545-
} elseif ($additionalItems === false) {
558+
} elseif (!$options->skipValidation && $additionalItems === false) {
546559
$this->fail(new ArrayException('Unexpected array item'), $path);
547560
}
548561
}
549562
++$index;
550563
}
551564
}
552565

553-
if ($this->uniqueItems) {
566+
if (!$options->skipValidation && $this->uniqueItems) {
554567
if (!UniqueItems::isValid($data)) {
555568
$this->fail(new ArrayException('Array is not unique'), $path);
556569
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php
2+
3+
namespace Swaggest\JsonSchema\Tests\PHPUnit\SkipValidation;
4+
5+
use Swaggest\JsonSchema\Context;
6+
use Swaggest\JsonSchema\Schema;
7+
8+
class SkipValidationTest extends \PHPUnit_Framework_TestCase
9+
{
10+
public function testSkipValidation()
11+
{
12+
$schema = Schema::integer();
13+
$schema->minimum = 5;
14+
$options = new Context();
15+
$options->skipValidation = true;
16+
$schema->in(4, $options);
17+
}
18+
19+
20+
public function testSkipValidationInObject()
21+
{
22+
$schema = Schema::object();
23+
$schema->setProperty('one', Schema::integer());
24+
$schema->properties->one->minimum = 5;
25+
26+
$options = new Context();
27+
$options->skipValidation = true;
28+
29+
$res = $schema->in(json_decode('{"one":4}'), $options);
30+
$this->assertSame(4, $res->one);
31+
}
32+
33+
}

tests/src/PHPUnit/Spec/SpecTest.php

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,43 @@ public function testSpecDraft4($schemaData, $data, $isValid)
6969
}
7070

7171

72+
/**
73+
* @dataProvider provider
74+
* @param $schemaData
75+
* @param $data
76+
* @param $isValid
77+
* @throws InvalidValue
78+
*/
79+
public function testSpecDraft4SkipValidation($schemaData, $data, $isValid)
80+
{
81+
$refProvider = self::getProvider();
82+
83+
$actualValid = true;
84+
$error = '';
85+
try {
86+
$options = new Context();
87+
$options->setRemoteRefProvider($refProvider);
88+
$schema = Schema::import($schemaData, $options);
89+
$context = new Context();
90+
$context->skipValidation = true;
91+
$res = $schema->in($data, $context);
92+
93+
$context = new Context();
94+
$context->skipValidation = true;
95+
$exported = $schema->out($res, $context);
96+
$this->assertEquals($data, $exported);
97+
} catch (InvalidValue $exception) {
98+
$actualValid = false;
99+
$error = $exception->getMessage();
100+
}
101+
102+
103+
$this->assertTrue($actualValid, "Schema:\n" . json_encode($schemaData, JSON_PRETTY_PRINT)
104+
. "\nData:\n" . json_encode($data, JSON_PRETTY_PRINT)
105+
. "\nError: " . $error . "\n");
106+
}
107+
108+
72109
public function provider()
73110
{
74111
$path = __DIR__ . '/../../../../spec/JSON-Schema-Test-Suite/tests/draft4';

0 commit comments

Comments
 (0)