Skip to content

Commit 409ba32

Browse files
shanginndg
authored andcommitted
Add support for properties hooks
1 parent 1e85ee7 commit 409ba32

File tree

4 files changed

+236
-8
lines changed

4 files changed

+236
-8
lines changed

src/PhpGenerator/Printer.php

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ public function printMethod(Method $method, ?PhpNamespace $namespace = null, boo
129129
}
130130

131131

132-
protected function printFunctionBody(Closure|GlobalFunction|Method $function): string
132+
protected function printFunctionBody(Closure|GlobalFunction|Method|PropertyHook $function): string
133133
{
134134
return ltrim(rtrim(Strings::normalize(
135135
Helpers::simplifyTaggedNames($function->getBody(), $this->namespace),
@@ -313,7 +313,7 @@ protected function printUses(PhpNamespace $namespace, string $of = PhpNamespace:
313313
}
314314

315315

316-
protected function printParameters(Closure|GlobalFunction|Method $function, int $column = 0): string
316+
protected function printParameters(Closure|GlobalFunction|Method|PropertyHook $function, int $column = 0): string
317317
{
318318
$special = false;
319319
foreach ($function->getParameters() as $param) {
@@ -332,13 +332,13 @@ protected function printParameters(Closure|GlobalFunction|Method $function, int
332332
}
333333

334334

335-
private function formatParameters(Closure|GlobalFunction|Method $function, bool $multiline): string
335+
private function formatParameters(Closure|GlobalFunction|Method|PropertyHook $function, bool $multiline): string
336336
{
337337
$params = $function->getParameters();
338338
$res = '';
339339

340340
foreach ($params as $param) {
341-
$variadic = $function->isVariadic() && $param === end($params);
341+
$variadic = !$function instanceof PropertyHook && $function->isVariadic() && $param === end($params);
342342
$attrs = $this->printAttributes($param->getAttributes(), inline: true);
343343
$res .=
344344
$this->printDocComment($param)
@@ -386,13 +386,18 @@ private function printProperty(Property $property, bool $readOnlyClass = false):
386386
. ltrim($this->printType($type, $property->isNullable()) . ' ')
387387
. '$' . $property->getName());
388388

389+
$hooks = $this->printHooks($property);
390+
391+
$defaultValue = $property->getValue() === null && !$property->isInitialized()
392+
? ''
393+
: ' = ' . $this->dump($property->getValue(), strlen($def) + 3); // 3 = ' = '
394+
389395
return $this->printDocComment($property)
390396
. $this->printAttributes($property->getAttributes())
391397
. $def
392-
. ($property->getValue() === null && !$property->isInitialized()
393-
? ''
394-
: ' = ' . $this->dump($property->getValue(), strlen($def) + 3)) // 3 = ' = '
395-
. ";\n";
398+
. $defaultValue
399+
. ($hooks ?? ';')
400+
. "\n";
396401
}
397402

398403

@@ -489,4 +494,28 @@ protected function isBraceOnNextLine(bool $multiLine, bool $hasReturnType): bool
489494
{
490495
return $this->bracesOnNextLine && (!$multiLine || $hasReturnType);
491496
}
497+
498+
499+
private function printHooks(Property $property): ?string
500+
{
501+
$getHook = $property->getGetHook();
502+
$setHook = $property->getSetHook();
503+
504+
if ($getHook === null && $setHook === null) {
505+
return null;
506+
}
507+
508+
$out = " {\n";
509+
510+
if ($getHook !== null) {
511+
$out .= $this->indent("get {\n" . $this->indent($this->printFunctionBody($getHook)) . "}\n");
512+
}
513+
514+
if ($setHook !== null) {
515+
$params = $this->printParameters($setHook);
516+
$out .= $this->indent('set ' . $params . ' {' . "\n" . $this->indent($this->printFunctionBody($setHook)) . "}\n");
517+
}
518+
519+
return $out . '}';
520+
}
492521
}

src/PhpGenerator/Property.php

Lines changed: 52 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 ?PropertyHook $getHook = null;
33+
private ?PropertyHook $setHook = null;
3234

3335

3436
public function setValue(mixed $val): static
@@ -113,6 +115,56 @@ public function isReadOnly(): bool
113115
}
114116

115117

118+
public function setSetHook(?PropertyHook $hook): static
119+
{
120+
$this->setHook = $hook;
121+
return $this;
122+
}
123+
124+
125+
public function getSetHook(): ?PropertyHook
126+
{
127+
return $this->setHook;
128+
}
129+
130+
131+
public function addSetHook(string $body, bool $shortcut = false): PropertyHook
132+
{
133+
return $this->setHook = (new PropertyHook)->setBody($body, $shortcut);
134+
}
135+
136+
137+
public function hasSetHook(): bool
138+
{
139+
return $this->setHook !== null;
140+
}
141+
142+
143+
public function setGetHook(?PropertyHook $hook): static
144+
{
145+
$this->getHook = $hook;
146+
return $this;
147+
}
148+
149+
150+
public function getGetHook(): ?PropertyHook
151+
{
152+
return $this->getHook;
153+
}
154+
155+
156+
public function addGetHook(string $body, bool $shortcut = false): PropertyHook
157+
{
158+
return $this->getHook = (new PropertyHook)->setBody($body, $shortcut);
159+
}
160+
161+
162+
public function hasGetHook(): bool
163+
{
164+
return $this->getHook !== null;
165+
}
166+
167+
116168
/** @throws Nette\InvalidStateException */
117169
public function validate(): void
118170
{

src/PhpGenerator/PropertyHook.php

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Nette\PhpGenerator;
6+
7+
class PropertyHook
8+
{
9+
use Traits\AttributeAware;
10+
use Traits\CommentAware;
11+
12+
private ?string $body = null;
13+
private bool $shortcut = false; // Whether to use the '=>' syntax
14+
private ?Parameter $parameter = null;
15+
private bool $returnReference = false;
16+
17+
18+
public function setBody(string $body, bool $shortcut = false): self
19+
{
20+
$this->body = $body;
21+
$this->shortcut = $shortcut;
22+
return $this;
23+
}
24+
25+
26+
public function getBody(): ?string
27+
{
28+
return $this->body;
29+
}
30+
31+
32+
public function isShortcut(): bool
33+
{
34+
return $this->shortcut;
35+
}
36+
37+
38+
/**
39+
* Adds a parameter. If it already exists, it overwrites it.
40+
* @param string $name without $
41+
*/
42+
public function setParameter(string $name = 'value'): Parameter
43+
{
44+
$param = new Parameter($name);
45+
return $this->parameter = $param;
46+
}
47+
48+
49+
public function getParameter(): ?Parameter
50+
{
51+
return $this->parameter;
52+
}
53+
54+
55+
/** @internal */
56+
public function getParameters(): array
57+
{
58+
return $this->parameter ? [$this->parameter] : [];
59+
}
60+
61+
62+
public function setReturnReference(bool $state = true): static
63+
{
64+
$this->returnReference = $state;
65+
return $this;
66+
}
67+
68+
69+
public function getReturnReference(): bool
70+
{
71+
return $this->returnReference;
72+
}
73+
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
<?php
2+
3+
/**
4+
* Test: Nette\PhpGenerator - PHP 8.4 property hooks for classes
5+
*/
6+
7+
declare(strict_types=1);
8+
9+
use Nette\PhpGenerator\ClassType;
10+
use Nette\PhpGenerator\PhpFile;
11+
use Nette\PhpGenerator\PropertyHook;
12+
use Nette\PhpGenerator\PsrPrinter;
13+
14+
require __DIR__ . '/../bootstrap.php';
15+
16+
$file = new PhpFile;
17+
$file->setStrictTypes();
18+
19+
$class = new ClassType('Locale');
20+
21+
$class->addProperty('languageCode')
22+
->setType('string')
23+
->setPublic();
24+
25+
$countryCodeSetHookClosure = (new PropertyHook)
26+
->setBody('$this->countryCode = strtoupper($countryCode);');
27+
28+
$countryCodeSetHookClosure->setParameter('countryCode')->setType('string');
29+
30+
$class->addProperty('countryCode')
31+
->setType('string')
32+
->setValue('AA')
33+
->setPublic()
34+
->setSetHook($countryCodeSetHookClosure);
35+
36+
$combinedCodeGetHookClosure = (new PropertyHook)
37+
->setBody('return \sprintf("%s_%s", $this->languageCode, $this->countryCode);');
38+
39+
$combinedCodeSetHookClosure = (new PropertyHook)
40+
->setBody('[$this->languageCode, $this->countryCode] = explode(\'_\', $value, 2);');
41+
42+
$combinedCodeSetHookClosure->setParameter('value')->setType('string');
43+
44+
$class->addProperty('combinedCode')
45+
->setType('string')
46+
->setPublic()
47+
->setGetHook($combinedCodeGetHookClosure)
48+
->setSetHook($combinedCodeSetHookClosure);
49+
50+
$expected = <<<'PHP'
51+
class Locale
52+
{
53+
public string $languageCode;
54+
55+
public string $countryCode = 'AA' {
56+
set (string $countryCode) {
57+
$this->countryCode = strtoupper($countryCode);
58+
}
59+
}
60+
61+
public string $combinedCode {
62+
get {
63+
return \sprintf("%s_%s", $this->languageCode, $this->countryCode);
64+
}
65+
set (string $value) {
66+
[$this->languageCode, $this->countryCode] = explode('_', $value, 2);
67+
}
68+
}
69+
}
70+
PHP;
71+
72+
dump((new PsrPrinter)->printClass($class));
73+
74+
same(rtrim($expected), rtrim((new PsrPrinter)->printClass($class)));

0 commit comments

Comments
 (0)