Skip to content

Commit 100fa20

Browse files
committed
Property can be abstract / final
1 parent 5bdad73 commit 100fa20

File tree

4 files changed

+101
-3
lines changed

4 files changed

+101
-3
lines changed

src/PhpGenerator/Printer.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -380,12 +380,14 @@ private function printProperty(Property $property, bool $readOnlyClass = false,
380380
{
381381
$property->validate();
382382
$type = $property->getType();
383-
$def = (($property->getVisibility() ?: 'public')
383+
$def = ($property->isAbstract() && !$isInterface ? 'abstract ' : '')
384+
. ($property->isFinal() ? 'final ' : '')
385+
. ($property->getVisibility() ?: 'public')
384386
. ($property->isStatic() ? ' static' : '')
385387
. (!$readOnlyClass && $property->isReadOnly() && $type ? ' readonly' : '')
386388
. ' '
387389
. ltrim($this->printType($type, $property->isNullable()) . ' ')
388-
. '$' . $property->getName());
390+
. '$' . $property->getName();
389391

390392
$defaultValue = $property->getValue() === null && !$property->isInitialized()
391393
? ''

src/PhpGenerator/Property.php

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ final class Property
2929
private bool $nullable = false;
3030
private bool $initialized = false;
3131
private bool $readOnly = false;
32+
private bool $final = false;
33+
private bool $abstract = false;
3234

3335
/** @var array<string, ?PropertyHook> */
3436
private array $hooks = ['set' => null, 'get' => null];
@@ -116,6 +118,32 @@ public function isReadOnly(): bool
116118
}
117119

118120

121+
public function setFinal(bool $state = true): static
122+
{
123+
$this->final = $state;
124+
return $this;
125+
}
126+
127+
128+
public function isFinal(): bool
129+
{
130+
return $this->final;
131+
}
132+
133+
134+
public function setAbstract(bool $state = true): static
135+
{
136+
$this->abstract = $state;
137+
return $this;
138+
}
139+
140+
141+
public function isAbstract(): bool
142+
{
143+
return $this->abstract;
144+
}
145+
146+
119147
/**
120148
* Replaces all hooks
121149
* @param PropertyHook[] $hooks
@@ -161,6 +189,15 @@ public function validate(): void
161189
{
162190
if ($this->readOnly && !$this->type) {
163191
throw new Nette\InvalidStateException("Property \$$this->name: Read-only properties are only supported on typed property.");
192+
193+
} elseif ($this->abstract && $this->final) {
194+
throw new Nette\InvalidStateException("Property \$$this->name cannot be abstract and final at the same time.");
195+
196+
} elseif (
197+
$this->abstract
198+
&& !Nette\Utils\Arrays::some($this->getHooks(), fn($hook) => $hook->isAbstract())
199+
) {
200+
throw new Nette\InvalidStateException("Property \$$this->name: Abstract property must have at least one abstract hook.");
164201
}
165202
}
166203
}

tests/PhpGenerator/Property.hooks.classes.phpt

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ $class->addProperty('first')
1717
->setType('string')
1818
->setValue('x')
1919
->setPublic()
20+
->setFinal()
2021
->addHook('set')
2122
->setBody('$value . ?', ['x'], short: true)
2223
->addComment('comment')
@@ -40,7 +41,7 @@ $prop->addHook('set', '$this->second = $value;')
4041
same(<<<'XX'
4142
class Demo
4243
{
43-
public string $first = 'x' {
44+
final public string $first = 'x' {
4445
/** comment */
4546
#[Example]
4647
set(string $value) => $value . 'x';
@@ -96,3 +97,40 @@ same(<<<'XX'
9697
}
9798

9899
XX, (string) $class);
100+
101+
102+
103+
// abstract properties
104+
105+
$class = (new ClassType('Demo'))
106+
->setAbstract();
107+
108+
$class->addProperty('first')
109+
->setType('string')
110+
->setAbstract()
111+
->addHook('set')
112+
->setAbstract();
113+
114+
$prop = $class->addProperty('second')
115+
->setType('string')
116+
->setAbstract();
117+
118+
$prop->addHook('set')
119+
->setAbstract();
120+
121+
$prop->addHook('get', 'return 123;');
122+
123+
same(<<<'XX'
124+
abstract class Demo
125+
{
126+
abstract public string $first { set; }
127+
128+
abstract public string $second {
129+
set;
130+
get {
131+
return 123;
132+
}
133+
}
134+
}
135+
136+
XX, (string) $class);
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
use Nette\PhpGenerator\Property;
6+
use Tester\Assert;
7+
8+
require __DIR__ . '/../bootstrap.php';
9+
10+
11+
Assert::exception(function () {
12+
$property = new Property('a');
13+
$property->setFinal()->setAbstract();
14+
$property->validate();
15+
}, Nette\InvalidStateException::class, 'Property $a cannot be abstract and final at the same time.');
16+
17+
Assert::exception(function () {
18+
$property = new Property('a');
19+
$property->setAbstract();
20+
$property->validate();
21+
}, Nette\InvalidStateException::class, 'Property $a: Abstract property must have at least one abstract hook.');

0 commit comments

Comments
 (0)