You can use the component spiral/reactor to generate PHP classes code using fluent declarative wrappers. This component
is based on nette/php-generator.
To install the component:
composer require spiral/reactor
Note
Please note that the spiral/framework >= 2.7 already includes this component.
To declare a class, use Spiral\Reactor\ClassDeclaration.
use Spiral\Reactor\ClassDeclaration;
$class = new ClassDeclaration('MyClass');
dump($class->render()); // or dump((string) $class);
The output:
class MyClass
{
}
You can get access to most of the declaration directly from the class.
To render class property:
$class = new ClassDeclaration('MyClass');
$class->addProperty('property', 'default')
    ->setProtected()
    ->setReadOnly()
    ->setType('string')
    ->setComment(['My property.', '@var string'])
    ->addAttribute('SomeAttribute');
dump((string) $class);
The output:
class MyClass
{
    /**
     * My property.
     * @var string
     */
    #[SomeAttribute]
    protected readonly string $property = 'default';
}
To render constant:
$class = new ClassDeclaration('MyClass');
$class->addConstant('MY_CONSTANT', 'default')
    ->setPublic()
    ->setFinal()
    ->addAttribute('SomeAttribute')
    ->setComment(['My constant']);
dump((string) $class);
The output:
class MyClass
{
    /** My constant */
    #[SomeAttribute]
    final public const MY_CONSTANT = 'default';
}
To add trait declaration:
$class = new ClassDeclaration('MyClass');
$class->addTrait(PrototypeTrait::class);
dump((string) $class);
The output:
class MyClass
{
    use Spiral\Prototype\Traits\PrototypeTrait;
}
To implement a given interface or extend a base class:
use Spiral\Reactor\ClassDeclaration;
use Cycle\ORM\Select\Repository;
$class = new ClassDeclaration('MyClass');
$class
    ->addImplement(\Countable::class)
    ->setExtends(Repository::class);
dump((string) $class);
The output:
class MyClass extends Cycle\ORM\Select\Repository implements Countable
{
}
To generate a class method:
$class = new ClassDeclaration('MyClass');
$class->addMethod('ping')
    ->setPublic()
    ->setComment('My method')
    ->setReturnType('string')
    ->setReturnNullable()
    ->setFinal()
    ->setBody('return $a;')
    ->addAttribute('SomeAttribute')
        ->addParameter('a', null)
        ->setType('string')
        ->setNullable(true);
dump((string) $class);
The output:
class MyClass
{
    /**
     * My method
     */
    #[SomeAttribute]
    final public function ping(?string $a = null): ?string
    {
        return $a;
    }
}
To create a class inside a specific namespace:
use Spiral\Reactor\Partial\PhpNamespace;
$namespace = new PhpNamespace('App\\Some');
$namespace->addClass('MyClass')
dump((string) $namespace);
The output:
namespace App\Some;
class MyClass
{
}
To declare an interface, use Spiral\Reactor\InterfaceDeclaration.
$interface = new InterfaceDeclaration('MyInterface');
$interface
    ->addExtend(\Countable::class)
    ->addComment('My interface')
    ->addMethod('someMethod')
        ->setPublic()
        ->setReturnType('int');
dump((string) $interface);
The output:
/**
 * My interface
 */
interface MyInterface extends Countable
{
    public function someMethod(): int;
}
To declare enum, use Spiral\Reactor\EnumDeclaration.
$enum = new EnumDeclaration('MyEnum');
$enum->addCase('First', 'first');
$enum->addCase('Second', 'second');
$enum
    ->setType('string')
    ->addConstant('FOO', 'bar')
    ->addComment('Description of enum')
    ->addAttribute('SomeAttribute');
$enum
    ->addMethod('getCase')
    ->setReturnType('string')
    ->addBody('return self::First->value;');
dump((string) $enum);
The output:
/**
 * Description of enum
 */
#[SomeAttribute]
enum MyEnum: string
{
    public const FOO = 'bar';
    case First = 'first';
    case Second = 'second';
    public function getCase(): string
    {
        return self::First->value;
    }
}
To declare a global function, use Spiral\Reactor\FunctionDeclaration.
$function = new FunctionDeclaration('myFunction');
$function
    ->addBody('return \'Hello world\';')
    ->setReturnType('string')
    ->addAttribute('SomeAttribute')
    ->addComment('Some function');
dump((string) $function);
The output:
/**
 * Some function
 */
#[SomeAttribute]
function myFunction(): string
{
    return 'Hello world';
}
To declare a trait, use Spiral\Reactor\TraitDeclaration.
$trait = new TraitDeclaration('MyTrait');
$trait
    ->setComment('Some trait')
    ->addMethod('myMethod')
        ->setPublic()
        ->setReturnType('void');
dump((string) $trait);
The output:
/**
 * Some trait
 */
trait MyTrait
{
    public function myMethod(): void
    {
    }
}
You can collect classes, interfaces, traits, global functions, and enums in a file:
$namespace = new PhpNamespace('MyNamespace');
$namespace
    ->addUse(\Countable::class)
    ->addUse(Repository::class, 'Repo') // with alias
    ->addUseFunction('count');
$class = $namespace->addClass('MyClass');
$class
    ->addImplement(\Countable::class)
    ->addMethod('count')
        ->setReturnType('int')
        ->addBody('return 1;');
$file = new FileDeclaration();
$file
    ->setStrictTypes()
    ->addNamespace($namespace);
dump((string) $file);
The output:
namespace MyNamespace;
use Countable;
use Cycle\ORM\Select\Repository as Repo;
use function count;
class MyClass implements Countable
{
    public function count(): int
    {
        return 1;
    }
}