ruudk/code-generator

A library for generating beautiful PHP code with automatic namespace import management.

0.1.6 2025-08-13 07:23 UTC

This package is auto-updated.

Last update: 2025-08-13 07:49:36 UTC


README

Latest Stable Version PHP Version Require Total Downloads License

Code Generator

A library for generating beautiful PHP code with automatic namespace import management.

Installation

Install the library via Composer:

composer require ruudk/code-generator

Example

<?php

declare(strict_types=1);

use Ruudk\CodeGenerator\CodeGenerator;

include 'vendor/autoload.php';

$generator = new CodeGenerator('Example\Demo');

echo $generator->dump([
    '// Auto-generated example file',
    '',

    $generator->dumpAttribute('Example\Attributes\Something'),
    sprintf(
        'final readonly class %s extends %s',
        $generator->import('Example\Demo'),
        $generator->import('Example\Parent'),
    ),
    '{',
    $generator->indent(function () use ($generator) {
        yield 'public function __construct(';
        yield $generator->indent(function () use ($generator) {
            yield sprintf('private %s $date,', $generator->import(DateTimeImmutable::class));
        });
        yield ') {';
        yield $generator->indent(function () use ($generator) {
            yield from $generator->statement($generator->dumpCall('parent', '__construct', [
                "'Hello, World!'",
                'true',
            ], true));
        });
        yield '}';
    }),
    '}',
]);

Output

<?php

declare(strict_types=1);

namespace Example\Demo;

use DateTimeImmutable;
use Example\Attributes\Something;
use Example\Demo;
use Example\Parent;

// Auto-generated example file

#[Something]
final readonly class Demo extends Parent
{
    public function __construct(
        private DateTimeImmutable $date,
    ) {
        parent::__construct(
            'Hello, World!',
            true,
        );
    }
}

Another Example

<?php

declare(strict_types=1);

use Ruudk\CodeGenerator\CodeGenerator;
use Ruudk\CodeGenerator\Group;

include 'vendor/autoload.php';

$generator = new CodeGenerator('Example\Demo');

// First, let's see what happens step by step
echo $generator->dump(function () use ($generator) {
    // Comments
    yield '// This file demonstrates ALL CodeGenerator features';
    yield '// Auto-generated example file';
    yield '';

    // Class with attributes
    yield $generator->dumpAttribute('Example\Attributes\Entity');
    yield $generator->dumpAttribute('Example\Attributes\Table');
    yield sprintf(
        'final class DemoClass extends %s implements %s, %s',
        $generator->import('Example\BaseClass'),
        $generator->import('Example\Interfaces\FirstInterface'),
        $generator->import('Example\Interfaces\SecondInterface', byParent: true),
    );
    yield '{';
    yield $generator->indent(function () use ($generator) {
        // Properties with indentation
        yield '// Class properties';
        yield sprintf('private %s $date;', $generator->import(DateTimeImmutable::class));
        yield '';
        yield ''; // multiple newlines are ignored
        yield sprintf('private %s $collection;', $generator->import('Doctrine\Common\Collections\Collection'));
        yield sprintf('private ?%s $optional = null;', $generator->import(stdClass::class));
        yield '';

        // Constructor using dumpCall

        yield 'public function __construct(';
        yield $generator->indent([
            sprintf('private readonly %s $service,', $generator->import('App\Services\MainService')),
            sprintf(
                'private %s $enum = %s,',
                $generator->import('App\Enums\Status'),
                $generator->import('App\Enums\Status') . '::Active',
            ),
            'string $name = \'default\',',
        ]);
        yield ') {';
        $generator->indent(function () use ($generator) {
            // Parent constructor call
            yield from $generator->dumpCall('parent', '__construct', [
                'enabled: true',
                'timeout: 30',
            ], static: true);

            yield '';

            // Method chaining with dumpCall
            yield from $generator->statement(
                $generator->dumpCall(
                    $generator->dumpCall(
                        $generator->dumpCall('$this', 'getBuilder'),
                        'setName',
                        ['$name'],
                    ),
                    'setDebug',
                    ['true'],
                ),
            );

            yield '';

            // Static method call
            yield from $generator->statement(
                $generator->dumpCall('Logger', 'info', [
                    '\'Constructor initialized\'',
                    '[\'class\' => __CLASS__]',
                ], static: true),
            );
        });
        yield '}';
        yield '';

        // Method with various features
        yield '/**';
        yield ' * Demonstrates various generator features';
        yield ' */';
        yield 'public function processData(array $data): void';
        yield '{';
        yield $generator->indent(function () use ($generator) {
            // Using prefixFirst and suffixLast
            yield from $generator->prefixFirst(
                '$result = ',
                $generator->suffixLast(';', ['$this->calculate($data)']),
            );
            yield '';

            // Using wrap
            yield from $generator->statement(
                $generator->wrap('$wrapped = [', ['$result'], ']'),
            );
            yield '';

            // Using maybeWrap with condition
            yield from $generator->maybeWrap(true, 'if ($result) { ', [
                '$this->save($result);',
            ], ' }');
            yield '';

            // Using allSuffix for array elements
            yield '$items = [';
            yield $generator->indent(
                $generator->allSuffix(',', [
                    '\'first\'',
                    '\'second\'',
                    '\'third\'',
                    '// Comment not suffixed',
                ]),
            );
            yield '];';
            yield '';

            // Using dumpFunctionCall
            yield from $generator->statement(
                $generator->dumpFunctionCall('array_map', [
                    'fn($x) => $x * 2',
                    '$items',
                ]),
            );
            yield '';

            // Using join
            $joined = $generator->join(', ', ['$a', '$b', '$c']);
            yield sprintf('$concatenated = sprintf(\'%%s\', %s);', $joined);
            yield '';

            // Using joinFirstPair
            yield from $generator->statement(
                $generator->joinFirstPair([
                    '$prefix',
                    ' = \'value\'',
                    ' . \'suffix\'',
                ]),
            );
            yield '';

            // Using suffixFirst
            yield 'foreach ($items as $item) {';
            yield $generator->indent(
                $generator->suffixFirst(':', [
                    'echo $item',
                    'echo PHP_EOL',
                ]),
            );
            yield '}';
            yield '';

            // Using new instance creation
            yield from $generator->statement(
                $generator->dumpCall(DateTime::class, '__construct', ['\'now\'']),
            );
            yield '';

            // Multi-line string with maybeNowDoc
            $multilineText = "This is a\nmulti-line\nstring example";
            yield sprintf('$text = %s;', $generator->maybeNowDoc($multilineText));
            yield '';

            // Class reference
            yield sprintf(
                '$className = %s;',
                $generator->dumpClassReference('Example\SomeClass'),
            );
            yield '';
        });
        yield '}';
        yield '';

        // Private method with Group nesting
        yield 'private function calculate(array $data): int';
        yield '{';
        yield $generator->indent(function () use ($generator) {
            yield 'return match($data[\'type\'] ?? null) {';
            yield $generator->indent([
                '\'sum\' => array_sum($data[\'values\']),',
                '\'count\' => count($data[\'values\']),',
                '\'max\' => max($data[\'values\']),',
                'default => 0,',
            ]);
            yield '};';
        });
        yield '}';
        yield '';

        // Static factory method
        yield sprintf('public static function create(): %s', $generator->import('self'));
        yield '{';
        yield $generator->indent([
            sprintf('return new %s(', $generator->import('self')),
            $generator->indent([
                sprintf('new %s(),', $generator->import('App\Services\MainService')),
            ]),
            ');',
        ]);
        yield '}';
    });
    yield '}';
    yield '';

    // Interface
    yield sprintf(
        'interface CustomInterface extends %s',
        $generator->import('Example\Interfaces\BaseInterface'),
    );
    yield '{';
    yield $generator->indent([
        'public function process(mixed $data): void;',
        '',
        'public function validate(array $rules): bool;',
    ]);
    yield '}';
    yield '';

    // Enum
    yield 'enum Color: string';
    yield '{';
    yield $generator->indent([
        'case RED = \'#FF0000\';',
        'case GREEN = \'#00FF00\';',
        'case BLUE = \'#0000FF\';',
        '',
        'public function toRgb(): array',
        '{',
        $generator->indent([
            'return match($this) {',
            $generator->indent([
                'self::RED => [255, 0, 0],',
                'self::GREEN => [0, 255, 0],',
                'self::BLUE => [0, 0, 255],',
            ]),
            '};',
        ]),
        '}',
    ]);
    yield '}';
    yield '';

    // Trait
    yield 'trait TimestampableTrait';
    yield '{';
    yield $generator->indent(function () use ($generator) {
        yield sprintf('private %s $createdAt;', $generator->import(DateTimeInterface::class));
        yield sprintf('private ?%s $updatedAt = null;', $generator->import(DateTimeInterface::class));
        yield '';
        yield 'public function updateTimestamps(): void';
        yield '{';
        yield $generator->indent([
            'if ($this->createdAt === null) {',
            $generator->indent(
                $generator->statement(['$this->createdAt = new \\DateTimeImmutable()']),
            ),
            '}',
            '$this->updatedAt = new \\DateTimeImmutable();',
        ]);
        yield '}';
    });
    yield '}';
    yield '';

    // Anonymous class
    yield from $generator->statement([
        '$anonymous = new class(',
        $generator->indent(['$dependency']),
        ') {',
        $generator->indent([
            'public function __construct(private mixed $dep) {}',
            '',
            'public function execute(): void',
            '{',
            $generator->indent(['echo \'Anonymous class method\';']),
            '}',
        ]),
        '}',
    ]);
    yield '';

    // Global function
    yield 'function helperFunction(string $input): string';
    yield '{';
    yield $generator->indent(
        $generator->statement([
            'return strtoupper($input)',
        ]),
    );
    yield '}';
});

Output

<?php

declare(strict_types=1);

namespace Example\Demo;

use App\Enums\Status;
use App\Services\MainService;
use DateTime;
use DateTimeImmutable;
use DateTimeInterface;
use Doctrine\Common\Collections\Collection;
use Example\Attributes\Entity;
use Example\Attributes\Table;
use Example\BaseClass;
use Example\Interfaces;
use Example\Interfaces\BaseInterface;
use Example\Interfaces\FirstInterface;
use Example\SomeClass;
use Logger;
use self;
use stdClass;

// This file demonstrates ALL CodeGenerator features
// Auto-generated example file

#[Entity]
#[Table]
final class DemoClass extends BaseClass implements FirstInterface, Interfaces\SecondInterface
{
    // Class properties
    private DateTimeImmutable $date;

    private Collection $collection;
    private ?stdClass $optional = null;

    public function __construct(
        private readonly MainService $service,
        private Status $enum = Status::Active,
        string $name = 'default',
    ) {
    }

    /**
     * Demonstrates various generator features
     */
    public function processData(array $data): void
    {
        $result = $this->calculate($data);

        $wrapped = [$result];

        if ($result) { $this->save($result); }

        $items = [
            'first',
            'second',
            'third',
            // Comment not suffixed
        ];

        array_map(
            fn($x) => $x * 2,
            $items,
        );

        $concatenated = sprintf('%s', $a, $b, $c);

        $prefix = 'value'
         . 'suffix';

        foreach ($items as $item) {
            echo $item:
            echo PHP_EOL
        }

        new DateTime('now');

        $text = <<<'EOD'
            This is a
            multi-line
            string example
            EOD;

        $className = SomeClass::class;
    }

    private function calculate(array $data): int
    {
        return match($data['type'] ?? null) {
            'sum' => array_sum($data['values']),
            'count' => count($data['values']),
            'max' => max($data['values']),
            default => 0,
        };
    }

    public static function create(): self
    {
        return new self(
            new MainService(),
        );
    }
}

interface CustomInterface extends BaseInterface
{
    public function process(mixed $data): void;

    public function validate(array $rules): bool;
}

enum Color: string
{
    case RED = '#FF0000';
    case GREEN = '#00FF00';
    case BLUE = '#0000FF';

    public function toRgb(): array
    {
        return match($this) {
            self::RED => [255, 0, 0],
            self::GREEN => [0, 255, 0],
            self::BLUE => [0, 0, 255],
        };
    }
}

trait TimestampableTrait
{
    private DateTimeInterface $createdAt;
    private ?DateTimeInterface $updatedAt = null;

    public function updateTimestamps(): void
    {
        if ($this->createdAt === null) {
            $this->createdAt = new \DateTimeImmutable();
        }
        $this->updatedAt = new \DateTimeImmutable();
    }
}

$anonymous = new class(
    $dependency
) {
    public function __construct(private mixed $dep) {}

    public function execute(): void
    {
        echo 'Anonymous class method';
    }
};

function helperFunction(string $input): string
{
    return strtoupper($input);
}

Development

Running Tests

vendor/bin/phpunit

Static Analysis

vendor/bin/phpstan analyse

Code Style

vendor/bin/php-cs-fixer fix

License

This library is released under the MIT License. See the LICENSE file for details.