Nette Documentation Preview

Generátor PHP kódu

Potřebujete vygenerovat PHP kód tříd, funkcí, jmenných prostorů atd? Pomůže vám tato knihovna s přívětivým API.

Instalace:

composer require nette/php-generator

PHP Generator podporuje všechny nové vlastnosti PHP 7.4 a 8.0.

Třídy

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

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

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

/**
 * 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 = new Nette\PhpGenerator\Printer;
echo $printer->printClass($class);

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

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

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

$class->addProperty('list')
	->setType('array') // nebo setType(Type::ARRAY)
	->setNullable()
	->setInitialized(); // pro PHP 7.4

Vygeneruje:

private const ID = 123;

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

public ?array $list = null;

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

$method = $class->addMethod('count')
	->addComment('Count it.')
	->addComment('@return int')
	->setFinal()
	->setProtected()
	->setReturnType('int') // návratové typy u metod
	->setReturnNullable() // nullable typy
	->setBody('return count($items ?: $this->items);');

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

Výsledkem je:

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

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

Č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:

$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():

$methodCount = $class->getMethod('count');
$methodRecount = $methodCount->cloneWithName('recount');
$class->addMember($methodRecount);

Tabulátory versus mezery

Vygenerovaný kód používá tabulátory pro odsazení. Pokud chcete mít výstup kompatibilní s PSR-2 nebo PSR-12, použijte PsrPrinter:

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

$printer = new Nette\PhpGenerator\PsrPrinter;
echo $printer->printClass($class); // odsazení 4 mezerami

Interface nebo traita

Podobným způsobem lze vytvářet rozhraní a traity, jen změníme typ:

$class = new Nette\PhpGenerator\ClassType('DemoInterface');
$class->setInterface();
// or $class->setTrait();

Literály

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

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:

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

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

Používání trait

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

Výsledek:

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

Anonymní třídy

Jako název předáme null a máme anonymní třídu:

$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 funkcí generuje třída GlobalFunction:

$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 kompatibilní s PSR-2 / PSR-12
// echo (new Nette\PhpGenerator\PsrPrinter)->printFunction($function);

Výsledek:

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

Anonymní funkce

Kód anonymních funkcí 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;

// nebo použijte PsrPrinter pro výstup kompatibilní s PSR-2 / PSR-12
// echo (new Nette\PhpGenerator\PsrPrinter)->printClosure($closure);

Výsledek:

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

Zkrácené arrow funkce

Můžete také vypsat zkrácenou anonymní funkci pomocí printeru:

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

// nebo použijte PsrPrinter pro výstup kompatibilní s PSR-2 / PSR-12
echo (new Nette\PhpGenerator\Printer)->printArrowFunction($closure);

Výsledek:

fn ($a, $b) => $a + $b

Generování těl metod a funkcí

Tělo lze předat najednou metodě setBody() nebo postupně (po řádcích) opakovaným voláním addBody():

$function = new Nette\PhpGenerator\GlobalFunction('foo');
$function->addBody('$a = rand(10, 20);');
$function->addBody('return $a;');
echo $function;

Výsledek

function foo()
{
	$a = rand(10, 20);
	return $a;
}

Můžete použít speciální zástupné symboly pro snadné vkládání proměnných.

Jednoduché zástupné symboly:

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

Výsledek

function foo()
{
	return strlen('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');

// 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, bude přepsána.

Můžete definovat klauzule use:

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

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í a použité vlastnosti) 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:

$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')
	->setType('Bar\OtherClass'); // bude přeložen na \Bar\OtherClass

echo $namespace;

// nebo použijte PsrPrinter pro výstup kompatibilní s PSR-2 / PSR-12
// echo (new Nette\PhpGenerator\PsrPrinter)->printNamespace($namespace);

Výsledek:

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:

$printer = new Nette\PhpGenerator\Printer; // nebo PsrPrinter
$printer->setTypeResolving(false);
echo $printer->printNamespace($namespace);

PHP soubory

Třídy a jmenné prostory lze seskupit do PHP souborů reprezentovaných třídou PhpFile:

$file = new Nette\PhpGenerator\PhpFile;
$file->addComment('This file is auto-generated.');
$file->setStrictTypes(); // přidá declare(strict_types=1)

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

echo $file;

// nebo použijte PsrPrinter pro výstup kompatibilní s PSR-2 / PSR-12
// echo (new Nette\PhpGenerator\PsrPrinter)->printFile($file);

Výsledek:

<?php

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

declare(strict_types=1);

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

Těla metod jsou ve výchozím stavu prázdná. Pokud je chcete také načíst, použijte tento způsob (vyžaduje instalaci balíčku nikic/php-parser a PhpGenerator verze 3.4):

$class = Nette\PhpGenerator\ClassType::withBodiesFrom(MyClass::class);

$function = Nette\PhpGenerator\GlobalFunction::withBodyFrom('dump');

Výpis proměnných

Třída Dumper převede proměnnou do parsovatelného PHP kódu. Poskytuje lepší a přehlednější výstup než standardní funkce var_export().

$dumper = new Nette\PhpGenerator\Dumper;

$var = ['a', 'b', 123];

echo $dumper->dump($var); // vypíše ['a', 'b', 123]

Vlastní printer

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é:

class MyPrinter extends Nette\PhpGenerator\Printer
{
	protected $indentation = "\t";
	protected $linesBetweenProperties = 0;
	protected $linesBetweenMethods = 1;
	protected $returnTypeColon = ': ';
}