PhpGenerator 3.6: novinky z PHP 8.1 a spousta navíc
PhpGenerator ve verzi 3.6 přináší podporu pro všechny novinky chystaného PHP 8.1. Ale to není zdaleka všechno.

Enum
Hlavní novinkou PHP 8.1 jsou výčtové typy. Pomocí generátoru je vytvoříte snadno:
$enum = Nette\PhpGenerator\ClassType::enum('Suit');
$enum->addCase('Clubs');
$enum->addCase('Diamonds');
$enum->addCase('Hearts');
$enum->addCase('Spades');
echo $enum;
Výsledek:
enum Suit
{
case Clubs;
case Diamonds;
case Hearts;
case Spades;
}
Můžete také definovat skalární ekvivalenty a vytvořit tak „backed“ výčet:
$enum->addCase('Clubs', '♣');
$enum->addCase('Diamonds', '♦');
$enum->addCase('Hearts', '♥');
$enum->addCase('Spades', '♠');
Výsledek:
enum Suit: string
{
case Clubs = '♣';
case Diamonds = '♦';
case Hearts = '♥';
case Spades = '♠';
}
Ke každému case
je možné přidat komentář nebo atributy
pomocí addComment()
resp. addAttribute()
.
Další novinky PHP 8.1
Nově lze konstanty označovat jako finální, vytvářet readonly
properties, používat intersection typy a operátor new
ve
výchozích hodnotách parametrů funkcí či metod:
use Nette\PhpGenerator\Literal;
use Nette\PhpGenerator\Type;
$class = new Nette\PhpGenerator\ClassType('Php81Features');
$class->addConstant('NAME', 'foo')
->setFinal();
$class->addProperty('foo')
->setType(Type::intersection('Foo', 'Bar')) // nebo 'Foo&Bar'
->setReadOnly();
$class->addMethod('__construct')
->addParameter('now', new Literal('new DateTime'));
echo $class;
Vypíše:
class Php81Features
{
final public const NAME = 'foo';
public readonly Foo&Bar $foo;
public function __construct($now = new DateTime)
{
}
}
Dále Dumper
umí vypisovat (statické) closures novou trojtečkovou
syntaxí.
Funkce v souborech a jmenných prostorech
Nově můžete do PHP souborů nebo jmenných prostorů přidávat i funkce:
$file = new Nette\PhpGenerator\PhpFile;
$class = $file->addClass('Foo\A');
$function = $file->addFunction('Foo\foo');
// nebo
$namespace = $file->addNamespace('Foo');
$class = $namespace->addClass('A');
$function = $namespace->addFunction('foo');
A také definovat use
pro funkce a konstanty:
// use function iter\range;
$namespace->addUseFunction('iter\range');
// use constant PHP_EOL;
$namespace->addUseConstant('PHP_EOL');
Načítání tříd ze souborů a řetězců
A pozor, tohle je velká novinka! Nyní lze reprezentace tříd, rozhraní, funkcí, enumů atd. načítat přímo z řetězce obsahujícího PHP kód, aniž by byl spuštěn:
$code = file_get_contents('classes.php');
$file = Nette\PhpGenerator\PhpFile::fromCode($code);
echo $file;
Pro načtení jediné třídy můžete použít rovnou:
$class = Nette\PhpGenerator\ClassType::fromCode($code);
Získáte tak přesnou reprezentaci třídy, včetně těl všech metod a funkcí.
(Vyžaduje doinstalovat
composer require nikic/php-parser
)
Načítání tříd z reflexe a traity
Už dříve bylo možné vytvářet reprezentace tříd na
základě existujících s vyžitím reflexe metodou
ClassType::from()
. To samozřejmě funguje stále. Jediná obtíž
s reflexí je ta, že neumí sdělit, které metody nebo properties pocházejí
z trait. Proto Nette jednoduše traity materializovalo, tj. reprezentace
neobsahovala use TraitFoo
, ale přímo těla metod a
vlastností.
trait TraitFoo
{
public function foo()
{
}
}
class Bar
{
use TraitFoo;
}
$class = Nette\PhpGenerator\ClassType::from(Bar::class);
echo $class;
Takto vytvořená třída se tedy vypsala jako:
class Bar
{
public function foo()
{
}
}
Což je funkčně zcela identické s originální třídou, ale přesnou podobu to nezachovává.
Nette se postupně naučilo celkem dobře použití trait odhadovat a dnes už umí vygenerovat kód bez materializace. Od verze 4.0 to bude dělat automaticky, zatím mu to sdělíme pomocí parametru:
$class = Nette\PhpGenerator\ClassType::from(Bar::class, materializeTraits: false);
// pro PHP < 8 použijte from(Bar::class, true, false);
Chytřejší literály
Pomocí literalů můžete předávat do reprezentací libovolný kód PHP, například pro výchozí hodnoty vlastností nebo parametrů atd. Nyní lze literálům předat parametry a nechat je zformátovat do platného kódu PHP pomocí zástupných znaků:
use Nette\PhpGenerator\Literal;
$a = 10;
$b = 20;
$class = new Nette\PhpGenerator\ClassType('Demo');
$class->addMethod('bar')
->addParameter('id', new Literal('? + ?', [$a, $b]));
echo $class;
Výsledek:
class Demo
{
public function bar($id = 10 + 20)
{
}
}
Komentáře u trait
Pokud třída používá traity, tyto mohou nově mít komentáře. Jsou důležité třeba pro PHPStan.
$class = new Nette\PhpGenerator\ClassType('Bar');
$class->addTrait('GenericTrait', true)
->addComment('@use GenericTrait<int>');
Vygeneruje:
class Bar
{
/** @use GenericTrait<int> */
use GenericTrait;
}
Důležitý je parametr true
– původně metoda
addTrait()
vracela objekt ClassType
(tedy
$this
), s uvedením true vrací nový objekt
TraitUse
, který právě umožňuje přidat komentář nebo
informace o rozlišení (addResolution('hello as protected')
). Ve
verzi 4.0 tohle bude výchozí chování.
Spousta další drobností
Nová verze má spoustu dalších vychytávek jako třeba:
- kontrola zakázaných názvů tříd
- možnost nastavit maximální šířku řádku
$printer->wrapLength
getType(true)
agetReturnType(true)
vrací objekty Type
Chcete-li odeslat komentář, přihlaste se