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

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

19 Sep 2025 02:01PM UTC coverage: 98.607%. First build
17860521157

Pull #92

github

wol-soft
remove unnecessary code
Pull Request #92: BuilderClassPostProcessor

144 of 146 new or added lines in 11 files covered. (98.63%)

3399 of 3447 relevant lines covered (98.61%)

566.07 hits per line

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

96.88
/src/SchemaProcessor/PostProcessor/BuilderClassPostProcessor.php
1
<?php
2

3
declare(strict_types = 1);
4

5
namespace PHPModelGenerator\SchemaProcessor\PostProcessor;
6

7
use PHPMicroTemplate\Render;
8
use PHPModelGenerator\Exception\FileSystemException;
9
use PHPModelGenerator\Exception\ValidationException;
10
use PHPModelGenerator\Interfaces\BuilderInterface;
11
use PHPModelGenerator\Interfaces\JSONModelInterface;
12
use PHPModelGenerator\Model\GeneratorConfiguration;
13
use PHPModelGenerator\Model\Property\PropertyInterface;
14
use PHPModelGenerator\Model\Property\PropertyType;
15
use PHPModelGenerator\Model\Schema;
16
use PHPModelGenerator\Model\Validator;
17
use PHPModelGenerator\PropertyProcessor\Decorator\TypeHint\TypeHintDecorator;
18
use PHPModelGenerator\PropertyProcessor\Decorator\TypeHint\TypeHintTransferDecorator;
19
use PHPModelGenerator\Utils\RenderHelper;
20
use UnitEnum;
21

22
class BuilderClassPostProcessor extends PostProcessor
23
{
24
    /** @var Schema[] */
25
    private array $schemas = [];
26
    private ?GeneratorConfiguration $generatorConfiguration;
27
    private ?RenderHelper $renderHelper;
28

29
    public function process(Schema $schema, GeneratorConfiguration $generatorConfiguration): void
6✔
30
    {
31
        // collect the schemas and generate builder classes in postProcess hook to make sure the related model class
32
        // already has been created
33
        $this->schemas[] = $schema;
6✔
34
        $this->generatorConfiguration ??= $generatorConfiguration;
6✔
35
        $this->renderHelper ??= new RenderHelper($generatorConfiguration);
6✔
36
    }
37

38
    public function preProcess(): void
6✔
39
    {
40
        $this->generatorConfiguration = null;
6✔
41
        $this->renderHelper = null;
6✔
42
    }
43

44
    public function postProcess(): void
6✔
45
    {
46
        parent::postProcess();
6✔
47

48
        foreach ($this->schemas as $schema) {
6✔
49
            $properties = [];
6✔
50
            foreach ($schema->getProperties() as $property) {
6✔
51
                if (!$property->isInternal()) {
6✔
52
                    $properties[] = (clone $property)
6✔
53
                        ->setReadOnly(false)
6✔
54
                        ->addTypeHintDecorator(new TypeHintTransferDecorator($property))
6✔
55
                        ->filterValidators(static fn(Validator $validator): bool => false);
6✔
56
                }
57
            }
58

59
            $namespace = trim(
6✔
60
                join('\\', [$this->generatorConfiguration->getNamespacePrefix(), $schema->getClassPath()]),
6✔
61
                '\\',
6✔
62
            );
6✔
63

64
            $result = file_put_contents(
6✔
65
                $filename = str_replace('.php', 'Builder.php', $schema->getTargetFileName()),
6✔
66
                (new Render(__DIR__ . DIRECTORY_SEPARATOR . 'Templates' . DIRECTORY_SEPARATOR))->renderTemplate(
6✔
67
                    'BuilderClass.phptpl',
6✔
68
                    [
6✔
69
                        'namespace'              => $namespace,
6✔
70
                        'class'                  => $schema->getClassName(),
6✔
71
                        'schema'                 => $schema,
6✔
72
                        'properties'             => $properties,
6✔
73
                        'use'                    => $this->getBuilderClassImports($properties, $schema->getUsedClasses(), $namespace),
6✔
74
                        'generatorConfiguration' => $this->generatorConfiguration,
6✔
75
                        'viewHelper'             => new RenderHelper($this->generatorConfiguration),
6✔
76
                    ],
6✔
77
                )
6✔
78
            );
6✔
79

80
            $fqcn = "$namespace\\{$schema->getClassName()}Builder";
6✔
81

82
            if ($result === false) {
6✔
83
                // @codeCoverageIgnoreStart
84
                throw new FileSystemException("Can't write builder class $fqcn.",);
85
                // @codeCoverageIgnoreEnd
86
            }
87

88
            require $filename;
6✔
89

90
            if ($this->generatorConfiguration->isOutputEnabled()) {
6✔
91
                // @codeCoverageIgnoreStart
92
                echo "Rendered builder class $fqcn\n";
93
                // @codeCoverageIgnoreEnd
94
            }
95
        }
96
    }
97

98
    /**
99
     * @param PropertyInterface[] $properties
100
     *
101
     * @return string[]
102
     */
103
    private function getBuilderClassImports(array $properties, array $originalClassImports, string $namespace): array
6✔
104
    {
105
        $imports = [BuilderInterface::class];
6✔
106
        $imports[] = $this->generatorConfiguration->collectErrors()
6✔
107
            ? $this->generatorConfiguration->getErrorRegistryClass()
2✔
108
            : ValidationException::class;
4✔
109

110
        foreach ($properties as $property) {
6✔
111
            // use typehint instead of type to cover multi-types
112
            foreach (array_unique([
6✔
113
                ...explode('|', $this->renderHelper->getTypeHintAnnotation($property)),
6✔
114
                ...explode('|', $this->renderHelper->getTypeHintAnnotation($property, true)),
6✔
115
            ]) as $typeAnnotation) {
6✔
116
                $type = str_replace('[]', '', $typeAnnotation);
6✔
117

118
                // as the typehint only knows the class name but not the fqcn, lookup in the original imports
119
                foreach ($originalClassImports as $originalClassImport) {
6✔
120
                    if (str_ends_with($originalClassImport, "\\$type")) {
6✔
NEW
121
                        $type = $originalClassImport;
×
122
                    }
123
                }
124

125
                // required for compatibility with the EnumPostProcessor
126
                if (enum_exists($type)) {
6✔
NEW
127
                    array_push($imports, $type, UnitEnum::class);
×
128
                }
129

130
                if (class_exists($type)) {
6✔
131
                    $imports[] = $type;
2✔
132

133
                    // for nested objects, allow additionally to pass an instance of the nested model also just plain
134
                    // arrays which will result in an object instantiation and validation during the build process
135
                    if (in_array(JSONModelInterface::class, class_implements($type))) {
2✔
136
                        $property->addTypeHintDecorator(new TypeHintDecorator(
2✔
137
                            [basename($type) . 'Builder' . (str_contains($typeAnnotation, '[]') ? '[]' : ''), 'array'],
2✔
138
                        ));
2✔
139

140
                        $property->setType();
2✔
141
                    }
142
                }
143
            }
144
        }
145

146
        return RenderHelper::filterClassImports(array_unique($imports), $namespace);
6✔
147
    }
148
}
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