Skip to content

Commit 2029797

Browse files
committed
Fix array type hints, add type hint tests
1 parent eca19ad commit 2029797

13 files changed

+263
-49
lines changed

src/PropertyProcessor/Decorator/TypeHint/ArrayTypeHintDecorator.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ public function __construct(PropertyInterface $nestedProperty)
3131
*/
3232
public function decorate(string $input): string
3333
{
34-
return "{$this->nestedProperty->getTypeHint()}[]";
34+
return implode('|', array_map(function (string $typeHint): string {
35+
return "{$typeHint}[]";
36+
}, explode('|', $this->nestedProperty->getTypeHint())));
3537
}
3638
}

src/PropertyProcessor/Property/AbstractValueProcessor.php

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,10 +56,6 @@ public function process(string $propertyName, array $propertyData): PropertyInte
5656
$this->schemaProcessor->getGeneratorConfiguration()->isImmutable()
5757
);
5858

59-
if ($this->schemaProcessor->getGeneratorConfiguration()->isImplicitNullAllowed() && !$property->isRequired()) {
60-
$property->addTypeHintDecorator(new TypeHintDecorator(['null']));
61-
}
62-
6359
$this->generateValidators($property, $propertyData);
6460

6561
if (isset($propertyData['filter'])) {

src/Templates/Model.phptpl

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ class {{ class }} implements \PHPModelGenerator\Interfaces\JSONModelInterface
2222
{% if generatorConfiguration.hasSerializationEnabled() %}use \PHPModelGenerator\Traits\SerializableTrait;{% endif %}
2323

2424
{% foreach properties as property %}
25-
/**{% if property.getTypeHint(true) %} @var {{ property.getTypeHint(true) }}{% endif %}{% if property.getDescription() %} {{ property.getDescription() }}{% endif %} */
25+
/**{% if viewHelper.getTypeHintAnnotation(property, true) %} @var {{ viewHelper.getTypeHintAnnotation(property, true) }}{% endif %}{% if property.getDescription() %} {{ property.getDescription() }}{% endif %} */
2626
protected ${{ property.getAttribute() }}{% if not viewHelper.isNull(property.getDefaultValue()) %} = {{ property.getDefaultValue() }}{% endif %};
2727
{% endforeach %}
2828
/** @var array */
@@ -99,7 +99,7 @@ class {{ class }} implements \PHPModelGenerator\Interfaces\JSONModelInterface
9999
*
100100
* {% if property.getDescription() %}{{ property.getDescription() }}{% endif %}
101101
*
102-
* {% if property.getTypeHint(true) %}@return {{ property.getTypeHint(true) }}{% endif %}
102+
* {% if viewHelper.getTypeHintAnnotation(property, true) %}@return {{ viewHelper.getTypeHintAnnotation(property, true) }}{% endif %}
103103
*/
104104
public function get{{ viewHelper.ucfirst(property.getAttribute()) }}()
105105
{% if property.getType(true) %}: {% if viewHelper.isPropertyNullable(property) %}?{% endif %}{{ property.getType(true) }}{% endif %}
@@ -111,14 +111,14 @@ class {{ class }} implements \PHPModelGenerator\Interfaces\JSONModelInterface
111111
/**
112112
* Set the value of {{ property.getName() }}.
113113
*
114-
* @param {{ property.getTypeHint() }} ${{ property.getAttribute() }}{% if property.getDescription() %} {{ property.getDescription() }}{% endif %}
114+
* @param {{ viewHelper.getTypeHintAnnotation(property) }} ${{ property.getAttribute() }}{% if property.getDescription() %} {{ property.getDescription() }}{% endif %}
115115
*
116116
* {% if property.getValidators() %}@throws {% if generatorConfiguration.collectErrors() %}{{ viewHelper.getSimpleClassName(generatorConfiguration.getErrorRegistryClass()) }}{% else %}ValidationException{% endif %}{% endif %}
117117
*
118118
* @return self
119119
*/
120120
public function set{{ viewHelper.ucfirst(property.getAttribute()) }}(
121-
{% if property.getType() %}{% if viewHelper.isPropertyNullable(property) %}?{% endif %}{{ property.getType() }} {% endif %}${{ property.getAttribute() }}
121+
{% if property.getType() %}{% if viewHelper.isPropertyNullable(property, true) %}?{% endif %}{{ property.getType() }} {% endif %}${{ property.getAttribute() }}
122122
): self {
123123
$value = $modelData['{{ property.getName() }}'] = ${{ property.getAttribute() }};
124124

src/Utils/RenderHelper.php

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,8 +100,23 @@ public function validationError(PropertyValidatorInterface $validator): string
100100
return "throw $exceptionConstructor;";
101101
}
102102

103-
public function isPropertyNullable(PropertyInterface $property): bool
103+
public function isPropertyNullable(PropertyInterface $property, bool $setter = false): bool
104104
{
105-
return $this->generatorConfiguration->isImplicitNullAllowed() && !$property->isRequired();
105+
return (!$setter || $this->generatorConfiguration->isImplicitNullAllowed()) && !$property->isRequired();
106+
}
107+
108+
public function getTypeHintAnnotation(PropertyInterface $property, bool $outputType = false): string
109+
{
110+
$typeHint = $property->getTypeHint($outputType);
111+
112+
if (!$typeHint) {
113+
return '';
114+
}
115+
116+
if ((!$outputType || $this->generatorConfiguration->isImplicitNullAllowed()) && !$property->isRequired()) {
117+
$typeHint = implode('|', array_unique(array_merge(explode('|', $typeHint), ['null'])));
118+
}
119+
120+
return $typeHint;
106121
}
107122
}

tests/AbstractPHPModelGeneratorTest.php

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
use RecursiveDirectoryIterator;
2020
use RecursiveIteratorIterator;
2121
use ReflectionClass;
22+
use ReflectionType;
2223

2324
/**
2425
* Class AbstractPHPModelGeneratorTest
@@ -132,6 +133,7 @@ protected function generateClassFromFile(
132133
* @param array $values
133134
* @param GeneratorConfiguration|null $generatorConfiguration
134135
* @param bool $escape
136+
* @param bool $implicitNull
135137
*
136138
* @return string
137139
*
@@ -143,7 +145,8 @@ protected function generateClassFromFileTemplate(
143145
string $file,
144146
array $values,
145147
GeneratorConfiguration $generatorConfiguration = null,
146-
bool $escape = true
148+
bool $escape = true,
149+
bool $implicitNull = true
147150
): string {
148151
return $this->generateClass(
149152
call_user_func_array(
@@ -158,7 +161,9 @@ function ($item) use ($escape) {
158161
)
159162
)
160163
),
161-
$generatorConfiguration
164+
$generatorConfiguration,
165+
false,
166+
$implicitNull
162167
);
163168
}
164169

@@ -440,6 +445,16 @@ protected function getMethodReturnTypeAnnotation($object, string $method): strin
440445
return $matches[1];
441446
}
442447

448+
protected function getParameterType($object, string $method): ReflectionType
449+
{
450+
return (new ReflectionClass($object))->getMethod($method)->getParameters()[0]->getType();
451+
}
452+
453+
protected function getReturnType($object, string $method): ReflectionType
454+
{
455+
return (new ReflectionClass($object))->getMethod($method)->getReturnType();
456+
}
457+
443458
protected function getGeneratedFiles(): array
444459
{
445460
return $this->generatedFiles;

tests/Basic/BasicSchemaGenerationTest.php

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,12 @@ public function testGetterAndSetterAreGeneratedForMutableObjects(bool $implicitN
4343
$this->assertSame('Bye', $object->getProperty());
4444

4545
// test if the property is typed correctly
46-
$returnType = (new ReflectionMethod($object, 'getProperty'))->getReturnType();
46+
$returnType = $this->getReturnType($object, 'getProperty');
4747
$this->assertSame('string', $returnType->getName());
48-
$this->assertSame($implicitNull, $returnType->allowsNull());
48+
// as the property is optional it may contain an initial null value
49+
$this->assertTrue($returnType->allowsNull());
4950

50-
$setType = (new ReflectionMethod($object, 'setProperty'))->getParameters()[0]->getType();
51+
$setType = $this->getParameterType($object, 'setProperty');
5152
$this->assertSame('string', $setType->getName());
5253
$this->assertSame($implicitNull, $setType->allowsNull());
5354
}

tests/ComposedValue/ComposedAnyOfTest.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ public function annotationDataProvider(): array
143143
return [
144144
'Multiple scalar types (no merged property)' => [
145145
'AnyOfType.json',
146-
'/^null\|string\|int\|bool$/',
146+
'/^string\|int\|bool\|null$/',
147147
1,
148148
],
149149
'Multiple scalar types required (no merged property)' => [
@@ -153,17 +153,17 @@ public function annotationDataProvider(): array
153153
],
154154
'Object with scalar type (no merged property - redirect to generated object)' => [
155155
'ReferencedObjectSchema.json',
156-
'/^null\|string\|ComposedAnyOfTest[\w]*Property[\w]*$/',
156+
'/^string\|ComposedAnyOfTest[\w]*Property[\w]*\|null$/',
157157
2,
158158
],
159159
'Multiple objects (merged property created)' => [
160160
'ReferencedObjectSchema2.json',
161-
'/^null\|ComposedAnyOfTest[\w]*_Merged_[\w]*$/',
161+
'/^ComposedAnyOfTest[\w]*_Merged_[\w]*\|null$/',
162162
4,
163163
],
164164
'Scalar type and multiple objects (merged property created)' => [
165165
'ReferencedObjectSchema3.json',
166-
'/^null\|string\|ComposedAnyOfTest[\w]*_Merged_[\w]*$/',
166+
'/^string\|ComposedAnyOfTest[\w]*_Merged_[\w]*\|null$/',
167167
4,
168168
],
169169
];

0 commit comments

Comments
 (0)