zetacomponents/php-generator
The Zeta Components PHP Generator provides an object-oriented API for programmatically generating PHP source code, including classes, methods, properties, parameters, constants, and docblocks. It is useful for scaffolding, automation, and dynamic code generation.
Install it like any Composer package:
composer require zetacomponents/php-generator
The library is a small source-code emitter. You describe a class in PHP objects — name, parents, use statements, methods, parameters — and then ask it to produce the file. Nothing is generated automatically; it is a blank canvas with ergonomic accessors.
The classes are PSR-4 autoloaded under the package namespace (Zetacomponents\PhpGenerator\PhpGenerator, PhpGeneratorMethod, PhpGeneratorParameter, PhpGeneratorDependency, PhpGeneratorReturnType, PhpGeneratorException). Older documentation and blog posts use the eZ Components names (ezcPhpGenerator, ezcPhpGeneratorMethod, …); the API is the same. Confirm the exact prefix by looking in vendor/zetacomponents/php-generator/src.
Where to look first: PhpGenerator (the only orchestrator — properties like $className, $extends, $uses, and addMethod()/addDependency()/generate()/save()), then PhpGeneratorMethod for $body and $returnType. There is no maintained docs site; treat the source as the spec.
First use case — scaffold a value object:
use Zetacomponents\PhpGenerator\PhpGenerator;
use Zetacomponents\PhpGenerator\PhpGeneratorMethod;
use Zetacomponents\PhpGenerator\PhpGeneratorParameter;
$gen = new PhpGenerator( __DIR__ . '/Generated/Money.php' );
$gen->className = 'Money';
$ctor = new PhpGeneratorMethod( '__construct' );
$ctor->addParameter( new PhpGeneratorParameter( 'amount', 'int' ) );
$ctor->addParameter( new PhpGeneratorParameter( 'currency', 'string', "'EUR'" ) );
$ctor->body = '$this->amount = $amount;' . "\n" . '$this->currency = $currency;';
$gen->addMethod( $ctor );
echo $gen->generate(); // inspect the string
$gen->save(); // write the file
generate() then save(). generate() returns the code as a string and touches nothing; save() writes it. Keep generate() in tests and previews, and only call save() after a lint pass.$method->body. Use nowdoc/heredoc plus sprintf() with placeholders to keep generated bodies readable; the package only handles signatures, visibility, docblocks, and braces.addDependency(). addDependency( new PhpGeneratorDependency( 'LoggerInterface', 'logger' ) ) emits a private typed property, a constructor parameter, and the $this->logger = $logger; assignment in one call — useful when generating service classes or factories.app/Generated/, add that directory to your PSR-4 map, and expose an Artisan command (make:dtos, generate:api-client). Run the command in CI so the committed output can't drift.PhpGeneratorReturnType to $this or static on generated setters/builders, and use addUse( 'App\\Contracts\\Foo' ) so fully-qualified names stay out of method bodies.addConstant(), addVariable(), and addUse() cover most of the class-level surface; use them to emit enums, typed properties, and imports without string concatenation.nette/php-generator, which is actively maintained and has a richer AST-like API — the migration cost is small because the concepts map almost 1:1.NOASSERTION. The code is Apache-2.0 (Zeta Components heritage), but verify the LICENSE file before shipping inside a commercial product.save() silently overwrites the target path. Write to a temp file and rename() if you need atomicity, and ensure the directory exists and is writable — otherwise you get a PhpGeneratorException.body. Broken PHP is emitted happily. Run php -l (or a PHP-Lint library) on every generated file in CI.$ that you do not want interpolated.new PhpGeneratorParameter( 'currency', 'string', "'EUR'" ) needs the quotes baked in; pass integers as '0'. Passing null omits the default entirely.className. Check the constructor/properties of the installed version before relying on it.PhpGenerator and override generate(), or post-process the returned string. Do not expect visitors or a template engine.var_dump( $gen->generate() ) before save(); because the output is plain text you can diff it against a fixture and eyeball whitespace regressions.zetacomponents/base. If you see ezcBase* autoload errors, Composer did not pull the base package (or your autoloader was hand-rolled). Let Composer resolve it rather than requiring classes individually.How can I help you explore Laravel packages today?