Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Code Generator Utils Laravel Package

ocramius/code-generator-utils

A small toolkit of utilities that simplifies programmatic PHP code generation. It offers helpers for building class and method definitions, sanitizing identifiers, and producing unique names—handy when writing proxy generators, scaffolding tools, or anything that emits PHP source at runtime.

View on GitHub
Deep Wiki
Context7

Getting Started

What this package actually is. ocramius/code-generator-utils is not a Laravel package, a scaffolder, or an artisan make:* replacement. It is a thin, opinionated layer over nikic/php-parser that answers one question: "given a ReflectionClass, how do I produce a rewritten copy of that class — renamed, decorated, delegating — and get it loaded?" Everything it ships exists to support that loop: an inflector (original FQCN ⇄ generated FQCN), a set of visitors (AST rewrites), a generator strategy (eval it or write it to disk), a file locator (FQCN → path), and an autoloader (SPL hook that generates-and-loads on demand).

Install and pin. The repo is archived, so treat the dependency as frozen infrastructure:

composer require ocramius/code-generator-utils:^1.0 nikic/php-parser:^5.0

Pin the exact version in composer.json ("ocramius/code-generator-utils": "1.0.x") and add a note in your team docs. There will be no upstream fixes; if you hit a bug, you vendor a fork.

Where to look first, in this order:

  1. src/Inflector/ClassNameInflector.php + ClassNameInflectorInterface — the naming contract for everything else.
  2. src/GeneratorStrategy/EvaluatingGeneratorStrategy.php and FileWriterGeneratorStrategy.php — the two execution models (eval vs. write-to-disk).
  3. src/Visitor/ClassDelegatingGeneratorVisitor.php — the one that makes the package worth using (turns every public method into a forwarding call).
  4. src/Autoloader/Autoloader.php + src/FileLocator/FileLocator.php — how generated classes get found at runtime.
  5. The README's worked proxy example — it is the only end-to-end wiring documentation that exists.

First use case (the one that justifies the dependency). You have a heavyweight service — an SDK client, a report builder, a PDF renderer — that is expensive to construct but cheap to call. You want a lazy-loading delegation proxy so the container can hand out an instance immediately:

// config/codegen.php
return [
    'namespace' => 'App\\Generated',
    'path'      => storage_path('framework/codegen'),
];
// app/Providers/CodeGenerationServiceProvider.php
use CodeGenerationUtils\Inflector\ClassNameInflector;
use CodeGenerationUtils\Inflector\ClassNameInflectorInterface;

public function register(): void
{
    $this->app->singleton(ClassNameInflectorInterface::class, fn () => new ClassNameInflector(
        config('codegen.namespace')
    ));
}

Then a ProxyFactory service resolves the inflected class name, checks whether the file exists, and — if not — reflects the original, parses its source file, runs the visitor chain, and hands the result to the configured strategy. From a controller or job, nothing changes: app(ReportBuilderProxy::class)->build($payload) looks like a normal call; the real object is only instantiated on the first method invocation.

Signature note: constructor and generate() shapes have shifted between 0.x and 1.x releases. Treat the snippets here as the shape of the wiring and confirm against the tag you pinned. Budget an afternoon for the first end-to-end proxy.

Implementation Patterns

1. Pre-generate at deploy time, never at request time. The single most important pattern. Add an artisan command that walks a configured list of proxied classes and runs the generator once, writing files into storage/framework/codegen:

final class GenerateProxiesCommand extends Command
{
    protected $signature = 'codegen:proxies {--force : Regenerate existing files}';

    public function handle(ProxyFactory $factory): int
    {
        foreach (config('codegen.proxied') as $abstract => $concrete) {
            $path = $factory->generate($concrete, force: $this->option('force'));
            $this->line("generated {$path}");
        }

        return self::SUCCESS;
    }
}

Hook it into your deploy script after composer install and before php artisan optimize. This eliminates write races, permission problems, and cold-start latency in one move.

2. Register the SPL autoloader exactly once, in a provider's register(). Generated classes are invisible to Composer's classmap. The package's Autoloader closes that gap:

public function register(): void
{
    if (app()->runningInConsole() && !app()->runningUnitTests()) {
        return; // console commands generate explicitly
    }

    spl_autoload_register(new Autoloader(
        new FileLocator(new FileWriterGeneratorStrategy(config('codegen.path')))
    ));
}

Keep it in register() (not boot()) so it is active before any other provider resolves a proxied binding.

3. Bind the strategy by environment. EvaluatingGeneratorStrategy (eval) for tests — no filesystem, no cleanup, no stale-file confusion. FileWriterGeneratorStrategy for production — opcache-friendly, inspectable, debuggable. Swap in a test service provider override or in TestCase::setUp() via $this->app->instance(GeneratorStrategyInterface::class, ...).

4. Compose the visitor chain deliberately; order matters. The canonical order is: resolve self/static/parent → rename the class → rewrite type declarations → add delegation. A ClassRenamingVisitor that runs before ReflectionClassNameVisitor will leave dangling references to the original FQCN inside method bodies, and you will only notice at runtime with a Class not found. Add your own NodeVisitorAbstract implementations at the end of the chain, not the beginning.

5. Decorate through the container, not through inheritance. Use $this->app->extend(SomeContract::class, ...) or a contextual binding to return the proxy. This keeps the proxy an implementation detail and means tests can bypass it entirely by binding a real or fake instance. It also sidesteps the final-class problem (below) because you proxy the interface, not the class, when the concrete is sealed.

6. Cache-busting with a custom inflector. When the source class changes, a stale generated file will happily be loaded. Either key the inflected name on a content hash (App\Generated\v1_<hash>\...) by implementing ClassNameInflectorInterface yourself, or regenerate unconditionally during deploy and clear the codegen directory in optimize:clear. The first is safer under Octane.

7. Testing. Write one test that asserts the generated source compiles: pretty-print the AST to a string, write to a temp file, run php -l (or simply eval inside a try). Assert on the generated text, not on the generated object's type — the type is guaranteed by the inflector, the behavior is not.

Gotchas and Tips

Final classes and final methods cannot be proxied. ClassDelegatingGeneratorVisitor works by overriding. A final public method stays final, and a final class cannot be extended at all. If your target SDK ships sealed classes, this package cannot help — decorate via an interface and the container instead. Check ReflectionMethod::isFinal() on your target before committing to the approach.

eval() is a debugging tax. With EvaluatingGeneratorStrategy, stack traces point at eval()'d code with no file, no line numbers, no coverage mapping, and Xdebug will complain. It also fails outright when eval is in disable_functions (some shared hosts, some hardened containers). Use it in tests only.

Opcache and Octane. Under Octane, workers boot once: a spl_autoload_register in register() runs per-worker, which is what you want. But if a generated file is rewritten while a worker holds the old bytecode, you serve stale behavior until the worker recycles. Either generate strictly at deploy time, or call opcache_invalidate($path, true) after writing. Never use EvaluatingGeneratorStrategy in a long-lived worker — you will re-parse and re-eval per request and leak memory.

Write races and permissions. Two concurrent requests hitting a missing proxy will both generate and both write the same file. Write to tempnam() and rename() atomically, or (better) follow pattern #1 and never generate on the hot path. In containerized deploys, storage/framework/codegen must be writable at build time if you pre-generate — chown it in the Dockerfile, not in an entrypoint that runs after the build.

Reflection gaps. Attributes, promoted constructor parameters, readonly properties, intersection types, and enums are frequently lossy in generated output. Generated code for internal PHP classes (e.g. proxying ArrayObject, PDO) is especially fragile — internal method reflection sometimes omits default values and tentative return types. Restrict yourself to userland classes you control the source of.

Composer's classmap-authoritative and opcache.preload don't know about generated files. The former is fine (Composer returns false and the next registered autoloader runs), but preloading is not — a preloaded class must exist before the process starts. If you preload, generate before the preload script runs.

optimize:clear doesn't touch your codegen directory. Wire it up explicitly:

Event::listen(\Illuminate\Foundation\Console\OptimizeClearCommand::class, fn () => File::cleanDirectory(config('codegen.path')));

Otherwise stale proxies survive a "clean" deploy and you will spend an hour debugging a class that no longer exists in source.

Debug by dumping the AST and the source. Before blaming the visitors, (new NodeDumper())->dump($ast) and compare against the original. Then pretty-print to a file and run php -l. Nine times out of ten the failure is a visitor that did not handle a node type, not the strategy or the autoloader.

Extension points, in order of usefulness: (a) a custom NodeVisitorAbstract for domain-specific rewrites — this is where the package earns its keep, since php-parser alone gives you no reflection-aware helpers; (b) a custom ClassNameInflectorInterface for hashing/versioning; (c) a custom GeneratorStrategyInterface that writes to a non-local disk or logs generated code for review. All three are small, stable interfaces — safe to implement against even though the package is archived.

And the honest scoping note: Laravel already solves lazy loading (Eloquent), decoration (Container::extend), and test doubles (Mockery/Pest). Reach for this package only when you genuinely need a class-level proxy of a concrete class — a third-party SDK client with a slow constructor, or a facade over an object you don't own. If you find yourself generating proxies for your own application code, restructure the code instead.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons