Skip to content

Commit aa026f6

Browse files
committed
implemented PHP 8 constructor property promotion
1 parent 0536bd0 commit aa026f6

File tree

11 files changed

+146
-8
lines changed

11 files changed

+146
-8
lines changed

readme.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,25 @@ final protected function count(array &$items = []): ?int
128128
}
129129
```
130130

131+
Promoted parameters introduced by PHP 8.0 can be passed to the constructor:
132+
133+
```php
134+
$method = $class->addMethod('__construct');
135+
$method->addPromotedParameter('name');
136+
$method->addPromotedParameter('args', [])
137+
->setPrivate();
138+
```
139+
140+
It results in:
141+
142+
```php
143+
public function __construct(
144+
public $name,
145+
private $args = [],
146+
) {
147+
}
148+
```
149+
131150
If the property, constant, method or parameter already exist, it will be overwritten.
132151

133152
Members can be removed using `removeProperty()`, `removeConstant()`, `removeMethod()` or `removeParameter()`.

src/PhpGenerator/Factory.php

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,10 @@ public function fromClassReflection(\ReflectionClass $from, bool $withBodies = f
4646
}
4747
$props = $methods = $consts = [];
4848
foreach ($from->getProperties() as $prop) {
49-
if ($prop->isDefault() && $prop->getDeclaringClass()->name === $from->name) {
49+
if ($prop->isDefault()
50+
&& $prop->getDeclaringClass()->name === $from->name
51+
&& (PHP_VERSION_ID < 80000 || !$prop->isPromoted())
52+
) {
5053
$props[] = $this->fromPropertyReflection($prop);
5154
}
5255
}
@@ -135,7 +138,9 @@ public function fromCallable(callable $from)
135138

136139
public function fromParameterReflection(\ReflectionParameter $from): Parameter
137140
{
138-
$param = new Parameter($from->name);
141+
$param = PHP_VERSION_ID >= 80000 && $from->isPromoted()
142+
? new PromotedParameter($from->name)
143+
: new Parameter($from->name);
139144
$param->setReference($from->isPassedByReference());
140145
$param->setType($from->getType() instanceof \ReflectionNamedType ? $from->getType()->getName() : null);
141146
$param->setNullable($from->hasType() && $from->getType()->allowsNull());

src/PhpGenerator/Method.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,19 @@ public function isAbstract(): bool
119119
}
120120

121121

122+
/**
123+
* @param string $name without $
124+
*/
125+
public function addPromotedParameter(string $name, $defaultValue = null): PromotedParameter
126+
{
127+
$param = new PromotedParameter($name);
128+
if (func_num_args() > 1) {
129+
$param->setDefaultValue($defaultValue);
130+
}
131+
return $this->parameters[$name] = $param;
132+
}
133+
134+
122135
/** @throws Nette\InvalidStateException */
123136
public function validate(): void
124137
{

src/PhpGenerator/Parameter.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,11 @@
1313

1414

1515
/**
16-
* Method parameter description.
16+
* Function/Method parameter description.
1717
*
1818
* @property mixed $defaultValue
1919
*/
20-
final class Parameter
20+
class Parameter
2121
{
2222
use Nette\SmartObject;
2323
use Traits\NameAware;

src/PhpGenerator/Printer.php

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -251,19 +251,29 @@ public function printParameters($function, PhpNamespace $namespace = null): stri
251251
{
252252
$params = [];
253253
$list = $function->getParameters();
254+
$special = false;
255+
254256
foreach ($list as $param) {
255257
$variadic = $function->isVariadic() && $param === end($list);
256258
$type = $param->getType();
257-
$params[] = ltrim($this->printType($type, $param->isNullable(), $namespace) . ' ')
259+
$promoted = $param instanceof PromotedParameter ? $param : null;
260+
$params[] =
261+
($promoted ? Helpers::formatDocComment((string) $promoted->getComment()) : '')
262+
. ($promoted ? ($promoted->getVisibility() ?: 'public') . ' ' : '')
263+
. ltrim($this->printType($type, $param->isNullable(), $namespace) . ' ')
258264
. ($param->isReference() ? '&' : '')
259265
. ($variadic ? '...' : '')
260266
. '$' . $param->getName()
261267
. ($param->hasDefaultValue() && !$variadic ? ' = ' . $this->dump($param->getDefaultValue()) : '');
268+
269+
$special = $special || $promoted;
262270
}
263271

264-
return strlen($tmp = implode(', ', $params)) > (new Dumper)->wrapLength && count($params) > 1
265-
? "(\n" . $this->indentation . implode(",\n" . $this->indentation, $params) . "\n)"
266-
: "($tmp)";
272+
$line = implode(', ', $params);
273+
274+
return count($params) > 1 && ($special || strlen($line) > (new Dumper)->wrapLength)
275+
? "(\n" . $this->indent(implode(",\n", $params)) . "\n)"
276+
: "($line)";
267277
}
268278

269279

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php
2+
3+
/**
4+
* This file is part of the Nette Framework (https://nette.org)
5+
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
6+
*/
7+
8+
declare(strict_types=1);
9+
10+
namespace Nette\PhpGenerator;
11+
12+
13+
/**
14+
* Promoted parameter in constructor.
15+
*/
16+
final class PromotedParameter extends Parameter
17+
{
18+
use Traits\VisibilityAware;
19+
use Traits\CommentAware;
20+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
3+
/**
4+
* @phpVersion 8.0
5+
*/
6+
7+
declare(strict_types=1);
8+
9+
use Nette\PhpGenerator\ClassType;
10+
11+
12+
require __DIR__ . '/../bootstrap.php';
13+
require __DIR__ . '/fixtures/classes.php80';
14+
15+
$res[] = ClassType::from(new Abc\Class8(null));
16+
17+
sameFile(__DIR__ . '/expected/ClassType.from.80.expect', implode("\n", $res));
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
use Nette\PhpGenerator\ClassType;
6+
7+
8+
require __DIR__ . '/../bootstrap.php';
9+
10+
11+
$class = new ClassType('Example');
12+
$method = $class->addMethod('__construct');
13+
$method->addParameter('a');
14+
$method->addPromotedParameter('b');
15+
$method->addPromotedParameter('c')
16+
->setPrivate()
17+
->setType('string')
18+
->addComment('promo');
19+
20+
sameFile(__DIR__ . '/expected/ClassType.promotion.expect', (string) $class);
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
class Class8
2+
{
3+
public function __construct(
4+
public $a,
5+
public int $b = 10,
6+
$c = null
7+
) {
8+
}
9+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
class Example
2+
{
3+
public function __construct(
4+
$a,
5+
public $b,
6+
/** promo */
7+
private string $c
8+
) {
9+
}
10+
}

0 commit comments

Comments
 (0)