Library / SDK
nette/php-generator avatar
nette/php-generator

The stated support is PHP 8.1 to 8.5, the pitch is 8.5

🐘 Generates neat PHP code for you. Supports new PHP 8.5 features.

2,259 stars144 forksPHPNOASSERTION

At a glance

What is it?
Nette PhpGenerator builds PHP source as objects rather than strings, with a chainable API that ends in an echo or a configurable printer. Its support statement covers PHP 8.1 through 8.5 even though the pitch is about the newest version's features.
Who is it for?
Nette PhpGenerator suits a project generating data transfer objects, enums, stubs or migration files where the output has to stay readable and comply with PSR-12. It does not suit a project that only needs one template file, nor a codebase that must still run on PHP older than 8.1.
Can I use it commercially?
Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
Is it still maintained?
Yes. The repository last received commits 90 days ago.
What is it written in?
Mainly PHP, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 28, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Eight features, one range, one sentence of support

The opening question is the one the library exists to answer: are you looking for a tool to generate PHP code for classes, global functions, or complete PHP files. The answer list that follows has four claims. It supports the latest PHP features such as property hooks, enums and attributes. It lets you modify existing classes rather than only generating new ones. Its output complies with the PSR-12 and PER coding styles. And it is described as highly mature, stable and widely used. The compatibility statement is one line further down: PhpGenerator 4.2 is compatible with PHP 8.1 to 8.5. So the headline talks about 8.5 features while the supported floor is 8.1, which is the more useful number for a consumer deciding whether their runtime qualifies. Installation is one Composer command, composer require nette/php-generator, and the documentation lives on doc.nette.org rather than in the repository.

The builder chain ends in an echo

The API shape is a fluent chain on a ClassType object, and the way it produces code is worth noting because it is unusual for a generator library:

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

$class
	->setFinal()
	->setExtends(ParentClass::class)
	->addImplement(Countable::class)
	->addComment("Class description.\nSecond line\n")
	->addComment('@property-read Nette\Forms\Form $form');

echo $class;

You build the class by calling methods that return the object, and then you typecast it to a string by echoing it. The output is a docblock assembled from the comment calls, including the second line of the comment and an @property-read tag, above a final class declaration with its extends and implements clauses. The comment handling is the part with a rule: a multi-line comment becomes separate docblock lines, and an annotation-looking comment becomes a tag on its own line, which is what saves you from escaping annotation syntax into the generated output. Everything the chain returns is the same object, so there is no intermediate representation to learn.

Defaults produce untyped properties with an annotation beside them

The property example is where the generator shows its own judgment. Adding a property with a default value and a type gives you this:

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

public ?array $list = null;

Two different results from two similar calls. The list property gets a nullable type and an explicit null default, produced by setInitialized, which outputs the assignment. The items property gets no type at all: you passed an array default, and the generator emitted a docblock with an @var annotation instead of a native type. So whether you get a typed property or an annotated one depends on which method you called, not on a setting. That is a defensible choice, since an untyped property keeps the generated file compatible with older targets, but it means the property surface has two conventions and reading generated output requires knowing which one produced it. Constants follow the same split, where a constant with a value and a visibility comes out as a final protected const with a type.

Promoted parameters and readonly are two more methods

Constructor promotion is handled with one method on the method object. You create __construct, call addPromotedParameter for each, and optionally set visibility per parameter:

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

The result is a constructor whose parameters carry their visibility inline, with a trailing comma on the last one and the visibility keyword per parameter, which is the formatting a formatter would produce anyway. Readonly properties and classes are marked with a setReadOnly call. Method signatures have the same shape as the class API: addParameter with a default, then setReference for a by-reference parameter and setType for its type, so array &$items = [] comes out of three calls. Bodies are set as strings with setBody, and return types with setReturnType. The level of control is high enough that you can produce nearly any signature, and low enough that you are not writing it.

Duplicates throw, and everything can be removed

The error behavior is stated in one line and it is the right one: if an added property, constant, method or parameter already exists, an exception is thrown. A generator that silently overwrites is a generator you cannot trust in a build script, so failing loudly on a collision is the correct default. The counterpart is a set of removal methods, removeProperty, removeConstant, removeMethod and removeParameter, which matter more than they sound because the library can also parse existing code. The feature list claims you can easily modify existing classes, and modification is the combination of reading an existing class into the object model, deleting what should not be there, adding what should, and printing the result. The remove methods are what make that a round trip rather than a one-way generation.

The printer exists because echoing is not configurable

Echoing the object is the short path and the printer is the real one:

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

The difference is that the printer, unlike echoing the object, can be further configured, and the documentation section it links to is titled printer and PSR compliance. So the formatting guarantee in the feature list is delivered by the printer rather than by the object, which means a project that echoes an object directly gets default formatting and a project that needs to control it constructs a Printer. That is a slightly awkward division, since the object is the thing you built and the printer is the thing you configure, but it keeps the class model free of presentation state. One small inconsistency in the surrounding documentation is worth noting: the class API links point at api.nette.org with a master path while the installation instructions and the feature links point at doc.nette.org, so the reference material is split across two hosts.

Where PhpGenerator is the wrong choice

Four cases. The first is a project that only needs a static template. If you are generating one configuration file or one view, a template engine is less machinery than an object model, a printer and a Composer dependency. The second is a team that wants generated code it does not own. Everything here produces source that a human then edits, which means the generator and the repository will drift, and the commit history of a generated file becomes hard to read. If your plan is to regenerate on every build, this is the wrong shape; if your plan is to generate once and commit, it is the right one. The third is code that must run on PHP older than 8.1, since that is the stated floor, and PHP 8.0 or older means looking at the 3.x line. The fourth is an expectation of runtime behavior. This library emits text; it does not execute it, and the claim that output complies with PSR-12 and PER is a formatting guarantee rather than a correctness one, so a generated constructor still has to be tested like any other.

Editorial conclusion

Nette PhpGenerator suits a project generating data transfer objects, enums, stubs or migration files where the output has to stay readable and comply with PSR-12. It does not suit a project that only needs one template file, nor a codebase that must still run on PHP older than 8.1. Before adopting, install with composer require nette/php-generator, check your runtime against the 8.1 to 8.5 range, print through the Printer rather than echoing when you care about formatting, and commit the generated code rather than regenerating it on every build.

Frequently asked questions

Which PHP versions does Nette PhpGenerator support?

PhpGenerator 4.2 is compatible with PHP 8.1 to 8.5. The pitch is about supporting the newest features such as property hooks, enums and attributes, but the stated floor is 8.1.

How do I install Nette PhpGenerator?

With Composer: composer require nette/php-generator. Documentation is on doc.nette.org/php-generator, and the API reference for ClassType, Property, Constant and Method is on api.nette.org.

How does PhpGenerator produce the code?

You build a ClassType with a chain of methods that each return the object, then echo it to typecast it to a string. A Printer can be used instead when you need to configure the output, which is how the PSR-12 and PER compliance is applied.

What happens when I add a member that already exists?

An exception is thrown, for properties, constants, methods and parameters alike. The matching removeProperty, removeConstant, removeMethod and removeParameter calls exist so an existing class can be modified rather than only created.

Does PhpGenerator support PHP 8 constructor promotion?

Yes, through addPromotedParameter on a method object, with optional per-parameter visibility. Readonly properties and classes are marked with setReadOnly.

Official sources

  1. Issues
  2. nette/php-generator on GitHub
  3. Project website
  4. README
  5. Releases