PHP Kod Oluşturucu
- 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