+- Potřebujete generovat kód tříd, funkcí, PHP souborů atd?
+- Umí všechny nejnovější vychytávky v PHP (jako enumy atd.)
+- Dovolí vám snadno modifikovat existující třídy
+- Výstup vyhovující PSR-12
+- Zralá, stabilní a široce používaná knihovna
+
+
+
+Instalace
+---------
+
+Knihovnu stáhnete a nainstalujete pomocí nástroje [Composer|/best-practices/composer]:
+
+```shell
+composer require nette/php-generator
+```
+
+Kompatibilitu s PHP naleznete v [tabulce |#Tabulka kompatibility].
+
+
+Třídy
+-----
+
+Začněme rovnou příkladem tvorby třídy pomocí [ClassType |api:Nette\PhpGenerator\ClassType]:
+
+```php
+$class = new Nette\PhpGenerator\ClassType('Demo');
+
+$class
+ ->setFinal()
+ ->setExtends(ParentClass::class)
+ ->addImplement(Countable::class)
+ ->addComment("Popis třídy.\nDruhý řádek\n")
+ ->addComment('@property-read Nette\Forms\Form $form');
+
+// kód jednoduše vygenerujete přetypováním na řetězec nebo použitím echo:
+echo $class;
+```
+
+Vrátí následující výsledek:
+
+```php
+/**
+ * Popis třídy
+ * Druhý řádek
+ *
+ * @property-read Nette\Forms\Form $form
+ */
+final class Demo extends ParentClass implements Countable
+{
+ use Nette\SmartObject;
+}
+```
+
+K vygenerování kódu můžeme také použít tzv. printer, který na rozdíl od `echo $class` budeme moci [dále konfigurovat |#Printer a soulad s PSR]:
+
+```php
+$printer = new Nette\PhpGenerator\Printer;
+echo $printer->printClass($class);
+```
+
+Můžeme přidat konstanty (třída [Constant |api:Nette\PhpGenerator\Constant]) a proměnné (třída [Property |api:Nette\PhpGenerator\Property]):
+
+```php
+$class->addConstant('ID', 123)
+ ->setProtected() // viditelnost konstant
+ ->setFinal();
+
+$class->addProperty('items', [1, 2, 3])
+ ->setPrivate() // nebo setVisibility('private')
+ ->setStatic()
+ ->addComment('@var int[]');
+
+$class->addProperty('list')
+ ->setType('?array')
+ ->setInitialized(); // vypíše '= null'
+```
+
+Vygeneruje:
+
+```php
+final protected const ID = 123;
+
+/** @var int[] */
+private static $items = [1, 2, 3];
+
+public ?array $list = null;
+```
+
+A můžeme přidat [metody|#Signatury metod a funkcí]:
+
+```php
+$method = $class->addMethod('count')
+ ->addComment('Count it.')
+ ->setFinal()
+ ->setProtected()
+ ->setReturnType('?int') // návratové typy u metod
+ ->setBody('return count($items ?: $this->items);');
+
+$method->addParameter('items', []) // $items = []
+ ->setReference() // &$items = []
+ ->setType('array'); // array &$items = []
+```
+
+Výsledkem je:
+
+```php
+/**
+ * Count it.
+ */
+final protected function count(array &$items = []): ?int
+{
+ return count($items ?: $this->items);
+}
+```
+
+Propagované parametry zavedené PHP 8.0 lze předat konstruktoru (od verze 3.5):
+
+```php
+$method = $class->addMethod('__construct');
+$method->addPromotedParameter('name');
+$method->addPromotedParameter('args', [])
+ ->setPrivate();
+```
+
+Výsledkem je:
+
+```php
+public function __construct(
+ public $name,
+ private $args = [],
+) {
+}
+```
+
+Vlastnosti určené pouze pro čtení zavedené v PHP 8.1 lze označit pomocí funkce `setReadOnly()`.
+
+------
+
+Pokud přidaná vlastnost, konstanta, metoda nebo parametr již existují, vyhodí se výjimka.
+
+Členy třídy lze odebrat pomocí `removeProperty()`, `removeConstant()`, `removeMethod()` nebo `removeParameter()`.
+
+Do třídy můžete také přidat existující objekty `Method`, `Property` nebo `Constant`:
+
+```php
+$method = new Nette\PhpGenerator\Method('getHandle');
+$property = new Nette\PhpGenerator\Property('handle');
+$const = new Nette\PhpGenerator\Constant('ROLE');
+
+$class = (new Nette\PhpGenerator\ClassType('Demo'))
+ ->addMember($method)
+ ->addMember($property)
+ ->addMember($const);
+```
+
+Můžete také klonovat stávající metody, vlastnosti a konstanty pod jiným názvem pomocí `cloneWithName()`:
+
+```php
+$methodCount = $class->getMethod('count');
+$methodRecount = $methodCount->cloneWithName('recount');
+$class->addMember($methodRecount);
+```
+
+
+Interface nebo traita
+---------------------
+
+Lze vytvářet rozhraní a traity:
+
+```php
+$interface = new Nette\PhpGenerator\InterfaceType('MyInterface');
+$trait = new Nette\PhpGenerator\TraitType('MyTrait');
+```
+
+Používání trait:
+
+```php
+$class = new Nette\PhpGenerator\ClassType('Demo');
+$class->addTrait('SmartObject');
+$class->addTrait('MyTrait')
+ ->addResolution('sayHello as protected')
+ ->addComment('@use MyTrait');
+echo $class;
+```
+
+Výsledek:
+
+```php
+class Demo
+{
+ use SmartObject;
+ use MyTrait {
+ sayHello as protected;
+ }
+}
+```
+
+
+Enums .{data-version:v3.6}
+--------------------------
+
+Výčty, které přináší PHP 8.1, můžete snadno vytvořit takto:
+
+```php
+$enum = new Nette\PhpGenerator\EnumType('Suit');
+$enum->addCase('Clubs');
+$enum->addCase('Diamonds');
+$enum->addCase('Hearts');
+$enum->addCase('Spades');
+
+echo $enum;
+```
+
+Výsledek:
+
+```php
+enum Suit
+{
+ case Clubs;
+ case Diamonds;
+ case Hearts;
+ case Spades;
+}
+```
+
+Můžete také definovat skalární ekvivalenty a vytvořit tak "backed" výčet:
+
+```php
+$enum->addCase('Clubs', '♣');
+$enum->addCase('Diamonds', '♦');
+```
+
+Ke každému *case* je možné přidat komentář nebo [#atributy] pomocí `addComment()` nebo `addAttribute()`.
+
+
+Anonymní třídy
+--------------
+
+Jako název předáme `null` a máme anonymní třídu:
+
+```php
+$class = new Nette\PhpGenerator\ClassType(null);
+$class->addMethod('__construct')
+ ->addParameter('foo');
+
+echo '$obj = new class ($val) ' . $class . ';';
+```
+
+Výsledek:
+
+```php
+$obj = new class ($val) {
+
+ public function __construct($foo)
+ {
+ }
+};
+```
+
+
+Globální funkce
+---------------
+
+Kód funkcí generuje třída [GlobalFunction |api:Nette\PhpGenerator\GlobalFunction]:
+
+```php
+$function = new Nette\PhpGenerator\GlobalFunction('foo');
+$function->setBody('return $a + $b;');
+$function->addParameter('a');
+$function->addParameter('b');
+echo $function;
+
+// nebo použijte PsrPrinter pro výstup v souladu s PSR-2 / PSR-12
+// echo (new Nette\PhpGenerator\PsrPrinter)->printFunction($function);
+```
+
+Výsledek:
+
+```php
+function foo($a, $b)
+{
+ return $a + $b;
+}
+```
+
+
+Anonymní funkce
+---------------
+
+Kód anonymních funkcí generuje třída [Closure |api:Nette\PhpGenerator\Closure]:
+
+```php
+$closure = new Nette\PhpGenerator\Closure;
+$closure->setBody('return $a + $b;');
+$closure->addParameter('a');
+$closure->addParameter('b');
+$closure->addUse('c')
+ ->setReference();
+echo $closure;
+
+// nebo použijte PsrPrinter pro výstup v souladu s PSR-2 / PSR-12
+// echo (new Nette\PhpGenerator\PsrPrinter)->printClosure($closure);
+```
+
+Výsledek:
+
+```php
+function ($a, $b) use (&$c) {
+ return $a + $b;
+}
+```
+
+
+Zkrácené arrow funkce
+---------------------
+
+Můžete také vypsat zkrácenou anonymní funkci pomocí printeru:
+
+```php
+$closure = new Nette\PhpGenerator\Closure;
+$closure->setBody('$a + $b');
+$closure->addParameter('a');
+$closure->addParameter('b');
+
+echo (new Nette\PhpGenerator\Printer)->printArrowFunction($closure);
+```
+
+Výsledek:
+
+```php
+fn($a, $b) => $a + $b
+```
+
+
+Signatury metod a funkcí
+------------------------
+
+Metody reprezentuje třída [Method |api:Nette\PhpGenerator\Method]. Můžete nastavit viditelnost, návratovou hodnotu, přidat komentáře, [atributy|#Atributy] atd:
+
+```php
+$method = $class->addMethod('count')
+ ->addComment('Count it.')
+ ->setFinal()
+ ->setProtected()
+ ->setReturnType('?int');
+```
+
+Jednotlivé parametry reprezentuje třídy [Parameter |api:Nette\PhpGenerator\Parameter]. Opět můžete nastavit všechny myslitelné vlastnosti:
+
+```php
+$method->addParameter('items', []) // $items = []
+ ->setReference() // &$items = []
+ ->setType('array'); // array &$items = []
+
+// function count(&$items = [])
+```
+
+Pro definici tzv. variadics parametrů (nebo též splat operátor) slouží `setVariadics()`:
+
+```php
+$method = $class->addMethod('count');
+$method->setVariadic(true);
+$method->addParameter('items');
+```
+
+Vygeneruje:
+
+```php
+function count(...$items)
+{
+}
+```
+
+
+Těla metod a funkcí
+-------------------
+
+Tělo lze předat najednou metodě `setBody()` nebo postupně (po řádcích) opakovaným voláním `addBody()`:
+
+```php
+$function = new Nette\PhpGenerator\GlobalFunction('foo');
+$function->addBody('$a = rand(10, 20);');
+$function->addBody('return $a;');
+echo $function;
+```
+
+Výsledek
+
+```php
+function foo()
+{
+ $a = rand(10, 20);
+ return $a;
+}
+```
+
+Můžete použít speciální zástupné znaky pro snadné vkládání proměnných.
+
+Jednoduché zástupné symboly `?`
+
+```php
+$str = 'any string';
+$num = 3;
+$function = new Nette\PhpGenerator\GlobalFunction('foo');
+$function->addBody('return substr(?, ?);', [$str, $num]);
+echo $function;
+```
+
+Výsledek
+
+```php
+function foo()
+{
+ return substr('any string', 3);
+}
+```
+
+Zástupný znak pro variadic `...?`
+
+```php
+$items = [1, 2, 3];
+$function = new Nette\PhpGenerator\GlobalFunction('foo');
+$function->setBody('myfunc(...?);', [$items]);
+echo $function;
+```
+
+Výsledek:
+
+```php
+function foo()
+{
+ myfunc(1, 2, 3);
+}
+```
+
+Můžete také použít pojmenované parametry pro PHP 8 pomocí `...?: `(od verze 3.5)
+
+```php
+$items = ['foo' => 1, 'bar' => true];
+$function->setBody('myfunc(...?:);', [$items]);
+
+// myfunc(foo: 1, bar: true);
+```
+
+Zástupný symbol se escapuje pomocí lomítka `\?`
+
+```php
+$num = 3;
+$function = new Nette\PhpGenerator\GlobalFunction('foo');
+$function->addParameter('a');
+$function->addBody('return $a \? 10 : ?;', [$num]);
+echo $function;
+```
+
+Výsledek:
+
+```php
+function foo($a)
+{
+ return $a ? 10 : 3;
+}
+```
+
+
+Printer a soulad s PSR
+----------------------
+
+PHP kód generují objekty `Printer`. K je vám tiskárna `PsrPrinter`, jejíž výstup je v souladu s PSR-2 a PSR-12 a k odsazování používá mezery, a dále `Printer`, která pro odsazování používá tabulátory.
+
+```php
+$class = new Nette\PhpGenerator\ClassType('Demo');
+// ...
+
+$printer = new Nette\PhpGenerator\PsrPrinter;
+echo $printer->printClass($class); // odsazení 4 mezerami
+```
+
+Potřebujete na míru upravit chování printeru? Vytvořte si vlastní poděděním třídy `Printer`. Můžete překonfigurovat tyto proměnné:
+
+```php
+class MyPrinter extends Nette\PhpGenerator\Printer
+{
+ public int $wrapLength = 120;
+ public string $indentation = "\t";
+ public int $linesBetweenProperties = 0;
+ public int $linesBetweenMethods = 2;
+ public int $linesBetweenUseTypes = 0;
+ public bool $bracesOnNextLine = true;
+ public string $returnTypeColon = ': ';
+}
+```
+
+
+Typy
+----
+
+Každý typ nebo union/intersection typ lze předat jako řetězec, můžete také použít předdefinované konstanty pro nativní typy:
+
+```php
+use Nette\PhpGenerator\Type;
+
+$member->setType('array'); // nebo Type::ARRAY;
+$member->setType('array|string'); // nebo Type::union('array', 'string')
+$member->setType('Foo&Bar'); // nebo Type::intersection(Foo::class, Bar::class)
+$member->setType(null); // odstraní typ
+```
+
+Totéž platí pro metodu `setReturnType()`.
+
+
+Literály
+--------
+
+Pomocí `Literal` můžete předávat libovolný kód PHP, například pro výchozí hodnoty vlastností nebo parametrů atd:
+
+```php
+use Nette\PhpGenerator\Literal;
+
+$class = new Nette\PhpGenerator\ClassType('Demo');
+
+$class->addProperty('foo', new Literal('Iterator::SELF_FIRST'));
+
+$class->addMethod('bar')
+ ->addParameter('id', new Literal('1 + 2'));
+
+echo $class;
+```
+
+Výsledek:
+
+```php
+class Demo
+{
+ public $foo = Iterator::SELF_FIRST;
+
+ public function bar($id = 1 + 2)
+ {
+ }
+}
+```
+
+Můžete také předat parametry do `Literal` a nechat je zformátovat do platného kódu PHP pomocí [zástupných znaků|#Generování těl metod a funkcí]:
+
+```php
+new Literal('substr(?, ?)', [$a, $b]);
+// generuje například: substr('hello', 5);
+```
+
+
+Atributy .{data-version:v3.5}
+-----------------------------
+
+PHP 8 atributy můžete přidat do všech tříd, metod, vlastností, konstant, enumů, funkcí, closures a parametrů. Jako hodnoty parametrů lze používat i [#literály].
+
+```php
+$class = new Nette\PhpGenerator\ClassType('Demo');
+$class->addAttribute('Deprecated');
+
+$class->addProperty('list')
+ ->addAttribute('WithArguments', [1, 2]);
+
+$method = $class->addMethod('count')
+ ->addAttribute('Foo\Cached', ['mode' => true]);
+
+$method->addParameter('items')
+ ->addAttribute('Bar');
+
+echo $class;
+```
+
+Výsledek:
+
+```php
+#[Deprecated]
+class Demo
+{
+ #[WithArguments(1, 2)]
+ public $list;
+
+
+ #[Foo\Cached(mode: true)]
+ public function count(#[Bar] $items)
+ {
+ }
+}
+```
+
+
+Jmenný prostor
+--------------
+
+Třídy, vlastnosti, rozhraní a výčty (dále jen třídy) lze seskupit do jmenných prostorů reprezentovaných třídou [PhpNamespace |api:Nette\PhpGenerator\PhpNamespace]:
+
+```php
+$namespace = new Nette\PhpGenerator\PhpNamespace('Foo');
+
+// vytvoříme nové třídy v namespace
+$class = $namespace->addClass('Task');
+$interface = $namespace->addInterface('Countable');
+$trait = $namespace->addTrait('NameAware');
+
+// nebo vložíme existující třídu do namespace
+$class = new Nette\PhpGenerator\ClassType('Task');
+$namespace->add($class);
+```
+
+Pokud třída již existuje, vyhodí se výjimka.
+
+Můžete definovat klauzule use:
+
+```php
+// use Http\Request;
+$namespace->addUse(Http\Request::class);
+// use Http\Request as HttpReq;
+$namespace->addUse(Http\Request::class, 'HttpReq');
+// use function iter\range;
+$namespace->addUseFunction('iter\range');
+```
+
+Chcete-li zjednodušit plně kvalifikovaný název třídy, funkce nebo konstanty podle definovaných aliasů, použijte metodu `simplifyName`:
+
+```php
+echo $namespace->simplifyName('Foo\Bar'); // 'Bar', protože 'Foo' je aktuální jmenný prostor
+echo $namespace->simplifyName('iter\range', $namespace::NAME_FUNCTION); // 'range', kvůli definovanému use-statement
+```
+
+Zjednodušený název třídy, funkce nebo konstanty můžete naopak převést na plně kvalifikovaný název pomocí metody `resolveName`:
+
+```php
+echo $namespace->resolveName('Bar'); // 'Foo\Bar'
+echo $namespace->resolveName('range', $namespace::NAME_FUNCTION); // 'iter\range'
+```
+
+
+Překlady názvů tříd
+-------------------
+
+**Když je třída součástí jmenného prostoru, je vykreslena mírně odlišně:** všechny typy (například typehinty, návratové typy, název rodičovské třídy,
+implementovaná rozhraní, použité vlastnosti a atributy) jsou automaticky *překládány* (pokud to nevypnete, viz níže).
+To znamená, že musíte v definicích **používat úplné názvy tříd** a ty budou nahrazeny
+za aliasy (podle klauzulí use) nebo za plně kvalifikovaná jména ve výsledném kódu:
+
+```php
+$namespace = new Nette\PhpGenerator\PhpNamespace('Foo');
+$namespace->addUse('Bar\AliasedClass');
+
+$class = $namespace->addClass('Demo');
+$class->addImplement('Foo\A') // bude zjednodušen na A
+ ->addTrait('Bar\AliasedClass'); // bude zjednodušen na AliasedClass
+
+$method = $class->addMethod('method');
+$method->addComment('@return ' . $namespace->simplifyName('Foo\D')); // v komentářích zjednodušíme manuálně
+$method->addParameter('arg')
+ ->setType('Bar\OtherClass'); // bude přeložen na \Bar\OtherClass
+
+echo $namespace;
+
+// nebo použijte PsrPrinter pro výstup v souladu s PSR-2 / PSR-12
+// echo (new Nette\PhpGenerator\PsrPrinter)->printNamespace($namespace);
+```
+
+Výsledek:
+
+```php
+namespace Foo;
+
+use Bar\AliasedClass;
+
+class Demo implements A
+{
+ use AliasedClass;
+
+ /**
+ * @return D
+ */
+ public function method(\Bar\OtherClass $arg)
+ {
+ }
+}
+```
+
+Automatické překládání lze vypnout tímto způsobem:
+
+```php
+$printer = new Nette\PhpGenerator\Printer; // nebo PsrPrinter
+$printer->setTypeResolving(false);
+echo $printer->printNamespace($namespace);
+```
+
+
+PHP soubory
+-----------
+
+Třídy, funkce a jmenné prostory lze seskupit do PHP souborů reprezentovaných třídou [PhpFile|api:Nette\PhpGenerator\PhpFile]:
+
+```php
+$file = new Nette\PhpGenerator\PhpFile;
+$file->addComment('This file is auto-generated.');
+$file->setStrictTypes(); // přidá declare(strict_types=1)
+
+$class = $file->addClass('Foo\A');
+$function = $file->addFunction('Foo\foo');
+
+// nebo
+// $namespace = $file->addNamespace('Foo');
+// $class = $namespace->addClass('A');
+// $function = $namespace->addFunction('foo');
+
+echo $file;
+
+// nebo použijte PsrPrinter pro výstup v souladu s PSR-2 / PSR-12
+// echo (new Nette\PhpGenerator\PsrPrinter)->printFile($file);
+```
+
+Výsledek:
+
+```php
+dump($var); // vypíše ['a', 'b', 123]
+```
+
+
+Tabulka kompatibility
+---------------------
+
+PhpGenerator 4.0 je kompatibilní s PHP 8.0 až 8.2
+PhpGenerator 3.6 je kompatibilní s PHP 7.2 až 8.2
+PhpGenerator 3.2 – 3.5 je kompatibilní s PHP 7.1 až 8.0
+PhpGenerator 3.1 je kompatibilní s PHP 7.1 až 7.3
+PhpGenerator 3.0 je kompatibilní s PHP 7.0 až 7.3
+PhpGenerator 2.6 je kompatibilní s PHP 5.6 až 7.3
+
+
+{{composer: nette/php-generator}}
+{{leftbar: /utils/@left-menu}}
diff --git a/docs/en/@home.texy b/docs/en/@home.texy
new file mode 100644
index 00000000..11242fb5
--- /dev/null
+++ b/docs/en/@home.texy
@@ -0,0 +1,822 @@
+PHP Code Generator
+******************
+
+
+- Need to generate PHP code for classes, functions, PHP files, etc.?
+- Supports all the latest PHP features like enums, attributes, etc.
+- Allows you to easily modify existing classes
+- PSR-12 compliant output
+- Highly mature, stable, and widely used library
+
+
+
+Installation
+------------
+
+Download and install the package using [Composer|/best-practices/composer]:
+
+```shell
+composer require nette/php-generator
+```
+
+For PHP compatibility, see the [table |#Compatibility Table].
+
+
+Classes
+-------
+
+Let's start with a straightforward example of generating class using [ClassType |api:Nette\PhpGenerator\ClassType]:
+
+```php
+$class = new Nette\PhpGenerator\ClassType('Demo');
+
+$class
+ ->setFinal()
+ ->setExtends(ParentClass::class)
+ ->addImplement(Countable::class)
+ ->addComment("Description of class.\nSecond line\n")
+ ->addComment('@property-read Nette\Forms\Form $form');
+
+// to generate PHP code simply cast to string or use echo:
+echo $class;
+```
+
+It will render this result:
+
+```php
+/**
+ * Description of class.
+ * Second line
+ *
+ * @property-read Nette\Forms\Form $form
+ */
+final class Demo extends ParentClass implements Countable
+{
+ use Nette\SmartObject;
+}
+```
+
+We can also use a printer to generate the code, which, unlike `echo $class`, we will be able to [further configure |#Printers and PSR compliance]:
+
+```php
+$printer = new Nette\PhpGenerator\Printer;
+echo $printer->printClass($class);
+```
+
+We can add constants (class [Constant |api:Nette\PhpGenerator\Constant]) and properties (class [Property |api:Nette\PhpGenerator\Property]):
+
+```php
+$class->addConstant('ID', 123)
+ ->setProtected() // constant visibility
+ ->setFinal();
+
+$class->addProperty('items', [1, 2, 3])
+ ->setPrivate() // or setVisibility('private')
+ ->setStatic()
+ ->addComment('@var int[]');
+
+$class->addProperty('list')
+ ->setType('?array')
+ ->setInitialized(); // prints '= null'
+```
+
+It generates:
+
+```php
+final protected const ID = 123;
+
+/** @var int[] */
+private static $items = [1, 2, 3];
+
+public ?array $list = null;
+```
+
+And we can add [methods|#Method and Function Signature]:
+
+```php
+$method = $class->addMethod('count')
+ ->addComment('Count it.')
+ ->setFinal()
+ ->setProtected()
+ ->setReturnType('?int') // method return type
+ ->setBody('return count($items ?: $this->items);');
+
+$method->addParameter('items', []) // $items = []
+ ->setReference() // &$items = []
+ ->setType('array'); // array &$items = []
+```
+
+It results in:
+
+```php
+/**
+ * Count it.
+ */
+final protected function count(array &$items = []): ?int
+{
+ return count($items ?: $this->items);
+}
+```
+
+Promoted parameters introduced by PHP 8.0 can be passed to the constructor (since v3.5):
+
+```php
+$method = $class->addMethod('__construct');
+$method->addPromotedParameter('name');
+$method->addPromotedParameter('args', [])
+ ->setPrivate();
+```
+
+It results in:
+
+```php
+public function __construct(
+ public $name,
+ private $args = [],
+) {
+}
+```
+
+Readonly properties introduced by PHP 8.1 can be marked via `setReadOnly()`.
+
+------
+
+If the added property, constant, method or parameter already exist, it throws exception.
+
+Members can be removed using `removeProperty()`, `removeConstant()`, `removeMethod()` or `removeParameter()`.
+
+You can also add existing `Method`, `Property` or `Constant` objects to the class:
+
+```php
+$method = new Nette\PhpGenerator\Method('getHandle');
+$property = new Nette\PhpGenerator\Property('handle');
+$const = new Nette\PhpGenerator\Constant('ROLE');
+
+$class = (new Nette\PhpGenerator\ClassType('Demo'))
+ ->addMember($method)
+ ->addMember($property)
+ ->addMember($const);
+```
+
+You can clone existing methods, properties and constants with a different name using `cloneWithName()`:
+
+```php
+$methodCount = $class->getMethod('count');
+$methodRecount = $methodCount->cloneWithName('recount');
+$class->addMember($methodRecount);
+```
+
+
+Interface or Trait
+------------------
+
+You can create interfaces and traits:
+
+```php
+$interface = new Nette\PhpGenerator\InterfaceType('MyInterface');
+$trait = new Nette\PhpGenerator\TraitType('MyTrait');
+```
+
+Using Traits:
+
+```php
+$class = new Nette\PhpGenerator\ClassType('Demo');
+$class->addTrait('SmartObject');
+$class->addTrait('MyTrait')
+ ->addResolution('sayHello as protected')
+ ->addComment('@use MyTrait');
+echo $class;
+```
+
+Result:
+
+```php
+class Demo
+{
+ use SmartObject;
+ use MyTrait {
+ sayHello as protected;
+ }
+}
+```
+
+
+Enums .{data-version:v3.6}
+--------------------------
+
+You can easily create the enums that PHP 8.1 brings:
+
+```php
+$enum = new Nette\PhpGenerator\EnumType('Suit');
+$enum->addCase('Clubs');
+$enum->addCase('Diamonds');
+$enum->addCase('Hearts');
+$enum->addCase('Spades');
+
+echo $enum;
+```
+
+Result:
+
+```php
+enum Suit
+{
+ case Clubs;
+ case Diamonds;
+ case Hearts;
+ case Spades;
+}
+```
+
+You can also define scalar equivalents for cases to create a backed enum:
+
+```php
+$enum->addCase('Clubs', '♣');
+$enum->addCase('Diamonds', '♦');
+```
+
+It is possible to add a comment or [#attributes] to each case using `addComment()` or `addAttribute()`.
+
+
+Anonymous Class
+---------------
+
+Give `null` as the name and you have an anonymous class:
+
+```php
+$class = new Nette\PhpGenerator\ClassType(null);
+$class->addMethod('__construct')
+ ->addParameter('foo');
+
+echo '$obj = new class ($val) ' . $class . ';';
+```
+
+Result:
+
+```php
+$obj = new class ($val) {
+
+ public function __construct($foo)
+ {
+ }
+};
+```
+
+
+Global Function
+---------------
+
+Code of functions will generate class [GlobalFunction |api:Nette\PhpGenerator\GlobalFunction]:
+
+```php
+$function = new Nette\PhpGenerator\GlobalFunction('foo');
+$function->setBody('return $a + $b;');
+$function->addParameter('a');
+$function->addParameter('b');
+echo $function;
+
+// or use PsrPrinter for output conforming to PSR-2 / PSR-12
+// echo (new Nette\PhpGenerator\PsrPrinter)->printFunction($function);
+```
+
+Result:
+
+```php
+function foo($a, $b)
+{
+ return $a + $b;
+}
+```
+
+
+Closure
+-------
+
+Code of closures will generate class [Closure |api:Nette\PhpGenerator\Closure]:
+
+```php
+$closure = new Nette\PhpGenerator\Closure;
+$closure->setBody('return $a + $b;');
+$closure->addParameter('a');
+$closure->addParameter('b');
+$closure->addUse('c')
+ ->setReference();
+echo $closure;
+
+// or use PsrPrinter for output conforming to PSR-2 / PSR-12
+// echo (new Nette\PhpGenerator\PsrPrinter)->printClosure($closure);
+```
+
+Result:
+
+```php
+function ($a, $b) use (&$c) {
+ return $a + $b;
+}
+```
+
+
+Arrow Function
+--------------
+
+You can also print closure as arrow function using printer:
+
+```php
+$closure = new Nette\PhpGenerator\Closure;
+$closure->setBody('$a + $b');
+$closure->addParameter('a');
+$closure->addParameter('b');
+
+echo (new Nette\PhpGenerator\Printer)->printArrowFunction($closure);
+```
+
+Result:
+
+```php
+fn($a, $b) => $a + $b
+```
+
+
+Method and Function Signature
+-----------------------------
+
+Methods are represented by the class [Method |api:Nette\PhpGenerator\Method]. You can set visibility, return value, add comments, [attributes|#Attributes] etc:
+
+```php
+$method = $class->addMethod('count')
+ ->addComment('Count it.')
+ ->setFinal()
+ ->setProtected()
+ ->setReturnType('?int');
+```
+
+Each parameter is represented by a class [Parameter |api:Nette\PhpGenerator\Parameter]. Again, you can set every conceivable property:
+
+```php
+$method->addParameter('items', []) // $items = []
+ ->setReference() // &$items = []
+ ->setType('array'); // array &$items = []
+
+// function count(&$items = [])
+```
+
+To define the so-called variadics parameters (or also the splat, spread, ellipsis, unpacking or three dots operator), use `setVariadics()`:
+
+```php
+$method = $class->addMethod('count');
+$method->setVariadics(true);
+$method->addParameter('items');
+```
+
+Generates:
+
+```php
+function count(...$items)
+{
+}
+```
+
+
+Method and Function Body
+------------------------
+
+The body can be passed to the `setBody()` method at once or sequentially (line by line) by repeatedly calling `addBody()`:
+
+```php
+$function = new Nette\PhpGenerator\GlobalFunction('foo');
+$function->addBody('$a = rand(10, 20);');
+$function->addBody('return $a;');
+echo $function;
+```
+
+Result
+
+```php
+function foo()
+{
+ $a = rand(10, 20);
+ return $a;
+}
+```
+
+You can use special placeholders for handy way to inject variables.
+
+Simple placeholders `?`
+
+```php
+$str = 'any string';
+$num = 3;
+$function = new Nette\PhpGenerator\GlobalFunction('foo');
+$function->addBody('return substr(?, ?);', [$str, $num]);
+echo $function;
+```
+
+Result:
+
+```php
+function foo()
+{
+ return substr('any string', 3);
+}
+```
+
+Variadic placeholder `...?`
+
+```php
+$items = [1, 2, 3];
+$function = new Nette\PhpGenerator\GlobalFunction('foo');
+$function->setBody('myfunc(...?);', [$items]);
+echo $function;
+```
+
+Result:
+
+```php
+function foo()
+{
+ myfunc(1, 2, 3);
+}
+```
+
+You can also use PHP 8 named parameters using placeholder `...?:`
+
+```php
+$items = ['foo' => 1, 'bar' => true];
+$function->setBody('myfunc(...?:);', [$items]);
+
+// myfunc(foo: 1, bar: true);
+```
+
+Escape placeholder using slash `\?`
+
+```php
+$num = 3;
+$function = new Nette\PhpGenerator\GlobalFunction('foo');
+$function->addParameter('a');
+$function->addBody('return $a \? 10 : ?;', [$num]);
+echo $function;
+```
+
+Result:
+
+```php
+function foo($a)
+{
+ return $a ? 10 : 3;
+}
+```
+
+
+Printers and PSR Compliance
+---------------------------
+
+PHP code is generated by `Printer` objects. There is a `PsrPrinter` whose output conforms to PSR-2 and PSR-12 and uses spaces for indentation, and a `Printer` that uses tabs for indentation.
+
+```php
+$class = new Nette\PhpGenerator\ClassType('Demo');
+// ...
+
+$printer = new Nette\PhpGenerator\PsrPrinter;
+echo $printer->printClass($class); // 4 spaces indentation
+```
+
+Need to customize printer behavior? Create your own by inheriting the `Printer` class. You can reconfigure these variables:
+
+```php
+class MyPrinter extends Nette\PhpGenerator\Printer
+{
+ public int $wrapLength = 120;
+ public string $indentation = "\t";
+ public int $linesBetweenProperties = 0;
+ public int $linesBetweenMethods = 2;
+ public int $linesBetweenUseTypes = 0;
+ public bool $bracesOnNextLine = true;
+ public string $returnTypeColon = ': ';
+}
+```
+
+
+Types
+-----
+
+Each type or union/intersection type can be passed as a string, you can also use predefined constants for native types:
+
+```php
+use Nette\PhpGenerator\Type;
+
+$member->setType('array'); // or Type::ARRAY;
+$member->setType('array|string'); // or Type::union('array', 'string')
+$member->setType('Foo&Bar'); // or Type::intersection(Foo::class, Bar::class)
+$member->setType(null); // removes type
+```
+
+The same applies to the method `setReturnType()`.
+
+
+Literals
+--------
+
+With `Literal` you can pass arbitrary PHP code to, for example, default property or parameter values etc:
+
+```php
+use Nette\PhpGenerator\Literal;
+
+$class = new Nette\PhpGenerator\ClassType('Demo');
+
+$class->addProperty('foo', new Literal('Iterator::SELF_FIRST'));
+
+$class->addMethod('bar')
+ ->addParameter('id', new Literal('1 + 2'));
+
+echo $class;
+```
+
+Result:
+
+```php
+class Demo
+{
+ public $foo = Iterator::SELF_FIRST;
+
+ public function bar($id = 1 + 2)
+ {
+ }
+}
+```
+
+You can also pass parameters to `Literal` and have it formatted into valid PHP code using [special placeholders|#method-and-function-body-generator]:
+
+```php
+new Literal('substr(?, ?)', [$a, $b]);
+// generates, for example: substr('hello', 5);
+```
+
+
+Attributes .{data-version:v3.5}
+-------------------------------
+
+You can add PHP 8 attributes to all classes, methods, properties, constants, enum cases, functions, closures and parameters. [#Literals] can also be used as parameter values.
+
+```php
+$class = new Nette\PhpGenerator\ClassType('Demo');
+$class->addAttribute('Deprecated');
+
+$class->addProperty('list')
+ ->addAttribute('WithArguments', [1, 2]);
+
+$method = $class->addMethod('count')
+ ->addAttribute('Foo\Cached', ['mode' => true]);
+
+$method->addParameter('items')
+ ->addAttribute('Bar');
+
+echo $class;
+```
+
+Result:
+
+```php
+#[Deprecated]
+class Demo
+{
+ #[WithArguments(1, 2)]
+ public $list;
+
+
+ #[Foo\Cached(mode: true)]
+ public function count(#[Bar] $items)
+ {
+ }
+}
+```
+
+
+Namespace
+---------
+
+Classes, traits, interfaces and enums (hereinafter classes) can be grouped into namespaces ([PhpNamespace |api:Nette\PhpGenerator\PhpNamespace]):
+
+```php
+$namespace = new Nette\PhpGenerator\PhpNamespace('Foo');
+
+// create new classes in the namespace
+$class = $namespace->addClass('Task');
+$interface = $namespace->addInterface('Countable');
+$trait = $namespace->addTrait('NameAware');
+
+// or insert an existing class into the namespace
+$class = new Nette\PhpGenerator\ClassType('Task');
+$namespace->add($class);
+```
+
+If the class already exists, it throws exception.
+
+You can define use-statements:
+
+```php
+// use Http\Request;
+$namespace->addUse(Http\Request::class);
+// use Http\Request as HttpReq;
+$namespace->addUse(Http\Request::class, 'HttpReq');
+// use function iter\range;
+$namespace->addUseFunction('iter\range');
+```
+
+To simplify a fully qualified class, function or constant name according to the defined aliases, use the `simplifyName` method:
+
+```php
+echo $namespace->simplifyName('Foo\Bar'); // 'Bar', because 'Foo' is current namespace
+echo $namespace->simplifyName('iter\range', $namespace::NAME_FUNCTION); // 'range', because of the defined use-statement
+```
+
+Conversely, you can convert a simplified class, function or constant name to a fully qualified one using the `resolveName` method:
+
+```php
+echo $namespace->resolveName('Bar'); // 'Foo\Bar'
+echo $namespace->resolveName('range', $namespace::NAME_FUNCTION); // 'iter\range'
+```
+
+
+Class Names Resolving
+---------------------
+
+**When the class is part of the namespace, it is rendered slightly differently**: all types (ie. type hints, return types, parent class name,
+implemented interfaces, used traits and attributes) are automatically *resolved* (unless you turn it off, see below).
+It means that you have to **use full class names** in definitions and they will be replaced
+with aliases (according to the use-statements) or fully qualified names in the resulting code:
+
+```php
+$namespace = new Nette\PhpGenerator\PhpNamespace('Foo');
+$namespace->addUse('Bar\AliasedClass');
+
+$class = $namespace->addClass('Demo');
+$class->addImplement('Foo\A') // it will simplify to A
+ ->addTrait('Bar\AliasedClass'); // it will simplify to AliasedClass
+
+$method = $class->addMethod('method');
+$method->addComment('@return ' . $namespace->simplifyName('Foo\D')); // in comments simplify manually
+$method->addParameter('arg')
+ ->setType('Bar\OtherClass'); // it will resolve to \Bar\OtherClass
+
+echo $namespace;
+
+// or use PsrPrinter for output conforming to PSR-2 / PSR-12
+// echo (new Nette\PhpGenerator\PsrPrinter)->printNamespace($namespace);
+```
+
+Result:
+
+```php
+namespace Foo;
+
+use Bar\AliasedClass;
+
+class Demo implements A
+{
+ use AliasedClass;
+
+ /**
+ * @return D
+ */
+ public function method(\Bar\OtherClass $arg)
+ {
+ }
+}
+```
+
+Auto-resolving can be turned off this way:
+
+```php
+$printer = new Nette\PhpGenerator\Printer; // or PsrPrinter
+$printer->setTypeResolving(false);
+echo $printer->printNamespace($namespace);
+```
+
+
+PHP Files
+---------
+
+Classes, functions and namespaces can be grouped into PHP files represented by the class [PhpFile|api:Nette\PhpGenerator\PhpFile]:
+
+```php
+$file = new Nette\PhpGenerator\PhpFile;
+$file->addComment('This file is auto-generated.');
+$file->setStrictTypes(); // adds declare(strict_types=1)
+
+$class = $file->addClass('Foo\A');
+$function = $file->addFunction('Foo\foo');
+
+// or
+// $namespace = $file->addNamespace('Foo');
+// $class = $namespace->addClass('A');
+// $function = $namespace->addFunction('foo');
+
+echo $file;
+
+// or use PsrPrinter for output conforming to PSR-2 / PSR-12
+// echo (new Nette\PhpGenerator\PsrPrinter)->printFile($file);
+```
+
+Result:
+
+```php
+dump($var); // prints ['a', 'b', 123]
+```
+
+
+Compatibility Table
+-------------------
+
+PhpGenerator 4.0 is compatible with PHP 8.0 to 8.2
+PhpGenerator 3.6 is compatible with PHP 7.2 to 8.2
+PhpGenerator 3.2 – 3.5 is compatible with PHP 7.1 to 8.0
+PhpGenerator 3.1 is compatible with PHP 7.1 to 7.3
+PhpGenerator 3.0 is compatible with PHP 7.0 to 7.3
+PhpGenerator 2.6 is compatible with PHP 5.6 to 7.3
+
+{{composer: nette/php-generator}}
+{{leftbar: /utils/@left-menu}}
diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon
index dd03b4fa..c826bc8e 100644
--- a/phpstan-baseline.neon
+++ b/phpstan-baseline.neon
@@ -1,10 +1,5 @@
parameters:
ignoreErrors:
- -
- message: '#^Match expression does not handle remaining value\: true$#'
- count: 1
- path: src/PhpGenerator/ClassLike.php
-
-
message: '#^Method Nette\\PhpGenerator\\ClassType\:\:addTrait\(\) has parameter \$deprecatedParam with no value type specified in iterable type array\.$#'
count: 1
@@ -15,16 +10,6 @@ parameters:
count: 1
path: src/PhpGenerator/EnumType.php
- -
- message: '#^Access to an undefined property PhpParser\\Node\:\:\$attrGroups\.$#'
- count: 1
- path: src/PhpGenerator/Extractor.php
-
- -
- message: '#^Access to an undefined property PhpParser\\Node\\Expr\:\:\$value\.$#'
- count: 1
- path: src/PhpGenerator/Extractor.php
-
-
message: '#^Call to an undefined method Nette\\PhpGenerator\\ClassLike\:\:addConstant\(\)\.$#'
count: 1
@@ -45,13 +30,18 @@ parameters:
count: 1
path: src/PhpGenerator/Extractor.php
+ -
+ message: '#^Call to an undefined method Nette\\PhpGenerator\\FunctionLike\:\:addPromotedParameter\(\)\.$#'
+ count: 1
+ path: src/PhpGenerator/Extractor.php
+
-
message: '#^Method Nette\\PhpGenerator\\Extractor\:\:addCommentAndAttributes\(\) has parameter \$element with no type specified\.$#'
count: 1
path: src/PhpGenerator/Extractor.php
-
- message: '#^Property class@anonymous/PhpGenerator/Extractor\.php\:176\:\:\$callback has no type specified\.$#'
+ message: '#^Property class@anonymous/PhpGenerator/Extractor\.php\:175\:\:\$callback has no type specified\.$#'
count: 1
path: src/PhpGenerator/Extractor.php
@@ -65,6 +55,11 @@ parameters:
count: 1
path: src/PhpGenerator/Factory.php
+ -
+ message: '#^Call to an undefined method ReflectionClass\