code-stencil/code-stencil

A beautiful way to create dynamic code

This package's canonical repository appears to be gone and the package has been frozen as a result.

v1.1.1 2024-02-27 18:26 UTC

README

Code Stencil

GitHub Workflow Status (master) Total Downloads Latest Version License

Introduction

Code Stencil provides an elegant, easy to read pattern that is great for generating stub files, and will streamline your code generation tasks. A common occurrence with code generation is that readability and maintenance suffer, but with Code Stencil, it's a piece of cake!

Create a class

Stencil::make()
    ->php()
    ->strictTypes()
    ->namespace('App/Models')
    ->curlyStatement('class MyClass', fn(Stencil $s) => $s
        ->line('private OtherClass $otherClass;')
        ->use(OtherClass::class) // This will add a use statement to the file
        ->line()
        ->phpdoc(
            summary: 'Create a new instance of MyClass',
            tags: ['param' => ['OtherClass $class', 'bool $options']]
        )
        ->curlyStatement('function __construct(OtherClass $class, bool $options = false)', fn(Stencil $s) => $s
            ->line('$this->otherClass = $class;')
        )
    )
    ->save('MyClass.php')
Show Result
<?php

declare(strict_types = 1);

namespace App\Models;

use OtherClass;

class MyClass
{
    private OtherClass $otherClass;

    /**
     * Create a new instance of MyClass
     * @param OtherClass $class
     * @param bool       $options
     */
    function __construct(OtherClass $class, bool $options = false)
    {
        $this->otherClass = $class;
    }
}

It can also become much more dynamic, with the addition of variables, functions, and conditional logic.

Variables, functions, and conditionals

$properties = ['Id', 'Name', 'StartAt'];

$includeTimestamp = false;

Stencil::make()
    ->php()
    ->variable('__class__', 'MyClass')
    ->function('lcfirst', fn(string $value) => lcfirst($value))
    ->curlyStatement('class __class__', fn(Stencil $s) => $s
        ->foreach($properties, fn(Stencil $s, string $property) => $s
            ->line("private string % lcfirst %($property);") // We can call our 'lcfirst' function that we defined above using this syntax
        )
        ->when($includeTimestamp, fn(Stencil $s) => $s->line('private DateTime $timestamp;'))
    )
    ->save('__class__.php') // File output will be to MyClass.php
Show Result
<?php

class MyClass {
    private string id;
    private string name;
    private string startAt;
}

This example is basic, but there's no limit to the complexity of code you can generate!

For Laravel

Native integration for stub files in Laravel is also supported. You'll be able to create stub files for use with existing laravel commands. This means that you can return a Stencil directly within a custom stub file, and it will then be processed just like any other stencil.

If you don't already have the stubs published in your project, you can run php artisan stub:publish.

Then within any of these files you can create a stencil like so.

<?php

use CodeStencil\Stencil;

return Stencil::make()
    ->php()
    ->namespace('App\Models')
    ->use('Illuminate\Database\Eloquent\Factories\HasFactory')
    ->use('Illuminate\Database\Eloquent\Model')
    ->curlyStatement('class i_name extends Model', fn(Stencil $s) => $s
        ->line('use HasFactory;')
    )
    ->overrideStubLocation(base_path('Domain/Other/Path/i_name.php'));

Changing the stub output location

In larger projects, you may not be using the default locations where Laravel generates the file. If you would like to change this, you can call the overrideStubLocation method on the Stencil, and provide a custom location where you would like the stub file to be written to. Note that this method is implemented via a macro, and code completion will not be available for it.

If you have a formatter installed, such as Pint, PHP CS Fixer, or StyleCI, your stencil will be formatted as well!