Skip to content

Commit 1e896b3

Browse files
committed
added support for PHP 8 attributes
1 parent aa026f6 commit 1e896b3

19 files changed

+288
-11
lines changed

readme.md

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,44 @@ Result:
346346
fn ($a, $b) => $a + $b
347347
```
348348

349+
Attributes
350+
----------
351+
352+
You can add PHP 8 attributes to all classes, methods, properties, constants, functions, closures and parameters.
353+
354+
```php
355+
$class = new Nette\PhpGenerator\ClassType('Demo');
356+
$class->addAttribute('Deprecated');
357+
358+
$class->addProperty('list')
359+
->addAttribute('WithArguments', [1, 2]);
360+
361+
$method = $class->addMethod('count')
362+
->addAttribute('Foo\Cached', ['mode' => true]);
363+
364+
$method->addParameter('items')
365+
->addAttribute('Bar');
366+
367+
echo $class;
368+
```
369+
370+
Result:
371+
372+
```php
373+
#[Deprecated]
374+
class Demo
375+
{
376+
#[WithArguments(1, 2)]
377+
public $list;
378+
379+
380+
#[Foo\Cached(mode: true)]
381+
public function count(#[Bar] $items)
382+
{
383+
}
384+
}
385+
```
386+
349387
Method and Function Body Generator
350388
----------------------------------
351389

@@ -468,7 +506,7 @@ Class Names Resolving
468506
---------------------
469507

470508
**When the class is part of the namespace, it is rendered slightly differently**: all types (ie. type hints, return types, parent class name,
471-
implemented interfaces and used traits) are automatically *resolved* (unless you turn it off, see below).
509+
implemented interfaces, used traits and attributes) are automatically *resolved* (unless you turn it off, see below).
472510
It means that you have to **use full class names** in definitions and they will be replaced
473511
with aliases (according to the use-statements) or fully qualified names in the resulting code:
474512

src/PhpGenerator/Attribute.php

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<?php
2+
3+
/**
4+
* This file is part of the Nette Framework (https://nette.org)
5+
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
6+
*/
7+
8+
declare(strict_types=1);
9+
10+
namespace Nette\PhpGenerator;
11+
12+
use Nette;
13+
14+
15+
/**
16+
* PHP Attribute.
17+
*/
18+
final class Attribute
19+
{
20+
use Nette\SmartObject;
21+
22+
/** @var string */
23+
private $name;
24+
25+
/** @var array */
26+
private $args;
27+
28+
29+
public function __construct(string $name, array $args)
30+
{
31+
if (!Helpers::isNamespaceIdentifier($name)) {
32+
throw new Nette\InvalidArgumentException("Value '$name' is not valid attribute name.");
33+
}
34+
$this->name = $name;
35+
$this->args = $args;
36+
}
37+
38+
39+
public function getName(): string
40+
{
41+
return $this->name;
42+
}
43+
44+
45+
public function getArguments(): array
46+
{
47+
return $this->args;
48+
}
49+
}

src/PhpGenerator/ClassType.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ final class ClassType
2222
{
2323
use Nette\SmartObject;
2424
use Traits\CommentAware;
25+
use Traits\AttributeAware;
2526

2627
public const
2728
TYPE_CLASS = 'class',

src/PhpGenerator/Closure.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ final class Closure
2121
{
2222
use Nette\SmartObject;
2323
use Traits\FunctionLike;
24+
use Traits\AttributeAware;
2425

2526
/** @var Parameter[] */
2627
private $uses = [];

src/PhpGenerator/Constant.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ final class Constant
2121
use Traits\NameAware;
2222
use Traits\VisibilityAware;
2323
use Traits\CommentAware;
24+
use Traits\AttributeAware;
2425

2526
/** @var mixed */
2627
private $value;

src/PhpGenerator/GlobalFunction.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ final class GlobalFunction
2323
use Traits\FunctionLike;
2424
use Traits\NameAware;
2525
use Traits\CommentAware;
26+
use Traits\AttributeAware;
2627

2728
public static function from(string $function): self
2829
{

src/PhpGenerator/Method.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ final class Method
2424
use Traits\NameAware;
2525
use Traits\VisibilityAware;
2626
use Traits\CommentAware;
27+
use Traits\AttributeAware;
2728

2829
/** @var string|null */
2930
private $body = '';

src/PhpGenerator/Parameter.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ class Parameter
2121
{
2222
use Nette\SmartObject;
2323
use Traits\NameAware;
24+
use Traits\AttributeAware;
2425

2526
/** @var bool */
2627
private $reference = false;

src/PhpGenerator/Printer.php

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ class Printer
3939
public function printFunction(GlobalFunction $function, PhpNamespace $namespace = null): string
4040
{
4141
return Helpers::formatDocComment($function->getComment() . "\n")
42+
. self::printAttributes($function->getAttributes(), $namespace)
4243
. 'function '
4344
. ($function->getReturnReference() ? '&' : '')
4445
. $function->getName()
@@ -58,7 +59,8 @@ public function printClosure(Closure $closure): string
5859
? "\n" . $this->indentation . implode(",\n" . $this->indentation, $uses) . "\n"
5960
: $tmp;
6061

61-
return 'function '
62+
return self::printAttributes($closure->getAttributes(), null, true)
63+
. 'function '
6264
. ($closure->getReturnReference() ? '&' : '')
6365
. $this->printParameters($closure, null)
6466
. ($uses ? " use ($useStr)" : '')
@@ -75,7 +77,8 @@ public function printArrowFunction(Closure $closure): string
7577
}
7678
}
7779

78-
return 'fn '
80+
return self::printAttributes($closure->getAttributes(), null)
81+
. 'fn '
7982
. ($closure->getReturnReference() ? '&' : '')
8083
. $this->printParameters($closure, null)
8184
. $this->printReturnType($closure, null)
@@ -87,6 +90,7 @@ public function printMethod(Method $method, PhpNamespace $namespace = null): str
8790
{
8891
$method->validate();
8992
return Helpers::formatDocComment($method->getComment() . "\n")
93+
. self::printAttributes($method->getAttributes(), $namespace)
9094
. ($method->isAbstract() ? 'abstract ' : '')
9195
. ($method->isFinal() ? 'final ' : '')
9296
. ($method->getVisibility() ? $method->getVisibility() . ' ' : '')
@@ -122,6 +126,7 @@ public function printClass(ClassType $class, PhpNamespace $namespace = null): st
122126
foreach ($class->getConstants() as $const) {
123127
$def = ($const->getVisibility() ? $const->getVisibility() . ' ' : '') . 'const ' . $const->getName() . ' = ';
124128
$consts[] = Helpers::formatDocComment((string) $const->getComment())
129+
. self::printAttributes($const->getAttributes(), $namespace)
125130
. $def
126131
. $this->dump($const->getValue(), strlen($def)) . ";\n";
127132
}
@@ -134,6 +139,7 @@ public function printClass(ClassType $class, PhpNamespace $namespace = null): st
134139
. '$' . $property->getName());
135140

136141
$properties[] = Helpers::formatDocComment((string) $property->getComment())
142+
. self::printAttributes($property->getAttributes(), $namespace)
137143
. $def
138144
. ($property->getValue() === null && !$property->isInitialized() ? '' : ' = ' . $this->dump($property->getValue(), strlen($def) + 3)) // 3 = ' = '
139145
. ";\n";
@@ -154,6 +160,7 @@ public function printClass(ClassType $class, PhpNamespace $namespace = null): st
154160

155161
return Strings::normalize(
156162
Helpers::formatDocComment($class->getComment() . "\n")
163+
. self::printAttributes($class->getAttributes(), $namespace)
157164
. ($class->isAbstract() ? 'abstract ' : '')
158165
. ($class->isFinal() ? 'final ' : '')
159166
. ($class->getName() ? $class->getType() . ' ' . $class->getName() . ' ' : '')
@@ -259,14 +266,15 @@ public function printParameters($function, PhpNamespace $namespace = null): stri
259266
$promoted = $param instanceof PromotedParameter ? $param : null;
260267
$params[] =
261268
($promoted ? Helpers::formatDocComment((string) $promoted->getComment()) : '')
262-
. ($promoted ? ($promoted->getVisibility() ?: 'public') . ' ' : '')
269+
. ($attrs = self::printAttributes($param->getAttributes(), $namespace, true))
270+
. ($promoted ? ($param->getVisibility() ?: 'public') . ' ' : '')
263271
. ltrim($this->printType($type, $param->isNullable(), $namespace) . ' ')
264272
. ($param->isReference() ? '&' : '')
265273
. ($variadic ? '...' : '')
266274
. '$' . $param->getName()
267275
. ($param->hasDefaultValue() && !$variadic ? ' = ' . $this->dump($param->getDefaultValue()) : '');
268276

269-
$special = $special || $promoted;
277+
$special = $special || $promoted || $attrs;
270278
}
271279

272280
$line = implode(', ', $params);
@@ -296,6 +304,22 @@ private function printReturnType($function, ?PhpNamespace $namespace): string
296304
}
297305

298306

307+
private function printAttributes(array $attrs, ?PhpNamespace $namespace, bool $inline = false): string
308+
{
309+
if (!$attrs) {
310+
return '';
311+
}
312+
$items = [];
313+
foreach ($attrs as $attr) {
314+
$args = (new Dumper)->format('...?', $attr->getArguments());
315+
$items[] = $this->printType($attr->getName(), false, $namespace) . ($args ? "($args)" : '');
316+
}
317+
return $inline
318+
? '#[' . implode(', ', $items) . '] '
319+
: '#[' . implode("]\n#[", $items) . "]\n";
320+
}
321+
322+
299323
private function joinProperties(array $props)
300324
{
301325
return $this->linesBetweenProperties

src/PhpGenerator/Property.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ final class Property
2323
use Traits\NameAware;
2424
use Traits\VisibilityAware;
2525
use Traits\CommentAware;
26+
use Traits\AttributeAware;
2627

2728
/** @var mixed */
2829
private $value;

0 commit comments

Comments
 (0)