Generator de coduri PHP

  • Aveți nevoie să generați cod PHP pentru clase, funcții, fișiere PHP, etc.?
  • Suportă toate cele mai recente caracteristici PHP, cum ar fi enums, atribute, etc.
  • Vă permite să modificați cu ușurință clasele existente
  • Ieșire conformă cu PSR-12
  • Bibliotecă extrem de matură, stabilă și utilizată pe scară largă

Instalare

Descărcați și instalați pachetul folosind Composer:

composer require nette/php-generator

Pentru compatibilitatea PHP, consultați tabelul.

Clase

Să începem cu un exemplu simplu de generare a unei clase folosind ClassType:

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

// pentru a genera cod PHP, pur și simplu transformați în șir de caractere sau utilizați echo:
echo $class;

Acesta va reda acest rezultat:

/**
 * Description of class.
 * Second line
 *
 * @property-read Nette\Forms\Form $form
 */
final class Demo extends ParentClass implements Countable
{

}

De asemenea, putem folosi o imprimantă pentru a genera codul, pe care, spre deosebire de echo $class, o vom putea configura ulterior:

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

Putem adăuga constante (clasa Constant) și proprietăți (clasa Property):

$class->addConstant('ID', 123)
	->setProtected() // vizibilitate constantă
	->setType('int')
	->setFinal();

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

$class->addProperty('list')
	->setType('?array')
	->setInitialized(); // tipărește '= null'

Se generează:

final protected const const int ID = 123;

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

public ?array $list = null;

Și putem adăuga metode:

$method = $class->addMethod('count')
	->addComment('Count it.')
	->setFinal()
	->setProtected()
	->setReturnType('?int') // tipul de returnare a metodei
	->setBody('return count($items ?: $this->items);');

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

Rezultă:

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

Parametrii promovați introduși de PHP 8.0 pot fi trecuți la constructor:

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

Aceasta are ca rezultat:

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

Proprietățile și clasele de citire exclusivă pot fi marcate prin intermediul setReadOnly().


În cazul în care proprietatea, constanta, metoda sau parametrul adăugat există deja, se aruncă o excepție.

Membrii pot fi eliminați utilizând removeProperty(), removeConstant(), removeMethod() sau removeParameter().

De asemenea, se pot adăuga la clasă obiecte existente Method, Property sau 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);

Puteți clona metodele, proprietățile și constantele existente cu un nume diferit folosind cloneWithName():

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

Interfață sau trăsătură

Puteți crea interfețe și trăsături (clasele InterfaceType și TraitType):

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

Utilizarea trăsăturilor:

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

Rezultat:

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

Enums

Puteți crea cu ușurință enumerațiile pe care le aduce PHP 8.1 (clasa EnumType):

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

echo $enum;

Rezultat:

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

De asemenea, puteți defini echivalenți scalari pentru cazuri pentru a crea un enum susținut:

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

Este posibil să se adauge un comentariu sau atribute la fiecare caz folosind addComment() sau addAttribute().

Clasa Anonymous

Dați null ca nume și veți avea o clasă anonimă:

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

echo '$obj = new class ($val) ' . $class . ';';

Rezultat:

$obj = new class ($val) {

	public function __construct($foo)
	{
	}
};

Funcția globală

Codul de funcții va genera clasa GlobalFunction:

$function = new Nette\PhpGenerator\GlobalFunction('foo');
$function->setBody('return $a + $b;');
$function->addParameter('a');
$function->addParameter('b');
echo $function;

// sau utilizați PsrPrinter pentru o ieșire conformă cu PSR-2 / PSR-12
// echo (new Nette\PhpGenerator\PsrPrinter)->printFunction($function);

Rezultat:

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

Închidere

Codul de închidere va genera clasa Closure:

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

// sau utilizați PsrPrinter pentru o ieșire conformă cu PSR-2 / PSR-12
// echo (new Nette\PhpGenerator\PsrPrinter)->printClosure($closure);

Rezultat:

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

Funcția săgeată

De asemenea, puteți imprima închiderea ca funcție săgeată folosind imprimanta:

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

echo (new Nette\PhpGenerator\Printer)->printArrowFunction($closure);

Rezultat:

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

Semnătura metodei și a funcției

Metodele sunt reprezentate de clasa Method. Puteți seta vizibilitatea, valoarea de returnare, adăuga comentarii, atribute etc:

$method = $class->addMethod('count')
	->addComment('Count it.')
	->setFinal()
	->setProtected()
	->setReturnType('?int');

Fiecare parametru este reprezentat de o clasă Parameter. Din nou, puteți seta toate proprietățile imaginabile:

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

// function count(&$items = [])

Pentru a defini așa-numiții parametri variadici (sau, de asemenea, operatorul splat, spread, elipsis, unpacking sau trei puncte), utilizați setVariadics():

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

Generates:

function count(...$items)
{
}

Metoda și corpul funcției

Corpul poate fi transmis metodei setBody() o dată sau secvențial (linie cu linie) prin apelarea repetată a metodei addBody():

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

Rezultat

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

Puteți utiliza caractere de poziție speciale pentru a injecta variabilele în mod practic.

Semne de poziție simple ?

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

Rezultat:

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;

Rezultat:

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

De asemenea, puteți utiliza parametrii numiți din PHP 8 folosind caractere de poziție ...?:

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

// myfunc(foo: 1, bar: true);

Scăpați de locul rezervat utilizând slash \?

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

Rezultatul:

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

Imprimantele și conformitatea PSR

Codul PHP este generat de obiectele Printer. Există un PsrPrinter a cărui ieșire este conformă cu PSR-2 și PSR-12 și care utilizează spații pentru indentare și un Printer care utilizează tabulatoare pentru indentare.

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

$printer = new Nette\PhpGenerator\PsrPrinter;
echo $printer->printClass($class); // 4 spații de indentare

Aveți nevoie să personalizați comportamentul imprimantei? Creați-vă propria imprimantă moștenind clasa Printer. Puteți reconfigura aceste variabile:

class MyPrinter extends Nette\PhpGenerator\Printer
{
	public int $wrapLength = 120;
	public string $indentation = "\t";
	public int $linesBetweenProperties = 0;
	public int $linesBetweenMethods = 2;
	public int $linesBetweenUseTypes = 0;
	public bool $bracesOnNextLine = true;
	public string $returnTypeColon = ': ';
}

Tipuri

Fiecare tip sau tip de uniune/intersecție poate fi transmis ca un șir de caractere, puteți utiliza, de asemenea, constante predefinite pentru tipurile native:

use Nette\PhpGenerator\Type;

$member->setType('array'); // sau Type::Array;
$member->setType('array|string'); // sau Type::union('array', 'string')
$member->setType('Foo&Bar'); // sau Type::intersection(Foo::class, Bar::class)
$member->setType(null); // elimină tipul

Același lucru este valabil și pentru metoda setReturnType().

Literali

Cu Literal puteți transmite cod PHP arbitrar, de exemplu, pentru valori implicite ale proprietăților sau parametrilor etc:

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;

Rezultat:

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

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

Puteți, de asemenea, să transmiteți parametri la Literal și să formatați codul PHP valid folosind caractere de poziție speciale:

new Literal('substr(?, ?)', [$a, $b]);
// generează, de exemplu: substr('hello', 5);

Atribute

Puteți adăuga atribute PHP 8 la toate clasele, metodele, proprietățile, constantele, cazurile enum, funcțiile, închiderile și parametrii. Literalii pot fi, de asemenea, utilizați ca valori de parametru.

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

Rezultat:

#[Deprecated]
class Demo
{
	#[WithArguments(1, 2)]
	public $list;


	#[Foo\Cached(mode: true)]
	public function count(#[Bar] $items)
	{
	}
}

Spațiul de nume

Clasele, trăsăturile, interfețele și enumerațiile (denumite în continuare clase) pot fi grupate în spații de nume (PhpNamespace):

$namespace = new Nette\PhpGenerator\PhpNamespace('Foo');

// crearea de noi clase în spațiul de nume
$class = $namespace->addClass('Task');
$interface = $namespace->addInterface('Countable');
$trait = $namespace->addTrait('NameAware');

// sau inserați o clasă existentă în spațiul de nume
$class = new Nette\PhpGenerator\ClassType('Task');
$namespace->add($class);

În cazul în care clasa există deja, se aruncă o excepție.

Puteți defini declarații de utilizare:

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

Pentru a simplifica un nume de clasă, funcție sau constantă complet calificat în conformitate cu aliasurile definite, utilizați metoda simplifyName:

echo $namespace->simplifyName('Foo\Bar'); // 'Bar', deoarece 'Foo' este spațiul de nume curent
echo $namespace->simplifyName('iter\range', $namespace::NameFunction); // "range", din cauza declarației de utilizare definite

În schimb, puteți converti un nume simplificat de clasă, funcție sau constantă într-un nume complet calificat utilizând metoda resolveName:

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

Rezolvarea numelor de clase

Când clasa face parte din spațiul de nume, este redată puțin diferit: toate tipurile (adică indicii de tip, tipuri de returnare, numele clasei părinte, interfețele implementate, trăsăturile și atributele utilizate) sunt automat rezolvate (cu excepția cazului în care dezactivați această funcție, a se vedea mai jos). Aceasta înseamnă că trebuie să utilizați nume de clasă complete în definiții și că acestea vor fi înlocuite cu alias-uri (în conformitate cu declarațiile de utilizare) sau cu nume complet calificate în codul rezultat:

$namespace = new Nette\PhpGenerator\PhpNamespace('Foo');
$namespace->addUse('Bar\AliasedClass');

$class = $namespace->addClass('Demo');
$class->addImplement('Foo\A') // se va simplifica la A
	->addTrait('Bar\AliasedClass'); // se va simplifica în AliasedClass

$method = $class->addMethod('method');
$method->addComment('@return ' . $namespace->simplifyType('Foo\D')); // în comentarii simplificați manual
$method->addParameter('arg')
	->setType('Bar\OtherClass'); // se va rezolva în \Bar\OtherClass

echo $namespace;

// sau utilizați PsrPrinter pentru o ieșire conformă cu PSR-2 / PSR-12
// echo (new Nette\PhpGenerator\PsrPrinter)->printNamespace($namespace);

Rezultat:

namespace Foo;

use Bar\AliasedClass;

class Demo implements A
{
	use AliasedClass;

	/**
	 * @return D
	 */
	public function method(\Bar\OtherClass $arg)
	{
	}
}

Rezolvarea automată poate fi dezactivată în acest mod:

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

Fișiere PHP

Clasele, funcțiile și spațiile de nume pot fi grupate în fișiere PHP reprezentate de clasa PhpFile:

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

$class = $file->addClass('Foo\A');
$function = $file->addFunction('Foo\foo');

// sau
// $namespace = $file->addNamespace('Foo');
// $class = $namespace->addClass('A');
// $function = $namespace->addFunction('foo');

echo $file;

// sau utilizați PsrPrinter pentru o ieșire conformă cu PSR-2 / PSR-12
// echo (new Nette\PhpGenerator\PsrPrinter)->printFile($file);

Rezultat:

<?php

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

declare(strict_types=1);

namespace Foo;

class A
{
}

function foo()
{
}

Generarea în funcție de cele existente

Pe lângă posibilitatea de a modela clase și funcții utilizând API-ul descris mai sus, puteți, de asemenea, să le generați automat în funcție de cele existente:

// creează o clasă identică cu clasa PDO
$class = Nette\PhpGenerator\ClassType::from(PDO::class);

// creează o funcție identică cu trim()
$function = Nette\PhpGenerator\GlobalFunction::from('trim');

// creează o închidere așa cum este specificat
$closure = Nette\PhpGenerator\Closure::from(
	function (stdClass $a, $b = null) {},
);

Corpurile funcțiilor și metodelor sunt goale în mod implicit. Dacă doriți să le încărcați și pe acestea, utilizați această modalitate (necesită instalarea nikic/php-parser ):

$class = Nette\PhpGenerator\ClassType::from(Foo::class, withBodies: true);

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

Încărcare din fișierul PHP

De asemenea, puteți încărca funcții, clase, interfețe și enume direct dintr-un șir de cod PHP. De exemplu, creăm obiectul ClassType în acest mod:

$class = Nette\PhpGenerator\ClassType::fromCode(<<<XX
	<?php

	class Demo
	{
		public $foo;
	}
	XX);

La încărcarea claselor din codul PHP, comentariile pe o singură linie din afara corpului metodelor sunt ignorate (de exemplu, pentru proprietăți etc.), deoarece această bibliotecă nu dispune de un API pentru a lucra cu acestea.

De asemenea, puteți încărca direct întregul fișier PHP, care poate conține orice număr de clase, funcții sau chiar mai multe spații de nume:

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

Se încarcă, de asemenea, comentariul inițial al fișierului și declarația strict_types. Pe de altă parte, toate celelalte coduri globale sunt ignorate.

Acest lucru necesită instalarea nikic/php-parser.

Dacă aveți nevoie să manipulați codul global din fișiere sau declarațiile individuale din corpurile metodelor, este mai bine să utilizați direct biblioteca nikic/php-parser.

Descărcător de variabile

Dumper returnează o reprezentare de tip șir de caractere PHP analizabilă a unei variabile. Oferă o ieșire mai bună și mai clară decât funcția nativă var_export().

$dumper = new Nette\PhpGenerator\Dumper;

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

echo $dumper->dump($var); // prints ['a', 'b', 123]

Tabel de compatibilitate

PhpGenerator 4.0 este compatibil cu PHP 8.0 până la 8.2

versiune: 4.0