Skip to content

Commit fe94009

Browse files
shanginndg
authored andcommitted
added support for properties hooks [Closes #171]
1 parent e267a77 commit fe94009

File tree

6 files changed

+324
-10
lines changed

6 files changed

+324
-10
lines changed

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "nette/php-generator",
3-
"description": "🐘 Nette PHP Generator: generates neat PHP code for you. Supports new PHP 8.3 features.",
3+
"description": "🐘 Nette PHP Generator: generates neat PHP code for you. Supports new PHP 8.4 features.",
44
"keywords": ["nette", "php", "code", "scaffolding"],
55
"homepage": "https://nette.org",
66
"license": ["BSD-3-Clause", "GPL-2.0-only", "GPL-3.0-only"],

readme.md

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ Are you looking for a tool to generate PHP code for [classes](#classes), [functi
88

99
<h3>
1010

11-
✅ Supports all the latest PHP features like [enums](#enums), [attributes](#attributes), etc.<br>
11+
✅ Supports all the latest PHP features like [property hooks](#property-hooks), [enums](#enums), [attributes](#attributes), etc.<br>
1212
✅ Allows you to easily modify [existing classes](#generating-from-existing-ones)<br>
1313
✅ Output compliant with [PSR-12 / PER coding style](#printer-and-psr-compliance)<br>
1414
✅ Highly mature, stable, and widely used library
@@ -665,6 +665,41 @@ class Demo
665665

666666
 <!---->
667667

668+
Property Hooks
669+
--------------
670+
671+
You can also define property hooks (represented by the class [PropertyHook](https://api.nette.org/php-generator/master/Nette/PhpGenerator/PropertyHook.html)) for get and set operations, a feature introduced in PHP 8.4:
672+
673+
```php
674+
$class = new Nette\PhpGenerator\ClassType('Demo');
675+
$prop = $class->addProperty('firstName')
676+
->setType('string');
677+
678+
$prop->addHook('set', 'strtolower($value)', short: true)
679+
->addParameter('value')
680+
->setType('string');
681+
682+
$prop->addHook('get', 'return ucfirst($this->firstName);');
683+
684+
echo $class;
685+
```
686+
687+
This generates:
688+
689+
```php
690+
class Demo
691+
{
692+
public string $firstName {
693+
set(string $value) => strtolower($value);
694+
get {
695+
return ucfirst($this->firstName);
696+
}
697+
}
698+
}
699+
```
700+
701+
 <!---->
702+
668703
Namespace
669704
---------
670705

src/PhpGenerator/Printer.php

Lines changed: 36 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)
@@ -351,6 +351,7 @@ private function formatParameters(Closure|GlobalFunction|Method $function, bool
351351
. ($variadic ? '...' : '')
352352
. '$' . $param->getName()
353353
. ($param->hasDefaultValue() && !$variadic ? ' = ' . $this->dump($param->getDefaultValue()) : '')
354+
. ($param instanceof PromotedParameter ? $this->printHooks($param) : '')
354355
. ($multiline ? ",\n" : ', ');
355356
}
356357

@@ -386,13 +387,16 @@ private function printProperty(Property $property, bool $readOnlyClass = false):
386387
. ltrim($this->printType($type, $property->isNullable()) . ' ')
387388
. '$' . $property->getName());
388389

390+
$defaultValue = $property->getValue() === null && !$property->isInitialized()
391+
? ''
392+
: ' = ' . $this->dump($property->getValue(), strlen($def) + 3); // 3 = ' = '
393+
389394
return $this->printDocComment($property)
390395
. $this->printAttributes($property->getAttributes())
391396
. $def
392-
. ($property->getValue() === null && !$property->isInitialized()
393-
? ''
394-
: ' = ' . $this->dump($property->getValue(), strlen($def) + 3)) // 3 = ' = '
395-
. ";\n";
397+
. $defaultValue
398+
. ($this->printHooks($property) ?: ';')
399+
. "\n";
396400
}
397401

398402

@@ -452,6 +456,30 @@ protected function printAttributes(array $attrs, bool $inline = false): string
452456
}
453457

454458

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

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+
}

src/PhpGenerator/Traits/PropertyLike.php

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
namespace Nette\PhpGenerator\Traits;
1111

12+
use Nette\PhpGenerator\PropertyHook;
1213

1314

1415
/**
@@ -20,6 +21,9 @@ trait PropertyLike
2021

2122
private bool $readOnly = false;
2223

24+
/** @var array<string, ?PropertyHook> */
25+
private array $hooks = ['set' => null, 'get' => null];
26+
2327

2428
public function setReadOnly(bool $state = true): static
2529
{
@@ -32,4 +36,47 @@ public function isReadOnly(): bool
3236
{
3337
return $this->readOnly;
3438
}
39+
40+
41+
/**
42+
* Replaces all hooks
43+
* @param PropertyHook[] $hooks
44+
*/
45+
public function setHooks(array $hooks): static
46+
{
47+
(function (PropertyHook ...$hooks) {})(...$hooks);
48+
$this->hooks = $hooks;
49+
return $this;
50+
}
51+
52+
53+
/** @return array<string, PropertyHook> */
54+
public function getHooks(): array
55+
{
56+
return array_filter($this->hooks);
57+
}
58+
59+
60+
/** @param 'set'|'get'|\PropertyHookType $type */
61+
public function addHook(string|\PropertyHookType $type, string $body = '', bool $short = false): PropertyHook
62+
{
63+
$type = is_string($type) ? $type : $type->value;
64+
return $this->hooks[$type] = (new PropertyHook)
65+
->setBody($body, short: $short);
66+
}
67+
68+
69+
/** @param 'set'|'get'|\PropertyHookType $type */
70+
public function getHook(string|\PropertyHookType $type): ?PropertyHook
71+
{
72+
$type = is_string($type) ? $type : $type->value;
73+
return $this->hooks[$type] ?? null;
74+
}
75+
76+
77+
/** @param 'set'|'get'|\PropertyHookType $type */
78+
public function hasHook(string|\PropertyHookType $type): bool
79+
{
80+
return isset($this->hooks[$type]);
81+
}
3582
}

0 commit comments

Comments
 (0)