News in PhpGenerator 3.5

4 years ago by David Grudl  

PhpGenerator has learned all the language features of PHP 8. Let's see how you can start using them right away.

Constructor Property Promotion

A very nice new feature of PHP 8 is the constructor property promotion, a syntactic sugar that saves writing the type twice and the variable four times. “Promoted” parameters are added using addPromotedParameter() and can be combined with classic parameters added by method addParameter():

use Nette\PhpGenerator\ClassType;

$class = new ClassType('Facade');
$method = $class->addMethod('__construct');

$method->addPromotedParameter('db')
	->setPrivate()
	->setType(Nette\Database\Connection::class);

$method->addPromotedParameter('mailer')
	->setPrivate()
	->setType(Nette\Mail\Mailer::class);

$method->addParameter('foo');

echo $class;

Result:

class Facade
{
	public function __construct(
		private Nette\Database\Connection $db,
		private Nette\Mail\Mailer $mailer,
		$foo,
	) {
	}
}

You may notice another news that PhpGenerator prints a trailing comma in the parameter definition when PHP 8 syntax is used.

Attributes

PHP 8 attributes provide a whole new way to write structured metadata for classes and all its members, as well as functions, closures, and their parameters. All this is also supported by PhpGenerator and each element has new methods addAttribute(string $name, array $args = []), setAttributes(Attribute[] $attrs) and getAttributes(): Attribute[].

use Nette\PhpGenerator\PhpNamespace;
use Nette\PhpGenerator\Literal;

$namespace = new PhpNamespace('');
$namespace->addUse(Nette\Application\Attributes::class);

$class = $namespace->addClass('SomePresenter');

$class->addAttribute('FooBar', [
	'foo' => new Literal('Foo::BAR'),
	'bar' => [1, 2, 3]
]);

$class->addProperty('page')
	->setType('int')
	->addAttribute(Nette\Application\Attributes\Persistent::class);

echo $class;

Generates:

#[FooBar(foo: Foo::BAR, bar: [1, 2, 3])]
class SomePresenter
{
	#[Attributes\Persistent]
	public int $page;
}

As you can see, use statements are used for attributes. And they can even be combined with promoted parameters.

Types

Everywhere is possible to use union types in the form of a string with vertical bars as typehint. You can also have the string generated with Type::union().

use Nette\PhpGenerator\Type;
use Nette\PhpGenerator\GlobalFunction;

$function = (new GlobalFunction('twice'))
	->setReturnType('int|float')
	->setBody('return $a * 2;');

$function->addParameter('a')
	->setType(Type::union(Type::INT, Type::FLOAT));

echo $function;

Lists:

function twice(int|float $a): int|float
{
	return $a * 2;
}

If we set the parameter setNullable(), it will be generated correctly int|float|null.

All other data types are also supported, such as mixed, etc.

Named parameters

Named parameters can also be used when generating PHP code using addBody(), setBody() or Dumper::format(). For classic positional parameters use placeholder ...?

$items = [1, 2, 3];
$function->setBody('myfunc(...?);', [$items]);
// myfunc(1, 2, 3);

For named parameters use placeholder ...?:

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