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

wol-soft / php-json-schema-model-generator / 30049163101

23 Jul 2026 10:12PM UTC coverage: 98.746% (+0.002%) from 98.744%
30049163101

push

github

wol-soft
Fix Issue168Test exception message assertions to match current quoting convention

InvalidTypeException's message format was updated to quote interpolated
identifiers, but this test (developed on a separate branch) still asserted
the pre-update wording, causing it to fail once both changes landed on master.

7086 of 7176 relevant lines covered (98.75%)

557.75 hits per line

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

97.73
/src/Draft/Modifier/DefaultValueModifier.php
1
<?php
2

3
declare(strict_types=1);
4

5
namespace PHPModelGenerator\Draft\Modifier;
6

7
use PHPModelGenerator\Exception\SchemaException;
8
use PHPModelGenerator\Model\Property\PropertyInterface;
9
use PHPModelGenerator\Model\Schema;
10
use PHPModelGenerator\Model\SchemaDefinition\JsonSchema;
11
use PHPModelGenerator\SchemaProcessor\SchemaProcessor;
12
use PHPModelGenerator\Utils\TypeConverter;
13

14
class DefaultValueModifier implements ModifierInterface
15
{
16
    public function modify(
2,563✔
17
        SchemaProcessor $schemaProcessor,
18
        Schema $schema,
19
        PropertyInterface $property,
20
        JsonSchema $propertySchema,
21
    ): void {
22
        $json = $propertySchema->getJson();
2,563✔
23

24
        if (!array_key_exists('default', $json)) {
2,563✔
25
            return;
2,523✔
26
        }
27

28
        // Scalar branch defaults are unreachable: the property value itself is what the
29
        // composition branch discriminates on, so a default can only fire when no value is
30
        // provided — but without a value there is no signal to select the branch in the
31
        // first place. Warn and drop rather than applying the default unconditionally.
32
        if ($this->isScalarInsideCompositionBranch($propertySchema)) {
112✔
33
            $schemaProcessor->getGeneratorConfiguration()->getLogger()->warning(
7✔
34
                "Property '{property}' declares a default value inside a composition branch in file"
7✔
35
                    . " '{file}'. Scalar branch defaults are unreachable and will be ignored.",
7✔
36
                ['property' => $property->getName(), 'file' => $propertySchema->getFile()],
7✔
37
            );
7✔
38

39
            return;
7✔
40
        }
41

42
        $default = $json['default'];
105✔
43
        $types   = isset($json['type']) ? (array) $json['type'] : [];
105✔
44

45
        if (empty($types)) {
105✔
46
            $property->setDefaultValue($default);
9✔
47
            return;
9✔
48
        }
49

50
        foreach ($types as $jsonType) {
96✔
51
            $phpType = TypeConverter::jsonSchemaToPHP($jsonType);
96✔
52

53
            // Allow integer literals as defaults for 'number' (float) properties
54
            if ($phpType === 'float' && is_int($default)) {
96✔
55
                $default = (float) $default;
5✔
56
            }
57

58
            $typeCheckFn = 'is_' . $phpType;
96✔
59

60
            // "array" additionally requires array_is_list(): a JSON object and a JSON array
61
            // both decode to a PHP array, so is_array() alone cannot tell a JSON-object-shaped
62
            // default apart from a genuine JSON-array default.
63
            $matchesType = $phpType === 'array'
96✔
64
                ? is_array($default) && array_is_list($default)
11✔
65
                : (function_exists($typeCheckFn) && $typeCheckFn($default));
85✔
66

67
            if ($matchesType) {
96✔
68
                $property->setDefaultValue($default);
69✔
69
                return;
69✔
70
            }
71
        }
72

73
        throw new SchemaException(
27✔
74
            sprintf(
27✔
75
                'Invalid type for default value of property %s in file %s',
27✔
76
                $property->getName(),
27✔
77
                $propertySchema->getFile(),
27✔
78
            ),
27✔
79
            $propertySchema,
27✔
80
        );
27✔
81
    }
82

83
    /**
84
     * Returns true when the given property schema is a scalar branch schema (not an object
85
     * with declared sub-properties) that sits directly at a composition branch level.
86
     *
87
     * Two cases must be distinguished:
88
     *
89
     * - A *named property inside an object-typed branch* (e.g. `sandbox` in
90
     *   `oneOf/1/properties/sandbox`) — its pointer ends with `/properties/<name>`.
91
     *   This is the object-level branch default handled by Phase 2; it must not be dropped.
92
     *
93
     * - A *branch schema that is itself scalar* (e.g. `oneOf/0` → `{type:string, default:"x"}`
94
     *   where the property value IS the discriminant) — its pointer ends with a branch-index
95
     *   segment, not `/properties/<name>`. This default is unreachable and must be dropped.
96
     *
97
     * A property whose pointer ends with `/properties/<name>` is always in the first category.
98
     * For the remaining pointers, stripping all `/properties/<name>` segments removes noise
99
     * from intermediate object-nesting, and the regex tests whether a composition keyword
100
     * segment is present in the structural path.
101
     */
102
    private function isScalarInsideCompositionBranch(JsonSchema $propertySchema): bool
112✔
103
    {
104
        if (isset($propertySchema->getJson()['properties'])) {
112✔
105
            return false;
×
106
        }
107

108
        $pointer = $propertySchema->getPointer();
112✔
109

110
        // A pointer ending in /properties/<name> means this is a named property inside an
111
        // object-typed branch — handled by per-branch runtime application, not warned about.
112
        if (preg_match('#/properties/[^/]+$#', $pointer)) {
112✔
113
            return false;
95✔
114
        }
115

116
        // Strip intermediate /properties/<name> segments to prevent false positives from
117
        // root properties coincidentally named after a composition keyword, then append '/'
118
        // so end-of-string branch segments are catchable by the regex.
119
        $structuralPointer = preg_replace('#/properties/[^/]+#', '', $pointer) . '/';
20✔
120

121
        return preg_match('#/(allOf|anyOf|oneOf)/\d+/#', $structuralPointer)
20✔
122
            || preg_match('#/(if|then|else)/#', $structuralPointer);
20✔
123
    }
124
}
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