PHP Kod Oluşturucu

Sınıflar, fonksiyonlar veya tüm dosyalar için PHP kodu üretecek bir araç mı arıyorsunuz?
  • En son PHP özelliklerini destekler (enumlar vb. gibi)
  • Mevcut sınıfları kolayca değiştirmenizi sağlar
  • PSR-12 / PER kodlama stili ile uyumlu çıkış
  • Olgun, kararlı ve yaygın olarak kullanılan kütüphane

Kurulum

Composer'ı kullanarak paketi indirin ve yükleyin:

composer require nette/php-generator

PHP uyumluluğu için tabloya bakın.

Sınıflar

ClassType kullanarak sınıf oluşturmanın basit bir örneği ile başlayalım:

$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 kodu oluşturmak için sadece dizgiye dönüştürün veya echo kullanın:
echo $class;

Bu sonucu verecektir:

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

Kodu oluşturmak için echo $class adresinden farklı olarak daha fazla yapılandırabileceğimiz bir yazıcı da kullanabiliriz:

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

Sabitleri ( Constant sınıfı) ve özellikleri ( Property sınıfı) ekleyebiliriz:

$class->addConstant('ID', 123)
	->setProtected() // sabit görünürlük
	->setType('int')
	->setFinal();

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

$class->addProperty('list')
	->setType('?array')
	->setInitialized(); // '= null' yazdırır

Üretiyor:

final protected const int ID = 123;

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

public ?array $list = null;

Ve yöntemler ekleyebiliriz:

$method = $class->addMethod('count')
	->addComment('Count it.')
	->setFinal()
	->setProtected()
	->setReturnType('?int') // yöntem dönüş türü
	->setBody('return count($items ?: $this->items);');

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

Sonuç olarak:

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

PHP 8.0 ile tanıtılan tanıtılan değiştirgeler kurucuya aktarılabilir:

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

Sonuç olarak:

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

Readonly özellikler ve sınıflar setReadOnly() aracılığıyla işaretlenebilir.


Eklenen özellik, sabit, yöntem veya parametre zaten mevcutsa, istisna atar.

Üyeler removeProperty(), removeConstant(), removeMethod() veya removeParameter() adresleri kullanılarak çıkarılabilir.

Ayrıca mevcut Method, Property veya Constant nesnelerini de sınıfa ekleyebilirsiniz:

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

Mevcut yöntemleri, özellikleri ve sabitleri cloneWithName() adresini kullanarak farklı bir adla klonlayabilirsiniz:

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

Arayüz veya Özellik

Arayüzler ve özellikler oluşturabilirsiniz ( InterfaceType ve TraitType sınıfları):

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

Özellikleri kullanma:

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

Sonuç:

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

Enumlar

PHP 8.1'in getirdiği enumları (EnumType sınıfı) kolayca oluşturabilirsiniz:

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

echo $enum;

Sonuç:

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

Destekli bir enum oluşturmak için durumlar için skaler eşdeğerler de tanımlayabilirsiniz:

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

addComment() veya addAttribute() adreslerini kullanarak her bir vakaya bir yorum veya nitelik eklemek mümkündür.

Anonim Sınıf

İsim olarak null verin ve anonim bir sınıfınız olsun:

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

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

Sonuç:

$obj = new class ($val) {

	public function __construct($foo)
	{
	}
};

Küresel İşlev

Fonksiyonların kodu GlobalFunction sınıfını oluşturacaktır:

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

// veya PSR-2 / PSR-12 / PER'ye uygun çıktı için PsrPrinter kullanın
// echo (new Nette\PhpGenerator\PsrPrinter)->printFunction($function);

Sonuç:

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

Kapanış

Kapanışların kodu Closure sınıfını oluşturacaktır:

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

// veya PSR-2 / PSR-12 / PER'ye uygun çıktı için PsrPrinter kullanın
// echo (new Nette\PhpGenerator\PsrPrinter)->printClosure($closure);

Sonuç:

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

Ok Fonksiyonu

Kapanışı yazıcı kullanarak ok işlevi olarak da yazdırabilirsiniz:

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

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

Sonuç:

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

Yöntem ve İşlev İmzası

Metotlar Metot sınıfı tarafından temsil edilir. Görünürlüğü, dönüş değerini ayarlayabilir, yorumlar, nitelikler vb. ekleyebilirsiniz:

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

Her parametre bir Parameter sınıfı ile temsil edilir. Yine, akla gelebilecek her özelliği ayarlayabilirsiniz:

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

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

Variadics parametrelerini (veya splat, spread, ellipsis, unpacking veya three dots operatörlerini) tanımlamak için setVariadic() adresini kullanın:

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

Üretir:

function count(...$items)
{
}

Yöntem ve İşlev Gövde

Gövde setBody() yöntemine bir kerede veya addBody() adresini tekrar tekrar çağırarak sırayla (satır satır) aktarılabilir:

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

Sonuç

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

Değişkenleri enjekte etmenin kullanışlı bir yolu için özel yer tutucular kullanabilirsiniz.

Basit yer tutucular ?

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

Sonuç:

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

Varyadik yer tutucu ...?

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

Sonuç:

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

Yer tutucu kullanarak PHP 8 isimli parametreleri de kullanabilirsiniz ...?:

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

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

Eğik çizgi kullanarak yer tutucudan kaçış \?

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

Sonuç:

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

Yazıcılar ve PSR Uyumluluğu

Printer sınıfı PHP kodu üretmek için kullanılır:

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

$printer = new Nette\PhpGenerator\Printer;
echo $printer->printClass($class); // aynısı: echo $class

printFunction(), printNamespace() gibi yöntemler sunarak diğer tüm öğeler için kod oluşturabilir.

Ayrıca, çıkışı PSR-2 / PSR-12 / PER kodlama stiline uygun olan PsrPrinter sınıfı da mevcuttur:

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

Davranışı ihtiyaçlarınıza göre ayarlamanız mı gerekiyor? Printer sınıfından miras alarak kendi yazıcınızı oluşturun. Bu değişkenleri yeniden yapılandırabilirsiniz:

class MyPrinter extends Nette\PhpGenerator\Printer
{
	// satırın kesileceği satır uzunluğu
	public int $wrapLength = 120;
	// girinti karakteri, bir dizi boşluk ile değiştirilebilir
	public string $indentation = "\t";
	// özellikler arasındaki boş satır sayısı
	public int $linesBetweenProperties = 0;
	// yöntemler arasındaki boş satır sayısı
	public int $linesBetweenMethods = 2;
	// sınıflar, fonksiyonlar ve sabitler için kullanım deyimleri grupları arasındaki boş satır sayısı
	public int $linesBetweenUseTypes = 0;
	// fonksiyonlar ve metotlar için açılış parantezinin konumu
	public bool $bracesOnNextLine = true;
	// bir özniteliğe sahip olsa veya terfi ettirilse bile bir parametreyi tek bir satıra yerleştirir
	public bool $singleParameterOnOneLine = false;
	// omits namespaces that do not contain any class or function
	public bool $omitEmptyNamespaces = true;
	// sağ parantez ile fonksiyon ve metotların dönüş tipi arasında ayırıcı
	public string $returnTypeColon = ': ';
}

Standart Printer ve PsrPrinter tam olarak nasıl ve neden farklıdır? Pakette neden sadece tek bir yazıcı, PsrPrinter yok?

Standart Printer kodu tüm Nette'de yaptığımız gibi biçimlendirir. Nette, PSR'den çok daha önce oluşturulduğundan ve ayrıca PSR uzun yıllar boyunca standartları zamanında teslim etmediğinden, hatta bazen PHP'de yeni bir özelliğin tanıtılmasından birkaç yıl gecikmeyle bile olsa, bu kodlama standardında birkaç küçük farklılığa neden oldu. En büyük fark ise boşluk yerine sekme kullanılmasıdır. Projelerimizde sekme kullanarak, görme engelli insanlar için çok önemli olan genişlik ayarlamasına izin verdiğimizi biliyoruz. Küçük bir farka örnek olarak, küme parantezinin işlevler ve yöntemler için ve her zaman ayrı bir satıra yerleştirilmesi verilebilir. PSR tavsiyesinin mantıksız olduğunu ve kod netliğinde azalmaya yol açtığını düşünüyoruz.

Türleri

Her tür veya birlik/kesişim türü bir dize olarak geçirilebilir, ayrıca yerel türler için önceden tanımlanmış sabitleri de kullanabilirsiniz:

use Nette\PhpGenerator\Type;

$member->setType('array'); // veya 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'); // veya Type::intersection(Foo::class, Bar::class)
$member->setType(null); // türü kaldırır

Aynı durum setReturnType() yöntemi için de geçerlidir.

Gerçekler

Literal ile, örneğin varsayılan özellik veya parametre değerleri vb. için rastgele PHP kodu aktarabilirsiniz:

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;

Sonuç:

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

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

Ayrıca Literal adresine parametre aktarabilir ve özel yer tutucular kullanarak geçerli PHP kodu olarak biçimlendirilmesini sağlayabilirsiniz:

new Literal('substr(?, ?)', [$a, $b]);
// oluşturur, örneğin: substr('hello', 5);

Yeni bir nesnenin oluşturulmasını temsil eden değişmez, new yöntemi tarafından kolayca oluşturulur:

Literal::new(Demo::class, [$a, 'foo' => $b]);
// oluşturur, örneğin: new Demo(10, foo: 20)

Nitelikler

PHP 8 özniteliklerini tüm sınıflara, yöntemlere, özelliklere, sabitlere, enum durumlarına, işlevlere, kapanışlara ve değiştirgelere ekleyebilirsiniz. Değiştirge değeri olarak değişmezler de kullanılabilir.

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

Sonuç:

#[Table(name: 'user', constraints: [new UniqueConstraint(name: 'ean', columns: ['ean'])])]
class Demo
{
	#[Deprecated]
	public $list;


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

İsim Alanı

Sınıflar, özellikler, arayüzler ve enumlar (bundan sonra sınıflar olarak anılacaktır) isim alanları (PhpNamespace) halinde gruplandırılabilir:

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

// isim alanında yeni sınıflar oluşturun
$class = $namespace->addClass('Task');
$interface = $namespace->addInterface('Countable');
$trait = $namespace->addTrait('NameAware');

// veya mevcut bir sınıfı ad alanına ekleyebilirsiniz
$class = new Nette\PhpGenerator\ClassType('Task');
$namespace->add($class);

Sınıf zaten mevcutsa, istisna atar.

Kullanım ifadelerini tanımlayabilirsiniz:

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

Tam nitelikli bir sınıf, fonksiyon veya sabit adını tanımlanan takma adlara göre basitleştirmek için simplifyName yöntemini kullanın:

echo $namespace->simplifyName('Foo\Bar'); // 'Bar', çünkü 'Foo' geçerli ad alanıdır
echo $namespace->simplifyName('iter\range', $namespace::NameFunction); // 'range', tanımlanan kullanım ifadesi nedeniyle

Tersine, resolveName yöntemini kullanarak basitleştirilmiş bir sınıf, fonksiyon veya sabit adını tam nitelikli bir ada dönüştürebilirsiniz:

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

Sınıf Adları Çözülüyor

Bir sınıf bir isim alanının parçası olduğunda, biraz farklı şekilde işlenir: tüm tipler (örneğin, tip ipuçları, dönüş tipleri, ana sınıf adı, uygulanan arayüzler, kullanılan özellikler ve nitelikler) otomatik olarak çözülür (kapatmadığınız sürece, aşağıya bakın). Bu, tanımlarda tam nitelikli sınıf adları kullanmanız gerektiği anlamına gelir ve bunlar sonuçta ortaya çıkan kodda takma adlarla (use cümlelerine dayalı olarak) veya tam nitelikli adlarla değiştirilecektir:

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

$class = $namespace->addClass('Demo');
$class->addImplement('Foo\A') // A'ya basitleştirilecektir
	->addTrait('Bar\AliasedClass'); // AliasedClass olarak basitleştirilecektir

$method = $class->addMethod('method');
$method->addComment('@return ' . $namespace->simplifyType('Foo\D')); // yorumlarda manuel olarak basitleştirin
$method->addParameter('arg')
	->setType('Bar\OtherClass'); // \Bar\OtherClass olarak çözümlenecektir

echo $namespace;

// veya PSR-2 / PSR-12 / PER'ye uygun çıktı için PsrPrinter kullanın
// echo (new Nette\PhpGenerator\PsrPrinter)->printNamespace($namespace);

Sonuç:

namespace Foo;

use Bar\AliasedClass;

class Demo implements A
{
	use AliasedClass;

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

Otomatik çözümleme bu şekilde kapatılabilir:

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

PHP Dosyaları

Sınıflar, işlevler ve isim alanları PhpFile sınıfı tarafından temsil edilen PHP dosyaları içinde gruplandırılabilir:

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

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

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

echo $file;

// veya PSR-2 / PSR-12 / PER'ye uygun çıktı için PsrPrinter kullanın
// echo (new Nette\PhpGenerator\PsrPrinter)->printFile($file);

Sonuç:

<?php

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

declare(strict_types=1);

namespace Foo;

class A
{
}

function foo()
{
}

Lütfen dikkat: Dosyalara fonksiyonlar ve sınıflar dışında hiçbir ek kod eklenemez.

Mevcut Olanlara Göre Üretme

Yukarıda açıklanan API'yi kullanarak sınıfları ve fonksiyonları modelleyebilmenin yanı sıra, mevcut olanları kullanarak bunların otomatik olarak oluşturulmasını da sağlayabilirsiniz:

// PDO sınıfına benzer bir sınıf oluşturur
$class = Nette\PhpGenerator\ClassType::from(PDO::class);

// trim() ile aynı işlevi oluşturur
$function = Nette\PhpGenerator\GlobalFunction::from('trim');

// belirtildiği gibi bir kapanış oluşturur
$closure = Nette\PhpGenerator\Closure::from(
	function (stdClass $a, $b = null) {},
);

Fonksiyon ve metot gövdeleri varsayılan olarak boştur. Bunları da yüklemek istiyorsanız, şu yolu kullanın ( nikic/php-parser adresinin yüklenmesini gerektirir):

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

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

PHP Dosyasından Yükleme

Fonksiyonları, sınıfları, arayüzleri ve enumları doğrudan bir PHP kodu dizisinden de yükleyebilirsiniz. Örneğin, ClassType nesnesini bu şekilde oluşturuyoruz:

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

	class Demo
	{
		public $foo;
	}
	XX);

PHP kodundan sınıflar yüklenirken, yöntem gövdelerinin dışındaki tek satırlık yorumlar yok sayılır (örneğin, özellikler vb. için) çünkü bu kütüphanenin bunlarla çalışmak için bir API'si yoktur.

Ayrıca, istediğiniz sayıda sınıf, işlev ve hatta birden fazla ad alanı içerebilen PHP dosyasının tamamını doğrudan yükleyebilirsiniz:

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

İlk dosya yorumu ve strict_types bildirimi de yüklenir. Öte yandan, diğer tüm global kodlar yok sayılır.

Bunun için nikic/php-parser adresinin yüklenmesi gerekir.

Dosyalardaki genel kodu veya yöntem gövdelerindeki tek tek ifadeleri değiştirmeniz gerekiyorsa, nikic/php-parser kütüphanesini doğrudan kullanmak daha iyidir.

Sınıf Manipülatörü

ClassManipulator sınıfı, sınıfları manipüle etmek için araçlar sağlar.

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

inheritMethod() yöntemi, bir üst sınıftaki veya uygulanan arayüzdeki bir yöntemi sınıfınıza kopyalar. Bu, yöntemi geçersiz kılmanıza veya imzasını genişletmenize olanak tanır:

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

inheritProperty() yöntemi, bir özelliği bir üst sınıftan sınıfınıza kopyalar. Bu, sınıfınızda aynı özelliğe sahip olmak, ancak muhtemelen farklı bir varsayılan değere sahip olmak istediğinizde kullanışlıdır:

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

implementInterface() yöntemi, sınıfınızda verilen arayüzdeki tüm yöntemleri otomatik olarak uygular:

$manipulator->implementInterface(SomeInterface::class);
// Şimdi sınıfınız SomeInterface'i uyguluyor ve tüm yöntemlerini içeriyor

Değişkenler Damperi

Dumper, bir değişkenin ayrıştırılabilir bir PHP dize gösterimini döndürür. Yerel işlevden daha iyi ve daha net çıktı sağlar var_export().

$dumper = new Nette\PhpGenerator\Dumper;

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

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

Uyumluluk Tablosu

PhpGenerator 4.0 ve 4.1 PHP 8.0 ila 8.3 ile uyumludur

versiyon: 4.0