infocyph/phpforge
PHPForge from infocyph is a lightweight PHP scaffolding and code generation toolkit for forging boilerplate classes, files, and project structures from simple commands or templates, helping streamline everyday development workflows.
PHPForge is a dev-only tooling bundle — treat it as a require-dev dependency, never something that ships in a production image. The pitch is that instead of hand-assembling PHPStan + Rector + a style fixer + a benchmark harness + release scripts in every repo, you pull one package that carries opinionated, pre-wired configs and binaries.
Minimal steps:
composer require --dev infocyph/phpforge
# 1. See what entry points it actually exposes
ls vendor/bin/
composer show infocyph/phpforge # check deps + version constraints it drags in
# 2. Read the shipped config stubs before running anything
ls vendor/infocyph/phpforge
Then, in order, look at:
vendor/bin/ — the binary names. This package's whole value proposition is invoked through these; if it only ships config files and no binaries, the integration story is "copy the configs," which changes everything below.src/, tests/, build/. A Laravel app uses app/, database/, routes/, config/.composer.json → scripts — whether the package expects to be driven via composer forge:*-style aliases or direct vendor/bin calls.First use case: don't start with Rector on a live Laravel app. Start with the read-only, non-destructive leg of the toolchain — static analysis and a style check in dry-run mode — on app/ only, with a baseline. That gives you a number ("we have 340 existing PHPStan errors") before you change a single line, and it proves the configs load correctly under your Laravel version and PHP version. Refactoring and release tooling come after the gate is green and enforced in CI.
Don't let developers memorize vendor/bin paths plus long flag lists. Wrap each forge capability in a named script so local and CI runs are identical:
"scripts": {
"qa": ["@qa:analyse", "@qa:style"],
"qa:analyse": "phpstan analyse --memory-limit=1G",
"qa:style": "php-cs-fixer fix --dry-run --diff",
"qa:refactor": "rector process --dry-run",
"qa:bench": "phpbench run --report=default"
},
"scripts-aliases": { "qa": [] }
This is the highest-leverage thing you do with a shared tooling package: the package owns the defaults, your composer.json owns the entry points, and CI just calls composer qa. If you later drop PHPForge for a hand-rolled setup, only the configs move — nobody's muscle memory breaks.
Shared configs assume a clean project. Real Laravel apps are not clean. Generate baselines on the first run and commit them:
vendor/bin/phpstan analyse app --generate-baseline
Then ratchet: add new code without new baseline entries, and shrink the baseline file deliberately in dedicated PRs. Never let "regenerate the baseline" become a reflex — that's how a quality gate quietly becomes a no-op. For Rector, the equivalent discipline is --dry-run in CI and apply changes in small, reviewable batches per rule set (start with deadCode, codeQuality, only then typeDeclarations).
If the package ships a base config, use its include mechanism rather than copying it into your repo — otherwise you inherit the maintenance burden with none of the upstream fixes:
# phpstan.neon
includes:
- vendor/infocyph/phpforge/config/phpstan-base.neon # confirm real path
- phpstan-baseline.neon
parameters:
level: 6
paths: [app, database, routes, config]
excludePaths:
- app/Console/Kernel.php # legacy Laravel scaffolding
- '*/Migrations/*'
The Laravel-specific wrinkle: Larastan vs. plain PHPStan. If the base config targets vanilla PHPStan, you'll get hundreds of false positives on Eloquent magic (Model::where, facades, container bindings). Either the package needs to compose with larastan/larastan, or you keep PHPForge's static-analysis leg off Laravel app code and use it only on framework-agnostic packages/micro-libraries inside the monorepo. Verify this in the first hour — it's the make-or-break integration point for a Laravel developer.
- run: composer install --no-interaction --prefer-dist
- run: composer qa # style + static analysis, dry-run
- run: composer qa:refactor # rector --dry-run, must produce zero diffs
Cache vendor/ and the analysis result cache keyed on the composer lock hash. Keep the refactor job advisory (non-blocking, artifact diff) for the first few sprints — a blocking Rector job on day one will make everyone hate the tool.
The benchmark leg is where most teams get no value, because they benchmark nothing in particular. Use it narrowly: a query-builder hot path, a serialization routine, a pricing/eligibility calculation. In Laravel terms, benchmark the service layer with container booted once, not full HTTP round-trips (that's what artisan serve + a load tester is for). Commit results and compare across PRs — relative regression, not absolute numbers, is the signal.
If you have a packages/* monorepo of shared Laravel libraries, PHPForge's release leg (version bump, changelog, tag) is genuinely useful — it's the repetitive part nobody automates until it bites. Keep it out of the application repo entirely; apps deploy from a commit, packages get tagged.
Dependency collision is the number-one risk. A tooling bundle pins PHPStan/PHPUnit/CS-fixer/Rector versions transitively. Laravel projects usually also want larastan and laravel/pint. Expect composer update conflicts where Larastan requires phpstan/phpstan ^1.11 and PHPForge pulls ^2.0 (or vice versa). Diagnose with composer why phpstan/phpstan and composer why-not phpstan/phpstan 2.x before you fight the config — the config is usually innocent. If it collides, install PHPForge in a separate Composer context (its own directory, run against your codebase by path) rather than forcing a resolution.
The package is a single-maintainer, single-star project. That's not disqualifying for dev tooling — it can't take down production — but it is a reason to: pin an exact version ("infocyph/phpforge": "1.2.3"), never rely on it transitively, and make sure composer qa degrades gracefully if the package is removed. Budget an hour to read the shipped configs so you can fork them into your repo if upstream goes quiet.
Path assumptions break silently. Shared configs reference src/ and tests/. In Laravel, analysis silently covers nothing if paths doesn't exist — PHPStan will happily report "no errors" on an empty path list in some configurations. Always confirm coverage: introduce a deliberate error in app/ and verify it's caught. Do this once per config change.
Pint and php-cs-fixer will fight. If the bundle ships a CS fixer and you also run Pint, you get conflicting rules and a ping-pong diff. Pick one as authoritative — usually Pint in a Laravel app — and disable or exclude the package's style leg.
Exclude what isn't yours. vendor/, storage/, bootstrap/cache/, public/build/, generated IDE-helper files, and *.blade.php from static analysis. Analysis time on a mid-size Laravel app jumps from seconds to minutes the moment storage/framework/views gets scanned.
Baselines rot. A committed baseline is a debt ledger, not a fix. Review baseline diffs in PRs the same way you review migrations — if a PR grows the baseline, ask why.
--memory-limit is not optional. PHPStan on a Laravel app hits the default memory ceiling routinely. Bake --memory-limit=1G (or -1 in CI) into the composer script so nobody debugs a spurious fatal error.
Never install it in production. composer install --no-dev --optimize-autoloader in the deploy image; ideally also a composer check-platform-reqs step. Tooling packages that slip into a release image bloat it and occasionally expose console entry points you don't want.
Extension points to check first: whether the configs are include-able (good) or meant to be copied (fragile), whether binaries accept a --config override (essential for monorepos with per-package rules), and whether the benchmark/release legs are decoupled from the analysis legs — so you can adopt one without the other. If everything is a single monolithic "run the forge" binary with no per-leg opt-out, that's a real integration cost for a Laravel team and worth knowing before you build CI around it.
How can I help you explore Laravel packages today?