Installation (via Composer):
composer require zendframework/zend-code
(Note: The package is now maintained as laminas/laminas-code; use that instead for new projects.)
First Use Case: Generate a simple class dynamically:
use Zend\Code\Generator\ClassGenerator;
use Zend\Code\Generator\MethodGenerator;
$class = new ClassGenerator('MyClass', 'MyNamespace');
$method = new MethodGenerator('doSomething');
$method->setBody('return "Hello, World!";');
$class->addMethodFromGenerator($method);
echo $class->generate();
Key Classes to Explore:
ClassGenerator: For generating entire classes.MethodGenerator: For generating methods.PropertyGenerator: For generating properties.ParameterGenerator: For generating method parameters.ValueGenerator: For generating PHP values (e.g., arrays, strings).Workflow: Generate classes at runtime (e.g., for proxies, DTOs, or dynamic APIs).
$class = new ClassGenerator('DynamicUser', 'App\\Entities');
$class->setExtendedClass('App\\BaseEntity');
$property = new PropertyGenerator('name', 'string');
$property->setVisibility('protected');
$class->addPropertyFromGenerator($property);
$method = new MethodGenerator('getFullName');
$method->setReturnType('string')
->setBody('return $this->name;');
$class->addMethodFromGenerator($method);
file_put_contents('DynamicUser.php', $class->generate());
Workflow: Parse existing code, modify it, and regenerate.
use Zend\Code\Scanner\ClassScanner;
$scanner = new ClassScanner();
$reflection = $scanner->scanFile('ExistingClass.php');
// Modify a method
$method = $reflection->getMethod('existingMethod');
$method->setBody('// Updated logic: ' . $method->getBody());
// Regenerate the file
file_put_contents('ExistingClass.php', $reflection->generate());
Pattern: Use ClassGenerator to create immutable DTOs.
$dtoClass = new ClassGenerator('UserDto', 'App\\Dto');
$dtoClass->setFinal(true);
$properties = [
'id' => ['type' => 'int', 'visibility' => 'private'],
'name' => ['type' => 'string', 'visibility' => 'private'],
];
foreach ($properties as $name => $config) {
$property = new PropertyGenerator($name, $config['type']);
$property->setVisibility($config['visibility']);
$dtoClass->addPropertyFromGenerator($property);
// Add getter
$getter = new MethodGenerator('get' . ucfirst($name));
$getter->setReturnType($config['type'])
->setBody('return $this->' . $name . ';');
$dtoClass->addMethodFromGenerator($getter);
}
file_put_contents('UserDto.php', $dtoClass->generate());
Pattern: Use for dynamic migrations, model generation, or API scaffolding.
// Example: Generate a migration file dynamically
$migration = new ClassGenerator('CreateUsersTable', 'Database\\Migrations');
$migration->setExtendedClass('Illuminate\\Database\\Migrations\\Migration');
$method = new MethodGenerator('up');
$method->setBody(
'$table = $this->schema->create(\'users\', function ($table) { ' .
'$table->id(); ' .
'$table->string(\'name\'); ' .
'$table->timestamps(); ' .
'});'
);
$migration->addMethodFromGenerator($method);
file_put_contents(
database_path('migrations/' . date('Y_m_d_His') . '_create_users_table.php'),
$migration->generate()
);
Pattern: Generate complex PHP values (e.g., nested arrays, objects).
use Zend\Code\Generator\ValueGenerator;
$value = new ValueGenerator();
$value->addArrayItem('key1', 'value1');
$value->addArrayItem('key2', new ValueGenerator(['nested' => true]));
echo $value->generate(); // Outputs: ['key1' => 'value1', 'key2' => ['nested' => true]]
Namespace Handling:
ClassGenerator to avoid issues with fully qualified names.$class = new ClassGenerator('User', 'App\\Models');
$class->setExtendedClass('App\\BaseModel'); // Correct
$class->setExtendedClass('BaseModel'); // Incorrect (relative)
Type Validation:
'foobar') will throw InvalidArgumentException.TypeGenerator for complex types:
$type = new TypeGenerator('App\\Models\\User[]');
$method->setReturnType($type);
PHP Version Compatibility:
void return types or nullable types (?string) require PHP 7.1+.DocBlock Generation:
DocBlockGenerator explicitly:
$method->setDocBlock(new DocBlockGenerator('/** @return string */'));
Method Overrides:
copyMethodSignature(), ensure the target class can actually override the method (e.g., visibility rules).Array Generation:
[]) is used by default. To force long syntax (array()), configure ValueGenerator:
$value = new ValueGenerator();
$value->setUseShortArraySyntax(false);
Inspect Generated Code:
$generator->generate() to preview code before writing to a file.echo $class->generate(); // Debug output
Scanner Limitations:
ClassScanner may not handle all edge cases (e.g., complex closures, dynamic code). For robust parsing, consider combining with PHPParser.use PhpParser\ParserFactory;
$parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7);
$code = file_get_contents('file.php');
$stmts = $parser->parse($code);
Visibility Issues:
public, protected, private).addMethodFromGenerator() is used (not addMethod() for raw strings).Performance:
$cache = file_get_contents('cache/class_cache.json');
if ($cache) {
$class = json_decode($cache, true);
} else {
$class = (new ClassGenerator('LargeClass'))->addMethods(...);
file_put_contents('cache/class_cache.json', json_encode($class->generate()));
}
Custom Generators:
AbstractGenerator to create domain-specific generators (e.g., for SQL, YAML).class SqlGenerator extends AbstractGenerator {
public function generate() { /* Custom logic */ }
}
Scanner Extensions:
ClassScanner to handle custom syntax (e.g., annotations, traits).class CustomScanner extends ClassScanner {
protected function scanAnnotations() { /* Custom logic */ }
}
ValueGenerator Hooks:
ValueGenerator to support custom value types (e.g., DateTime objects):
$value = new ValueGenerator();
$value->addValue(new CustomValueGenerator(new \DateTime()));
Integration with Laravel:
$this->app->bind('code-generator', function () {
return new ClassGenerator();
});
class CodeGeneratorFacade extends Facade {
protected static function getFacadeAccessor() { return 'code-generator'; }
How can I help you explore Laravel packages today?