Skip to content

Commit 86dd8da

Browse files
shanginndg
authored andcommitted
Interfaces can have properties hooks
1 parent 4acefba commit 86dd8da

File tree

3 files changed

+65
-8
lines changed

3 files changed

+65
-8
lines changed

src/PhpGenerator/InterfaceType.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ final class InterfaceType extends ClassLike
1919
{
2020
use Traits\ConstantsAware;
2121
use Traits\MethodsAware;
22+
use Traits\PropertiesAware;
2223

2324
/** @var string[] */
2425
private array $extends = [];
@@ -54,12 +55,13 @@ public function addExtend(string $name): static
5455
/**
5556
* Adds a member. If it already exists, throws an exception or overwrites it if $overwrite is true.
5657
*/
57-
public function addMember(Method|Constant $member, bool $overwrite = false): static
58+
public function addMember(Method|Constant|Property $member, bool $overwrite = false): static
5859
{
5960
$name = $member->getName();
6061
[$type, $n] = match (true) {
6162
$member instanceof Constant => ['consts', $name],
6263
$member instanceof Method => ['methods', strtolower($name)],
64+
$member instanceof Property => ['properties', $name],
6365
};
6466
if (!$overwrite && isset($this->$type[$n])) {
6567
throw new Nette\InvalidStateException("Cannot add member '$name', because it already exists.");
@@ -75,5 +77,6 @@ public function __clone(): void
7577
$clone = fn($item) => clone $item;
7678
$this->consts = array_map($clone, $this->consts);
7779
$this->methods = array_map($clone, $this->methods);
80+
$this->properties = array_map($clone, $this->properties);
7881
}
7982
}

src/PhpGenerator/Printer.php

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -198,9 +198,11 @@ public function printClass(
198198
}
199199

200200
$properties = [];
201-
if ($class instanceof ClassType || $class instanceof TraitType) {
201+
if ($class instanceof ClassType || $class instanceof TraitType || $class instanceof InterfaceType) {
202202
foreach ($class->getProperties() as $property) {
203-
$properties[] = $this->printProperty($property, $readOnlyClass);
203+
if (!$class instanceof InterfaceType || ($property->hasGetHook() || $property->hasSetHook())) {
204+
$properties[] = $this->printProperty($property, $readOnlyClass, $class->isInterface());
205+
}
204206
}
205207
}
206208

@@ -375,7 +377,7 @@ private function printConstant(Constant $const): string
375377
}
376378

377379

378-
private function printProperty(Property $property, bool $readOnlyClass = false): string
380+
private function printProperty(Property $property, bool $readOnlyClass = false, bool $isInterface = false): string
379381
{
380382
$property->validate();
381383
$type = $property->getType();
@@ -386,11 +388,15 @@ private function printProperty(Property $property, bool $readOnlyClass = false):
386388
. ltrim($this->printType($type, $property->isNullable()) . ' ')
387389
. '$' . $property->getName());
388390

389-
$hooks = !$property->getSetHook() && !$property->getGetHook()
390-
? ';'
391-
: " {\n" . $this->printHooks($property) . '}';
391+
if (!$property->getSetHook() && !$property->getGetHook()) {
392+
$hooks = ';';
393+
} elseif ($isInterface) {
394+
$hooks = ' { ' . implode(' ', array_filter([$property->getSetHook() ? 'set;' : null, $property->getGetHook() ? 'get;' : null])) . ' }';
395+
} else {
396+
$hooks = " {\n" . $this->printHooks($property) . '}';
397+
}
392398

393-
$defaultValue = $property->getValue() === null && !$property->isInitialized()
399+
$defaultValue = $isInterface || ($property->getValue() === null && !$property->isInitialized())
394400
? ''
395401
: ' = ' . $this->dump($property->getValue(), strlen($def) + 3); // 3 = ' = '
396402

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<?php
2+
3+
/**
4+
* Test: Nette\PhpGenerator - PHP 8.4 property hooks for interfaces
5+
*/
6+
7+
declare(strict_types=1);
8+
9+
use Nette\PhpGenerator\InterfaceType;
10+
use Nette\PhpGenerator\PhpFile;
11+
use Nette\PhpGenerator\PropertyHook;
12+
use Nette\PhpGenerator\PsrPrinter;
13+
use Nette\PhpGenerator\Type;
14+
15+
require __DIR__ . '/../bootstrap.php';
16+
17+
$file = new PhpFile;
18+
$file->setStrictTypes();
19+
20+
$namespace = $file->addNamespace('Abc');
21+
22+
$interface = new InterfaceType('HasAuthor');
23+
24+
// This will not be printed because it does not have any hooks
25+
$interface->addProperty('isVisible')
26+
->setType(Type::Bool)
27+
->setPublic();
28+
29+
$interface->addProperty('score')
30+
->setType(Type::Int)
31+
->setPublic()
32+
->setGetHook(new PropertyHook);
33+
34+
$interface->addProperty('author')
35+
->setType('Author')
36+
->setPublic()
37+
->setGetHook(new PropertyHook)
38+
->setSetHook(new PropertyHook);
39+
40+
$expected = <<<'PHP'
41+
interface HasAuthor
42+
{
43+
public int $score { get; }
44+
public Author $author { set; get; }
45+
}
46+
PHP;
47+
48+
same(rtrim($expected), rtrim((new PsrPrinter)->printClass($interface)));

0 commit comments

Comments
 (0)