Skip to content

Commit

Permalink
Property can be abstract / final
Browse files Browse the repository at this point in the history
  • Loading branch information
dg committed Nov 27, 2024
1 parent ff93ade commit 3455866
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 3 deletions.
6 changes: 4 additions & 2 deletions src/PhpGenerator/Printer.php
Original file line number Diff line number Diff line change
Expand Up @@ -380,12 +380,14 @@ private function printProperty(Property $property, bool $readOnlyClass = false,
{
$property->validate();
$type = $property->getType();
$def = (($property->getVisibility() ?: 'public')
$def = ($property->isAbstract() && !$isInterface ? 'abstract ' : '')
. ($property->isFinal() ? 'final ' : '')
. ($property->getVisibility() ?: 'public')
. ($property->isStatic() ? ' static' : '')
. (!$readOnlyClass && $property->isReadOnly() && $type ? ' readonly' : '')
. ' '
. ltrim($this->printType($type, $property->isNullable()) . ' ')
. '$' . $property->getName());
. '$' . $property->getName();

$defaultValue = $property->getValue() === null && !$property->isInitialized()
? ''
Expand Down
37 changes: 37 additions & 0 deletions src/PhpGenerator/Property.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ final class Property
private ?string $type = null;
private bool $nullable = false;
private bool $initialized = false;
private bool $final = false;
private bool $abstract = false;


public function setValue(mixed $val): static
Expand Down Expand Up @@ -99,11 +101,46 @@ public function isInitialized(): bool
}


public function setFinal(bool $state = true): static
{
$this->final = $state;
return $this;
}


public function isFinal(): bool
{
return $this->final;
}


public function setAbstract(bool $state = true): static
{
$this->abstract = $state;
return $this;
}


public function isAbstract(): bool
{
return $this->abstract;
}


/** @throws Nette\InvalidStateException */
public function validate(): void
{
if ($this->readOnly && !$this->type) {
throw new Nette\InvalidStateException("Property \$$this->name: Read-only properties are only supported on typed property.");

} elseif ($this->abstract && $this->final) {
throw new Nette\InvalidStateException("Property \$$this->name cannot be abstract and final at the same time.");

} elseif (
$this->abstract
&& !Nette\Utils\Arrays::some($this->getHooks(), fn($hook) => $hook->isAbstract())
) {
throw new Nette\InvalidStateException("Property \$$this->name: Abstract property must have at least one abstract hook.");
}
}
}
40 changes: 39 additions & 1 deletion tests/PhpGenerator/Property.hooks.classes.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ $class->addProperty('first')
->setType('string')
->setValue('x')
->setPublic()
->setFinal()
->addHook('set')
->setBody('$value . ?', ['x'], short: true)
->addComment('comment')
Expand All @@ -40,7 +41,7 @@ $prop->addHook('set', '$this->second = $value;')
same(<<<'XX'
class Demo
{
public string $first = 'x' {
final public string $first = 'x' {
/** comment */
#[Example]
set(string $value) => $value . 'x';
Expand Down Expand Up @@ -96,3 +97,40 @@ same(<<<'XX'
}

XX, (string) $class);



// abstract properties

$class = (new ClassType('Demo'))
->setAbstract();

$class->addProperty('first')
->setType('string')
->setAbstract()
->addHook('set')
->setAbstract();

$prop = $class->addProperty('second')
->setType('string')
->setAbstract();

$prop->addHook('set')
->setAbstract();

$prop->addHook('get', 'return 123;');

same(<<<'XX'
abstract class Demo
{
abstract public string $first { set; }
abstract public string $second {
set;
get {
return 123;
}
}
}

XX, (string) $class);
21 changes: 21 additions & 0 deletions tests/PhpGenerator/Property.validate.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

declare(strict_types=1);

use Nette\PhpGenerator\Property;
use Tester\Assert;

require __DIR__ . '/../bootstrap.php';


Assert::exception(function () {
$property = new Property('a');
$property->setFinal()->setAbstract();
$property->validate();
}, Nette\InvalidStateException::class, 'Property $a cannot be abstract and final at the same time.');

Assert::exception(function () {
$property = new Property('a');
$property->setAbstract();
$property->validate();
}, Nette\InvalidStateException::class, 'Property $a: Abstract property must have at least one abstract hook.');

0 comments on commit 3455866

Please sign in to comment.