• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

wol-soft / php-json-schema-model-generator-production / 29520360842

16 Jul 2026 05:32PM UTC coverage: 43.546% (+22.0%) from 21.548%
29520360842

Pull #19

github

claude
Add a data-instance JSON pointer to ValidationException

Addresses the "full property path" request from issue #131: alongside the
existing getJsonPointer() (which points into the schema), every
ValidationException now exposes getInstancePointer() — an RFC 6901 pointer
into the data that was validated (e.g. "/goals/0/kind").

Resolution is lazy, via a parent-chain set up by setInstancePointerParent():
each wrapping exception (InvalidItemException, InvalidTupleException,
InvalidAdditionalPropertiesException, InvalidPatternPropertiesException,
NestedObjectException, InvalidComposedValueException, ConditionalException)
links the exceptions it collects to itself as exceptions bubble up from the
point of failure to the root. Resolving lazily (rather than eagerly
concatenating strings at each level) is what makes arbitrarily deep nesting
work correctly — composition inside an array item inside another array, for
example — since a wrapper added after a child was constructed is still
picked up when the pointer is finally read.

Two wrinkles handled along the way:
- Composition/conditional branches validate the same value at the same
  position as their parent, so they link to it without replacing their own
  segment (they contribute nothing themselves); array/tuple items and
  additional/pattern properties instead replace their child's segment,
  since those children were constructed with a placeholder propertyName
  shared by every instance of the collection.
- InvalidAdditionalPropertiesException and InvalidPatternPropertiesException
  themselves run as base validators with no access to the property name
  their parent used to reach them, so they fall back to the generated class
  name for message text only; that placeholder is suppressed from their own
  instance pointer segment so it doesn't leak into the resolved path.
Pull Request #19: Re-indent nested composition/conditional messages under their own bullet

57 of 59 new or added lines in 8 files covered. (96.61%)

1 existing line in 1 file now uncovered.

361 of 829 relevant lines covered (43.55%)

1.38 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

81.08
/src/Exception/ComposedValue/ConditionalException.php
1
<?php
2

3
declare(strict_types=1);
4

5
namespace PHPModelGenerator\Exception\ComposedValue;
6

7
use Exception;
8
use PHPModelGenerator\Exception\ErrorRegistryExceptionInterface;
9
use PHPModelGenerator\Exception\ValidationException;
10

11
/**
12
 * Class ConditionalException
13
 *
14
 * @package PHPModelGenerator\Exception\ComposedValue
15
 */
16
class ConditionalException extends ValidationException
17
{
18
    /**
19
     * ConditionalException constructor.
20
     *
21
     * @param $providedValue
22
     */
23
    public function __construct(
2✔
24
        $providedValue,
25
        string $propertyName,
26
        string $jsonPointer,
27
        private readonly ?Exception $ifException,
28
        private readonly ?Exception $thenException,
29
        private readonly ?Exception $elseException
30
    ) {
31
        // A conditional branch validates the same value at the same position as the conditional
32
        // itself — it consumes no path segment of its own, so branch errors only need the parent
33
        // link (not a segment replacement) to inherit wherever this conditional ends up.
34
        foreach ([$this->ifException, $this->thenException, $this->elseException] as $branchException) {
2✔
35
            $this->linkInstancePointerParent($branchException);
2✔
36
        }
37

38
        parent::__construct($this->getErrorMessage($propertyName), $propertyName, $providedValue, $jsonPointer);
2✔
39
    }
40

41
    private function linkInstancePointerParent(?Exception $exception): void
2✔
42
    {
43
        if ($exception instanceof ErrorRegistryExceptionInterface) {
2✔
44
            foreach ($exception->getErrors() as $error) {
1✔
45
                $error->setInstancePointerParent($this);
1✔
46
            }
47
        } elseif ($exception instanceof ValidationException) {
2✔
48
            $exception->setInstancePointerParent($this);
1✔
49
        }
50
    }
51

UNCOV
52
    public function getIfException(): ?Exception
×
53
    {
54
        return $this->ifException;
×
55
    }
56

57
    public function getThenException(): ?Exception
×
58
    {
59
        return $this->thenException;
×
60
    }
61

62
    public function getElseException(): ?Exception
×
63
    {
64
        return $this->elseException;
×
65
    }
66

67
    private function getErrorMessage(string $propertyName): string
2✔
68
    {
69
        $message = "Invalid value for $propertyName declined by conditional composition constraint\n";
2✔
70

71
        $message .= $this->ifException
2✔
72
            ? "  - Condition: Failed" . $this->getExceptionMessage($this->ifException)
×
73
            : '  - Condition: Valid';
2✔
74

75
        return $message . "\n  - Conditional branch failed:" .
2✔
76
            $this->getExceptionMessage($this->thenException ?: $this->elseException);
2✔
77
    }
78

79
    private function getExceptionMessage(Exception $exception): string
2✔
80
    {
81
        return $exception instanceof ErrorRegistryExceptionInterface
2✔
82
            ? implode(
1✔
83
                "\n    * ",
1✔
84
                str_replace(
1✔
85
                    "\n",
1✔
86
                    "\n    ",
1✔
87
                    array_map(
1✔
88
                        fn(ValidationException $exception): string => $exception->getMessage(),
1✔
89
                        $exception->getErrors(),
1✔
90
                    ),
1✔
91
                ),
1✔
92
            )
1✔
93
            : "\n    * " . str_replace("\n", "\n    ", $exception->getMessage());
2✔
94
    }
95
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc