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

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

03 Jun 2026 08:37PM UTC coverage: 98.72% (+0.006%) from 98.714%
26912113495

push

github

wol-soft
Merge remote-tracking branch 'origin/master'

157 of 168 new or added lines in 9 files covered. (93.45%)

42 existing lines in 5 files now uncovered.

6172 of 6252 relevant lines covered (98.72%)

574.49 hits per line

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

96.51
/src/SchemaProcessor/PostProcessor/PatternPropertiesAccessorPostProcessor.php
1
<?php
2

3
declare(strict_types=1);
4

5
namespace PHPModelGenerator\SchemaProcessor\PostProcessor;
6

7
use PHPModelGenerator\Accessor\PatternPropertiesAccessor;
8
use PHPModelGenerator\Exception\FileSystemException;
9
use PHPModelGenerator\Exception\Object\UnknownPatternPropertyException;
10
use PHPModelGenerator\Exception\SchemaException;
11
use PHPModelGenerator\Model\GeneratorConfiguration;
12
use PHPModelGenerator\Model\Property\Property;
13
use PHPModelGenerator\Model\Property\PropertyType;
14
use PHPModelGenerator\Model\Schema;
15
use PHPModelGenerator\Model\Validator\PatternPropertiesValidator;
16
use PHPModelGenerator\Utils\RenderHelper;
17
use ReflectionClass;
18

19
class PatternPropertiesAccessorPostProcessor extends PostProcessor
20
{
21
    use CompanionGeneratorTrait;
22

23
    /**
24
     * Add the patternProperties() accessor method to the provided schema.
25
     *
26
     * @throws SchemaException
27
     */
28
    public function process(Schema $schema, GeneratorConfiguration $generatorConfiguration): void
25✔
29
    {
30
        $json = $schema->getJsonSchema()->getJson();
25✔
31

32
        if (!isset($json['patternProperties'])) {
25✔
33
            return;
2✔
34
        }
35

36
        $patternTypes = [];
24✔
37

38
        foreach ($schema->getBaseValidators() as $validator) {
24✔
39
            if (is_a($validator, PatternPropertiesValidator::class)) {
24✔
40
                $patternTypes[] = $validator->getValidationProperty()->getType(true);
24✔
41
            }
42
        }
43

44
        $hasCompanion = $this->shouldGenerateCompanion($patternTypes);
24✔
45

46
        $schema->addProperty(
24✔
47
            (new Property(
24✔
48
                'patternPropertiesAccessor',
24✔
49
                null,
24✔
50
                $schema->getJsonSchema(),
24✔
51
                'Cached accessor instance for pattern properties',
24✔
52
            ))
24✔
53
                ->setDefaultValue('null', true)
24✔
54
                ->setInternal(true),
24✔
55
        );
24✔
56

57
        $this->addAccessorMethod($schema, $generatorConfiguration, $hasCompanion);
24✔
58

59
        if ($hasCompanion) {
24✔
60
            $this->pendingCompanions[] = [
24✔
61
                'schema' => $schema,
24✔
62
                'generatorConfiguration' => $generatorConfiguration,
24✔
63
                'patternTypes' => $patternTypes,
24✔
64
            ];
24✔
65
        }
66
    }
67

68
    /**
69
     * @throws FileSystemException
70
     */
71
    protected function renderCompanionFromEntry(array $entry): void
24✔
72
    {
73
        $this->renderCompanionClass($entry['schema'], $entry['generatorConfiguration'], $entry['patternTypes']);
24✔
74
    }
75

76
    private function addAccessorMethod(
24✔
77
        Schema $schema,
78
        GeneratorConfiguration $generatorConfiguration,
79
        bool $hasCompanion,
80
    ): void {
81
        $accessorType = $hasCompanion
24✔
82
            ? $schema->getClassName() . 'PatternProperties'
24✔
UNCOV
83
            : (new ReflectionClass(PatternPropertiesAccessor::class))->getShortName();
×
84

85
        if (!$hasCompanion) {
24✔
UNCOV
86
            $schema->addUsedClass(PatternPropertiesAccessor::class);
×
87
        }
88

89
        $schema->addMethod(
24✔
90
            'patternProperties',
24✔
91
            new RenderedMethod(
24✔
92
                $schema,
24✔
93
                $generatorConfiguration,
24✔
94
                'PatternProperties/PatternPropertiesAccessorMethod.phptpl',
24✔
95
                ['accessorType' => $accessorType],
24✔
96
            )
24✔
97
        );
24✔
98
    }
99

100
    /**
101
     * A companion is generated when at least one pattern property type is not the bare mixed/untyped case,
102
     * giving the get() return annotation a more specific type.
103
     *
104
     * @param PropertyType[] $patternTypes
105
     */
106
    private function shouldGenerateCompanion(array $patternTypes): bool
24✔
107
    {
108
        foreach ($patternTypes as $patternType) {
24✔
109
            if ($patternType !== null && $patternType->getNames() !== []) {
24✔
110
                return true;
24✔
111
            }
112
        }
113

UNCOV
114
        return false;
×
115
    }
116

117
    /**
118
     * @param PropertyType[] $patternTypes
119
     *
120
     * @throws FileSystemException
121
     */
122
    private function renderCompanionClass(
24✔
123
        Schema $schema,
124
        GeneratorConfiguration $generatorConfiguration,
125
        array $patternTypes,
126
    ): void {
127
        $companionClassName = $schema->getClassName() . 'PatternProperties';
24✔
128
        $namespace = $this->resolveCompanionNamespace($schema, $generatorConfiguration);
24✔
129

130
        $use = RenderHelper::filterClassImports(
24✔
131
            [UnknownPatternPropertyException::class],
24✔
132
            $namespace,
24✔
133
        );
24✔
134

135
        $this->writeAndRequireCompanionFile(
24✔
136
            $schema,
24✔
137
            $companionClassName,
24✔
138
            $namespace,
24✔
139
            join(DIRECTORY_SEPARATOR, ['Companion', 'PatternPropertiesCompanion.phptpl']),
24✔
140
            [
24✔
141
                'namespace' => $namespace,
24✔
142
                'use' => $use,
24✔
143
                'companionClassName' => $companionClassName,
24✔
144
                'getReturnAnnotation' => $this->buildReturnAnnotation($patternTypes),
24✔
145
            ],
24✔
146
        );
24✔
147
    }
148

149
    /**
150
     * @param PropertyType[] $patternTypes
151
     */
152
    private function buildReturnAnnotation(array $patternTypes): string
24✔
153
    {
154
        $baseTypes = array_unique(
24✔
155
            array_merge(
24✔
156
                ...array_map(
24✔
157
                    static fn(PropertyType $type): array => $type->getNames(),
24✔
158
                    array_filter($patternTypes),
24✔
159
                )
24✔
160
            )
24✔
161
        );
24✔
162

163
        $nullable = array_reduce(
24✔
164
            $patternTypes,
24✔
165
            static fn(bool $carry, ?PropertyType $type): bool => $carry || ($type !== null && $type->isNullable()),
24✔
166
            false,
24✔
167
        );
24✔
168

169
        if ($nullable) {
24✔
170
            $baseTypes[] = 'null';
2✔
171
        }
172

173
        // Wrap union types in parentheses so that e.g. ['DateTime', 'null'] becomes '(DateTime|null)[]',
174
        // not 'DateTime[]|null[]' — the latter means "an array of nulls" to static analysers.
175
        if (count($baseTypes) > 1) {
24✔
176
            return '(' . implode('|', $baseTypes) . ')[]';
21✔
177
        }
178

179
        return $baseTypes[0] . '[]';
3✔
180
    }
181
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc