Nette Documentation Preview

syntax
Generátor PHP kódu
******************

.[perex]
Generujte PHP kód, třídy, jmenné prostory apod. pomocí jednoduchého API.

Instalace:

```shell
composer require nette/php-generator
```


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')
	->addImplement('Countable')
	->addTrait('Nette\SmartObject')
	->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;
}
```

Můžeme přidat konstanty ([Constant |api:Nette\PhpGenerator\Constant]) a proměnné ([Property |api:Nette\PhpGenerator\Property]):

```php
$class->addConstant('ID', 123);

$class->addProperty('items', [1, 2, 3])
	->setVisibility('private')
	->setStatic()
	->addComment('@var int[]');
```

Vygeneruje:

```php
const ID = 123;

/** @var int[] */
private static $items = [1, 2, 3];
```

A můžeme přidat metody ([Method |api:Nette\PhpGenerator\Method]) s parametry ([Parameter |api:Nette\PhpGenerator\Parameter]):

```php
$method = $class->addMethod('count')
	->addComment('Count it.')
	->addComment('@return int')
	->setFinal()
	->setVisibility('protected')
	->setBody('return count($items ?: $this->items);');

$method->addParameter('items', []) // $items = []
		->setReference() // &$items = []
		->setTypeHint('array'); // array &$items = []
```

Výsledkem je:

```php
/**
 * Count it.
 * @return int
 */
final protected function count(array &$items = [])
{
	return count($items ?: $this->items);
}
```

Pokud vlastnost, konstanta, metoda nebo parametr již existuje, bude přepsán.

PHP Generator podporuje všechny nové vlastnosti PHP 7.3:

```php
$class = new Nette\PhpGenerator\ClassType('Demo');

$class->addConstant('ID', 123)
	->setVisibility('private'); // viditelnost konstant

$method = $class->addMethod('getValue')
	->setReturnType('int') // návratové typy u metod
	->setReturnNullable() // nullable typy
	->setBody('return count($this->items);');

$method->addParameter('id')
		->setTypeHint('int') // scalar type hint
		->setNullable(); // nullable type hint

echo $class;
```

Výsledek:

```php
class Demo
{
	private const ID = 123;

	public function getValue(?int $id): ?int
	{
		return count($this->items);
	}
}
```


Tabulátory versus mezery
------------------------

Vygenerovaný kód používá pro odsazování tabulátory, díky čemuž je lze snadno změnit na libovolný počet mezer:

```php
use Nette\PhpGenerator\Helpers;

$class = new Nette\PhpGenerator\ClassType('Demo');
// ...

echo Helpers::tabsToSpaces((string) $class); // odsazení 4 mezerami
echo Helpers::tabsToSpaces((string) $class, 2); // odsazení 2 mezerami
```


Literály
--------

Jako výchozí hodnoty vlastností nebo parametrů lze předat jakýkolv PHP kód přes `PhpLiteral`:

```php
use Nette\PhpGenerator\PhpLiteral;

$class = new Nette\PhpGenerator\ClassType('Demo');

$class->addProperty('foo', new PhpLiteral('Iterator::SELF_FIRST'));

$class->addMethod('bar')
	->addParameter('id', new PhpLiteral('1 + 2'));

echo $class;
```

Výsledek:

```php
class Demo
{
	public $foo = Iterator::SELF_FIRST;

	public function bar($id = 1 + 2)
	{
	}
}
```


Interface nebo traita
---------------------

```php
$class = new Nette\PhpGenerator\ClassType('DemoInterface');
$class->setType('interface');
// nebo $class->setType('trait');
```


Řešení trait a viditelnost
--------------------------

```php
$class = new Nette\PhpGenerator\ClassType('Demo');
$class->addTrait('SmartObject', ['sayHello as protected']);
echo $class;
```

Výsledek:

```php
class Demo
{
	use SmartObject {
		sayHello as protected;
	}
}
```


Anonymní třídy
--------------

```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 funkce 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;
```

Výsledek:

```php
function foo($a, $b)
{
	return $a + $b;
}
```


Anonymní funkce
---------------

Kód anonymní funkce 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;
```

Výsledek:

```php
function ($a, $b) use (&$c) {
	return $a + $b;
}
```


Generování kódu metod a funkcí
------------------------------

Můžete použít speciální zástupné symboly pro snadné vytvoření kódu metod nebo funkcí.

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ý symbol 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);
}
```

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;
}
```


Jmenný prostor
--------------

Třídy, vlastnosti a rozhraní (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');

$class = $namespace->addClass('Task');
$interface = $namespace->addInterface('Countable');
$trait = $namespace->addTrait('NameAware');
```

Pokud třída již existuje, bude přepsána.

Můžete definovat klauzule use:

```php
$namespace->addUse('Http\Request'); // use Http\Request;
$namespace->addUse('Http\Request', 'HttpReq'); // use Http\Request as HttpReq;
```

**DŮLEŽITÁ POZNÁMKA:** když je třída součástí jmenného prostoru, je vykreslena mírně odlišně: všechny typy (např. typehinty, návratové typy, název rodičovské třídy,
implementovaná rozhraní a použité traity) *automaticky překládá*. To znamená, že **musíte používat v definice plné názvy tříd**
a ty budou nahrazeny aliasy (podle klauzulí use) nebo plně kvalifikovanými jmény 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 přeložen na A
	->addTrait('Bar\AliasedClass'); // bude přeložen na AliasedClass

$method = $class->addMethod('method');
$method->addComment('@return ' . $namespace->unresolveName('Foo\D')); // v komentářích překládáme manuálně
$method->addParameter('arg')
	->setTypeHint('Bar\OtherClass'); // bude přeložen na \Bar\OtherClass

echo $namespace;
```

Výsledek:

```php
namespace Foo;

use Bar\AliasedClass;

class Demo implements A
{
	use AliasedClass;

	/**
	 * @return D
	 */
	public function method(\Bar\OtherClass $arg)
	{
	}
}
```


PHP soubory
-----------

Soubory PHP mohou obsahovat více tříd, jmenných prostorů a komentářů:

```php
$file = new Nette\PhpGenerator\PhpFile;
$file->addComment('This file is auto-generated.');

$namespace = $file->addNamespace('Foo');
$class = $namespace->addClass('A');
$class->addMethod('hello');

echo $file;
```

Výsledek:

```php
<?php

/**
 * This file is auto-generated.
 */

namespace Foo;

class A
{
	public function hello()
	{
	}
}
```


Generování podle vzorů
----------------------

Dalším častým případem použití je vytvoření třídy či funkce podle již existujících:

```php
$class = Nette\PhpGenerator\ClassType::from(PDO::class);

$function = Nette\PhpGenerator\GlobalFunction::from('trim');

$closure = Nette\PhpGenerator\Closure::from(
	function (stdClass $a, $b = null) {}
);
```
{{leftbar: nette:@menu-topics}}

Generátor PHP kódu

Generujte PHP kód, třídy, jmenné prostory apod. pomocí jednoduchého API.

Instalace:

composer require nette/php-generator

Třídy

Začněme rovnou příkladem tvorby třídy pomocí ClassType:

$class = new Nette\PhpGenerator\ClassType('Demo');

$class
	->setFinal()
	->setExtends('ParentClass')
	->addImplement('Countable')
	->addTrait('Nette\SmartObject')
	->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:

/**
 * Popis třídy
 * Druhý řádek
 *
 * @property-read Nette\Forms\Form $form
 */
final class Demo extends ParentClass implements Countable
{
	use Nette\SmartObject;
}

Můžeme přidat konstanty (Constant) a proměnné (Property):

$class->addConstant('ID', 123);

$class->addProperty('items', [1, 2, 3])
	->setVisibility('private')
	->setStatic()
	->addComment('@var int[]');

Vygeneruje:

const ID = 123;

/** @var int[] */
private static $items = [1, 2, 3];

A můžeme přidat metody (Method) s parametry (Parameter):

$method = $class->addMethod('count')
	->addComment('Count it.')
	->addComment('@return int')
	->setFinal()
	->setVisibility('protected')
	->setBody('return count($items ?: $this->items);');

$method->addParameter('items', []) // $items = []
		->setReference() // &$items = []
		->setTypeHint('array'); // array &$items = []

Výsledkem je:

/**
 * Count it.
 * @return int
 */
final protected function count(array &$items = [])
{
	return count($items ?: $this->items);
}

Pokud vlastnost, konstanta, metoda nebo parametr již existuje, bude přepsán.

PHP Generator podporuje všechny nové vlastnosti PHP 7.3:

$class = new Nette\PhpGenerator\ClassType('Demo');

$class->addConstant('ID', 123)
	->setVisibility('private'); // viditelnost konstant

$method = $class->addMethod('getValue')
	->setReturnType('int') // návratové typy u metod
	->setReturnNullable() // nullable typy
	->setBody('return count($this->items);');

$method->addParameter('id')
		->setTypeHint('int') // scalar type hint
		->setNullable(); // nullable type hint

echo $class;

Výsledek:

class Demo
{
	private const ID = 123;

	public function getValue(?int $id): ?int
	{
		return count($this->items);
	}
}

Tabulátory versus mezery

Vygenerovaný kód používá pro odsazování tabulátory, díky čemuž je lze snadno změnit na libovolný počet mezer:

use Nette\PhpGenerator\Helpers;

$class = new Nette\PhpGenerator\ClassType('Demo');
// ...

echo Helpers::tabsToSpaces((string) $class); // odsazení 4 mezerami
echo Helpers::tabsToSpaces((string) $class, 2); // odsazení 2 mezerami

Literály

Jako výchozí hodnoty vlastností nebo parametrů lze předat jakýkolv PHP kód přes PhpLiteral:

use Nette\PhpGenerator\PhpLiteral;

$class = new Nette\PhpGenerator\ClassType('Demo');

$class->addProperty('foo', new PhpLiteral('Iterator::SELF_FIRST'));

$class->addMethod('bar')
	->addParameter('id', new PhpLiteral('1 + 2'));

echo $class;

Výsledek:

class Demo
{
	public $foo = Iterator::SELF_FIRST;

	public function bar($id = 1 + 2)
	{
	}
}

Interface nebo traita

$class = new Nette\PhpGenerator\ClassType('DemoInterface');
$class->setType('interface');
// nebo $class->setType('trait');

Řešení trait a viditelnost

$class = new Nette\PhpGenerator\ClassType('Demo');
$class->addTrait('SmartObject', ['sayHello as protected']);
echo $class;

Výsledek:

class Demo
{
	use SmartObject {
		sayHello as protected;
	}
}

Anonymní třídy

$class = new Nette\PhpGenerator\ClassType(null);
$class->addMethod('__construct')
	->addParameter('foo');

echo '$obj = new class ($val) ' . $class . ';';

Výsledek:

$obj = new class ($val) {

	public function __construct($foo)
	{
	}
};

Globální funkce

Kód funkce generuje třída GlobalFunction:

$function = new Nette\PhpGenerator\GlobalFunction('foo');
$function->setBody('return $a + $b;');
$function->addParameter('a');
$function->addParameter('b');
echo $function;

Výsledek:

function foo($a, $b)
{
	return $a + $b;
}

Anonymní funkce

Kód anonymní funkce generuje třída Closure:

$closure = new Nette\PhpGenerator\Closure;
$closure->setBody('return $a + $b;');
$closure->addParameter('a');
$closure->addParameter('b');
$closure->addUse('c')
	->setReference();
echo $closure;

Výsledek:

function ($a, $b) use (&$c) {
	return $a + $b;
}

Generování kódu metod a funkcí

Můžete použít speciální zástupné symboly pro snadné vytvoření kódu metod nebo funkcí.

Jednoduché zástupné symboly:

$str = 'any string';
$num = 3;
$function = new Nette\PhpGenerator\GlobalFunction('foo');
$function->addBody('return substr(?, ?);', [$str, $num]);
echo $function;

Výsledek

function foo()
{
	return substr('any string', 3);
}

Zástupný symbol variadic:

$items = [1, 2, 3];
$function = new Nette\PhpGenerator\GlobalFunction('foo');
$function->setBody('myfunc(...?);', [$items]);
echo $function;

Výsledek:

function foo()
{
	myfunc(1, 2, 3);
}

Zástupný symbol se escapuje pomocí lomítka:

$num = 3;
$function = new Nette\PhpGenerator\GlobalFunction('foo');
$function->addParameter('a');
$function->addBody('return $a \? 10 : ?;', [$num]);
echo $function;

Výsledek:

function foo($a)
{
	return $a ? 10 : 3;
}

Jmenný prostor

Třídy, vlastnosti a rozhraní (dále jen třídy) lze seskupit do jmenných prostorů reprezentovaných třídou (PhpNamespace):

$namespace = new Nette\PhpGenerator\PhpNamespace('Foo');

$class = $namespace->addClass('Task');
$interface = $namespace->addInterface('Countable');
$trait = $namespace->addTrait('NameAware');

Pokud třída již existuje, bude přepsána.

Můžete definovat klauzule use:

$namespace->addUse('Http\Request'); // use Http\Request;
$namespace->addUse('Http\Request', 'HttpReq'); // use Http\Request as HttpReq;

DŮLEŽITÁ POZNÁMKA: když je třída součástí jmenného prostoru, je vykreslena mírně odlišně: všechny typy (např. typehinty, návratové typy, název rodičovské třídy, implementovaná rozhraní a použité traity) automaticky překládá. To znamená, že musíte používat v definice plné názvy tříd a ty budou nahrazeny aliasy (podle klauzulí use) nebo plně kvalifikovanými jmény ve výsledném kódu:

$namespace = new Nette\PhpGenerator\PhpNamespace('Foo');
$namespace->addUse('Bar\AliasedClass');

$class = $namespace->addClass('Demo');
$class->addImplement('Foo\A') // bude přeložen na A
	->addTrait('Bar\AliasedClass'); // bude přeložen na AliasedClass

$method = $class->addMethod('method');
$method->addComment('@return ' . $namespace->unresolveName('Foo\D')); // v komentářích překládáme manuálně
$method->addParameter('arg')
	->setTypeHint('Bar\OtherClass'); // bude přeložen na \Bar\OtherClass

echo $namespace;

Výsledek:

namespace Foo;

use Bar\AliasedClass;

class Demo implements A
{
	use AliasedClass;

	/**
	 * @return D
	 */
	public function method(\Bar\OtherClass $arg)
	{
	}
}

PHP soubory

Soubory PHP mohou obsahovat více tříd, jmenných prostorů a komentářů:

$file = new Nette\PhpGenerator\PhpFile;
$file->addComment('This file is auto-generated.');

$namespace = $file->addNamespace('Foo');
$class = $namespace->addClass('A');
$class->addMethod('hello');

echo $file;

Výsledek:

<?php

/**
 * This file is auto-generated.
 */

namespace Foo;

class A
{
	public function hello()
	{
	}
}

Generování podle vzorů

Dalším častým případem použití je vytvoření třídy či funkce podle již existujících:

$class = Nette\PhpGenerator\ClassType::from(PDO::class);

$function = Nette\PhpGenerator\GlobalFunction::from('trim');

$closure = Nette\PhpGenerator\Closure::from(
	function (stdClass $a, $b = null) {}
);