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) {}
);