Skip to content

Latest commit

 

History

History
773 lines (553 loc) · 16.7 KB

@home.texy

File metadata and controls

773 lines (553 loc) · 16.7 KB
Generátor PHP kódu ****************** .[perex] 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. PHP Generator podporuje všechny nové vlastnosti PHP 8.1. Instalace --------- Knihovnu stáhnete a nainstalujete pomocí nástroje [Composer|/best-practices/composer]: ```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::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: ```php $printer = new Nette\PhpGenerator\Printer; echo $printer->printClass($class); ``` Můžeme přidat konstanty ([Constant |api:Nette\PhpGenerator\Constant]) a proměnné ([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') ->setNullable() ->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 ([Method |api:Nette\PhpGenerator\Method]) s parametry ([Parameter |api:Nette\PhpGenerator\Parameter]): ```php $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: ```php /** * Count it. * @return int */ 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); ``` 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()`. 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`: ```php $class = new Nette\PhpGenerator\ClassType('Demo'); // ... $printer = new Nette\PhpGenerator\PsrPrinter; echo $printer->printClass($class); // odsazení 4 mezerami ``` Interface nebo traita --------------------- Lze vytvářet rozhraní a traity: ```php $interface = Nette\PhpGenerator\InterfaceType('MyInterface'); $trait = Nette\PhpGenerator\TraitType('MyTrait'); ``` Enums .{data-version:v3.6} -------------------------- Výčty, které přináší PHP 8.1, můžete snadno vytvořit takto: ```php $enum = 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()`. 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); ``` 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; } } ``` 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 kompatibilní 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 kompatibilní 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'); // nebo použijte PsrPrinter pro výstup kompatibilní s PSR-2 / PSR-12 echo (new Nette\PhpGenerator\Printer)->printArrowFunction($closure); ``` Výsledek: ```php fn($a, $b) => $a + $b ``` 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) { } } ``` 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()`: ```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; } ``` 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 kompatibilní 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 kompatibilní s PSR-2 / PSR-12 // echo (new Nette\PhpGenerator\PsrPrinter)->printFile($file); ``` Výsledek: ```php 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é: ```php class MyPrinter extends Nette\PhpGenerator\Printer { public int $wrapLength = 120; public string $indentation = "\t"; public int $linesBetweenProperties = 0; public int $linesBetweenMethods = 2; public string $returnTypeColon = ': '; } ``` {{composer: nette/php-generator}} {{leftbar: /@menu-topics}}