Skip to content

Commit de29f20

Browse files
committed
Best-effort loose validation for logical constraints
1 parent 1b37606 commit de29f20

File tree

2 files changed

+76
-50
lines changed

2 files changed

+76
-50
lines changed

src/Context.php

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

22+
/** @var bool */
2223
public $skipValidation = false;
2324

25+
/**
26+
* @param boolean $skipValidation
27+
* @return Context
28+
*/
29+
public function setSkipValidation($skipValidation = true)
30+
{
31+
$this->skipValidation = $skipValidation;
32+
return $this;
33+
}
34+
35+
2436
/**
2537
* ProcessingOptions constructor.
2638
* @param RemoteRefProvider $remoteRefProvider

src/Schema.php

Lines changed: 64 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -205,56 +205,6 @@ public function process($data, Context $options, $path = '#', $result = null)
205205
}
206206
}
207207

208-
if ($this->oneOf !== null) {
209-
$successes = 0;
210-
$failures = '';
211-
foreach ($this->oneOf as $index => $item) {
212-
try {
213-
$result = $item->process($data, $options, $path . '->oneOf:' . $index);
214-
$successes++;
215-
if ($successes > 1) {
216-
break;
217-
}
218-
} catch (InvalidValue $exception) {
219-
$failures .= ' ' . $index . ': ' . Helper::padLines(' ', $exception->getMessage()) . "\n";
220-
// Expected exception
221-
}
222-
}
223-
if ($successes === 0) {
224-
$this->fail(new LogicException('Failed due to logical constraint: no valid results for oneOf {' . "\n" . substr($failures, 0, -1) . "\n}"), $path);
225-
} elseif ($successes > 1) {
226-
$this->fail(new LogicException('Failed due to logical constraint: '
227-
. $successes . '/' . count($this->oneOf) . ' valid results for oneOf'), $path);
228-
}
229-
}
230-
231-
if ($this->anyOf !== null) {
232-
$successes = 0;
233-
$failures = '';
234-
foreach ($this->anyOf as $index => $item) {
235-
try {
236-
$result = $item->process($data, $options, $path . '->anyOf:' . $index);
237-
$successes++;
238-
if ($successes) {
239-
break;
240-
}
241-
} catch (InvalidValue $exception) {
242-
$failures .= ' ' . $index . ': ' . $exception->getMessage() . "\n";
243-
// Expected exception
244-
}
245-
}
246-
if (!$successes) {
247-
$this->fail(new LogicException('Failed due to logical constraint: no valid results for anyOf {' . "\n" . substr(Helper::padLines(' ', $failures), 0, -1) . "\n}"), $path);
248-
}
249-
}
250-
251-
if ($this->allOf !== null) {
252-
foreach ($this->allOf as $index => $item) {
253-
$result = $item->process($data, $options, $path . '->allOf' . $index);
254-
}
255-
}
256-
257-
258208
if (is_string($data)) {
259209
if ($this->minLength !== null) {
260210
if (mb_strlen($data, 'UTF-8') < $this->minLength) {
@@ -317,6 +267,70 @@ public function process($data, Context $options, $path = '#', $result = null)
317267

318268
skipValidation:
319269

270+
if ($this->oneOf !== null) {
271+
$successes = 0;
272+
$failures = '';
273+
$skipValidation = false;
274+
if ($options->skipValidation) {
275+
$skipValidation = true;
276+
$options->skipValidation = false;
277+
}
278+
279+
foreach ($this->oneOf as $index => $item) {
280+
try {
281+
$result = $item->process($data, $options, $path . '->oneOf:' . $index);
282+
$successes++;
283+
if ($successes > 1 || $options->skipValidation) {
284+
break;
285+
}
286+
} catch (InvalidValue $exception) {
287+
$failures .= ' ' . $index . ': ' . Helper::padLines(' ', $exception->getMessage()) . "\n";
288+
// Expected exception
289+
}
290+
}
291+
if ($skipValidation) {
292+
$options->skipValidation = true;
293+
if ($successes === 0) {
294+
$result = $this->oneOf[0]->process($data, $options, $path . '->oneOf:' . 0);
295+
}
296+
}
297+
298+
if (!$options->skipValidation) {
299+
if ($successes === 0) {
300+
$this->fail(new LogicException('Failed due to logical constraint: no valid results for oneOf {' . "\n" . substr($failures, 0, -1) . "\n}"), $path);
301+
} elseif ($successes > 1) {
302+
$this->fail(new LogicException('Failed due to logical constraint: '
303+
. $successes . '/' . count($this->oneOf) . ' valid results for oneOf'), $path);
304+
}
305+
}
306+
}
307+
308+
if ($this->anyOf !== null) {
309+
$successes = 0;
310+
$failures = '';
311+
foreach ($this->anyOf as $index => $item) {
312+
try {
313+
$result = $item->process($data, $options, $path . '->anyOf:' . $index);
314+
$successes++;
315+
if ($successes) {
316+
break;
317+
}
318+
} catch (InvalidValue $exception) {
319+
$failures .= ' ' . $index . ': ' . $exception->getMessage() . "\n";
320+
// Expected exception
321+
}
322+
}
323+
if (!$successes && !$options->skipValidation) {
324+
$this->fail(new LogicException('Failed due to logical constraint: no valid results for anyOf {' . "\n" . substr(Helper::padLines(' ', $failures), 0, -1) . "\n}"), $path);
325+
}
326+
}
327+
328+
if ($this->allOf !== null) {
329+
foreach ($this->allOf as $index => $item) {
330+
$result = $item->process($data, $options, $path . '->allOf' . $index);
331+
}
332+
}
333+
320334
if ($data instanceof \stdClass) {
321335
if (!$options->skipValidation && $this->required !== null) {
322336
foreach ($this->required as $item) {

0 commit comments

Comments
 (0)