ajthinking/php-file-manipulator
Manipulate PHP files programmatically with a clean, fluent API. Read, modify, and rewrite classes, methods, properties, and namespaces without fragile regex, making code generation and refactoring safe and predictable.
Archetype is an AST-based PHP code inspection and transformation package. In day-to-day Laravel work, treat it as a tool for scripted refactors, code generation, and structural audits — not as a runtime dependency in application requests.
For 2.x, start on PHP 8.1–8.4 and Laravel 10–13 with nikic/php-parser ^5. If you are on older Laravel/PHP, 1.x remains the compatible line: PHP 7.4–8.1, Laravel 6–9, php-parser ^4.11.
Minimal first steps:
composer require ajthinking/archetype
composer show nikic/php-parser
commands_root and controllers_root.Archetype\LaravelFile::models(), controllers(), and serviceProviders() — these are the intended entry points for scanning conventional Laravel structures.A good first use case is an audit script: list all models, find classes with a given method or property, or report throw expressions. Once that is reliable, move to controlled mutation on a single file, then bulk refactors.
Treat 2.0.1 as the first 2.x release with a credible support matrix. 2.0.0 may have worked, but 2.0.1 is the first release actually verified across current PHP/Laravel combinations.
Use Archetype in three main modes:
Read-only inspection
Load files with the file/string entry points and query for node types such as classes, methods, properties, and throw expressions. This is the safest way to learn how the package models your codebase before mutating anything.
Single-file mutation
Load one file, locate the target node, apply the change, assert the resulting AST, then save. Keep changes small and commit generated output so regressions are visible in diffs. This fits well for adding a method, property, interface, or trait to a known class.
Bulk refactor / generation commands
Wrap Archetype in an Artisan command or test helper. Enumerate files via LaravelFile helpers or your own file list, apply idempotent changes, and run a dry-run/diff mode before writing. This is the highest-leverage day-to-day use: enforcing conventions across many models/controllers, adding boilerplate, or migrating repeated patterns.
Laravel integration tips:
LaravelFile::models(), controllers(), and serviceProviders() when the project follows Laravel conventions. For non-standard layouts, pass explicit file lists and keep discovery logic in your own command.commands_root and controllers_root aligned with the real app paths. If you have an older published config, verify these values — 2.0.1 fixes the shipped defaults, but existing published configs are not automatically rewritten.getReflection() for class-level filtering, but expect it to skip files whose parent classes or traits cannot be autoloaded. In 2.0.1 that skip is safe; before, an Error could abort the whole query.->throw() queries normally on 2.0.1. It was broken in 2.0.0 because php-parser 5 moved Stmt\Throw_ to Expr\Throw_; 2.0.1 fixes the mapping and adds a regression test.For CI, run the same broad matrix the package now targets: at least one PHP 8.1 job and one current PHP job, with the Laravel versions you actually support. If you use Statamic, note that Statamic 5/6 resolve 2.0.1 and produce identical output to 2.0.0 across the paths Statamic exercises; Statamic 3.4/4 remain on 1.x.
nikic/php-parser to ^4.11, which conflicts with anything requiring php-parser 5, including Pest 3 and newer. If Composer refuses to resolve ajthinking/archetype, that constraint is usually why. No application code changes are required for the upgrade.->throw() node mapping, getReflection() catching Throwable, PHP 8.4 implicit-nullable deprecations, and the shipped config paths.commands_root and controllers_root previously both pointed at the controllers directory. If your config was published under an older version, update it manually or republish and merge carefully.getReflection() can return nothing for a discovered file. It now catches Throwable, so a model/controller/service provider with an unloadable parent or trait is skipped rather than killing the query. Do not assume every enumerated file produced a reflection.->throw() as fixed in 2.0.1 only. If you are pinned to 2.0.0, throw-expression queries are effectively broken; either upgrade or avoid that query path.fromFile() call, inspect the parsed nodes, then widen. Check composer show nikic/php-parser when node classes behave unexpectedly.How can I help you explore Laravel packages today?