Skip to content

Commit

Permalink
Interfaces can have properties hooks
Browse files Browse the repository at this point in the history
  • Loading branch information
shanginn authored and dg committed Nov 21, 2024
1 parent 4acefba commit 86dd8da
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 8 deletions.
5 changes: 4 additions & 1 deletion src/PhpGenerator/InterfaceType.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ final class InterfaceType extends ClassLike
{
use Traits\ConstantsAware;
use Traits\MethodsAware;
use Traits\PropertiesAware;

/** @var string[] */
private array $extends = [];
Expand Down Expand Up @@ -54,12 +55,13 @@ public function addExtend(string $name): static
/**
* Adds a member. If it already exists, throws an exception or overwrites it if $overwrite is true.
*/
public function addMember(Method|Constant $member, bool $overwrite = false): static
public function addMember(Method|Constant|Property $member, bool $overwrite = false): static
{
$name = $member->getName();
[$type, $n] = match (true) {
$member instanceof Constant => ['consts', $name],
$member instanceof Method => ['methods', strtolower($name)],
$member instanceof Property => ['properties', $name],
};
if (!$overwrite && isset($this->$type[$n])) {
throw new Nette\InvalidStateException("Cannot add member '$name', because it already exists.");
Expand All @@ -75,5 +77,6 @@ public function __clone(): void
$clone = fn($item) => clone $item;
$this->consts = array_map($clone, $this->consts);
$this->methods = array_map($clone, $this->methods);
$this->properties = array_map($clone, $this->properties);
}
}
20 changes: 13 additions & 7 deletions src/PhpGenerator/Printer.php
Original file line number Diff line number Diff line change
Expand Up @@ -198,9 +198,11 @@ public function printClass(
}

$properties = [];
if ($class instanceof ClassType || $class instanceof TraitType) {
if ($class instanceof ClassType || $class instanceof TraitType || $class instanceof InterfaceType) {
foreach ($class->getProperties() as $property) {
$properties[] = $this->printProperty($property, $readOnlyClass);
if (!$class instanceof InterfaceType || ($property->hasGetHook() || $property->hasSetHook())) {
$properties[] = $this->printProperty($property, $readOnlyClass, $class->isInterface());
}
}
}

Expand Down Expand Up @@ -375,7 +377,7 @@ private function printConstant(Constant $const): string
}


private function printProperty(Property $property, bool $readOnlyClass = false): string
private function printProperty(Property $property, bool $readOnlyClass = false, bool $isInterface = false): string
{
$property->validate();
$type = $property->getType();
Expand All @@ -386,11 +388,15 @@ private function printProperty(Property $property, bool $readOnlyClass = false):
. ltrim($this->printType($type, $property->isNullable()) . ' ')
. '$' . $property->getName());

$hooks = !$property->getSetHook() && !$property->getGetHook()
? ';'
: " {\n" . $this->printHooks($property) . '}';
if (!$property->getSetHook() && !$property->getGetHook()) {
$hooks = ';';
} elseif ($isInterface) {
$hooks = ' { ' . implode(' ', array_filter([$property->getSetHook() ? 'set;' : null, $property->getGetHook() ? 'get;' : null])) . ' }';
} else {
$hooks = " {\n" . $this->printHooks($property) . '}';
}

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

Expand Down
48 changes: 48 additions & 0 deletions tests/PhpGenerator/Property.hooks.interfaces.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?php

/**
* Test: Nette\PhpGenerator - PHP 8.4 property hooks for interfaces
*/

declare(strict_types=1);

use Nette\PhpGenerator\InterfaceType;
use Nette\PhpGenerator\PhpFile;
use Nette\PhpGenerator\PropertyHook;
use Nette\PhpGenerator\PsrPrinter;
use Nette\PhpGenerator\Type;

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

$file = new PhpFile;
$file->setStrictTypes();

$namespace = $file->addNamespace('Abc');

$interface = new InterfaceType('HasAuthor');

// This will not be printed because it does not have any hooks
$interface->addProperty('isVisible')
->setType(Type::Bool)
->setPublic();

$interface->addProperty('score')
->setType(Type::Int)
->setPublic()
->setGetHook(new PropertyHook);

$interface->addProperty('author')
->setType('Author')
->setPublic()
->setGetHook(new PropertyHook)
->setSetHook(new PropertyHook);

$expected = <<<'PHP'
interface HasAuthor
{
public int $score { get; }
public Author $author { set; get; }
}
PHP;

same(rtrim($expected), rtrim((new PsrPrinter)->printClass($interface)));

0 comments on commit 86dd8da

Please sign in to comment.