Skip to content

Commit 9162f74

Browse files
committed
implemented support for PHP 8 union types
1 parent 865d106 commit 9162f74

File tree

11 files changed

+77
-14
lines changed

11 files changed

+77
-14
lines changed

readme.md

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ $class->addProperty('items', [1, 2, 3])
8282
->addComment('@var int[]');
8383

8484
$class->addProperty('list')
85-
->setType('array') // or setType(Type::ARRAY)
85+
->setType('array')
8686
->setNullable()
8787
->setInitialized(); // prints '= null'
8888
```
@@ -172,6 +172,23 @@ $methodRecount = $methodCount->cloneWithName('recount');
172172
$class->addMember($methodRecount);
173173
```
174174

175+
Types
176+
-----
177+
178+
Each type or union type can be passed as a string, you can also use predefined constants for native types:
179+
180+
```php
181+
use Nette\PhpGenerator\Type;
182+
183+
$member->setType('array');
184+
$member->setType(Type::ARRAY);
185+
$member->setType('array|string');
186+
$member->setType(null); // removes type
187+
```
188+
189+
The same applies to the method `setReturnType()`.
190+
191+
175192
Tabs versus Spaces
176193
------------------
177194

src/PhpGenerator/Factory.php

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,8 @@ public function fromMethodReflection(\ReflectionMethod $from): Method
104104
if ($from->getReturnType() instanceof \ReflectionNamedType) {
105105
$method->setReturnType($from->getReturnType()->getName());
106106
$method->setReturnNullable($from->getReturnType()->allowsNull());
107+
} elseif ($from->getReturnType() instanceof \ReflectionUnionType) {
108+
$method->setReturnType((string) $from->getReturnType());
107109
}
108110
return $method;
109111
}
@@ -123,6 +125,8 @@ public function fromFunctionReflection(\ReflectionFunction $from, bool $withBody
123125
if ($from->getReturnType() instanceof \ReflectionNamedType) {
124126
$function->setReturnType($from->getReturnType()->getName());
125127
$function->setReturnNullable($from->getReturnType()->allowsNull());
128+
} elseif ($from->getReturnType() instanceof \ReflectionUnionType) {
129+
$function->setReturnType((string) $from->getReturnType());
126130
}
127131
$function->setBody($withBody ? $this->loadFunctionBody($from) : '');
128132
return $function;
@@ -145,8 +149,12 @@ public function fromParameterReflection(\ReflectionParameter $from): Parameter
145149
? new PromotedParameter($from->name)
146150
: new Parameter($from->name);
147151
$param->setReference($from->isPassedByReference());
148-
$param->setType($from->getType() instanceof \ReflectionNamedType ? $from->getType()->getName() : null);
149-
$param->setNullable($from->hasType() && $from->getType()->allowsNull());
152+
if ($from->getType() instanceof \ReflectionNamedType) {
153+
$param->setType($from->getType()->getName());
154+
$param->setNullable($from->getType()->allowsNull());
155+
} elseif ($from->getType() instanceof \ReflectionUnionType) {
156+
$param->setType((string) $from->getType());
157+
}
150158
if ($from->isDefaultValueAvailable()) {
151159
$param->setDefaultValue($from->isDefaultValueConstant()
152160
? new Literal($from->getDefaultValueConstantName())
@@ -184,10 +192,14 @@ public function fromPropertyReflection(\ReflectionProperty $from): Property
184192
? ClassType::VISIBILITY_PRIVATE
185193
: ($from->isProtected() ? ClassType::VISIBILITY_PROTECTED : ClassType::VISIBILITY_PUBLIC)
186194
);
187-
if (PHP_VERSION_ID >= 70400 && ($from->getType() instanceof \ReflectionNamedType)) {
188-
$prop->setType($from->getType()->getName());
189-
$prop->setNullable($from->getType()->allowsNull());
190-
$prop->setInitialized(array_key_exists($prop->getName(), $defaults));
195+
if (PHP_VERSION_ID >= 70400) {
196+
if ($from->getType() instanceof \ReflectionNamedType) {
197+
$prop->setType($from->getType()->getName());
198+
$prop->setNullable($from->getType()->allowsNull());
199+
} elseif ($from->getType() instanceof \ReflectionUnionType) {
200+
$prop->setType((string) $from->getType());
201+
}
202+
$prop->setInitialized($from->hasType() && array_key_exists($prop->getName(), $defaults));
191203
}
192204
$prop->setComment(Helpers::unformatDocComment((string) $from->getDocComment()));
193205
$prop->setAttributes(self::getAttributes($from));

src/PhpGenerator/PhpNamespace.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,12 @@ public function getUses(): array
126126
}
127127

128128

129+
public function unresolveUnionType(string $type): string
130+
{
131+
return implode('|', array_map([$this, 'unresolveName'], explode('|', $type)));
132+
}
133+
134+
129135
public function unresolveName(string $name): string
130136
{
131137
if (isset(self::KEYWORDS[strtolower($name)]) || $name === '') {

src/PhpGenerator/Printer.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ public function printClass(ClassType $class, PhpNamespace $namespace = null): st
113113
{
114114
$class->validate();
115115
$resolver = $this->resolveTypes && $namespace
116-
? [$namespace, 'unresolveName']
116+
? [$namespace, 'unresolveUnionType']
117117
: function ($s) { return $s; };
118118

119119
$traits = [];
@@ -288,7 +288,7 @@ public function printParameters($function, PhpNamespace $namespace = null): stri
288288
public function printType(?string $type, bool $nullable = false, PhpNamespace $namespace = null): string
289289
{
290290
return $type
291-
? ($nullable ? '?' : '') . ($this->resolveTypes && $namespace ? $namespace->unresolveName($type) : $type)
291+
? ($nullable ? '?' : '') . ($this->resolveTypes && $namespace ? $namespace->unresolveUnionType($type) : $type)
292292
: '';
293293
}
294294

tests/PhpGenerator/ClassType.from.80.phpt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,6 @@ require __DIR__ . '/fixtures/classes.php80';
1414

1515
$res[] = ClassType::from(new Abc\Class8(null));
1616
$res[] = ClassType::from(new Abc\Class9);
17+
$res[] = ClassType::from(new Abc\Class10);
1718

1819
sameFile(__DIR__ . '/expected/ClassType.from.80.expect', implode("\n", $res));

tests/PhpGenerator/ClassType.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ $method->addParameter('item');
121121

122122
$method->addParameter('res', null)
123123
->setReference(true)
124-
->setType(Type::ARRAY);
124+
->setType(Type::union(Type::ARRAY, 'null'));
125125

126126

127127
$class->addTrait('foo');

tests/PhpGenerator/PhpNamespace.phpt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@ Assert::same('\A', $namespace->unresolveName('\A'));
3636
Assert::same('\A', $namespace->unresolveName('A'));
3737
Assert::same('A', $namespace->unresolveName('foo\A'));
3838

39+
Assert::same('A', $namespace->unresolveUnionType('foo\A'));
40+
Assert::same('null|A', $namespace->unresolveUnionType('null|foo\A'));
41+
Assert::same('', $namespace->unresolveUnionType(''));
42+
3943
$namespace->addUse('Bar\C');
4044
Assert::same(['C' => 'Bar\\C'], $namespace->getUses());
4145

@@ -71,11 +75,13 @@ $classA
7175

7276
$method = $classA->addMethod('test');
7377
$method->addAttribute('Foo\\A');
78+
$method->setReturnType('static|Foo\\A');
7479

7580
$method->addParameter('a')->setType('Bar\C')->addAttribute('Bar\\D');
7681
$method->addParameter('b')->setType('self');
7782
$method->addParameter('c')->setType('parent');
7883
$method->addParameter('d')->setType('array');
7984
$method->addParameter('e')->setType('callable');
85+
$method->addParameter('f')->setType('Bar\C|string');
8086

8187
sameFile(__DIR__ . '/expected/PhpNamespace.expect', (string) $namespace);

tests/PhpGenerator/expected/ClassType.expect

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,5 +42,5 @@ abstract class Example extends ParentClass implements IExample, IOne
4242
}
4343

4444

45-
abstract public function show($item, array &$res = null);
45+
abstract public function show($item, array|null &$res = null);
4646
}

tests/PhpGenerator/expected/ClassType.from.80.expect

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ class Class8
22
{
33
public function __construct(
44
public $a,
5-
public int $b = 10,
5+
public string|int $b = 10,
66
$c = null,
77
) {
88
}
@@ -33,3 +33,13 @@ class Class9
3333
{
3434
}
3535
}
36+
37+
class Class10
38+
{
39+
public string|int $prop;
40+
41+
42+
public function test(string|int $param): string|int
43+
{
44+
}
45+
}

tests/PhpGenerator/expected/PhpNamespace.expect

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ class A implements A, C
1414
parent $c,
1515
array $d,
1616
callable $e,
17-
) {
17+
C|string $f,
18+
): static|A {
1819
}
1920
}
2021

0 commit comments

Comments
 (0)