PHP kód generátor
- Támogatja az összes legújabb PHP-funkciót (például enums stb.)
- Lehetővé teszi a meglévő osztályok egyszerű módosítását
- PSR-12 / PER kódolási stílusnak megfelelő kimenet
- Kiforrott, stabil és széles körben használt könyvtár
Telepítés
Töltse le és telepítse a csomagot a Composer segítségével:
composer require nette/php-generator
A PHP-kompatibilitásról lásd a táblázatot.
Osztályok
Kezdjük egy egyszerű példával az osztály generálására a ClassType használatával:
$class = new Nette\PhpGenerator\ClassType('Demo');
$class
->setFinal()
->setExtends(ParentClass::class)
->addImplement(Countable::class)
->addComment("Description of class.\nSecond line\n")
->addComment('@property-read Nette\Forms\Form $form');
// PHP kód generálásához egyszerűen csak öntsd stringre, vagy használd az echo-t:
echo $class;
A következő eredményt adja ki:
/**
* Description of class.
* Second line
*
* @property-read Nette\Forms\Form $form
*/
final class Demo extends ParentClass implements Countable
{
}
A kód generálásához használhatunk nyomtatót is, amelyet a echo $class
címmel ellentétben tovább konfigurálhatunk:
$printer = new Nette\PhpGenerator\Printer;
echo $printer->printClass($class);
Konstanciákat (class Constant) és tulajdonságokat (class Property) adhatunk hozzá:
$class->addConstant('ID', 123)
->setProtected() // konstans láthatóság
->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'
Ez 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('Count it.')
->setFinal()
->setProtected()
->setReturnType('?int') // módszer visszatérési típusa
->setBody('return count($items ?: $this->items);');
$method->addParameter('items', []) // $items = []
->setReference() // &$items = []
->setType('array'); // array &$items = []
Ez a következőket eredményezi:
/**
* Count it.
*/
final protected function count(array &$items = []): ?int
{
return count($items ?: $this->items);
}
A PHP 8.0 által bevezetett támogatott paraméterek átadhatók a konstruktornak:
$method = $class->addMethod('__construct');
$method->addPromotedParameter('name');
$method->addPromotedParameter('args', [])
->setPrivate();
Ez a következőket eredményezi:
public function __construct(
public $name,
private $args = [],
) {
}
A csak olvasható tulajdonságok és osztályok a setReadOnly()
címen keresztül jelölhetők meg.
Ha a hozzáadott tulajdonság, konstans, metódus vagy paraméter már létezik, akkor kivételt dob.
A tagok eltávolítása a removeProperty()
, removeConstant()
, removeMethod()
vagy a
removeParameter()
segítségével történhet.
A már létező Method
, Property
vagy Constant
objektumokat is hozzáadhatja az
osztályhoz:
$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);
A meglévő metódusokat, tulajdonságokat és konstansokat más névvel klónozhatja a cloneWithName()
segítségével:
$methodCount = $class->getMethod('count');
$methodRecount = $methodCount->cloneWithName('recount');
$class->addMember($methodRecount);
Interface vagy Trait
Interfészeket és tulajdonságokat hozhat létre ( InterfaceType és TraitType osztályok):
$interface = new Nette\PhpGenerator\InterfaceType('MyInterface');
$trait = new Nette\PhpGenerator\TraitType('MyTrait');
Vonások 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;
}
}
Enums
A PHP 8.1 által hozott enumokat ( EnumType osztály) könnyen létrehozhatod:
$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;
}
Az esetek skaláris megfelelőit is definiálhatja, hogy létrehozzon egy támogatott enumot:
$enum->addCase('Clubs', '♣');
$enum->addCase('Diamonds', '♦');
A addComment()
vagy a addAttribute()
segítségével minden egyes esethez hozzáadhatunk egy
megjegyzést vagy attribútumokat.
Névtelen osztály
Adjuk meg a null
nevet, é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 funkció
A függvények kódja létrehozza a GlobalFunction osztályt:
$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 szabványnak megfelelő kimenethez.
// echo (new Nette\PhpGenerator\PsrPrinter)->printFunction($function);
Eredmény:
function foo($a, $b)
{
return $a + $b;
}
Zárás
A lezárások kódja a Closure osztályt fogja létrehozni:
$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 szabványnak megfelelő kimenethez.
// echo (new Nette\PhpGenerator\PsrPrinter)->printClosure($closure);
Eredmény:
function ($a, $b) use (&$c) {
return $a + $b;
}
Nyíl funkció
A lezárást nyíl funkcióként is kinyomtathatja a nyomtató 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
Módszer és funkció aláírása
A metódusokat a Method osztály képviseli. Beállíthatja a láthatóságot, a visszatérési értéket, megjegyzéseket, attribútumokat stb. adhat hozzá:
$method = $class->addMethod('count')
->addComment('Count it.')
->setFinal()
->setProtected()
->setReturnType('?int');
Minden paramétert egy Parameter osztály képvisel. Ismét minden elképzelhető tulajdonságot beállíthat:
$method->addParameter('items', []) // $items = []
->setReference() // &$items = []
->setType('array'); // array &$items = []
// function count(&$items = [])
Az úgynevezett variadics paraméterek (vagy akár a splat, spread, ellipszis, kipakolás vagy a három pont operátor)
definiálásához használja a setVariadic()
:
$method = $class->addMethod('count');
$method->setVariadic(true);
$method->addParameter('items');
Generates:
function count(...$items)
{
}
Módszer és funkciótest
A test átadható a setBody()
metódusnak egyszerre vagy szekvenciálisan (soronként) a 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 helyőrzőket használhat a változók befecskendezésének praktikus módjához.
Egyszerű helytartók ?
$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);
}
Variadic placeholder ...?
$items = [1, 2, 3];
$function = new Nette\PhpGenerator\GlobalFunction('foo');
$function->setBody('myfunc(...?);', [$items]);
echo $function;
Eredmény:
function foo()
{
myfunc(1, 2, 3);
}
A PHP 8 névvel ellátott paramétereket is használhatja a helyőrző használatával. ...?:
$items = ['foo' => 1, 'bar' => true];
$function->setBody('myfunc(...?:);', [$items]);
// myfunc(foo: 1, bar: true);
Helytartó szláv segítségével menekülhet \?
$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;
}
Nyomtatók és PSR-megfelelőség
A Printer osztály PHP kód generálására szolgál:
$class = new Nette\PhpGenerator\ClassType('Demo');
// ...
$printer = new Nette\PhpGenerator\Printer;
echo $printer->printClass($class); // ugyanaz, mint: echo $class
Minden más elemhez képes kódot generálni, és olyan módszereket kínál, mint a printFunction()
,
printNamespace()
, stb.
Ezenkívül rendelkezésre áll a PsrPrinter
osztály, amelynek kimenete megfelel a PSR-2 / PSR-12 / PER
kódolási stílusnak:
$printer = new Nette\PhpGenerator\PsrPrinter;
echo $printer->printClass($class);
Szüksége van a viselkedés finomhangolására az Ön igényei szerint? Hozzon létre saját nyomtatót a Printer
osztály öröklésével. Ezeket a változókat átkonfigurálhatja:
class MyPrinter extends Nette\PhpGenerator\Printer
{
// a sor hossza, amely után a sor megszakad.
public int $wrapLength = 120;
// behúzás karakter, helyettesíthető szóközökkel
public string $indentation = "\t";
// a tulajdonságok közötti üres sorok száma
public int $linesBetweenProperties = 0;
// üres sorok száma a metódusok között
public int $linesBetweenMethods = 2;
// üres sorok száma az osztályok, függvények és konstansok használati utasításcsoportjai között
public int $linesBetweenUseTypes = 0;
// a nyitó zárójel pozíciója függvények és metódusok esetén
public bool $bracesOnNextLine = true;
// egy paramétert egy sorba helyezünk, még akkor is, ha attribútummal rendelkezik vagy előléptetve van.
public bool $singleParameterOnOneLine = false;
// omits namespaces that do not contain any class or function
public bool $omitEmptyNamespaces = true;
// elválasztó a függvények és metódusok jobb oldali zárójel és a visszatérési típus között.
public string $returnTypeColon = ': ';
}
Miben és miért különbözik pontosan a szabványos Printer
és a PsrPrinter
? Miért nincs csak egy
nyomtató, a PsrPrinter
, a csomagban?
A szabványos Printer
úgy formázza a kódot, ahogyan azt az egész Nette-ben tesszük. Mivel a Nette sokkal
korábban készült, mint a PSR, és mivel a PSR sok éven át nem időben, hanem néha akár több éves késéssel szállította
a szabványokat egy-egy új funkció bevezetésétől a PHP-ben, ez néhány kisebb eltérést eredményezett a kódolási szabványban. A nagyobb különbség csupán a
tabulátorok használata a szóközök helyett. Tudjuk, hogy a tabulátorok használatával a projektjeinkben lehetővé tesszük
a szélesség beállítását, ami a látássérült emberek számára
elengedhetetlen. Egy példa a kisebb különbségre a függvények és metódusok esetében a görbe zárójel külön sorba
helyezése és mindig. A PSR ajánlását logikátlannak látjuk, és a kód áttekinthetőségének csökkenéséhez
vezet.
Típusok
Minden típus vagy union/intersection típus átadható stringként, a natív típusokhoz előre definiált konstansokat is használhat:
use Nette\PhpGenerator\Type;
$member->setType('array'); // vagy Type::Array;
$member->setType('?array'); // or Type::nullable(Type::Array);
$member->setType('array|string'); // or 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()
módszerre is.
Literálisok
A Literal
segítségével tetszőleges PHP kódot adhat át, például alapértelmezett tulajdonság vagy
paraméter értékeket 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)
{
}
}
A Literal
oldalnak paramétereket is átadhat, és azt speciális
helyőrzőket használva érvényes PHP-kóddá formázhatja:
new Literal('substr(?, ?)', [$a, $b]);
// generál, például: substr('hello', 5);
Az új objektum létrehozását jelképező literál a new
módszerrel könnyen létrehozható:
Literal::new(Demo::class, [$a, 'foo' => $b]);
// generál, például: new Demo(10, foo: 20)
Attribútumok
A PHP 8 attribútumokat minden osztályhoz, metódushoz, tulajdonsághoz, konstanshoz, enum esetekhez, függvényekhez, lezárásokhoz és paraméterekhez hozzáadhatja. Irodalmi karakterek is használhatók paraméterértékként.
$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,
) {
}
}
Namespace
Az osztályok, tulajdonságok, interfészek és enumok (a továbbiakban osztályok) névterekbe (PhpNamespace) csoportosíthatók:
$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 egy 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, akkor kivételt dob.
Használati utasításokat definiálhat:
// 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');
A simplifyName
módszerrel egyszerűsíthet egy teljesen minősített osztály-, függvény- vagy konstansnevet a
definiált aliasoknak megfelelően:
echo $namespace->simplifyName('Foo\Bar'); // 'Bar', mert 'Foo' az aktuális névtér
echo $namespace->simplifyName('iter\range', $namespace::NameFunction); // 'range', a definiált use-statement miatt
Ezzel szemben az egyszerűsített osztály-, függvény- vagy konstansnevet a resolveName
módszerrel alakíthatja
át teljes minősítésűvé:
echo $namespace->resolveName('Bar'); // 'Foo\Bar'
echo $namespace->resolveName('range', $namespace::NameFunction); // 'iter\range'
Osztálynevek feloldása
Ha egy osztály egy névtér része, akkor kissé másképp jelenik meg: minden típus (pl. a típushivatkozások, visszatérési típusok, szülőosztály neve, implementált interfészek, használt tulajdonságok és attribútumok) automatikusan feloldódik (hacsak ki nem kapcsolod, lásd alább). Ez azt jelenti, hogy a definíciókban teljesen minősített osztályneveket kell használnia, és ezek helyébe aliasok (a használati záradékok alapján) vagy teljesen minősített nevek lépnek az eredményül kapott kódban:
$namespace = new Nette\PhpGenerator\PhpNamespace('Foo');
$namespace->addUse('Bar\AliasedClass');
$class = $namespace->addClass('Demo');
$class->addImplement('Foo\A') // egyszerűsödik A
->addTrait('Bar\AliasedClass'); // AliasedClass-ra fog egyszerűsödni
$method = $class->addMethod('method');
$method->addComment('@return ' . $namespace->simplifyType('Foo\D')); // a megjegyzésekben manuálisan egyszerűsítünk
$method->addParameter('arg')
->setType('Bar\OtherClass'); // feloldódik \Bar\OtherClass-ra
echo $namespace;
// vagy használja a PsrPrintert a PSR-2 / PSR-12 / PER szabványnak megfelelő 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 feloldás így kikapcsolható:
$printer = new Nette\PhpGenerator\Printer; // vagy PsrPrinter
$printer->setTypeResolving(false);
echo $printer->printNamespace($namespace);
PHP Fájlok
Az osztályok, függvények és névterek PHP fájlokba csoportosíthatók, amelyeket a PhpFile osztály képvisel:
$file = new Nette\PhpGenerator\PhpFile;
$file->addComment('This file is auto-generated.');
$file->setStrictTypes(); // adds 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 szabványnak megfelelő kimenethez.
// echo (new Nette\PhpGenerator\PsrPrinter)->printFile($file);
Eredmény:
<?php
/**
* This file is auto-generated.
*/
declare(strict_types=1);
namespace Foo;
class A
{
}
function foo()
{
}
Figyelem: A függvényeken és osztályokon kívül nem lehet további kódot hozzáadni a fájlokhoz.
A meglévők szerint generálva
Amellett, hogy az osztályokat és függvényeket a fent leírt API segítségével modellezhetjük, lehetőségünk van arra is, hogy automatikusan generáljuk őket a meglévők alapján:
// létrehoz egy, a PDO osztállyal azonos osztályt
$class = Nette\PhpGenerator\ClassType::from(PDO::class);
// létrehoz egy, a trim() függvénnyel azonos függvényt
$function = Nette\PhpGenerator\GlobalFunction::from('trim');
// létrehoz egy lezárást a megadottak szerint
$closure = Nette\PhpGenerator\Closure::from(
function (stdClass $a, $b = null) {},
);
A függvény- és metódustestek alapértelmezés szerint üresek. Ha ezeket is be akarja tölteni, használja ezt a módszert
(ehhez a nikic/php-parser
oldal telepítése szükséges):
$class = Nette\PhpGenerator\ClassType::from(Foo::class, withBodies: true);
$function = Nette\PhpGenerator\GlobalFunction::from('foo', withBody: true);
Betöltés PHP fájlból
A függvényeket, osztályokat, interfészeket és enumokat közvetlenül egy PHP kódsorozatból is betöltheti. Például
így hozzuk létre a ClassType
objektumot:
$class = Nette\PhpGenerator\ClassType::fromCode(<<<XX
<?php
class Demo
{
public $foo;
}
XX);
Az osztályok PHP kódból történő betöltésekor a metódus testén kívüli egysoros megjegyzéseket figyelmen kívül hagyjuk (pl. tulajdonságok stb. esetén), mivel ez a könyvtár nem rendelkezik API-val ezek kezelésére.
A teljes PHP-fájlt közvetlenül is betöltheti, amely tetszőleges számú osztályt, függvényt vagy akár több névteret is tartalmazhat:
$file = Nette\PhpGenerator\PhpFile::fromCode(file_get_contents('classes.php'));
A kezdeti fájlkommentár és a strict_types
nyilatkozat is betöltődik. Másrészt minden más globális kódot
figyelmen kívül hagyunk.
Ehhez telepíteni kell a nikic/php-parser
oldalt.
Ha fájlokban lévő globális kódot vagy a metódusok testében lévő egyes utasításokat kell manipulálnia,
jobb, ha közvetlenül a nikic/php-parser
könyvtárat használja.
Osztály Manipulátor
A 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);
A inheritMethod()
metódus egy metódust másol át egy szülő osztályból vagy egy implementált interfészből
az osztályodba. 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('...');
A inheritProperty()
metódus egy tulajdonságot másol át egy szülő osztályból az Ön osztályába. Ez akkor
hasznos, ha ugyanazt a tulajdonságot szeretné az osztályában, de esetleg más alapértelmezett értékkel:
$property = $manipulator->inheritProperty('foo');
$property->setValue('new value');
A implementInterface()
metódus automatikusan megvalósítja az adott interfész összes metódusát az
osztályodban:
$manipulator->implementInterface(SomeInterface::class);
// Most az osztályod megvalósítja a SomeInterface-t és tartalmazza annak összes metódusát.
Változók Dumper
A Dumper egy változó egy elemezhető PHP-string reprezentációját adja vissza. Jobb és egyértelműbb kimenetet
biztosít, mint a natív var_export()
függvény.
$dumper = new Nette\PhpGenerator\Dumper;
$var = ['a', 'b', 123];
echo $dumper->dump($var); // prints ['a', 'b', 123]
Kompatibilitási táblázat
A PhpGenerator 4.0 és 4.1 kompatibilis a PHP 8.0 és 8.3 közötti változatokkal.