@ihelpee/prumo-plugin-php-laravel
v0.17.1
Published
PHP/Laravel plugin for Prumo: guarded mutation routes, FormRequest discipline, parameterized SQL, and oracle isolation invariants.
Readme
@ihelpee/prumo-plugin-php-laravel
PHP/Laravel invariants for Prumo. It analyzes, never executes: the PHP subprocess only tokenizes with token_get_all (stdlib) and serializes the canonical AST into JSON — not a single line of the analyzed code runs, and the gate never touches the target project's vendor/.
Installation
Peer extension — install the gate and the plugin at the same @ihelpee/* version:
npm install -D @ihelpee/prumo @ihelpee/prumo-plugin-php-laravelRequires php >= 8.1 on the gate host (not in the analyzed project). Without php, the gate reports php-runtime-missing (warn) instead of pretending it audited.
Usage
// prumo.config.ts do consumidor Laravel
import { defineConfig } from '@ihelpee/prumo';
import { phpLaravel } from '@ihelpee/prumo-plugin-php-laravel';
export default defineConfig({
layout: {
src: ['app'],
oracle: ['tests/Oracle'],
contract: ['.prumo/contract'],
invariants: ['.prumo/invariants'],
intent: ['.prumo/intent'],
waivers: ['.prumo/waivers'],
},
invariants: [phpLaravel({
oracleDirs: ['tests/Oracle'], // oracle convention (default: tests/Oracle, tests/Feature/Oracle, oracle)
authSymbols: ['2fa'], // R2.2: EXTRA symbols that count as auth (exact or `*` glob)
publicSymbols: ['public'], // R2.3: opt-in convention for an intentionally public route
srcDirs: ['app'], // R3.2: production prefixes for the reverse boundary
testNamespaces: ['Tests'], // R3.2: namespaces that are test code
})],
testFilePatterns: ['tests/**/*.php', 'tests/**/*Test.php', 'tests/**/*_test.php'],
});Invariants
| Rule | Sev | Catches | Does not catch |
|---|---|---|---|
| mutation-routes-guarded | error | Route::post/put/patch/delete without auth on the route, on the nested group, or on the controller; throttle:api does NOT count as auth | global middleware of the web/api stack (read bootstrap/app.php/Kernel yourself); GET |
| form-request-validated | error | raw Request with all()/input()/json() without validate(); raw payload + unguard() | inline $request->validate([...]); FormRequest with rules() |
| form-request-hollow | warn | FormRequest without a filled-in rules() | — |
| no-raw-queries | error | interpolated DB::raw/whereRaw/selectRaw/DB::select... or without bindings | raw SQL with ?/named bindings |
| oracle-no-src | error | use App\... or service-locator app(FQCN::class)/resolve/->make/Container::make\|get inside oracleDirs (anti-bypass: a dynamic import is an import) | entrypoint (bootstrap/app.php) |
| src-no-oracle | error | app/** with use Tests\... (test fixtures leaking into production) | — |
| oracle-no-auth-forge | error | withoutMiddleware()/actingAs() in the oracle | real login via the HTTP kernel (waiver for the legitimate case) |
| oracle-no-container-fake | error | mock/partialMock/swap/fake/Container::set in the oracle | — |
| test-mock-only | warn | tests with only expects/assertCalled and no domain assertion | — |
| php-parse-error | error | a file the tokenizer could not read (invalid syntax detected via TOKEN_PARSE) | — |
| php-runtime-missing | warn | php missing on the host | — |
Why a PHP subprocess instead of nikic/php-parser?
npx prumo gate in a Laravel repo must work without composer install of the parser. The php/ast_extractor.php script is stdlib-only and embedded in the npm package (files: ["php"]). nikic/php-parser packaged into the plugin is the declared upgrade path for the fix/prune phase — never the consumer's vendor/. The law lives in the Node core; PHP is just a parser.
License & Brand
Copyright © 2026 IHELPEE TECNOLOGIA LTDA (https://ihelpee.com). All rights reserved. Created and authored by Alex Gotardi.
Proprietary software, © IHELPEE TECNOLOGIA LTDA. From version 0.17.0, use is governed by the Prumo Software License; earlier versions keep the license they were published with.
The "Prumo" name and logo are trademarks of IHELPEE TECNOLOGIA LTDA and are not licensed under the software license.
