Skip to content

Commit fbb50e5

Browse files
author
Enno Woortmann
committed
Draft
1 parent 1d339fe commit fbb50e5

File tree

8 files changed

+114
-42
lines changed

8 files changed

+114
-42
lines changed

src/Model/RenderJob.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ public function __construct(
3333
/**
3434
* @param PostProcessor[] $postProcessors
3535
*/
36-
public function postProcess(array $postProcessors, GeneratorConfiguration $generatorConfiguration): void
36+
public function executePostProcessors(array $postProcessors, GeneratorConfiguration $generatorConfiguration): void
3737
{
3838
foreach ($postProcessors as $postProcessor) {
3939
$postProcessor->process($this->schema, $generatorConfiguration);
@@ -66,6 +66,8 @@ public function render(GeneratorConfiguration $generatorConfiguration): void
6666
// @codeCoverageIgnoreEnd
6767
}
6868

69+
require $this->schema->getTargetFileName();
70+
6971
if ($generatorConfiguration->isOutputEnabled()) {
7072
echo sprintf(
7173
"Rendered class %s\n",

src/SchemaProcessor/PostProcessor/BuilderClassPostProcessor.php

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
use PHPModelGenerator\PropertyProcessor\Decorator\TypeHint\TypeHintDecorator;
1919
use PHPModelGenerator\PropertyProcessor\Decorator\TypeHint\TypeHintTransferDecorator;
2020
use PHPModelGenerator\Utils\RenderHelper;
21+
use UnitEnum;
2122

2223
class BuilderClassPostProcessor extends PostProcessor
2324
{
@@ -59,7 +60,7 @@ public function postProcess(): void
5960
);
6061

6162
$result = file_put_contents(
62-
str_replace('.php', 'Builder.php', $schema->getTargetFileName()),
63+
$filename = str_replace('.php', 'Builder.php', $schema->getTargetFileName()),
6364
(new Render(__DIR__ . DIRECTORY_SEPARATOR . 'Templates' . DIRECTORY_SEPARATOR))->renderTemplate(
6465
'BuilderClass.phptpl',
6566
[
@@ -74,14 +75,16 @@ public function postProcess(): void
7475
)
7576
);
7677

77-
$fqcn = "{$schema->getClassPath()}\\{$schema->getClassName()}Builder";
78+
$fqcn = "$namespace\\{$schema->getClassName()}Builder";
7879

7980
if ($result === false) {
8081
// @codeCoverageIgnoreStart
8182
throw new FileSystemException("Can't write builder class $fqcn.",);
8283
// @codeCoverageIgnoreEnd
8384
}
8485

86+
require $filename;
87+
8588
if ($this->generatorConfiguration->isOutputEnabled()) {
8689
// @codeCoverageIgnoreStart
8790
echo "Rendered builder class $fqcn\n";
@@ -114,13 +117,18 @@ private function getBuilderClassImports(array $properties, array $originalClassI
114117
}
115118
}
116119

120+
// required for compatibility with the EnumPostProcessor
121+
if (enum_exists($type)) {
122+
array_push($imports, $type, UnitEnum::class);
123+
}
124+
117125
if (class_exists($type)) {
118126
$imports[] = $type;
119127

120128
// for nested objects, allow additionally to pass an instance of the nested model also just plain
121129
// arrays which will result in an object instantiation and validation during the build process
122130
if (in_array(JSONModelInterface::class, class_implements($type))) {
123-
$property->addTypeHintDecorator(new TypeHintDecorator(['array', basename($type) . 'Builder']));
131+
$property->addTypeHintDecorator(new TypeHintDecorator([basename($type) . 'Builder', 'array']));
124132
$property->setType();
125133
}
126134
}

src/SchemaProcessor/PostProcessor/EnumPostProcessor.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,7 @@ private function renderEnum(
257257
}
258258

259259
$result = file_put_contents(
260-
$this->targetDirectory . DIRECTORY_SEPARATOR . $name . '.php',
260+
$filename = $this->targetDirectory . DIRECTORY_SEPARATOR . $name . '.php',
261261
$this->renderer->renderTemplate(
262262
'Enum.phptpl',
263263
[
@@ -277,6 +277,8 @@ private function renderEnum(
277277
// @codeCoverageIgnoreEnd
278278
}
279279

280+
require $filename;
281+
280282
if ($generatorConfiguration->isOutputEnabled()) {
281283
// @codeCoverageIgnoreStart
282284
echo "Rendered enum $fqcn\n";

src/SchemaProcessor/RenderQueue.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ public function execute(GeneratorConfiguration $generatorConfiguration, array $p
4444
}
4545

4646
foreach ($this->jobs as $job) {
47-
$job->postProcess($postProcessors, $generatorConfiguration);
47+
$job->executePostProcessors($postProcessors, $generatorConfiguration);
4848
$job->render($generatorConfiguration);
4949
}
5050

tests/AbstractPHPModelGeneratorTestCase.php

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -260,8 +260,6 @@ public function getClassName(
260260

261261
foreach ($generatedFiles as $path) {
262262
$this->generatedFiles[] = $path;
263-
264-
require $path;
265263
}
266264

267265
return $className;
@@ -283,8 +281,6 @@ protected function generateDirectory(string $directory, GeneratorConfiguration $
283281

284282
foreach ($generatedClasses as $path) {
285283
$this->generatedFiles[] = $path;
286-
287-
require $path;
288284
}
289285

290286
return $generatedClasses;

tests/PostProcessor/BuilderClassPostProcessorTest.php

Lines changed: 75 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,15 @@ public function setUp(): void
2020
};
2121
}
2222

23-
public function testPopulateMethod(): void
23+
public function testBuilder(): void
2424
{
2525
$className = $this->generateClassFromFile(
2626
'BasicSchema.json',
2727
(new GeneratorConfiguration())->setSerialization(true),
28+
implicitNull: false,
2829
);
2930

30-
$this->includeGeneratedBuilder(1);
31+
$this->assertGeneratedBuilders(1);
3132

3233
$builderClassName = $className . 'Builder';
3334
$builderObject = new $builderClassName();
@@ -43,7 +44,7 @@ public function testPopulateMethod(): void
4344
$this->assertEqualsCanonicalizing(['name' => 'Albert', 'age' => 65], $builderObject->getRawModelDataInput());
4445

4546
$this->assertSame('string', $this->getParameterTypeAnnotation($builderObject, 'setName'));
46-
$this->assertSame('int|null', $this->getParameterTypeAnnotation($builderObject, 'setAge'));
47+
$this->assertSame('int', $this->getParameterTypeAnnotation($builderObject, 'setAge'));
4748
$this->assertSame('string|null', $this->getReturnTypeAnnotation($builderObject, 'getName'));
4849
$this->assertSame('int|null', $this->getReturnTypeAnnotation($builderObject, 'getAge'));
4950

@@ -64,15 +65,81 @@ public function testPopulateMethod(): void
6465
$this->assertEqualsCanonicalizing(['name' => 'Albert', 'age' => 65], $validatedObject->toArray());
6566
}
6667

67-
private function includeGeneratedBuilder(int $expectedGeneratedBuilders): void
68+
public function testImplicitNull(): void
69+
{
70+
$className = $this->generateClassFromFile('BasicSchema.json');
71+
72+
$builderClassName = $className . 'Builder';
73+
$builderObject = new $builderClassName();
74+
75+
$this->assertSame('string', $this->getParameterTypeAnnotation($builderObject, 'setName'));
76+
$this->assertSame('int|null', $this->getParameterTypeAnnotation($builderObject, 'setAge'));
77+
$this->assertSame('string|null', $this->getReturnTypeAnnotation($builderObject, 'getName'));
78+
$this->assertSame('int|null', $this->getReturnTypeAnnotation($builderObject, 'getAge'));
79+
}
80+
81+
public function testNestedObject(): void
82+
{
83+
$className = $this->generateClassFromFile('NestedObject.json');
84+
85+
$this->assertGeneratedBuilders(2);
86+
87+
$builderClassName = $className . 'Builder';
88+
$builderObject = new $builderClassName();
89+
90+
$nestedObjectClassName = null;
91+
foreach ($this->getGeneratedFiles() as $file) {
92+
if (str_contains($file, 'Address')) {
93+
$nestedObjectClassName = str_replace('.php', '', basename($file));
94+
95+
break;
96+
}
97+
}
98+
99+
$this->assertNotEmpty($nestedObjectClassName);
100+
$expectedTypeHint = "$nestedObjectClassName|{$nestedObjectClassName}Builder|array|null";
101+
$this->assertSame($expectedTypeHint, $this->getParameterTypeAnnotation($builderObject, 'setAddress'));
102+
$this->assertSame($expectedTypeHint, $this->getReturnTypeAnnotation($builderObject, 'getAddress'));
103+
104+
// test generate nested object from array
105+
$addressArray = ['street' => 'Test street', 'number' => 10];
106+
$builderObject->setAddress($addressArray);
107+
$this->assertSame($addressArray, $builderObject->getAddress());
108+
$this->assertSame(['address' => $addressArray], $builderObject->getRawModelDataInput());
109+
$object = $builderObject->validate();
110+
$this->assertSame('Test street', $object->getAddress()->getStreet());
111+
$this->assertSame(10, $object->getAddress()->getNumber());
112+
113+
// test generate nested object from nested builder
114+
$nestedBuilderClassName = $nestedObjectClassName . 'Builder';
115+
$nestedBuilderObject = new $nestedBuilderClassName();
116+
$this->assertSame('string|null', $this->getParameterTypeAnnotation($nestedBuilderObject, 'setStreet'));
117+
$this->assertSame('int|null', $this->getParameterTypeAnnotation($nestedBuilderObject, 'setNumber'));
118+
$this->assertSame('string|null', $this->getReturnTypeAnnotation($nestedBuilderObject, 'getStreet'));
119+
$this->assertSame('int|null', $this->getReturnTypeAnnotation($nestedBuilderObject, 'getNumber'));
120+
121+
$nestedBuilderObject->setStreet('Test street')->setNumber(10);
122+
$this->assertSame($addressArray, $nestedBuilderObject->getRawModelDataInput());
123+
$builderObject->setAddress($nestedBuilderObject);
124+
$this->assertSame($nestedBuilderObject, $builderObject->getAddress());
125+
$object = $builderObject->validate();
126+
$this->assertSame('Test street', $object->getAddress()->getStreet());
127+
$this->assertSame(10, $object->getAddress()->getNumber());
128+
129+
// test add validated object
130+
$nestedObject = new $nestedObjectClassName($addressArray);
131+
$builderObject->setAddress($nestedObject);
132+
$this->assertSame($nestedObject, $builderObject->getAddress());
133+
$object = $builderObject->validate();
134+
$this->assertSame('Test street', $object->getAddress()->getStreet());
135+
$this->assertSame(10, $object->getAddress()->getNumber());
136+
}
137+
138+
private function assertGeneratedBuilders(int $expectedGeneratedBuilders): void
68139
{
69140
$dir = sys_get_temp_dir() . '/PHPModelGeneratorTest/Models';
70141
$files = array_filter(scandir($dir), fn (string $file): bool => str_ends_with($file, 'Builder.php'));
71142

72143
$this->assertCount($expectedGeneratedBuilders, $files);
73-
74-
foreach ($files as $file) {
75-
require_once $dir . DIRECTORY_SEPARATOR . $file;
76-
}
77144
}
78145
}

tests/PostProcessor/EnumPostProcessorTest.php

Lines changed: 5 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ public function testStringOnlyEnum(): void
4747
false,
4848
);
4949

50-
$this->includeGeneratedEnums(1);
50+
$this->assertGeneratedEnums(1);
5151

5252
$object = new $className(['property' => 'hans', 'stringProperty' => 'abc']);
5353
$this->assertSame('hans', $object->getProperty()->value);
@@ -105,7 +105,6 @@ public function testInvalidStringOnlyEnumValueThrowsAnException(): void
105105
{
106106
$this->addPostProcessor();
107107
$className = $this->generateClassFromFileTemplate('EnumProperty.json', ['["Hans", "Dieter"]'], null, false);
108-
$this->includeGeneratedEnums(1);
109108

110109
$this->expectException(EnumException::class);
111110
$this->expectExceptionMessage('Invalid value for property declined by enum constraint');
@@ -147,8 +146,6 @@ public function testMappedStringOnlyEnum(): void
147146
false,
148147
);
149148

150-
$this->includeGeneratedEnums(1);
151-
152149
$object = new $className(['property' => 'Hans']);
153150
$this->assertSame('Hans', $object->getProperty()->value);
154151
$this->assertSame('Ceo', $object->getProperty()->name);
@@ -220,7 +217,7 @@ public function testUnmappedEnumIsSkippedWithEnabledSkipOption(): void
220217

221218
$className = $this->generateClassFromFileTemplate('EnumProperty.json', ['[0, 1, 2]'], null, false);
222219

223-
$this->includeGeneratedEnums(0);
220+
$this->assertGeneratedEnums(0);
224221

225222
$object = new $className(['property' => 1]);
226223
$this->assertSame(1, $object->getProperty());
@@ -271,8 +268,6 @@ public function testIntOnlyEnum(): void
271268
false,
272269
);
273270

274-
$this->includeGeneratedEnums(1);
275-
276271
$object = new $className(['property' => 10]);
277272
$this->assertSame(10, $object->getProperty()->value);
278273
$this->assertSame(['property' => 10], $object->toArray());
@@ -335,8 +330,6 @@ public function testMixedEnum(): void
335330
false,
336331
);
337332

338-
$this->includeGeneratedEnums(1);
339-
340333
$object = new $className(['property' => 'Hans']);
341334
$this->assertSame('Hans', $object->getProperty()->value());
342335
$this->assertSame(['property' => 'Hans'], $object->toArray());
@@ -410,7 +403,7 @@ public function testIdenticalEnumsAreMappedToOneEnum(string $file, array $enums)
410403
false,
411404
);
412405

413-
$this->includeGeneratedEnums(1);
406+
$this->assertGeneratedEnums(1);
414407

415408
$object = new $className(['property1' => 'Hans', 'property2' => 'Dieter']);
416409
$this->assertSame('Hans', $object->getProperty1()->value);
@@ -451,7 +444,7 @@ public function testDifferentEnumsAreNotMappedToOneEnum(string $file, array $enu
451444
false,
452445
);
453446

454-
$this->includeGeneratedEnums(2);
447+
$this->assertGeneratedEnums(2);
455448
$object = new $className(['property1' => 'Hans', 'property2' => 'Dieter']);
456449

457450
$this->assertSame('Hans', $object->getProperty1()->value);
@@ -500,8 +493,6 @@ public function testDefaultValue(): void
500493

501494
$className = $this->generateClassFromFile('EnumPropertyDefaultValue.json');
502495

503-
$this->includeGeneratedEnums(1);
504-
505496
$object = new $className();
506497
$this->assertSame('Dieter', $object->getProperty()->value);
507498
}
@@ -515,8 +506,6 @@ public function testNotProvidedRequiredEnumThrowsAnException(): void
515506

516507
$className = $this->generateClassFromFile('EnumPropertyRequired.json');
517508

518-
$this->includeGeneratedEnums(1);
519-
520509
$this->expectException(RequiredValueException::class);
521510
$this->expectExceptionMessage('Missing required value for property');
522511

@@ -535,8 +524,6 @@ public function testRequiredEnum(): void
535524
(new GeneratorConfiguration())->setImmutable(false)->setCollectErrors(false),
536525
);
537526

538-
$this->includeGeneratedEnums(1);
539-
540527
$object = new $className(['property' => 'Dieter']);
541528
$this->assertSame('Dieter', $object->getProperty()->value);
542529

@@ -587,8 +574,6 @@ public function testNameNormalization(string $name, string $expectedNormalizedNa
587574

588575
$className = $this->generateClassFromFileTemplate('EnumProperty.json', [sprintf('["%s"]', $name)], null, false);
589576

590-
$this->includeGeneratedEnums(1);
591-
592577
$object = new $className();
593578

594579
$returnType = $this->getReturnType($object, 'getProperty');
@@ -621,15 +606,11 @@ private function addPostProcessor(): void
621606
};
622607
}
623608

624-
private function includeGeneratedEnums(int $expectedGeneratedEnums): void
609+
private function assertGeneratedEnums(int $expectedGeneratedEnums): void
625610
{
626611
$dir = sys_get_temp_dir() . '/PHPModelGeneratorTest/Enum';
627612
$files = array_diff(scandir($dir), ['.', '..']);
628613

629614
$this->assertCount($expectedGeneratedEnums, $files);
630-
631-
foreach ($files as $file) {
632-
require_once $dir . DIRECTORY_SEPARATOR . $file;
633-
}
634615
}
635616
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"type": "object",
3+
"properties": {
4+
"address": {
5+
"type": "object",
6+
"properties": {
7+
"street": {
8+
"type": "string"
9+
},
10+
"number": {
11+
"type": "integer"
12+
}
13+
}
14+
}
15+
}
16+
}

0 commit comments

Comments
 (0)