Skip to content

Commit 4fb302f

Browse files
shanginndg
authored andcommitted
Add support for properties hooks [Closes #171]
1 parent b991c7f commit 4fb302f

File tree

4 files changed

+243
-8
lines changed

4 files changed

+243
-8
lines changed

src/PhpGenerator/Printer.php

Lines changed: 35 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-
private function printFunctionBody(Closure|GlobalFunction|Method $function): string
132+
private function printFunctionBody(Closure|GlobalFunction|Method|PropertyHook $function): string
133133
{
134134
$code = Helpers::simplifyTaggedNames($function->getBody(), $this->namespace);
135135
$code = Strings::normalize($code);
@@ -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,16 @@ private function printProperty(Property $property, bool $readOnlyClass = false):
386386
. ltrim($this->printType($type, $property->isNullable()) . ' ')
387387
. '$' . $property->getName());
388388

389+
$defaultValue = $property->getValue() === null && !$property->isInitialized()
390+
? ''
391+
: ' = ' . $this->dump($property->getValue(), strlen($def) + 3); // 3 = ' = '
392+
389393
return $this->printDocComment($property)
390394
. $this->printAttributes($property->getAttributes())
391395
. $def
392-
. ($property->getValue() === null && !$property->isInitialized()
393-
? ''
394-
: ' = ' . $this->dump($property->getValue(), strlen($def) + 3)) // 3 = ' = '
395-
. ";\n";
396+
. $defaultValue
397+
. ($this->printHooks($property) ?: ';')
398+
. "\n";
396399
}
397400

398401

@@ -452,6 +455,30 @@ protected function printAttributes(array $attrs, bool $inline = false): string
452455
}
453456

454457

458+
private function printHooks(Property|PromotedParameter $property): string
459+
{
460+
$hooks = $property->getHooks();
461+
if (!$hooks) {
462+
return '';
463+
}
464+
465+
foreach ($property->getHooks() as $type => $hook) {
466+
$hooks[$type] = $this->printDocComment($hook)
467+
. $this->printAttributes($hook->getAttributes())
468+
. ($hook->isFinal() ? 'final ' : '')
469+
. ($hook->getReturnReference() ? '&' : '')
470+
. $type
471+
. ($hook->getParameters() ? $this->printParameters($hook) : '')
472+
. ' '
473+
. ($hook->isShort()
474+
? '=> ' . $hook->getBody() . ';'
475+
: "{\n" . $this->indent($this->printFunctionBody($hook)) . '}');
476+
}
477+
478+
return " {\n" . $this->indent(implode("\n", $hooks)) . "\n}";
479+
}
480+
481+
455482
public function setTypeResolving(bool $state = true): static
456483
{
457484
$this->resolveTypes = $state;

src/PhpGenerator/Property.php

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ final class Property
3030
private bool $initialized = false;
3131
private bool $readOnly = false;
3232

33+
/** @var array<string, ?PropertyHook> */
34+
private array $hooks = ['set' => null, 'get' => null];
35+
3336

3437
public function setValue(mixed $val): static
3538
{
@@ -113,6 +116,46 @@ public function isReadOnly(): bool
113116
}
114117

115118

119+
/**
120+
* Replaces all hooks
121+
* @param PropertyHook[] $hooks
122+
*/
123+
public function setHooks(array $hooks): static
124+
{
125+
(function (PropertyHook ...$hooks) {})(...$hooks);
126+
$this->hooks = $hooks;
127+
return $this;
128+
}
129+
130+
131+
/** @return array<string, PropertyHook> */
132+
public function getHooks(): array
133+
{
134+
return array_filter($this->hooks);
135+
}
136+
137+
138+
public function getHook(\PropertyHookType|string $type): ?PropertyHook
139+
{
140+
$type = is_string($type) ? $type : $type->value;
141+
return $this->hooks[$type] ?? null;
142+
}
143+
144+
145+
public function addHook(\PropertyHookType|string $type, string $body = '', bool $short = false): PropertyHook
146+
{
147+
$type = is_string($type) ? $type : $type->value;
148+
return $this->hooks[$type] = (new PropertyHook)
149+
->setBody($body, short: $short);
150+
}
151+
152+
153+
public function hasHook(\PropertyHookType|string $type): bool
154+
{
155+
return isset($this->hooks[$type]);
156+
}
157+
158+
116159
/** @throws Nette\InvalidStateException */
117160
public function validate(): void
118161
{

src/PhpGenerator/PropertyHook.php

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Nette\PhpGenerator;
6+
7+
use JetBrains\PhpStorm\Language;
8+
9+
10+
class PropertyHook
11+
{
12+
use Traits\AttributeAware;
13+
use Traits\CommentAware;
14+
15+
private string $body = '';
16+
private bool $short = false;
17+
private bool $final = false;
18+
19+
/** @var Parameter[] */
20+
private array $parameters = [];
21+
private bool $returnReference = false;
22+
23+
24+
/** @param ?mixed[] $args */
25+
public function setBody(
26+
#[Language('PHP')]
27+
string $code,
28+
?array $args = null,
29+
bool $short = false,
30+
): static
31+
{
32+
$this->body = $args === null
33+
? $code
34+
: (new Dumper)->format($code, ...$args);
35+
$this->short = $short;
36+
return $this;
37+
}
38+
39+
40+
public function getBody(): string
41+
{
42+
return $this->body;
43+
}
44+
45+
46+
public function isShort(): bool
47+
{
48+
return $this->short && trim($this->body) !== '';
49+
}
50+
51+
52+
public function setFinal(bool $state = true): static
53+
{
54+
$this->final = $state;
55+
return $this;
56+
}
57+
58+
59+
public function isFinal(): bool
60+
{
61+
return $this->final;
62+
}
63+
64+
65+
/** @internal */
66+
public function setParameters(array $val): static
67+
{
68+
(function (Parameter ...$val) {})(...$val);
69+
$this->parameters = [];
70+
foreach ($val as $v) {
71+
$this->parameters[$v->getName()] = $v;
72+
}
73+
74+
return $this;
75+
}
76+
77+
78+
/** @internal */
79+
public function getParameters(): array
80+
{
81+
return $this->parameters;
82+
}
83+
84+
85+
/**
86+
* Adds a parameter. If it already exists, it overwrites it.
87+
* @param string $name without $
88+
*/
89+
public function addParameter(string $name): Parameter
90+
{
91+
return $this->parameters[$name] = new Parameter($name);
92+
}
93+
94+
95+
public function setReturnReference(bool $state = true): static
96+
{
97+
$this->returnReference = $state;
98+
return $this;
99+
}
100+
101+
102+
public function getReturnReference(): bool
103+
{
104+
return $this->returnReference;
105+
}
106+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
<?php
2+
3+
/**
4+
* Test: PHP 8.4 property hooks for classes
5+
*/
6+
7+
declare(strict_types=1);
8+
9+
use Nette\PhpGenerator\ClassType;
10+
11+
require __DIR__ . '/../bootstrap.php';
12+
13+
14+
$class = new ClassType('Demo');
15+
16+
$class->addProperty('first')
17+
->setType('string')
18+
->setValue('x')
19+
->setPublic()
20+
->addHook('set')
21+
->setBody('$value . ?', ['x'], short: true)
22+
->addComment('comment')
23+
->addAttribute('Example')
24+
->addParameter('value')
25+
->setType('string');
26+
27+
$prop = $class->addProperty('second')
28+
->setType('string')
29+
->setPublic();
30+
31+
$prop->addHook('get')
32+
->setBody('return $this->second;')
33+
->setReturnReference()
34+
->setFinal();
35+
36+
$prop->addHook('set', '$this->second = $value;')
37+
->addParameter('value')
38+
->setType('string');
39+
40+
same(<<<'XX'
41+
class Demo
42+
{
43+
public string $first = 'x' {
44+
/** comment */
45+
#[Example]
46+
set(string $value) => $value . 'x';
47+
}
48+
49+
public string $second {
50+
set(string $value) {
51+
$this->second = $value;
52+
}
53+
final &get {
54+
return $this->second;
55+
}
56+
}
57+
}
58+
59+
XX, (string) $class);

0 commit comments

Comments
 (0)