strukt / generator
Typed class generation, lightweight templating, and PHPDoc annotations.
Requires
- php: >=8.2
- strukt/base: dev-main
- strukt/commons: dev-main
Requires (Dev)
- pestphp/pest: ^4.0
Suggests
- strukt/console: Adds color() support for huecli() templates.
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-10-25 15:20:20 UTC
README
Typed PHP class generation, lightweight text templating, and PHPDoc annotation utilities for PHP 8.2 and later.
This package is the Strukt 3 rewrite of 'strukt2/generator'. It keeps the template, huecli, and deblock helpers while making parsing deterministic, handling missing values safely, adding runtime validation, and avoiding stateful rendering bugs.
Installation
composer require strukt/generator
The package depends on:
- strukt/base for helper registration;
- strukt/commons for the string and application utility layer.
Optional console color rendering is available when a compatible strukt/console package provides Strukt\Console\Color and the color() helper.
Quick start
Render a template:
<?php $html = template( '<h1>{{title}}</h1>', ['title' => 'Dashboard'], );
Parse annotations:
$annotations = Strukt\BasicBlock::parse(<<<'DOC' /** * @Route(/users) * @Method(GET, POST) */ DOC); // [ // 'Route' => '/users', // 'Method' => ['GET', 'POST'], // ]
Build a class:
$source = (string) new Strukt\ClassBuilder([ 'ns' => 'App\\Controller', 'class_name' => 'UserController', 'extends' => 'BaseController', 'methods' => [ [ 'name' => 'index', 'params' => '', 'body' => "\treturn [];", ], ], ]);
Global helpers
The autoloaded src/helpers.php registers each helper conditionally and adds its name to the Strukt helper registry.
| Helper | Parameters | Return type | Description |
|---|---|---|---|
| template | string $template, array $data | string | Renders values, loops, and conditions. |
| huecli | string $template | string | Resolves optional console color sections. |
| deblock | string $input | string | Removes PHPDoc comment delimiters. |
The helpers do not overwrite functions already supplied by an application.
Template syntax
Values
Use a word-shaped key between double braces:
Hello {{name}}!
Missing values render as an empty string. Null and false values also render as
empty text. Scalar values are converted to strings; arrays and objects are
JSON encoded unless an object exposes __toString().
Lists
Use a matching begin and end key:
{{begin:people}}
<b>{{name}}</b> {{surname}}
{{end:people}}
The value at people must be an array of arrays. Each item is rendered against its own values. Empty lines around the expanded list are normalized.
Conditions
Use a truthy data key:
{if enabled}
This section is visible.
{/if}
The complete section is trimmed when enabled and removed when the value is missing or false.
Rendering order
Templator::create() applies directives in this order:
- list loops;
- direct values;
- conditions.
This ordering allows placeholders inside list items to be resolved before conditional sections are finalized.
Templator API
Strukt\Templator contains static operations for the supported template syntax.
create
public static function create( string $template, array $data, ): string
Renders loops, direct values, and conditions in the documented order.
trimBlanks
public static function trimBlanks( string|array $text, ): string|array
Normalizes line endings, removes surrounding whitespace, and collapses repeated blank lines. Arrays are mapped recursively at the first level while retaining their keys.
element
public static function element( string $template, array $data, ): string
Replaces direct placeholders shaped like {{name}}.
loop
public static function loop( string $template, array $data, ): string
Expands matching {{begin:name}} and {{end:name}} blocks. Non-array or missing list values produce no rendered items.
condition
public static function condition( string $template, array $data, ): string
Resolves {if name} and {/if} blocks using PHP truthiness.
console
public static function console(string $template): string
Resolves {color name} and {/color} sections when both a console color class and color() helper are available. Without them, the template is returned unchanged.
DocBlocker
Strukt\DocBlocker converts between plain annotation text and PHPDoc comment blocks.
Constructor
public function __construct(string $block)
Accepts a complete docblock or plain lines. Opening and closing delimiters, leading stars, and surrounding whitespace are normalized.
deBlock
public static function deBlock(string $block): string
Returns the plain contents of a PHPDoc block without its comment delimiters. Blank lines inside the block are retained.
__toString
public function __toString(): string
Returns a normalized PHPDoc block. Blank input returns an empty string.
Example:
$plain = "@Route(/)\n@Method(GET, POST)"; $docblock = (string) new Strukt\DocBlocker($plain); // /** // * @Route(/) // * @Method(GET, POST) // */
BasicBlock
Strukt\BasicBlock parses annotation lines with parenthesized values.
parse
public static function parse(string $doc): array
Returns an array keyed by annotation name. A single argument returns as a string, comma-separated arguments return as a list, and repeated annotation names are merged into one list.
$result = BasicBlock::parse(<<<'DOC' /** * @Route(/roles) * @Method(GET, POST) * @Middleware(AuthToken) * @Middleware(GVerify) */ DOC); // [ // 'Route' => '/roles', // 'Method' => ['GET', 'POST'], // 'Middleware' => ['AuthToken', 'GVerify'], // ]
Arguments are split while respecting quoted text and nested brackets, so values containing commas can be represented safely.
ClassBuilder
Strukt\ClassBuilder renders a PHP class declaration with optional inheritance, interfaces, constructors, methods, and method annotations.
Constructor
public function __construct(array $params)
The params array requires:
| Key | Type | Description |
|---|---|---|
| ns | string | Namespace declaration. |
| class_name | string | Class name. |
Optional keys are:
| Key | Type | Description |
|---|---|---|
| extends | string | Parent class name. |
| implements | string or array | Interface name or comma-separated interface names. |
| methods | array | Method definitions. |
Missing or empty ns and class_name values throw InvalidArgumentException.
addInit
public function addInit( ?array $params = null, ): static
Adds a constructor with the supplied parameter source strings. It returns the builder for fluent use.
$builder->addInit(['string $name', 'int $id']);
Method definitions
Each methods entry may contain:
[
'name' => 'find',
'params' => 'int $id = 1',
'body' => "\treturn $id;",
'block' => [
'param' => 'int $id',
'return' => 'User',
],
]
The block map becomes PHPDoc tags. Array tag values are joined with commas. The body defaults to a comment placeholder when omitted.
__toString
public function __toString(): string
Returns generated PHP source. Rendering is repeatable: calling it more than once does not duplicate configured methods.
Example output shape:
namespace App\Controller; class UserController extends BaseController{ /** * @return array */ public function index(){ return []; } }
Parser
Strukt\Annotation\Parser reads single-line annotations from the class, property,
and method docblocks of a reflection target. It recognises @Name,
@Name(item), @Name(a, b, c), and @Name(key=value, key=value) forms.
Constructor
public function __construct(\ReflectionClass $reflection)
Parsing happens on construction.
getNotes
public function getNotes(): array
Returns a class_name key plus optional class, properties, and methods
keys holding the per-member annotations.
/** * @Name(worker) * @Role(lead) */ final class Worker { /** @Type(full) */ private string $name = 'strukt'; /** * @Route(/ping) * @Method(GET) */ public function ping(): string { return 'pong'; } } $notes = (new Strukt\Annotation\Parser(new ReflectionClass(Worker::class)))->getNotes(); // [ // 'class_name' => Worker::class, // 'class' => [ // 'Name' => ['name' => 'Name', 'item' => 'worker'], // 'Role' => ['name' => 'Role', 'item' => 'lead'], // ], // 'properties' => ['name' => ['Type' => ['name' => 'Type', 'item' => 'full']]], // 'methods' => ['ping' => [ // 'Route' => ['name' => 'Route', 'item' => '/ping'], // 'Method' => ['name' => 'Method', 'item' => 'GET'], // ]], // ]
Repeated names are merged into one entry whose items list holds every value.
Fixtures
The fixtures directory contains a small Payroll application and Contract abstract classes for generator experiments. It is included through Composer's development autoload configuration and is not part of the runtime package.
Performance and safety
- Template rendering uses one regex pass per directive type.
- Missing placeholders do not emit PHP notices.
- Annotation parsing avoids repeated string-wrapper allocation.
- ClassBuilder does not mutate its configured method list while rendering.
- Generated blocks are indented in one pass.
- Invalid class-builder configuration fails early with a clear exception.
Development
Install development dependencies and run the Pest suite:
composer update
composer test
Validate package metadata:
composer validate --no-check-publish
The test suite covers templates, loops, conditions, safe missing values, docblock round-tripping, annotation parsing, class generation, repeatable rendering, and helper registration.
License
Strukt Generator is released under the MIT license.