Skip to content

Commit 37461ee

Browse files
shanginndg
authored andcommitted
Interfaces can have properties hooks
1 parent 409ba32 commit 37461ee

File tree

3 files changed

+67
-10
lines changed

3 files changed

+67
-10
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: 15 additions & 9 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,9 +388,9 @@ private function printProperty(Property $property, bool $readOnlyClass = false):
386388
. ltrim($this->printType($type, $property->isNullable()) . ' ')
387389
. '$' . $property->getName());
388390

389-
$hooks = $this->printHooks($property);
391+
$hooks = $this->printHooks($property, $isInterface);
390392

391-
$defaultValue = $property->getValue() === null && !$property->isInitialized()
393+
$defaultValue = $isInterface || ($property->getValue() === null && !$property->isInitialized())
392394
? ''
393395
: ' = ' . $this->dump($property->getValue(), strlen($def) + 3); // 3 = ' = '
394396

@@ -496,7 +498,7 @@ protected function isBraceOnNextLine(bool $multiLine, bool $hasReturnType): bool
496498
}
497499

498500

499-
private function printHooks(Property $property): ?string
501+
private function printHooks(Property $property, bool $isInterface = false): ?string
500502
{
501503
$getHook = $property->getGetHook();
502504
$setHook = $property->getSetHook();
@@ -505,15 +507,19 @@ private function printHooks(Property $property): ?string
505507
return null;
506508
}
507509

508-
$out = " {\n";
510+
$out = ' {' . ($isInterface ? ' ' : "\n");
509511

510512
if ($getHook !== null) {
511-
$out .= $this->indent("get {\n" . $this->indent($this->printFunctionBody($getHook)) . "}\n");
513+
$out .= $isInterface
514+
? 'get; '
515+
: $this->indent("get {\n" . $this->indent($this->printFunctionBody($getHook)) . "}\n");
512516
}
513517

514518
if ($setHook !== null) {
515519
$params = $this->printParameters($setHook);
516-
$out .= $this->indent('set ' . $params . ' {' . "\n" . $this->indent($this->printFunctionBody($setHook)) . "}\n");
520+
$out .= $isInterface
521+
? 'set; '
522+
: $this->indent('set ' . $params . ' {' . "\n" . $this->indent($this->printFunctionBody($setHook)) . "}\n");
517523
}
518524

519525
return $out . '}';
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 { get; set; }
45+
}
46+
PHP;
47+
48+
same(rtrim($expected), rtrim((new PsrPrinter)->printClass($interface)));

0 commit comments

Comments
 (0)