Nette PhpGenerator

Eszközt keres osztályok, függvények vagy teljes PHP fájlok kódjának generálásához?
  • Ismeri az összes legújabb PHP funkciót (mint a property hookok, enumok, attribútumok stb.)
  • Lehetővé teszi a meglévő osztályok egyszerű módosítását
  • A kimeneti kód megfelel a PSR-12 / PER kódolási stílusnak
  • Érett, stabil és széles körben használt könyvtár

Telepítés

A könyvtárat a Composer segítségével töltheti le és telepítheti:

composer require nette/php-generator 

A PHP kompatibilitást a kompatibilitási táblázatban találja.

Osztályok

Kezdjük rögtön egy példával egy osztály létrehozására a ClassType segítségével:

$class = new Nette\PhpGenerator\ClassType('Demo'); $class	->setFinal()	->setExtends(ParentClass::class)	->addImplement(Countable::class)	->addComment("Osztály leírása.\nMásodik sor\n")	->addComment('@property-read Nette\Forms\Form $form'); // a kódot egyszerűen generálhatja stringgé alakítással vagy az echo használatával: echo $class; 

A következő eredményt adja vissza:

/** * Osztály leírása * Második sor * * @property-read Nette\Forms\Form $form */ final class Demo extends ParentClass implements Countable { } 

A kód generálásához használhatunk egy ún. printert is, amelyet az echo $class-szal ellentétben tovább konfigurálni tudunk:

$printer = new Nette\PhpGenerator\Printer; echo $printer->printClass($class); 

Hozzáadhatunk konstansokat (Constant osztály) és propertyket (Property osztály):

$class->addConstant('ID', 123)	->setProtected() // konstansok láthatósága	->setType('int')	->setFinal(); $class->addProperty('items', [1, 2, 3])	->setPrivate() // vagy setVisibility('private')	->setStatic()	->addComment('@var int[]'); $class->addProperty('list')	->setType('?array')	->setInitialized(); // kiírja '= null' 

Generálja:

final protected const int ID = 123; /** @var int[] */ private static $items = [1, 2, 3]; public ?array $list = null; 

És hozzáadhatunk metódusokat:

$method = $class->addMethod('count')	->addComment('Számold meg.')	->setFinal()	->setProtected()	->setReturnType('?int') // visszatérési típusok metódusoknál	->setBody('return count($items ?: $this->items);'); $method->addParameter('items', []) // $items = []	->setReference() // &$items = []	->setType('array'); // array &$items = [] 

Az eredmény:

/** * Számold meg. */ final protected function count(array &$items = []): ?int {	return count($items ?: $this->items); } 

A PHP 8.0 által bevezetett promoted paramétereket átadhatjuk a konstruktornak:

$method = $class->addMethod('__construct'); $method->addPromotedParameter('name'); $method->addPromotedParameter('args', [])	->setPrivate(); 

Az eredmény:

public function __construct(	public $name,	private $args = [], ) { } 

A csak olvasható propertyket és osztályokat a setReadOnly() függvénnyel lehet megjelölni.


Ha a hozzáadott property, konstans, metódus vagy paraméter már létezik, kivétel dobódik.

Az osztály tagjait eltávolíthatjuk a removeProperty(), removeConstant(), removeMethod() vagy removeParameter() segítségével.

Az osztályhoz hozzáadhatunk meglévő Method, Property vagy Constant objektumokat is:

$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); 

Klónozhatunk meglévő metódusokat, propertyket és konstansokat más néven a cloneWithName() segítségével:

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

Interfész vagy Trait

Létrehozhat interfészeket és traitteket (InterfaceType és TraitType osztályok):

$interface = new Nette\PhpGenerator\InterfaceType('MyInterface'); $trait = new Nette\PhpGenerator\TraitType('MyTrait'); 

Trait használata:

$class = new Nette\PhpGenerator\ClassType('Demo'); $class->addTrait('SmartObject'); $class->addTrait('MyTrait')	->addResolution('sayHello as protected')	->addComment('@use MyTrait<Foo>'); echo $class; 

Eredmény:

class Demo {	use SmartObject;	/** @use MyTrait<Foo> */	use MyTrait {	sayHello as protected;	} } 

Enumok

A PHP 8.1 által bevezetett enumokat könnyen létrehozhatja így: (EnumType osztály):

$enum = new Nette\PhpGenerator\EnumType('Suit'); $enum->addCase('Clubs'); $enum->addCase('Diamonds'); $enum->addCase('Hearts'); $enum->addCase('Spades'); echo $enum; 

Eredmény:

enum Suit {	case Clubs;	case Diamonds;	case Hearts;	case Spades; } 

Definiálhat skaláris ekvivalenseket is, és létrehozhat egy “backed” enumot:

$enum->addCase('Clubs', '♣'); $enum->addCase('Diamonds', '♦'); 

Minden case-hez hozzáadhat kommentet vagy Attribútumok a addComment() vagy addAttribute() segítségével.

Névtelen osztályok

Névként null-t adunk át, és máris van egy névtelen osztályunk:

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

Eredmény:

$obj = new class ($val) {	public function __construct($foo)	{	} }; 

Globális függvények

A függvények kódját a GlobalFunction osztály generálja:

$function = new Nette\PhpGenerator\GlobalFunction('foo'); $function->setBody('return $a + $b;'); $function->addParameter('a'); $function->addParameter('b'); echo $function; // vagy használja a PsrPrintert a PSR-2 / PSR-12 / PER szerinti kimenethez // echo (new Nette\PhpGenerator\PsrPrinter)->printFunction($function); 

Eredmény:

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

Névtelen függvények

A névtelen függvények kódját a Closure osztály generálja:

$closure = new Nette\PhpGenerator\Closure; $closure->setBody('return $a + $b;'); $closure->addParameter('a'); $closure->addParameter('b'); $closure->addUse('c')	->setReference(); echo $closure; // vagy használja a PsrPrintert a PSR-2 / PSR-12 / PER szerinti kimenethez // echo (new Nette\PhpGenerator\PsrPrinter)->printClosure($closure); 

Eredmény:

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

Rövidített nyíl függvények

Kiírhat egy rövidített névtelen függvényt is a printer segítségével:

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

Eredmény:

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

Metódus és függvény szignatúrák

A metódusokat a Method osztály reprezentálja. Beállíthatja a láthatóságot, a visszatérési értéket, hozzáadhat kommenteket, attribútumokat stb.:

$method = $class->addMethod('count')	->addComment('Számold meg.')	->setFinal()	->setProtected()	->setReturnType('?int'); 

Az egyes paramétereket a Parameter osztály reprezentálja. Ismét beállíthat minden elképzelhető tulajdonságot:

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

Az ún. variadics paraméterek (vagy splat operátor) definiálására a setVariadic() szolgál:

$method = $class->addMethod('count'); $method->setVariadic(true); $method->addParameter('items'); 

Generálja:

function count(...$items) { } 

Metódus és függvény törzsek

A törzset átadhatjuk egyszerre a setBody() metódusnak, vagy fokozatosan (soronként) az addBody() ismételt hívásával:

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

Eredmény

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

Speciális helyettesítő karaktereket használhat a változók egyszerű beillesztéséhez.

Egyszerű helyettesítő szimbólumok ?

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

Eredmény

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

Helyettesítő karakter variadic-hoz ...?

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

Eredmény:

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

Használhat névvel ellátott paramétereket is a PHP 8-hoz a ...?: segítségével

$items = ['foo' => 1, 'bar' => true]; $function->setBody('myfunc(...?:);', [$items]); // myfunc(foo: 1, bar: true); 

A helyettesítő szimbólumot a \ karakterrel lehet escape-elni \?

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

Eredmény:

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

Printer és PSR megfelelőség

A PHP kód generálására a Printer osztály szolgál:

$class = new Nette\PhpGenerator\ClassType('Demo'); // ... $printer = new Nette\PhpGenerator\Printer; echo $printer->printClass($class); // ugyanaz, mint: echo $class 

Képes generálni az összes többi elem kódját, kínál metódusokat, mint a printFunction(), printNamespace(), stb.

Rendelkezésre áll a PsrPrinter osztály is, amelynek kimenete megfelel a PSR-2 / PSR-12 / PER kódolási stílusnak:

$printer = new Nette\PhpGenerator\PsrPrinter; echo $printer->printClass($class); 

Szeretné testre szabni a viselkedést? Hozzon létre saját verziót a Printer osztály öröklésével. Ezeket a változókat lehet újrakonfigurálni:

class MyPrinter extends Nette\PhpGenerator\Printer {	// sor hossza, amely után sortörés történik	public int $wrapLength = 120;	// behúzás karaktere, helyettesíthető szóközök sorozatával	public string $indentation = "\t";	// üres sorok száma a propertyk között	public int $linesBetweenProperties = 0;	// üres sorok száma a metódusok között	public int $linesBetweenMethods = 2;	// üres sorok száma az 'use statements' csoportok között osztályokhoz, függvényekhez és konstansokhoz	public int $linesBetweenUseTypes = 0;	// nyitó kapcsos zárójel pozíciója függvényeknél és metódusoknál	public bool $bracesOnNextLine = true;	// helyezzen egy paramétert egy sorba, még akkor is, ha attribútuma van vagy promoted	public bool $singleParameterOnOneLine = false;	// kihagyja azokat a névtereket, amelyek nem tartalmaznak osztályt vagy függvényt	public bool $omitEmptyNamespaces = true;	// elválasztó a jobb zárójel és a függvények és metódusok visszatérési típusa között	public string $returnTypeColon = ': '; } 

Hogyan és miért különbözik valójában a standard Printer és a PsrPrinter? Miért nincs csak egy printer a csomagban, mégpedig a PsrPrinter?

A standard Printer úgy formázza a kódot, ahogyan azt az egész Nette-ben tesszük. Mivel a Nette sokkal korábban jött létre, mint a PSR, és mivel a PSR évekig nem szállított időben szabványokat, hanem például többéves késéssel az új PHP funkció bevezetése után, előfordult, hogy a kódolási szabvány néhány apróságban eltér. A nagyobb különbség csak a tabulátorok használata szóközök helyett. Tudjuk, hogy a tabulátorok használata projektjeinkben lehetővé teszi a szélesség testreszabását, ami látássérültek számára elengedhetetlen. Egy apró eltérés példája a kapcsos zárójel elhelyezése külön sorban a függvényeknél és metódusoknál, és ez mindig így van. A PSR ajánlása számunkra logikátlannak tűnik, és a kód olvashatóságának csökkenéséhez vezet.

Típusok

Minden típust vagy union/intersection típust átadhatunk stringként, használhatunk előre definiált konstansokat is a natív típusokhoz:

use Nette\PhpGenerator\Type; $member->setType('array'); // vagy Type::Array; $member->setType('?array'); // vagy Type::nullable(Type::Array); $member->setType('array|string'); // vagy Type::union(Type::Array, Type::String) $member->setType('Foo&Bar'); // vagy Type::intersection(Foo::class, Bar::class) $member->setType(null); // eltávolítja a típust 

Ugyanez vonatkozik a setReturnType() metódusra is.

Literálok

A Literal segítségével tetszőleges PHP kódot adhatunk át, például propertyk vagy paraméterek alapértelmezett értékeihez stb.:

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; 

Eredmény:

class Demo {	public $foo = Iterator::SELF_FIRST;	public function bar($id = 1 + 2)	{	} } 

Paramétereket is átadhat a Literal-nak, és hagyhatja, hogy érvényes PHP kóddá formázza őket helyettesítő karaktereket használva:

new Literal('substr(?, ?)', [$a, $b]); // generál például: substr('hello', 5); 

Egy új objektum létrehozását reprezentáló literált könnyen generálhatunk a new metódussal:

Literal::new(Demo::class, [$a, 'foo' => $b]); // generál például: new Demo(10, foo: 20) 

Attribútumok

A PHP 8 attribútumokat hozzáadhatja az összes osztályhoz, metódushoz, propertyhez, konstanshoz, enumhoz, függvényhez, closure-höz és paraméterhez. Paraméterértékként Literálok is használhatók.

$class = new Nette\PhpGenerator\ClassType('Demo'); $class->addAttribute('Table', [	'name' => 'user',	'constraints' => [	Literal::new('UniqueConstraint', ['name' => 'ean', 'columns' => ['ean']]),	], ]); $class->addProperty('list')	->addAttribute('Deprecated'); $method = $class->addMethod('count')	->addAttribute('Foo\Cached', ['mode' => true]); $method->addParameter('items')	->addAttribute('Bar'); echo $class; 

Eredmény:

#[Table(name: 'user', constraints: [new UniqueConstraint(name: 'ean', columns: ['ean'])])] class Demo {	#[Deprecated]	public $list;	#[Foo\Cached(mode: true)]	public function count(	#[Bar]	$items,	) {	} } 

Property Hookok

A property hookok segítségével (PropertyHook osztály által reprezentálva) definiálhat get és set műveleteket a propertykhez, ami a PHP 8.4-ben bevezetett funkció:

$class = new Nette\PhpGenerator\ClassType('Demo'); $prop = $class->addProperty('firstName') ->setType('string'); $prop->addHook('set', 'strtolower($value)') ->addParameter('value') ->setType('string'); $prop->addHook('get')	->setBody('return ucfirst($this->firstName);'); echo $class; 

Generálja:

class Demo { public string $firstName { set(string $value) => strtolower($value); get { return ucfirst($this->firstName); } } } 

A propertyk és property hookok lehetnek absztraktak vagy finálak:

$class->addProperty('id') ->setType('int') ->addHook('get') ->setAbstract(); $class->addProperty('role') ->setType('string') ->addHook('set', 'strtolower($value)') ->setFinal(); 

Aszimmetrikus láthatóság

A PHP 8.4 bevezeti az aszimmetrikus láthatóságot a propertykhez. Különböző hozzáférési szinteket állíthat be az olvasáshoz és íráshoz.

A láthatóságot beállíthatja vagy a setVisibility() metódussal két paraméterrel, vagy a setPublic(), setProtected() vagy setPrivate() metódusokkal a mode paraméterrel, amely meghatározza, hogy a láthatóság az olvasásra vagy az írásra vonatkozik-e. Az alapértelmezett mód 'get'.

$class = new Nette\PhpGenerator\ClassType('Demo'); $class->addProperty('name') ->setType('string') ->setVisibility('public', 'private'); // public olvasáshoz, private íráshoz $class->addProperty('id') ->setType('int') ->setProtected('set'); // protected íráshoz echo $class; 

Generálja:

class Demo { public private(set) string $name; protected(set) int $id; } 

Névtér

Az osztályokat, propertyket, interfészeket és enumokat (továbbiakban osztályok) csoportosíthatjuk névterekbe, amelyeket a PhpNamespace osztály reprezentál:

$namespace = new Nette\PhpGenerator\PhpNamespace('Foo'); // új osztályok létrehozása a névtérben $class = $namespace->addClass('Task'); $interface = $namespace->addInterface('Countable'); $trait = $namespace->addTrait('NameAware'); // vagy meglévő osztály beillesztése a névtérbe $class = new Nette\PhpGenerator\ClassType('Task'); $namespace->add($class); 

Ha az osztály már létezik, kivétel dobódik.

Definiálhat use klózokat:

// 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'); 

Ha egyszerűsíteni szeretné a teljesen minősített osztály-, függvény- vagy konstansnevet a definiált aliasok szerint, használja a simplifyName metódust:

echo $namespace->simplifyName('Foo\Bar'); // 'Bar', mert a 'Foo' az aktuális névtér echo $namespace->simplifyName('iter\range', $namespace::NameFunction); // 'range', a definiált use-statement miatt 

Az egyszerűsített osztály-, függvény- vagy konstansnevet fordítva átalakíthatja teljesen minősített névre a resolveName metódussal:

echo $namespace->resolveName('Bar'); // 'Foo\Bar' echo $namespace->resolveName('range', $namespace::NameFunction); // 'iter\range' 

Osztálynevek fordítása

Ha egy osztály egy névtér része, kissé eltérően jelenik meg: minden típus (például typehintek, visszatérési típusok, szülőosztály neve, implementált interfészek, használt propertyk és attribútumok) automatikusan lefordításra kerül (hacsak nem kapcsolja ki, lásd alább). Ez azt jelenti, hogy a definíciókban teljes osztályneveket kell használnia, és ezeket aliasokra (a use klózok szerint) vagy teljesen minősített nevekre cseréli a végső kódban:

$namespace = new Nette\PhpGenerator\PhpNamespace('Foo'); $namespace->addUse('Bar\AliasedClass'); $class = $namespace->addClass('Demo'); $class->addImplement('Foo\A') // A-ra lesz egyszerűsítve	->addTrait('Bar\AliasedClass'); // AliasedClass-ra lesz egyszerűsítve $method = $class->addMethod('method'); $method->addComment('@return ' . $namespace->simplifyType('Foo\D')); // kommentekben manuálisan egyszerűsítünk $method->addParameter('arg')	->setType('Bar\OtherClass'); // \Bar\OtherClass-ra lesz fordítva echo $namespace; // vagy használja a PsrPrintert a PSR-2 / PSR-12 / PER szerinti kimenethez // echo (new Nette\PhpGenerator\PsrPrinter)->printNamespace($namespace); 

Eredmény:

namespace Foo; use Bar\AliasedClass; class Demo implements A {	use AliasedClass;	/** * @return D */	public function method(\Bar\OtherClass $arg)	{	} } 

Az automatikus fordítást így lehet kikapcsolni:

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

PHP fájlok

Az osztályokat, függvényeket és névtereket PHP fájlokba csoportosíthatjuk, amelyeket a PhpFile osztály reprezentál:

$file = new Nette\PhpGenerator\PhpFile; $file->addComment('Ez a fájl automatikusan generált.'); $file->setStrictTypes(); // hozzáadja declare(strict_types=1) $class = $file->addClass('Foo\A'); $function = $file->addFunction('Foo\foo'); // vagy // $namespace = $file->addNamespace('Foo'); // $class = $namespace->addClass('A'); // $function = $namespace->addFunction('foo'); echo $file; // vagy használja a PsrPrintert a PSR-2 / PSR-12 / PER szerinti kimenethez // echo (new Nette\PhpGenerator\PsrPrinter)->printFile($file); 

Eredmény:

<?php /** * Ez a fájl automatikusan generált. */ declare(strict_types=1); namespace Foo; class A { } function foo() { } 

Figyelmeztetés: A fájlokhoz nem lehet további kódot hozzáadni a függvényeken és osztályokon kívül.

Generálás meglévők alapján

Amellett, hogy az osztályokat és függvényeket a fent leírt API segítségével modellezheti, automatikusan is generáltathatja őket meglévő minták alapján:

// létrehoz egy osztályt, amely megegyezik a PDO osztállyal $class = Nette\PhpGenerator\ClassType::from(PDO::class); // létrehoz egy függvényt, amely azonos a trim() függvénnyel $function = Nette\PhpGenerator\GlobalFunction::from('trim'); // létrehoz egy closure-t a megadott alapján $closure = Nette\PhpGenerator\Closure::from(	function (stdClass $a, $b = null) {}, ); 

A függvények és metódusok törzsei alapértelmezés szerint üresek. Ha ezeket is be szeretné tölteni, használja ezt a módszert (szükséges a nikic/php-parser csomag telepítése):

$class = Nette\PhpGenerator\ClassType::from(Foo::class, withBodies: true); $function = Nette\PhpGenerator\GlobalFunction::from('foo', withBody: true); 

Betöltés PHP fájlokból

Függvényeket, osztályokat, interfészeket és enumokat közvetlenül PHP kódot tartalmazó stringből is betölthet. Például így hozunk létre egy ClassType objektumot:

$class = Nette\PhpGenerator\ClassType::fromCode(<<<XX	<?php	class Demo	{	public $foo;	}	XX); 

Ha osztályokat PHP kódból tölt be, az egysoros kommentek a metódustörzseken kívül (pl. propertyknél stb.) figyelmen kívül maradnak, mivel ez a könyvtár nem rendelkezik API-val a kezelésükhöz.

Közvetlenül betölthet egy teljes PHP fájlt is, amely tetszőleges számú osztályt, függvényt vagy akár névteret tartalmazhat:

$file = Nette\PhpGenerator\PhpFile::fromCode(file_get_contents('classes.php')); 

Betöltődik a fájl bevezető kommentje és a strict_types deklaráció is. Ezzel szemben minden más globális kód figyelmen kívül marad.

Szükséges, hogy a nikic/php-parser telepítve legyen.

Ha globális kódot kell manipulálnia fájlokban vagy egyes utasításokat metódustörzsekben, jobb közvetlenül a nikic/php-parser könyvtárat használni.

Class Manipulator

ClassManipulator osztály eszközöket biztosít az osztályok manipulálásához.

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

Az inheritMethod() metódus átmásol egy metódust a szülőosztályból vagy implementált interfészből az Ön osztályába. Ez lehetővé teszi a metódus felülírását vagy a szignatúrájának kiterjesztését:

$method = $manipulator->inheritMethod('bar'); $method->setBody('...'); 

Az inheritProperty() metódus átmásol egy propertyt a szülőosztályból az Ön osztályába. Ez akkor hasznos, ha ugyanazt a propertyt szeretné az osztályában, de esetleg más alapértelmezett értékkel:

$property = $manipulator->inheritProperty('foo'); $property->setValue('new value'); 

Az implement() metódus automatikusan implementálja az összes metódust és propertyt a megadott interfészből vagy absztrakt osztályból az Ön osztályában:

$manipulator->implement(SomeInterface::class); // Most az Ön osztálya implementálja a SomeInterface-t és tartalmazza annak összes metódusát 

Változók kiírása

A Dumper osztály átalakít egy változót elemezhető PHP kóddá. Jobb és áttekinthetőbb kimenetet biztosít, mint a standard var_export() függvény.

$dumper = new Nette\PhpGenerator\Dumper; $var = ['a', 'b', 123]; echo $dumper->dump($var); // kiírja ['a', 'b', 123] 

Kompatibilitási táblázat

A PhpGenerator 4.1 kompatibilis a PHP 8.0-tól 8.4-ig.

verzió: 4.x