murtukov/php-code-generator
PHPCodeGenerator generates PHP 7.4 source code via a fluent API. Build files with namespaces and imports, create classes/interfaces/traits/enums, methods/functions/closures, control structures, arrays, literals, comments, and configure global formatting options.
Installation Add the package via Composer:
composer require murtukov/php-code-generator
Requires PHP 7.4+.
Basic Usage with Constructor Promotion Import the core class and generate code with constructor property promotion:
use Murtukov\CodeGenerator\Generator;
$generator = new Generator();
$code = $generator->class('UserDTO')
->addProperty('name', 'string', '$name', true) // Enable constructor promotion
->addProperty('email', 'string', '$email', true)
->getCode();
First Use Case: Scaffold a Model with Constructor Promotion Generate an Eloquent model with constructor property promotion:
$generator = new Generator();
$modelCode = $generator->class('Post')
->extends('App\Models\Model')
->addProperty('title', 'string', '$title', true) // Constructor promotion
->addProperty('content', 'string', '$content', true)
->addMethod('getRouteKeyName')
->addStatement('return $this->slug;')
->getCode();
Generate Data Transfer Objects (DTOs) with constructor promotion for cleaner initialization:
$generator = new Generator();
$dtoClass = $generator->class('UserDTO')
->addProperty('id', 'int', '$id', true)
->addProperty('name', 'string', '$name', true)
->addMethod('toArray')
->addStatement('return [')
->addStatement(' "id" => $this->id,')
->addStatement(' "name" => $this->name,')
->addStatement('];')
->getCode();
Scaffold models with constructor promotion via a custom Artisan command:
// app/Console/Commands/GenerateModel.php
use Murtukov\CodeGenerator\Generator;
class GenerateModel extends Command {
protected $signature = 'generate:model {name}';
public function handle() {
$generator = new Generator();
$code = $generator->class($this->argument('name'))
->extends('App\Models\Model')
->addProperty('title', 'string', '$title', true) // Constructor promotion
->addProperty('slug', 'string', '$slug', true)
->addProperty('created_at', 'datetime', 'new DateTime()')
->addProperty('updated_at', 'datetime', 'new DateTime()')
->getCode();
file_put_contents("app/Models/{$this->argument('name')}.php", $code);
}
}
Use constructor promotion in test stubs for concise initialization:
$testGenerator = new Generator();
$testClass = $testGenerator->class('UserTest')
->extends('Tests\TestCase')
->addMethod('testConstructorPromotion')
->addStatement('$user = new User(name: "John", email: "john@example.com");')
->addStatement('$this->assertEquals("John", $user->name);')
->getCode();
Leverage the improved Literal component for dynamic property defaults:
$generator = new Generator();
$configClass = $generator->class('AppConfig')
->addProperty('debug', 'bool', 'env("APP_DEBUG") === "true"')
->addProperty('cache', 'array', '[]')
->addMethod('get')
->addStatement('return $this->{$key} ?? null;')
->getCode();
Bind the generator to the container for dependency injection:
// app/Providers/AppServiceProvider.php
public function register() {
$this->app->singleton(Generator::class, function () {
return new Generator();
});
}
Generate TypeScript interfaces dynamically:
$tsGenerator = new Generator();
$interface = $tsGenerator->interface('UserInterface')
->addProperty('id', 'number')
->addProperty('name', 'string', '""')
->getCode();
Override methods to add Laravel-specific syntax (e.g., $fillable + constructor promotion):
$generator->class('User')
->addFillable(['name', 'email'])
->addProperty('name', 'string', '$name', true) // Constructor promotion
->addProperty('email', 'string', '$email', true);
Constructor Promotion Syntax Quirks
Ensure the third argument for addProperty is a valid literal (e.g., $name for promotion):
// Correct
$generator->addProperty('name', 'string', '$name', true);
// Incorrect (will not promote)
$generator->addProperty('name', 'string', 'null', true);
Empty Block Rendering Changes
The way empty blocks ({}) are rendered may differ across components. Test thoroughly:
$generator->addMethod('emptyMethod')
->addStatement('{}'); // Verify rendering in generated code
No Namespacing by Default Manually set the namespace or extend the generator:
$generator->class('User')->setNamespace('App\Models');
Limited PHP 8+ Support for New Features
While constructor promotion is supported, avoid other PHP 8+ features (e.g., match, enum) unless explicitly patched.
No Built-in File Writing The generator only outputs strings; handle file I/O separately:
file_put_contents("app/Models/{$className}.php", $generator->getCode());
Inspect Generated Code
Use dd($generator->getCode()) to debug before writing to disk, especially for constructor promotion:
$generator->addProperty('name', 'string', '$name', true);
dd($generator->getCode());
Check Method Chaining
Ensure methods are chained correctly (e.g., ->addProperty()->addMethod()), and verify the true flag for constructor promotion:
$generator->class('User')
->addProperty('name', 'string', '$name', true) // Constructor promotion
->addMethod('getName');
Validate PHP Syntax
Use php -l to lint generated files:
php -l app/Models/GeneratedClass.php
Custom Directives for Constructor Promotion Extend the generator to support Laravel-specific directives with constructor promotion:
$generator->addDirective('fillable', ['name', 'email'])
->addProperty('name', 'string', '$name', true);
Plugin System for Reusable Generators Create a trait for reusable generators with constructor promotion:
trait EloquentGenerator {
public function addTimestamps() {
return $this->addProperties([
'created_at' => ['datetime', 'new DateTime()'],
'updated_at' => ['datetime', 'new DateTime()'],
]);
}
public function addConstructorPromotion($properties) {
foreach ($properties as $name => $type) {
$this->addProperty($name, $type, '$' . $name, true);
}
return $this;
}
}
Integration with IDE Helpers
Generate @property annotations for PHPStan/PSR-12, including constructor-promoted properties:
$generator->addMethod('getName')
->addDocBlock('/** @return string */');
$generator->addProperty('name', 'string', '$name', true)
->addDocBlock('/** @property string $name */');
Dynamic Property Types with Literals
Use mixed or array as fallbacks for dynamic properties, and leverage the improved Literal component:
$generator->addProperty('metadata', 'array', '[]');
$generator->addProperty('config', 'array', '$config', true); // Constructor promotion
bootstrap/cache/generated to avoid regeneration.How can I help you explore Laravel packages today?